diff --git a/docs/blocks.md b/docs/blocks.md index eac0702..4a0e0e0 100644 --- a/docs/blocks.md +++ b/docs/blocks.md @@ -46,6 +46,10 @@ modules/blocks/sops/docs/default.md modules/blocks/ssl/docs/default.md ``` +```{=include=} chapters html:into-file=//blocks-nginx.html +modules/blocks/nginx/docs/default.md +``` + ## Introspection {#blocks-category-introspection} ```{=include=} chapters html:into-file=//blocks-monitoring.html diff --git a/docs/redirects.json b/docs/redirects.json index 6ff0db9..21414a9 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -314,9 +314,6 @@ "blocks-authelia-options-shb.authelia.subdomain": [ "blocks-authelia.html#blocks-authelia-options-shb.authelia.subdomain" ], - "blocks-authelia-shb-forward-auth": [ - "blocks-authelia.html#blocks-authelia-shb-forward-auth" - ], "blocks-authelia-shb-oidc": [ "blocks-authelia.html#blocks-authelia-shb-oidc" ], @@ -1139,6 +1136,78 @@ "blocks-monitoring-usage-smtp": [ "blocks-monitoring.html#blocks-monitoring-usage-smtp" ], + "blocks-nginx": [ + "blocks-nginx.html#blocks-nginx" + ], + "blocks-nginx-options": [ + "blocks-nginx.html#blocks-nginx-options" + ], + "blocks-nginx-options-shb.nginx.accessLog": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.accessLog" + ], + "blocks-nginx-options-shb.nginx.debugLog": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.debugLog" + ], + "blocks-nginx-options-shb.nginx.vhosts": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts" + ], + "blocks-nginx-options-shb.nginx.vhosts._.authEndpoint": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.authEndpoint" + ], + "blocks-nginx-options-shb.nginx.vhosts._.autheliaRules": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.autheliaRules" + ], + "blocks-nginx-options-shb.nginx.vhosts._.domain": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.domain" + ], + "blocks-nginx-options-shb.nginx.vhosts._.extraConfig": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.extraConfig" + ], + "blocks-nginx-options-shb.nginx.vhosts._.ssl": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.ssl" + ], + "blocks-nginx-options-shb.nginx.vhosts._.ssl.paths": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.ssl.paths" + ], + "blocks-nginx-options-shb.nginx.vhosts._.ssl.paths.cert": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.ssl.paths.cert" + ], + "blocks-nginx-options-shb.nginx.vhosts._.ssl.paths.key": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.ssl.paths.key" + ], + "blocks-nginx-options-shb.nginx.vhosts._.ssl.systemdService": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.ssl.systemdService" + ], + "blocks-nginx-options-shb.nginx.vhosts._.subdomain": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.subdomain" + ], + "blocks-nginx-options-shb.nginx.vhosts._.upstream": [ + "blocks-nginx.html#blocks-nginx-options-shb.nginx.vhosts._.upstream" + ], + "blocks-nginx-usage": [ + "blocks-nginx.html#blocks-nginx-usage" + ], + "blocks-nginx-usage-accesslog": [ + "blocks-nginx.html#blocks-nginx-usage-accesslog" + ], + "blocks-nginx-usage-debuglog": [ + "blocks-nginx.html#blocks-nginx-usage-debuglog" + ], + "blocks-nginx-usage-extraconfig": [ + "blocks-nginx.html#blocks-nginx-usage-extraconfig" + ], + "blocks-nginx-usage-forwardauth": [ + "blocks-nginx.html#blocks-nginx-usage-forwardauth" + ], + "blocks-nginx-usage-shbforwardauth": [ + "blocks-nginx.html#blocks-nginx-usage-shbforwardauth" + ], + "blocks-nginx-usage-ssl": [ + "blocks-nginx.html#blocks-nginx-usage-ssl" + ], + "blocks-nginx-usage-upstream": [ + "blocks-nginx.html#blocks-nginx-usage-upstream" + ], "blocks-postgresql": [ "blocks-postgresql.html#blocks-postgresql" ], diff --git a/flake.nix b/flake.nix index 21302c2..7e3da84 100644 --- a/flake.nix +++ b/flake.nix @@ -99,6 +99,7 @@ }; "blocks/mitmdump" = ./modules/blocks/mitmdump.nix; "blocks/monitoring" = ./modules/blocks/monitoring.nix; + "blocks/nginx" = ./modules/blocks/nginx.nix; "blocks/postgresql" = ./modules/blocks/postgresql.nix; "blocks/restic" = ./modules/blocks/restic.nix; "blocks/sops" = ./modules/blocks/sops.nix; diff --git a/modules/blocks/authelia/docs/default.md b/modules/blocks/authelia/docs/default.md index 7d2bef0..ef41a50 100644 --- a/modules/blocks/authelia/docs/default.md +++ b/modules/blocks/authelia/docs/default.md @@ -8,9 +8,10 @@ This block sets up an [Authelia][] service for Single-Sign On integration. Compared to the upstream nixpkgs module, this module is tightly integrated with SHB which allows easy configuration of SSO with [OIDC integration](#blocks-authelia-shb-oidc) -or with [forward auth integration](#blocks-authelia-shb-forward-auth) as well as some extensive [troubleshooting](#blocks-authelia-troubleshooting) features. +Note that forward authentication is configured with the [nginx block](blocks-nginx.html#blocks-nginx-usage-shbforwardauth). + ## Global Setup {#blocks-authelia-global-setup} Authelia cannot work without SSL and LDAP. @@ -191,40 +192,9 @@ Inspiration can be taken from SelfHostBlocks' source code. To access the UI, we will need to create an `open-webui_user` and `open-webui_admin` LDAP group and assign our user to it. -## SHB Forward Auth {#blocks-authelia-shb-forward-auth} - -For services provided by SelfHostBlocks that do not handle [OIDC integration][OIDC], -this block can provide [forward authentication][] which still allows the service to be protected by Authelia. - -The user could still be required to authenticate to the service itself, -although some services can automatically users authorized by Authelia. - -[forward authentication]: https://doc.traefik.io/traefik/middlewares/http/forwardauth/ - -Integrating with this block is done with the following code: - -```nix -shb..authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; -``` - ## Forward Auth {#blocks-authelia-forward-auth} -To integrate a service that does not handle OIDC integration -and which is not provided by SelfHostBlocks with this Authelia block, -the necessary configuration is: - -```nix -shb.nginx.vhosts = [ - { - subdomain = ""; - domain = "example.com"; - ssl = config.shb.certs.certs.letsencrypt."example.com"; - upstream = "http://127.0.0.1:${toString config.services..port}/"; - } -]; -``` - -This configuration assumes usage of the [SSL block][]. +Forward authentication is provided by the [nginx block](blocks-nginx.html#blocks-nginx-usage-ssl). ## Troubleshooting {#blocks-authelia-troubleshooting} diff --git a/modules/blocks/nginx.nix b/modules/blocks/nginx.nix index a648869..15af0a5 100644 --- a/modules/blocks/nginx.nix +++ b/modules/blocks/nginx.nix @@ -48,10 +48,34 @@ let default = [ ]; description = "Authelia rule configuration"; example = lib.literalExpression '' - [{ - policy = "two_factor"; - subject = ["group:service_user"]; - }]''; + [ + # Protect /admin endpoint with 2FA + # and only allow access to admin users. + { + domain = "myapp.example.com"; + policy = "two_factor"; + subject = [ "group:service_admin" ]; + resources = [ + "^/admin" + ]; + } + # Leave /api endpoint open - assumes an API key is used to protect it. + { + domain = "myapp.example.com"; + policy = "bypass"; + resources = [ + "^/api" + ]; + }, + # Protect rest of app with 1FA + # and allow access to normal and admin users. + { + domain = "myapp.example.com"; + policy = "one_factor"; + subject = ["group:service_user"]; + }, + ] + ''; }; extraConfig = lib.mkOption { diff --git a/modules/blocks/nginx/docs/default.md b/modules/blocks/nginx/docs/default.md new file mode 100644 index 0000000..c864ee6 --- /dev/null +++ b/modules/blocks/nginx/docs/default.md @@ -0,0 +1,208 @@ +# Nginx Block {#blocks-nginx} + +Defined in [`/modules/blocks/nginx.nix`](@REPO@/modules/blocks/nginx.nix). + +This block sets up a [Nginx](https://nginx.org/) instance. + +It complements the upstream nixpkgs with some authentication and debugging improvements as shows in the Usage section. + +## Usage {#blocks-nginx-usage} + +### Access Logging {#blocks-nginx-usage-accesslog} + +JSON access logging is enabled with the [`shb.nginx.accessLog`](#blocks-nginx-options-shb.nginx.accessLog) option: + +```nix +{ + shb.nginx.accessLog = true; +} +``` + +Looking at the systemd logs (`journalctl -fu nginx`) will show for example: + +```json +nginx[969]: server nginx: + { + "remote_addr":"192.168.1.1", + "remote_user":"-", + "time_local":"29/Dec/2025:14:22:41 +0000", + "request":"POST /api/firstfactor HTTP/2.0", + "request_length":"264", + "server_name":"auth_example_com", + "status":"200", + "bytes_sent":"855", + "body_bytes_sent":"60", + "referrer":"-", + "user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0", + "gzip_ration":"-", + "post":"{\x22username\x22:\x22charlie\x22,\x22password\x22:\x22CharliePassword\x22,\x22keepMeLoggedIn\x22:false,\x22targetURL\x22:\x22https://f.example.com/\x22,\x22requestMethod\x22:null}", + "upstream_addr":"127.0.0.1:9091", + "upstream_status":"200", + "request_time":"0.873", + "upstream_response_time":"0.873", + "upstream_connect_time":"0.001", + "upstream_header_time":"0.872" + } +``` + +This _will_ log the body of POST queries so it should only be enabled for debug logging. + +### Debug Logging {#blocks-nginx-usage-debuglog} + +Debug logging is enabled with the [`shb.nginx.debugLog`](#blocks-nginx-options-shb.nginx.debugLog) option: + +```nix +{ + shb.nginx.debugLog = true; +} +``` + +If enabled, it sets: + +``` +error_log stderr warn; +``` + +### Virtual Host Upstream Proxy {#blocks-nginx-usage-upstream} + +Easy upstream proxy setup is done with the [`shb.nginx.vhosts.*.upstream`](#blocks-nginx-options-shb.nginx.vhosts._.upstream) option: + +```nix +{ + shb.nginx.vhosts = [ + { + domain = "example.com"; + subdomain = "mysubdomain"; + upstream = "http://127.0.0.1:9090"; + } + ]; +} +``` + +This will set also a few headers. +Some are shown here and others please see in the [nginx](@REPO@/modules/blocks/nginx.nix) module: + +- `Host` = `$host`; +- `X-Real-IP` = `$remote_addr`; +- `X-Forwarded-For` = `$proxy_add_x_forwarded_for`; +- `X-Forwarded-Proto` = `$scheme`; + +### Virtual Host SSL Generator Contract Integration {#blocks-nginx-usage-ssl} + +This module integrates with the [SSL Generator Contract](./contracts-ssl.html) +to setup HTTPs with the [`shb.nginx.vhosts.*.ssl`](#blocks-nginx-options-shb.nginx.vhosts._.ssl) option: + +```nix +{ + shb.nginx.vhosts = [ + { + domain = "example.com"; + subdomain = "mysubdomain"; + ssl = config.shb.certs.certs.letsencrypt.${domain};; + } + ]; + + shb.certs.certs.letsencrypt.${domain} = { + inherit domain; + }; +} +``` + +### Virtual Host SHB Forward Authentication {#blocks-nginx-usage-shbforwardauth} + +For services provided by SelfHostBlocks that do not handle [OIDC integration][OIDC], +this block can provide [forward authentication][] which still allows the service +to still be protected by an SSO server. + +[OIDC]: blocks-authelia.html#blocks-authelia-shb-oidc + +The user could still be required to authenticate to the service itself, +although some services can automatically users authorized by Authelia. + +[forward authentication]: https://doc.traefik.io/traefik/middlewares/http/forwardauth/ + +Integrating with this block is done with the following code: + +```nix +shb..authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; +``` + +### Virtual Host Forward Authentication {#blocks-nginx-usage-forwardauth} + +Forward authentication is when Nginx talks with the SSO service directly +and the user is authenticated before reaching the upstream application. + +The SSO service responds with the username, group and more information about the user. +This is then forwarded to the upstream application by Nginx. + +Note that _every_ request is authenticated this way with the SSO server +so it involves more hops than a direct [OIDC integration](blocks-authelia.html#blocks-authelia-shb-oidc). + +```nix +{ + shb.nginx.vhosts = [ + { + domain = "example.com"; + subdomain = "mysubdomain"; + authEndpoint = "authelia.example.com"; + autheliaRules = [ + [ + # Protect /admin endpoint with 2FA + # and only allow access to admin users. + { + domain = "myapp.example.com"; + policy = "two_factor"; + subject = [ "group:service_admin" ]; + resources = [ + "^/admin" + ]; + } + # Leave /api endpoint open - assumes an API key is used to protect it. + { + domain = "myapp.example.com"; + policy = "bypass"; + resources = [ + "^/api" + ]; + }, + # Protect rest of app with 1FA + # and allow access to normal and admin users. + { + domain = "myapp.example.com"; + policy = "one_factor"; + subject = ["group:service_user"]; + }, + ] + ]; + } + ]; +} +``` + +### Virtual Host Extra Config {#blocks-nginx-usage-extraconfig} + +To add extra configuration to a virtual host, +use the [`shb.nginx.vhosts.*.extraConfig`](#blocks-nginx-options-shb.nginx.vhosts._.extraConfig) option. +This can be used to add headers, for example: + +```nix +{ + shb.nginx.vhosts = [ + { + domain = "example.com"; + subdomain = "mysubdomain"; + extraConfig = '' + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; + ''; + } + ]; +} +``` + +## Options Reference {#blocks-nginx-options} + +```{=include=} options +id-prefix: blocks-nginx-options- +list-id: selfhostblocks-block-nginx-options +source: @OPTIONS_JSON@ +```