From 44465c37c222fd43ecf9235260ac69454f745d5e Mon Sep 17 00:00:00 2001 From: ibizaman Date: Sat, 2 Sep 2023 15:00:41 -0700 Subject: [PATCH] add sso with authelia for hledger --- README.md | 5 +++ modules/authelia.nix | 75 +++++++++++++++++++----------------- modules/hledger.nix | 91 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 42c7ffa..4c63b7f 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,8 @@ services. Also, the design will be extendable to allow users to add services not - [X] LDAP auth through `jellyfin_user` and `jellyfin_admin` LDAP groups. - [X] SSO auth. - [X] Backup support. +- [X] Hledger + - [ ] Export metrics to Prometheus. + - [X] LDAP auth through `hledger_user` LDAP group. + - [X] SSO auth. + - [ ] Backup support. diff --git a/modules/authelia.nix b/modules/authelia.nix index 1f29934..614cb10 100644 --- a/modules/authelia.nix +++ b/modules/authelia.nix @@ -226,42 +226,47 @@ in sslCertificate = "/var/lib/acme/${cfg.domain}/cert.pem"; sslCertificateKey = "/var/lib/acme/${cfg.domain}/key.pem"; forceSSL = true; - locations."/" = { - # Taken from https://matwick.ca/authelia-nginx-sso/ - extraConfig = '' - # Basic Proxy Config - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Original-URL $scheme://$http_host$request_uri; - proxy_set_header X-Original-Method $request_method; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Method $request_method; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Forwarded-Uri $request_uri; - proxy_set_header X-Forwarded-Ssl on; - proxy_set_header Connection ""; - proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead - proxy_redirect http:// $scheme://; - proxy_http_version 1.1; - proxy_cache_bypass $cookie_session; - proxy_no_cache $cookie_session; - proxy_buffers 64 256k; + # Taken from https://github.com/authelia/authelia/issues/178 + # TODO: merge with config from https://matwick.ca/authelia-nginx-sso/ + locations."/".extraConfig = '' + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; - # If behind reverse proxy, forwards the correct IP - set_real_ip_from 10.0.0.0/8; - set_real_ip_from 172.0.0.0/8; - set_real_ip_from 192.168.0.0/16; - set_real_ip_from fc00::/7; - real_ip_header X-Forwarded-For; - real_ip_recursive on; - ''; - proxyPass = - let - autheliaServerCfg = autheliaCfg.settings.server; - in - "http://${toString autheliaServerCfg.host}:${toString autheliaServerCfg.port}/"; - }; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_cache_bypass $http_upgrade; + + proxy_pass http://127.0.0.1:${toString autheliaCfg.settings.server.port}; + proxy_intercept_errors on; + if ($request_method !~ ^(POST)$){ + error_page 401 = /error/401; + error_page 403 = /error/403; + error_page 404 = /error/404; + } + ''; + + locations."/api/verify".extraConfig = '' + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; + + proxy_set_header Host $http_x_forwarded_host; + proxy_pass http://127.0.0.1:${toString autheliaCfg.settings.server.port}; + ''; }; services.redis.servers.authelia = { diff --git a/modules/hledger.nix b/modules/hledger.nix index 3db36ca..b520bce 100644 --- a/modules/hledger.nix +++ b/modules/hledger.nix @@ -33,12 +33,19 @@ in default = null; example = "192.168.1.1/24"; }; + + oidcEndpoint = lib.mkOption { + type = lib.types.str; + description = "OIDC endpoint for SSO"; + example = "https://authelia.example.com"; + }; }; config = lib.mkIf cfg.enable { services.hledger-web = { enable = true; - baseUrl = fqdn; + # Must be empty otherwise it repeats the fqdn, we get something like https://${fqdn}/${fqdn}/ + baseUrl = ""; stateDir = "/var/lib/hledger"; journalFiles = ["hledger.journal"]; @@ -56,6 +63,15 @@ in ]; }; + systemd.services.hledger-web = { + # If the hledger.journal file does not exist, hledger-web refuses to start, so we create an + # empty one if it does not exist yet.. + preStart = '' + test -f /var/lib/hledger/hledger.journal || touch /var/lib/hledger/hledger.journal + ''; + serviceConfig.StateDirectory = "hledger"; + }; + services.nginx = { enable = true; @@ -64,14 +80,70 @@ in sslCertificate = "/var/lib/acme/${cfg.domain}/cert.pem"; sslCertificateKey = "/var/lib/acme/${cfg.domain}/key.pem"; - locations."/" = { - proxyPass = "http://${toString config.services.hledger-web.host}:${toString config.services.hledger-web.port}"; - # proxyWebsockets = true; + # Taken from https://github.com/authelia/authelia/issues/178 + locations."/".extraConfig = '' + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; - extraConfig = lib.mkIf (cfg.localNetworkIPRange != null) '' - allow ${cfg.localNetworkIPRange}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_cache_bypass $http_upgrade; + + auth_request /authelia; + auth_request_set $user $upstream_http_remote_user; + auth_request_set $groups $upstream_http_remote_groups; + proxy_set_header X-Forwarded-User $user; + proxy_set_header X-Forwarded-Groups $groups; + # TODO: Are those needed? + # auth_request_set $name $upstream_http_remote_name; + # auth_request_set $email $upstream_http_remote_email; + # proxy_set_header Remote-Name $name; + # proxy_set_header Remote-Email $email; + # TODO: Would be nice to have this working, I think. + # set $new_cookie $http_cookie; + # if ($http_cookie ~ "(.*)(?:^|;)\s*example\.com\.session\.id=[^;]+(.*)") { + # set $new_cookie $1$2; + # } + # proxy_set_header Cookie $new_cookie; + + auth_request_set $redirect $scheme://$http_host$request_uri; + error_page 401 =302 ${cfg.oidcEndpoint}?rd=$redirect; + error_page 403 = ${cfg.oidcEndpoint}/error/403; + + proxy_pass http://${toString config.services.hledger-web.host}:${toString config.services.hledger-web.port}; ''; - }; + + # Virtual endpoint created by nginx to forward auth requests. + locations."/authelia".extraConfig = '' + internal; + proxy_pass ${cfg.oidcEndpoint}/api/verify; + + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Original-URL $scheme://$host$request_uri; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + # TODO: Would be nice to be able to enable this. + # proxy_ssl_verify on; + # proxy_ssl_trusted_certificate "/etc/ssl/certs/DST_Root_CA_X3.pem"; + proxy_ssl_protocols TLSv1.2; + proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; + proxy_ssl_verify_depth 2; + proxy_ssl_server_name on; + ''; + }; }; @@ -81,15 +153,12 @@ in # policy = "bypass"; # resources = [ # "^/api.*" - # "^/auth/token.*" - # "^/.external_auth=." - # "^/service_worker.js" - # "^/static/.*" # ]; # } { domain = fqdn; policy = "two_factor"; + subject = ["group:hledger_user"]; } ];