From 7e11641fe7fb596b3d21b1cf0100c0ee034fb681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Schwarz=C3=A4ugl?= Date: Mon, 9 Jun 2025 21:20:40 +0200 Subject: [PATCH] feat: add initial oauth2-proxy and freshrss oidc --- SwarselSystems.org | 505 ++++++++++++++------- index.html | 707 +++++++++++++++++++----------- modules/nixos/server/freshrss.nix | 71 ++- modules/nixos/server/kanidm.nix | 428 +++++++++++------- secrets/winters/secrets.yaml | 11 +- 5 files changed, 1166 insertions(+), 556 deletions(-) diff --git a/SwarselSystems.org b/SwarselSystems.org index 01aed2f..a1395a8 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -7518,7 +7518,7 @@ Here we just define some aliases for rebuilding the system, and we allow some in } #+end_src -**** paperless +**** paperless (tika, gotenberg) :PROPERTIES: :CUSTOM_ID: h:89638fb5-0593-4420-9567-f85f0223e341 :END: @@ -8254,6 +8254,8 @@ FreshRSS is a more 'classical' RSS aggregator that I can just host as a distinct It serves both a Greader API at https://signpost.swarsel.win/api/greader.php, as well as a Fever API at https://signpost.swarsel.win/api/fever.php. +I am using this with CapyReader on my phone, set it up as a FreshRSS account with Server URL =https://signpost.swarsel.win/api/greader.php + #+begin_src nix :tangle modules/nixos/server/freshrss.nix { lib, config, ... }: { @@ -8268,24 +8270,91 @@ It serves both a Greader API at https://signpost.swarsel.win/api/greader.php, as users.groups.freshrss = { }; - sops.secrets.fresh = { owner = "freshrss"; }; + sops = { + secrets = { + fresh = { owner = "freshrss"; }; + "kanidm-freshrss-client" = { owner = "freshrss"; group = "freshrss"; mode = "0440"; }; + "oidc-crypto-key" = { owner = "freshrss"; group = "freshrss"; mode = "0440"; }; + }; + + # templates = { + # "freshrss-env" = { + # content = '' + # DATA_PATH=${config.services.freshrss.dataDir} + # OIDC_ENABLED=1 + # OIDC_PROVIDER_METADATA_URL=https://sso.swarsel.win/.well-known/openid-configuration + # OIDC_CLIENT_ID=freshrss + # OIDC_CLIENT_SECRET=${config.sops.placeholder.kanidm-freshrss-client} + # OIDC_CLIENT_CRYPTO_KEY=${config.sops.placeholder.oidc-crypto-key} + # OIDC_REMOTE_USER_CLAIM=preferred_username + # OIDC_SCOPES=openid groups email profile + # OIDC_X_FORWARDED_HEADERS=X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto + # ''; + # owner = "freshrss"; + # group = "freshrss"; + # mode = "0440"; + # }; + # }; + }; services.freshrss = { enable = true; virtualHost = "signpost.swarsel.win"; baseUrl = "https://signpost.swarsel.win"; - # authType = "none"; + authType = "none"; dataDir = "/Vault/data/tt-rss"; defaultUser = "Swarsel"; passwordFile = config.sops.secrets.fresh.path; }; + # systemd.services.freshrss-config.serviceConfig.EnvironmentFile = [ + # config.sops.templates.freshrss-env.path + # ]; + services.nginx = { virtualHosts = { "signpost.swarsel.win" = { enableACME = true; forceSSL = true; acmeRoot = null; + locations = { + "/" = { + extraConfig = '' + auth_request /oauth2/auth; + error_page 401 = /oauth2/sign_in; + + # pass information via X-User and X-Email headers to backend, + # requires running with --set-xauthrequest flag (done by NixOS) + auth_request_set $user $upstream_http_x_auth_request_preferred_username; + # Set the email to our own domain in case user change their mail + auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@swarsel.win"; + proxy_set_header X-User $user; + proxy_set_header X-Email $email; + + # if you enabled --cookie-refresh, this is needed for it to work with auth_request + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + ''; + }; + "/oauth2/" = { + proxyPass = "http://oauth2-proxy"; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + ''; + }; + "= /oauth2/auth" = { + proxyPass = "http://oauth2-proxy/oauth2/auth"; + extraConfig = '' + internal; + + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; + }; }; }; }; @@ -8495,7 +8564,7 @@ It serves both a Greader API at https://signpost.swarsel.win/api/greader.php, as } #+end_src -**** kanidm +**** IDM (kanidm + oauth2-proxy) The forgejo configuration is a little broken and will show a 500 error when signing in through kanidm. However, when pressing back and refreshing the page, I am logged in. Currently I cannot be bothered to fix this. @@ -8509,7 +8578,9 @@ To get other URLs (token, etc.), use https:///oauth2/openid//oauth2/openid//oauth2/openid/ - + SwarselSystems: NixOS + Emacs Configuration @@ -263,9 +263,9 @@
  • 3.1.4.3. Home-manager only (default non-NixOS)
  • 3.1.4.4. ChaosTheatre (Demo Physical/VM)
  • @@ -305,7 +305,7 @@
  • 3.2.1.27. fhs
  • 3.2.1.28. swarsel-displaypower
  • 3.2.1.29. swarsel-mgba
  • -
  • 3.2.1.30. sshrm
  • +
  • 3.2.1.30. sshrm
  • 3.2.2. Overlays (additions, overrides, nixpkgs-stable)
  • @@ -313,28 +313,28 @@ @@ -379,7 +379,7 @@
  • 3.3.1.22.3. enable GVfs
  • 3.3.1.22.4. interception-tools: Make CAPS work as ESC/CTRL
  • 3.3.1.22.5. power-profiles-daemon
  • -
  • 3.3.1.22.6. SwayOSD
  • +
  • 3.3.1.22.6. SwayOSD
  • 3.3.1.23. Hardware compatibility settings (Yubikey, Ledger, Keyboards) - udev rules @@ -421,7 +421,7 @@
  • 3.3.2.14. matrix
  • 3.3.2.15. nextcloud
  • 3.3.2.16. immich
  • -
  • 3.3.2.17. paperless
  • +
  • 3.3.2.17. paperless (tika, gotenberg)
  • 3.3.2.18. transmission
  • 3.3.2.19. syncthing
  • 3.3.2.20. restic
  • @@ -431,7 +431,7 @@
  • 3.3.2.24. FreshRSS
  • 3.3.2.25. forgejo (git server)
  • 3.3.2.26. Anki Sync Server
  • -
  • 3.3.2.27. kanidm
  • +
  • 3.3.2.27. IDM (kanidm + oauth2-proxy)
  • 3.3.3. Darwin @@ -446,11 +446,11 @@
  • 3.3.4.3. VmWare
  • 3.3.4.4. Auto-login
  • 3.3.4.5. nswitch-rcm
  • -
  • 3.3.4.6. Framework
  • -
  • 3.3.4.7. AMD CPU
  • -
  • 3.3.4.8. AMD GPU
  • -
  • 3.3.4.9. Hibernation
  • -
  • 3.3.4.10. BTRFS
  • +
  • 3.3.4.6. Framework
  • +
  • 3.3.4.7. AMD CPU
  • +
  • 3.3.4.8. AMD GPU
  • +
  • 3.3.4.9. Hibernation
  • +
  • 3.3.4.10. BTRFS
  • 3.3.4.11. work
  • 3.3.4.12. Minimal Install
  • @@ -499,7 +499,7 @@
  • 3.4.1.29.1. gnome-keyring
  • 3.4.1.29.2. KDE Connect
  • 3.4.1.29.3. Mako
  • -
  • 3.4.1.29.4. SwayOSD
  • +
  • 3.4.1.29.4. SwayOSD
  • 3.4.1.29.5. yubikey-touch-detector
  • @@ -524,7 +524,7 @@ @@ -702,7 +702,7 @@ @@ -711,7 +711,7 @@

    -This file has 64380 words spanning 16957 lines and was last revised on 2025-06-09 19:11:36 +0200. +This file has 65085 words spanning 17158 lines and was last revised on 2025-06-10 00:08:59 +0200.

    @@ -764,7 +764,7 @@ This section defines my Emacs configuration. For a while, I considered to use ry

    -My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-06-09 19:11:36 +0200) +My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-06-10 00:08:59 +0200)

    @@ -2885,8 +2885,8 @@ This is just a demo host. It applies all the configuration found in the common p I also set the WLR_RENDERER_ALLOW_SOFTWARE=1 to allow this configuration to run in a virtualized environment. I also enable qemuGuest for a smoother experience when testing on QEMU.

    -
    -
    3.1.4.4.1. Main configuration
    +
    +
    3.1.4.4.1. Main configuration
    { self, inputs, config, pkgs, lib, primaryUser, ... }:
    @@ -2965,8 +2965,8 @@ in
     
    -
    -
    3.1.4.4.2. NixOS dummy options configuration
    +
    +
    3.1.4.4.2. NixOS dummy options configuration
    _:
    @@ -2976,8 +2976,8 @@ in
     
    -
    -
    3.1.4.4.3. home-manager dummy options configuration
    +
    +
    3.1.4.4.3. home-manager dummy options configuration
    _:
    @@ -4758,8 +4758,8 @@ appimageTools.wrapType2 {
     
    -
    -
    3.2.1.30. sshrm
    +
    +
    3.2.1.30. sshrm

    This programs simply runs ssh-keygen on the last host that I tried to ssh into. I need this frequently when working with cloud-init usually. @@ -4916,8 +4916,8 @@ in

    -
    -
    3.2.3.1.1. Personal
    +
    +
    3.2.3.1.1. Personal
    { lib, config, ... }:
    @@ -4984,8 +4984,8 @@ in
     
    -
    -
    3.2.3.1.2. Chaostheatre
    +
    +
    3.2.3.1.2. Chaostheatre
    { lib, config, ... }:
    @@ -5049,8 +5049,8 @@ in
     
    -
    -
    3.2.3.1.3. toto
    +
    +
    3.2.3.1.3. toto
    { lib, config, ... }:
    @@ -5082,8 +5082,8 @@ in
     
    -
    -
    3.2.3.1.4. Work
    +
    +
    3.2.3.1.4. Work
    { lib, config, ... }:
    @@ -5104,8 +5104,8 @@ in
     
    -
    -
    3.2.3.1.5. Framework
    +
    +
    3.2.3.1.5. Framework
    { lib, config, ... }:
    @@ -5126,8 +5126,8 @@ in
     
    -
    -
    3.2.3.1.6. AMD CPU
    +
    +
    3.2.3.1.6. AMD CPU
    { lib, config, ... }:
    @@ -5148,8 +5148,8 @@ in
     
    -
    -
    3.2.3.1.7. AMD GPU
    +
    +
    3.2.3.1.7. AMD GPU
    { lib, config, ... }:
    @@ -5170,8 +5170,8 @@ in
     
    -
    -
    3.2.3.1.8. Hibernation
    +
    +
    3.2.3.1.8. Hibernation
    { lib, config, ... }:
    @@ -5192,8 +5192,8 @@ in
     
    -
    -
    3.2.3.1.9. BTRFS
    +
    +
    3.2.3.1.9. BTRFS
    { lib, config, ... }:
    @@ -5214,8 +5214,8 @@ in
     
    -
    -
    3.2.3.1.10. Local Server
    +
    +
    3.2.3.1.10. Local Server
    { lib, config, ... }:
    @@ -5268,8 +5268,8 @@ in
     
    -
    -
    3.2.3.1.11. OCI Sync Server
    +
    +
    3.2.3.1.11. OCI Sync Server
    { lib, config, ... }:
    @@ -5326,8 +5326,8 @@ in
     
    -
    -
    3.2.3.2.1. Personal
    +
    +
    3.2.3.2.1. Personal
    { lib, config, ... }:
    @@ -5384,8 +5384,8 @@ in
     
    -
    -
    3.2.3.2.2. Chaostheatre
    +
    +
    3.2.3.2.2. Chaostheatre
    { lib, config, ... }:
    @@ -5437,8 +5437,8 @@ in
     
    -
    -
    3.2.3.2.3. toto
    +
    +
    3.2.3.2.3. toto
    { lib, config, ... }:
    @@ -5458,8 +5458,8 @@ in
     
    -
    -
    3.2.3.2.4. Work
    +
    +
    3.2.3.2.4. Work
    { lib, config, ... }:
    @@ -5479,8 +5479,8 @@ in
     
    -
    -
    3.2.3.2.5. Framework
    +
    +
    3.2.3.2.5. Framework
    { lib, config, ... }:
    @@ -5501,8 +5501,8 @@ in
     
    -
    -
    3.2.3.2.6. Darwin
    +
    +
    3.2.3.2.6. Darwin
    { lib, config, ... }:
    @@ -5520,8 +5520,8 @@ in
     
    -
    -
    3.2.3.2.7. Local Server
    +
    +
    3.2.3.2.7. Local Server
    { lib, config, ... }:
    @@ -7054,8 +7054,8 @@ Most of the time I am using power-saver, however, it is good to be
     
    -
    -
    3.3.1.22.6. SwayOSD
    +
    +
    3.3.1.22.6. SwayOSD
    { lib, pkgs, config, ... }:
    @@ -8812,7 +8812,7 @@ in
     
    -
    3.3.2.17. paperless
    +
    3.3.2.17. paperless (tika, gotenberg)

    This is my personal document management system. It automatically pulls documents from several sources, the only manual step for physical documents is to put them in my scanner and use email delivery. @@ -9561,6 +9561,10 @@ FreshRSS is a more 'classical' RSS aggregator that I can just host as a distinct It serves both a Greader API at https://signpost.swarsel.win/api/greader.php, as well as a Fever API at https://signpost.swarsel.win/api/fever.php.

    +

    +I am using this with CapyReader on my phone, set it up as a FreshRSS account with Server URL =https://signpost.swarsel.win/api/greader.php +

    +
    { lib, config, ... }:
     {
    @@ -9575,24 +9579,91 @@ It serves both a Greader API at 
     
    -
    -
    3.3.2.27. kanidm
    +
    -
    -
    3.3.4.6. Framework
    +
    +
    3.3.4.6. Framework

    This holds configuration that is specific to framework laptops. @@ -10269,8 +10472,8 @@ This holds configuration that is specific to framework laptops.

    -
    -
    3.3.4.7. AMD CPU
    +
    +
    3.3.4.7. AMD CPU
    { lib, config, ... }:
    @@ -10286,8 +10489,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.8. AMD GPU
    +
    +
    3.3.4.8. AMD GPU
    { lib, config, ... }:
    @@ -10309,8 +10512,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.9. Hibernation
    +
    +
    3.3.4.9. Hibernation
    { lib, config, ... }:
    @@ -10341,8 +10544,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.10. BTRFS
    +
    +
    3.3.4.10. BTRFS
    { lib, config, ... }:
    @@ -13290,8 +13493,8 @@ The `extraConfig` section here CANNOT be reindented. This has something to do wi
     
    -
    -
    3.4.1.29.4. SwayOSD
    +
    +
    3.4.1.29.4. SwayOSD
    { lib, config, ... }:
    @@ -14540,8 +14743,8 @@ in
     
    -
    -
    3.4.4.3. Framework
    +
    +
    3.4.4.3. Framework

    This holds configuration that is specific to framework laptops. @@ -18381,8 +18584,8 @@ autocmd DocStart vc-impimba-1.m.imp.ac.at/ui/webconsole mode ignore

    -
    -

    6.3. tridactyl theme

    +
    +

    6.3. tridactyl theme

    @@ -18879,7 +19082,7 @@ sync USER HOST:
     

    Author: Leon Schwarzäugl

    -

    Created: 2025-06-09 Mo 19:11

    +

    Created: 2025-06-10 Di 00:09

    Validate

    diff --git a/modules/nixos/server/freshrss.nix b/modules/nixos/server/freshrss.nix index 9687b0f..3f5c946 100644 --- a/modules/nixos/server/freshrss.nix +++ b/modules/nixos/server/freshrss.nix @@ -11,24 +11,91 @@ users.groups.freshrss = { }; - sops.secrets.fresh = { owner = "freshrss"; }; + sops = { + secrets = { + fresh = { owner = "freshrss"; }; + "kanidm-freshrss-client" = { owner = "freshrss"; group = "freshrss"; mode = "0440"; }; + "oidc-crypto-key" = { owner = "freshrss"; group = "freshrss"; mode = "0440"; }; + }; + + # templates = { + # "freshrss-env" = { + # content = '' + # DATA_PATH=${config.services.freshrss.dataDir} + # OIDC_ENABLED=1 + # OIDC_PROVIDER_METADATA_URL=https://sso.swarsel.win/.well-known/openid-configuration + # OIDC_CLIENT_ID=freshrss + # OIDC_CLIENT_SECRET=${config.sops.placeholder.kanidm-freshrss-client} + # OIDC_CLIENT_CRYPTO_KEY=${config.sops.placeholder.oidc-crypto-key} + # OIDC_REMOTE_USER_CLAIM=preferred_username + # OIDC_SCOPES=openid groups email profile + # OIDC_X_FORWARDED_HEADERS=X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto + # ''; + # owner = "freshrss"; + # group = "freshrss"; + # mode = "0440"; + # }; + # }; + }; services.freshrss = { enable = true; virtualHost = "signpost.swarsel.win"; baseUrl = "https://signpost.swarsel.win"; - # authType = "none"; + authType = "none"; dataDir = "/Vault/data/tt-rss"; defaultUser = "Swarsel"; passwordFile = config.sops.secrets.fresh.path; }; + # systemd.services.freshrss-config.serviceConfig.EnvironmentFile = [ + # config.sops.templates.freshrss-env.path + # ]; + services.nginx = { virtualHosts = { "signpost.swarsel.win" = { enableACME = true; forceSSL = true; acmeRoot = null; + locations = { + "/" = { + extraConfig = '' + auth_request /oauth2/auth; + error_page 401 = /oauth2/sign_in; + + # pass information via X-User and X-Email headers to backend, + # requires running with --set-xauthrequest flag (done by NixOS) + auth_request_set $user $upstream_http_x_auth_request_preferred_username; + # Set the email to our own domain in case user change their mail + auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@swarsel.win"; + proxy_set_header X-User $user; + proxy_set_header X-Email $email; + + # if you enabled --cookie-refresh, this is needed for it to work with auth_request + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + ''; + }; + "/oauth2/" = { + proxyPass = "http://oauth2-proxy"; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + ''; + }; + "= /oauth2/auth" = { + proxyPass = "http://oauth2-proxy/oauth2/auth"; + extraConfig = '' + internal; + + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; + }; }; }; }; diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index c2d60d0..4926573 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -2,7 +2,9 @@ let certsSopsFile = self + /secrets/certs/secrets.yaml; kanidmDomain = "sso.swarsel.win"; + oauth2ProxyDomain = "soauth.swarsel.win"; kanidmPort = 8300; + oauth2ProxyPort = 3004; in { options.swarselsystems.modules.server.kanidm = lib.mkEnableOption "enable kanidm on server"; @@ -15,164 +17,280 @@ in users.groups.kanidm = { }; - sops.secrets = { - "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-idm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-immich" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-paperless" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-forgejo" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-grafana" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-nextcloud" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + sops = { + secrets = { + "oauth2-cookie-secret" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; }; + "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-idm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-immich" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-paperless" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-forgejo" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-grafana" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-nextcloud" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-freshrss" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-oauth2-proxy" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + "kanidm-oauth2-proxy-client" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; }; + }; + + templates = { + "kanidm-oauth2-proxy-client-env" = { + content = '' + OAUTH2_PROXY_CLIENT_SECRET="${config.sops.placeholder.kanidm-oauth2-proxy-client}" + OAUTH2_PROXY_COOKIE_SECRET=${config.sops.placeholder.oauth2-cookie-secret} + ''; + owner = "oauth2-proxy"; + group = "oauth2-proxy"; + mode = "0440"; + }; + }; }; - services.kanidm = { - package = pkgs.kanidmWithSecretProvisioning; - enableServer = true; - serverSettings = { - domain = kanidmDomain; - origin = "https://${kanidmDomain}"; - tls_chain = config.sops.secrets.kanidm-self-signed-crt.path; - tls_key = config.sops.secrets.kanidm-self-signed-key.path; - bindaddress = "0.0.0.0:${toString kanidmPort}"; - trust_x_forward_for = true; + services = { + kanidm = { + package = pkgs.kanidmWithSecretProvisioning; + enableServer = true; + serverSettings = { + domain = kanidmDomain; + origin = "https://${kanidmDomain}"; + tls_chain = config.sops.secrets.kanidm-self-signed-crt.path; + tls_key = config.sops.secrets.kanidm-self-signed-key.path; + bindaddress = "0.0.0.0:${toString kanidmPort}"; + trust_x_forward_for = true; + }; + enableClient = true; + clientSettings = { + uri = config.services.kanidm.serverSettings.origin; + verify_ca = true; + verify_hostnames = true; + }; + provision = { + enable = true; + adminPasswordFile = config.sops.secrets.kanidm-admin-pw.path; + idmAdminPasswordFile = config.sops.secrets.kanidm-idm-admin-pw.path; + groups = { + "immich.access" = { }; + "paperless.access" = { }; + "forgejo.access" = { }; + "forgejo.admins" = { }; + "grafana.access" = { }; + "grafana.editors" = { }; + "grafana.admins" = { }; + "grafana.server-admins" = { }; + "nextcloud.access" = { }; + "nextcloud.admins" = { }; + "navidrome.access" = { }; + "freshrss.access" = { }; + }; + persons = { + swarsel = { + present = true; + mailAddresses = [ "leon@swarsel.win" ]; + legalName = "Leon Schwarzäugl"; + groups = [ + "immich.access" + "paperless.access" + "grafana.access" + "forgejo.access" + "nextcloud.access" + "freshrss.access" + "navidrome.access" + ]; + displayName = "Swarsel"; + }; + }; + systems = { + oauth2 = { + immich = { + displayName = "Immich"; + originUrl = [ + "https://shots.swarsel.win/auth/login" + "https://shots.swarsel.win/user-settings" + "app.immich:///oauth-callback" + "https://shots.swarsel.win/api/oauth/mobile-redirect" + ]; + originLanding = "https://shots.swarsel.win/"; + basicSecretFile = config.sops.secrets.kanidm-immich.path; + preferShortUsername = true; + enableLegacyCrypto = true; # can use RS256 / HS256, not ES256 + scopeMaps."immich.access" = [ + "openid" + "email" + "profile" + ]; + }; + paperless = { + displayName = "Paperless"; + originUrl = "https://scan.swarsel.win/accounts/oidc/kanidm/login/callback/"; + originLanding = "https://scan.swarsel.win/"; + basicSecretFile = config.sops.secrets.kanidm-paperless.path; + preferShortUsername = true; + scopeMaps."paperless.access" = [ + "openid" + "email" + "profile" + ]; + }; + forgejo = { + displayName = "Forgejo"; + originUrl = "https://swagit.swarsel.win/user/oauth2/kanidm/callback"; + originLanding = "https://swagit.swarsel.win/"; + basicSecretFile = config.sops.secrets.kanidm-forgejo.path; + scopeMaps."forgejo.access" = [ + "openid" + "email" + "profile" + ]; + # XXX: PKCE is currently not supported by gitea/forgejo, + # see https://github.com/go-gitea/gitea/issues/21376. + allowInsecureClientDisablePkce = true; + preferShortUsername = true; + claimMaps.groups = { + joinType = "array"; + valuesByGroup."forgejo.admins" = [ "admin" ]; + }; + }; + grafana = { + displayName = "Grafana"; + originUrl = "https://status.swarsel.win/login/generic_oauth"; + originLanding = "https://status.swarsel.win/"; + basicSecretFile = config.sops.secrets.kanidm-grafana.path; + preferShortUsername = true; + scopeMaps."grafana.access" = [ + "openid" + "email" + "profile" + ]; + claimMaps.groups = { + joinType = "array"; + valuesByGroup = { + "grafana.editors" = [ "editor" ]; + "grafana.admins" = [ "admin" ]; + "grafana.server-admins" = [ "server_admin" ]; + }; + }; + }; + nextcloud = { + displayName = "Nextcloud"; + originUrl = " https://stash.swarsel.win/apps/sociallogin/custom_oidc/kanidm"; + originLanding = "https://stash.swarsel.win/"; + basicSecretFile = config.sops.secrets.kanidm-nextcloud.path; + allowInsecureClientDisablePkce = true; + scopeMaps."nextcloud.access" = [ + "openid" + "email" + "profile" + ]; + preferShortUsername = true; + claimMaps.groups = { + joinType = "array"; + valuesByGroup = { + "nextcloud.admins" = [ "admin" ]; + }; + }; + }; + # freshrss = { + # displayName = "FreshRSS"; + # originUrl = "https://signpost.swarsel.win/apps/sociallogin/custom_oidc/kanidm"; + # originLanding = "https://signpost.swarsel.win/"; + # basicSecretFile = config.sops.secrets.kanidm-freshrss.path; + # allowInsecureClientDisablePkce = true; + # scopeMaps."freshrss.access" = [ + # "openid" + # "email" + # "profile" + # ]; + # preferShortUsername = true; + # }; + oauth2-proxy = { + displayName = "Oauth2-Proxy"; + originUrl = "https://${oauth2ProxyDomain}/oauth2/callback"; + originLanding = "https://${oauth2ProxyDomain}/"; + basicSecretFile = config.sops.secrets.kanidm-oauth2-proxy.path; + scopeMaps."freshrss.access" = [ + "openid" + "email" + "profile" + ]; + scopeMaps."navidrome.access" = [ + "openid" + "email" + "profile" + ]; + preferShortUsername = true; + claimMaps.groups = { + joinType = "array"; + valuesByGroup."freshrss.access" = [ "ttrss_access" ]; + valuesByGroup."navidrome.access" = [ "navidrome_access" ]; + }; + }; + }; + }; + }; }; - enableClient = true; - clientSettings = { - uri = config.services.kanidm.serverSettings.origin; - verify_ca = true; - verify_hostnames = true; - }; - provision = { + oauth2-proxy = { enable = true; - adminPasswordFile = config.sops.secrets.kanidm-admin-pw.path; - idmAdminPasswordFile = config.sops.secrets.kanidm-idm-admin-pw.path; - groups = { - "immich.access" = { }; - "paperless.access" = { }; - "forgejo.access" = { }; - "forgejo.admins" = { }; - "grafana.access" = { }; - "grafana.editors" = { }; - "grafana.admins" = { }; - "grafana.server-admins" = { }; - "nextcloud.access" = { }; - "nextcloud.admins" = { }; + cookie = { + domain = ".swarsel.win"; + secure = true; + expire = "900m"; + secret = null; # set by service EnvironmentFile }; - persons = { - swarsel = { - present = true; - mailAddresses = [ "leon@swarsel.win" ]; - legalName = "Leon Schwarzäugl"; - groups = [ - "immich.access" - "paperless.access" - "grafana.access" - "forgejo.access" - "nextcloud.access" - ]; - displayName = "Swarsel"; - }; - }; - systems = { - oauth2 = { - immich = { - displayName = "Immich"; - originUrl = [ - "https://shots.swarsel.win/auth/login" - "https://shots.swarsel.win/user-settings" - "app.immich:///oauth-callback" - "https://shots.swarsel.win/api/oauth/mobile-redirect" - ]; - originLanding = "https://shots.swarsel.win/"; - basicSecretFile = config.sops.secrets.kanidm-immich.path; - preferShortUsername = true; - enableLegacyCrypto = true; # can use RS256 / HS256, not ES256 - scopeMaps."immich.access" = [ - "openid" - "email" - "profile" - ]; - }; - paperless = { - displayName = "Paperless"; - originUrl = "https://scan.swarsel.win/accounts/oidc/kanidm/login/callback/"; - originLanding = "https://scan.swarsel.win/"; - basicSecretFile = config.sops.secrets.kanidm-paperless.path; - preferShortUsername = true; - scopeMaps."paperless.access" = [ - "openid" - "email" - "profile" - ]; - }; - forgejo = { - displayName = "Forgejo"; - originUrl = "https://swagit.swarsel.win/user/oauth2/kanidm/callback"; - originLanding = "https://swagit.swarsel.win/"; - basicSecretFile = config.sops.secrets.kanidm-forgejo.path; - scopeMaps."forgejo.access" = [ - "openid" - "email" - "profile" - ]; - # XXX: PKCE is currently not supported by gitea/forgejo, - # see https://github.com/go-gitea/gitea/issues/21376. - allowInsecureClientDisablePkce = true; - preferShortUsername = true; - claimMaps.groups = { - joinType = "array"; - valuesByGroup."forgejo.admins" = [ "admin" ]; - }; - }; - grafana = { - displayName = "Grafana"; - originUrl = "https://status.swarsel.win/login/generic_oauth"; - originLanding = "https://status.swarsel.win/"; - basicSecretFile = config.sops.secrets.kanidm-grafana.path; - preferShortUsername = true; - scopeMaps."grafana.access" = [ - "openid" - "email" - "profile" - ]; - claimMaps.groups = { - joinType = "array"; - valuesByGroup = { - "grafana.editors" = [ "editor" ]; - "grafana.admins" = [ "admin" ]; - "grafana.server-admins" = [ "server_admin" ]; - }; - }; - }; - nextcloud = { - displayName = "Nextcloud"; - originUrl = " https://stash.swarsel.win/apps/sociallogin/custom_oidc/kanidm"; - originLanding = "https://stash.swarsel.win/"; - basicSecretFile = config.sops.secrets.kanidm-nextcloud.path; - allowInsecureClientDisablePkce = true; - scopeMaps."nextcloud.access" = [ - "openid" - "email" - "profile" - ]; - preferShortUsername = true; - claimMaps.groups = { - joinType = "array"; - valuesByGroup = { - "nextcloud.admins" = [ "admin" ]; - }; - }; - }; - }; + clientSecret = null; # set by service EnvironmentFile + reverseProxy = true; + httpAddress = "0.0.0.0:${builtins.toString oauth2ProxyPort}"; + redirectURL = "https://${oauth2ProxyDomain}/oauth2/callback"; + setXauthrequest = true; + extraConfig = { + code-challenge-method = "S256"; + whitelist-domain = ".swarsel.win"; + set-authorization-header = true; + pass-access-token = true; + skip-jwt-bearer-tokens = true; + upstream = "static://202"; + oidc-issuer-url = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy"; + provider-display-name = "Kanidm"; }; + provider = "oidc"; + scope = "openid email"; + loginURL = "https://${kanidmDomain}/ui/oauth2"; + redeemURL = "https://${kanidmDomain}/oauth2/token"; + validateURL = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy/userinfo"; + clientID = "oauth2-proxy"; + email.domains = [ "*" ]; }; }; - systemd.services.kanidm.serviceConfig.RestartSec = "30"; + systemd.services = { + kanidm.serviceConfig.RestartSec = "30"; + oauth2-proxy = { + after = [ "kanidm.service" ]; + serviceConfig = { + RuntimeDirectory = "oauth2-proxy"; + RuntimeDirectoryMode = "0750"; + UMask = "007"; # TODO remove once https://github.com/oauth2-proxy/oauth2-proxy/issues/2141 is fixed + RestartSec = "60"; # Retry every minute + EnvironmentFile = [ + config.sops.templates.kanidm-oauth2-proxy-client-env.path + ]; + }; + }; + }; services.nginx = { + upstreams = { + kanidm = { + servers = { + "192.168.1.2:${builtins.toString kanidmPort}" = { }; + }; + }; + oauth2-proxy = { + servers = { + "192.168.1.2:${builtins.toString oauth2ProxyPort}" = { }; + }; + }; + }; virtualHosts = { "${kanidmDomain}" = { enableACME = true; @@ -180,13 +298,27 @@ in acmeRoot = null; locations = { "/" = { - proxyPass = "https://localhost:${toString kanidmPort}"; + proxyPass = "https://kanidm"; }; }; extraConfig = '' proxy_ssl_verify off; ''; }; + "${oauth2ProxyDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://oauth2-proxy"; + }; + }; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + ''; + }; }; }; }; diff --git a/secrets/winters/secrets.yaml b/secrets/winters/secrets.yaml index cd73b9b..8ace1fd 100644 --- a/secrets/winters/secrets.yaml +++ b/secrets/winters/secrets.yaml @@ -42,6 +42,8 @@ kanidm-grafana-client: ENC[AES256_GCM,data:tV25k0XoFZ9wLF0UWvAabgigayowr3wo0g==, prometheusadminpass: ENC[AES256_GCM,data:NYUbSnAl0f3FUtvCjvJHFr2wMRsVsbVIeg==,iv:TP4NMwJsft8aEixxJBJCX/0I6BJVBnltFYJDKuXq1hM=,tag:yMY+KZsRjbn8ItgKgjzqSA==,type:str] #ENC[AES256_GCM,data:QnIF/xhWguX5tw==,iv:yTUBtPaZk6BXi+SC1P/OOtnc2x9UZ/jXirD5oaxhyQY=,tag:c33L5r5BaPZN6zkwduBCwQ==,type:comment] fresh: ENC[AES256_GCM,data:aPF8D96BvgDXhcc=,iv:Ubq3/sUmBipRanLgkAXXeAfXAz51AuR+NojMifsy8S0=,tag:mHf0YYYxulLXAIByqmnOsA==,type:str] +kanidm-freshrss-client: ENC[AES256_GCM,data:jBplXWOX/mRTQf6cKmP3C5PZJoBAmb3mhg==,iv:5hcLNGuEQ0T9FiczznGKMul38Ftv8PmG3q0Vaao10oI=,tag:tpx+EDvA31HCnG1/XJOBWg==,type:str] +oidc-crypto-key: ENC[AES256_GCM,data:O48Va8j2L/GDdTZRQEtVsoy1jsZSCLx0IxFYnCBGhoGRwDW+t0LKPw==,iv:DLCeGhRqRp/JfFaY3vva86OzMwGlcXxiBbQ4Tayjyq4=,tag:We5W8cIntW3D/5vdC/t8IA==,type:str] #ENC[AES256_GCM,data:+lbLElpVOYo=,iv:DaVuudlnW+vy2PZOs9eiwZhOyILnqEX9KUehFlX2gWE=,tag:lvM6r0JM0DZir4y7iVTeKg==,type:comment] kanidm-forgejo-client: ENC[AES256_GCM,data:pitJ6re5xm2w1MSs5Ul7Tl1/H1KSR7Ps7w==,iv:4k8/cxpLqWxCgJuk/y9K3OAMCkzu8gb8CDxY+gUuOvg=,tag:OocTFS54teDUfHaHAHZiHw==,type:str] #ENC[AES256_GCM,data:Ur0/rfBv5g==,iv:eH+KbbkmtBWbobqAIUFF0jIrGhbHnk9g8hLZoxE3swI=,tag:3dnoA+O5GXW5Dvxcx4jiTw==,type:comment] @@ -56,6 +58,11 @@ kanidm-paperless: ENC[AES256_GCM,data:bJJC20q8aJVzmIMXAHWvOoH652lSCFXDNg==,iv:0c kanidm-forgejo: ENC[AES256_GCM,data:zw0LcfNJw4q28l1E9q58D9bTKtl/CjGA3w==,iv:fYRGasFiM7PXeP5sWW6whj10CUKIqCfhIYQCNZjxQGo=,tag:sxQJa+ItPA+L3keWZ34SJA==,type:str] kanidm-grafana: ENC[AES256_GCM,data:61PEA1fBcaRy8+x0dn9WrH9P0D+NOkbeZw==,iv:kbR3JWzHsmsef+VlFGciZmyforxJCdvzHijvGFvFwpk=,tag:K+6baLIKy0L37KrJEQUgPg==,type:str] kanidm-nextcloud: ENC[AES256_GCM,data:9FjsOzBos18ouHBeuzrzHIpCDowFt0Aktw==,iv:iqUQUsWsO5N+KZqHyqNxMxSija/yPrrrAqvz4b1NG1M=,tag:/WC3wg/eYXV3hLJPRVWLog==,type:str] +kanidm-oauth2-proxy: ENC[AES256_GCM,data:DQ5tj7N+P1b8vFnF+MGhaUBvbVQoE4sVhQ==,iv:Xy4bdi8fSFuFHsQKgZ3PswFFYsqtiAeqeSRam1k/H0E=,tag:9W4LRPPYtDOrSpxRDK/7sg==,type:str] +kanidm-freshrss: ENC[AES256_GCM,data:4y0X3sSOfs5pKNCmZGJhxlAKH7GD1UACdw==,iv:LuQQCfOpsTqglwQvohHMFpNGaOjoZ8PKDgG50qBP02k=,tag:Z5mVYP/9nToerQ1qui1eWQ==,type:str] +#ENC[AES256_GCM,data:8eDo+FQoBEKMTRY2,iv:ZSrV+Z+1S5AoW+jq49LBFzSwd/NJl3aZYHe7oUvGriU=,tag:3cw3hUigrPViQ+XsuMiksg==,type:comment] +oauth2-cookie-secret: ENC[AES256_GCM,data:l8BPYA7t9NG9MPFs/LDlFHqwbnwsvie7FM5v613358E+jLf2wD+tipyUb6c=,iv:1kZ6G6Z0cSQS53kc/hygh/1Ke491agWDlYHR9Yq0jT0=,tag:mi7Un2JBnrq1dnP3jZX4ng==,type:str] +kanidm-oauth2-proxy-client: ENC[AES256_GCM,data:+mcA/sz3AZuw+I44iIdOEfDmtjEVdxi2fg==,iv:m4NpieUicS7xsR+F5AgPqkcUFRF+CGOA8IK6GeS9tgM=,tag:1wypxpiHPdQBD8Td/PSdMw==,type:str] sops: age: - recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 @@ -67,8 +74,8 @@ sops: MEZ1UWw3alF1WnJZMFZvMFBpbDFJZlUKGRnoEEgjgJ9SSblmldtY6d8MdAy01yxl qkvEIoXbL+ky2ira7EgjD0legThzCnmlXUlcSn3SpwbkAGgcfd2kWA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-06-09T16:04:54Z" - mac: ENC[AES256_GCM,data:ggq/mHOw4kaIalgVNI9YASGewzOwR8+DxhvuuOLo3L4Qnn71/HtXkYnKPMm+Ip58AJi7yH5adNOP2q7MZ/wlG/Ygg95PiM/dBso7l79suycrBo+Zz2bGwUjnT6d35Sz2lqsAIDZgpSwk2M51FjivVXD+Un0aWlt/dj5XOwBhlnU=,iv:WRuIlZ1zc+ITNC4R4Zn2ORy7G2hRFnlEBvnjts4n+RE=,tag:wduo+u6Kjm3LyvkLO8OG+w==,type:str] + lastmodified: "2025-06-09T19:50:17Z" + mac: ENC[AES256_GCM,data:Cx7bI5HRkVVmZTcs/Q3uPtLZaaGd28ZqUsyPRcqd8yEaRxNN0JU6EcQ2ZjU5Zi9jLRLDiR/PxuWsWcmDWH8vW0UZGh6ao75Cw3UO7QhKZHfM5cHqnleo/RIIl5d/Q0hnS9EQmcEPA3qKLQUIrOa+MAgMCkti50ZuNcZnkywLn54=,iv:QPDj4K22G6Go6RDE5ZGbCntmC+mn/5mMyH6ohyQYNuY=,tag:NJ3mjmo2LRmJRXKrYIJZKg==,type:str] pgp: - created_at: "2024-12-17T16:24:32Z" enc: |-