diff --git a/SwarselSystems.org b/SwarselSystems.org index 3d13955..38778b4 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -437,8 +437,8 @@ A short overview over each input and what it does: swarsel-modules.url = "github:Swarsel/swarsel-modules/main"; swarsel-nix.url = "github:Swarsel/swarsel-nix/main"; home-manager = { - url = "github:nix-community/home-manager"; - # url = "github:Swarsel/home-manager/main"; + # url = "github:nix-community/home-manager"; + url = "github:Swarsel/home-manager/main"; inputs.nixpkgs.follows = "nixpkgs"; }; swarsel.url = "github:Swarsel/.dotfiles"; @@ -2472,6 +2472,7 @@ This is my main server that I run at home. It handles most tasks that require bi ankisync = lib.mkDefault true; # snipeit = lib.mkDefault false; homebox = lib.mkDefault true; + opkssh = lib.mkDefault true; }; } @@ -8788,317 +8789,335 @@ A stupid (but simple) way to get the =originUrl= is to simply set any URL there To get other URLs (token, etc.), use https:///oauth2/openid//.well-known/oauth-authorization-server, e.g. https:///oauth2/openid/nextcloud/.well-known/oauth-authorization-server, with clienID being the client name as specified in kanidm. #+begin_src nix-ts :tangle modules/nixos/server/kanidm.nix -{ self, lib, pkgs, config, globals, ... }: -let - certsSopsFile = self + /secrets/certs/secrets.yaml; - inherit (config.swarselsystems) sopsFile; + { self, lib, pkgs, config, globals, ... }: + let + certsSopsFile = self + /secrets/certs/secrets.yaml; + inherit (config.swarselsystems) sopsFile; - servicePort = 8300; - serviceUser = "kanidm"; - serviceGroup = serviceUser; - serviceName = "kanidm"; - serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + servicePort = 8300; + serviceUser = "kanidm"; + serviceGroup = serviceUser; + serviceName = "kanidm"; + serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; + serviceAddress = globals.hosts.winters.ipv4; - oauth2ProxyDomain = globals.services.oauth2Proxy.domain; - immichDomain = globals.services.immich.domain; - paperlessDomain = globals.services.paperless.domain; - forgejoDomain = globals.services.forgejo.domain; - grafanaDomain = globals.services.grafana.domain; - nextcloudDomain = globals.services.nextcloud.domain; + oauth2ProxyDomain = globals.services.oauth2Proxy.domain; + immichDomain = globals.services.immich.domain; + paperlessDomain = globals.services.paperless.domain; + forgejoDomain = globals.services.forgejo.domain; + grafanaDomain = globals.services.grafana.domain; + nextcloudDomain = globals.services.nextcloud.domain; - certBase = "/etc/ssl"; - certsDir = "${certBase}/certs"; - privateDir = "${certBase}/private"; - certPath = "${certsDir}/${serviceName}.crt"; - keyPath = "${privateDir}/${serviceName}.key"; -in -{ - options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; - config = lib.mkIf config.swarselmodules.server.${serviceName} { + certBase = "/etc/ssl"; + certsDir = "${certBase}/certs"; + privateDir = "${certBase}/private"; + certPath = "${certsDir}/${serviceName}.crt"; + keyPath = "${privateDir}/${serviceName}.key"; + in + { + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { - users.users.${serviceUser} = { - group = serviceGroup; - isSystemUser = true; - }; - - users.groups.${serviceGroup} = { }; - - sops = { - secrets = { - "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-admin-pw" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-idm-admin-pw" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-immich" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-paperless" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-forgejo" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-grafana" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-nextcloud" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-freshrss" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; - "kanidm-oauth2-proxy" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + users.users.${serviceUser} = { + group = serviceGroup; + isSystemUser = true; }; - }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + users.groups.${serviceGroup} = { }; - globals.services.${serviceName}.domain = serviceDomain; + sops = { + secrets = { + "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-admin-pw" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-idm-admin-pw" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-immich" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-paperless" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-forgejo" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-grafana" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-nextcloud" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-freshrss" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-oauth2-proxy" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + }; + }; - system.activationScripts."generateSSLCert-${serviceName}" = - let - daysValid = 3650; - renewBeforeDays = 365; - in - { - text = '' - set -eu + networking.firewall.allowedTCPPorts = [ servicePort ]; - ${pkgs.coreutils}/bin/install -d -m 0755 ${certsDir} - ${pkgs.coreutils}/bin/install -d -m 0750 ${privateDir} + globals.services.${serviceName}.domain = serviceDomain; - need_gen=0 - if [ ! -f "${certPath}" ] || [ ! -f "${keyPath}" ]; then - need_gen=1 - else - enddate="$(${pkgs.openssl}/bin/openssl x509 -noout -enddate -in "${certPath}" | cut -d= -f2)" - end_epoch="$(${pkgs.coreutils}/bin/date -d "$enddate" +%s)" - now_epoch="$(${pkgs.coreutils}/bin/date +%s)" - seconds_left=$(( end_epoch - now_epoch )) - days_left=$(( seconds_left / 86400 )) - if [ "$days_left" -lt ${toString renewBeforeDays} ]; then + system.activationScripts."generateSSLCert-${serviceName}" = + let + daysValid = 3650; + renewBeforeDays = 365; + in + { + text = '' + set -eu + + ${pkgs.coreutils}/bin/install -d -m 0755 ${certsDir} + ${pkgs.coreutils}/bin/install -d -m 0750 ${privateDir} + + need_gen=0 + if [ ! -f "${certPath}" ] || [ ! -f "${keyPath}" ]; then need_gen=1 + else + enddate="$(${pkgs.openssl}/bin/openssl x509 -noout -enddate -in "${certPath}" | cut -d= -f2)" + end_epoch="$(${pkgs.coreutils}/bin/date -d "$enddate" +%s)" + now_epoch="$(${pkgs.coreutils}/bin/date +%s)" + seconds_left=$(( end_epoch - now_epoch )) + days_left=$(( seconds_left / 86400 )) + if [ "$days_left" -lt ${toString renewBeforeDays} ]; then + need_gen=1 + fi fi - fi - if [ "$need_gen" -eq 1 ]; then - ${pkgs.openssl}/bin/openssl req -x509 -nodes -days ${toString daysValid} -newkey rsa:4096 -sha256 \ - -keyout "${keyPath}" \ - -out "${certPath}" \ - -subj "/CN=${serviceDomain}" \ - -addext "subjectAltName=DNS:${serviceDomain}" + if [ "$need_gen" -eq 1 ]; then + ${pkgs.openssl}/bin/openssl req -x509 -nodes -days ${toString daysValid} -newkey rsa:4096 -sha256 \ + -keyout "${keyPath}" \ + -out "${certPath}" \ + -subj "/CN=${serviceDomain}" \ + -addext "subjectAltName=DNS:${serviceDomain}" - chmod 0644 "${certPath}" - chmod 0600 "${keyPath}" - chown ${serviceUser}:${serviceGroup} "${certPath}" "${keyPath}" - fi - ''; - deps = [ "etc" ]; - }; - - services = { - ${serviceName} = { - package = pkgs.kanidmWithSecretProvisioning_1_7; - enableServer = true; - serverSettings = { - domain = serviceDomain; - origin = "https://${serviceDomain}"; - # tls_chain = config.sops.secrets.kanidm-self-signed-crt.path; - tls_chain = certPath; - # tls_key = config.sops.secrets.kanidm-self-signed-key.path; - tls_key = keyPath; - bindaddress = "0.0.0.0:${toString servicePort}"; - 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" = { }; - "firefly.access" = { }; - "radicale.access" = { }; - "slink.access" = { }; - }; - - inherit (config.repo.secrets.local) persons; - - systems = { - oauth2 = { - immich = { - displayName = "Immich"; - originUrl = [ - "https://${immichDomain}/auth/login" - "https://${immichDomain}/user-settings" - "app.immich:///oauth-callback" - "https://${immichDomain}/api/oauth/mobile-redirect" - ]; - originLanding = "https://${immichDomain}/"; - 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://${paperlessDomain}/accounts/oidc/kanidm/login/callback/"; - originLanding = "https://${paperlessDomain}/"; - basicSecretFile = config.sops.secrets.kanidm-paperless.path; - preferShortUsername = true; - scopeMaps."paperless.access" = [ - "openid" - "email" - "profile" - ]; - }; - forgejo = { - displayName = "Forgejo"; - originUrl = "https://${forgejoDomain}/user/oauth2/kanidm/callback"; - originLanding = "https://${forgejoDomain}/"; - 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://${grafanaDomain}/login/generic_oauth"; - originLanding = "https://${grafanaDomain}/"; - 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://${nextcloudDomain}/apps/sociallogin/custom_oidc/kanidm"; - originLanding = "https://${nextcloudDomain}/"; - 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" ]; - }; - }; - }; - 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" - ]; - "navidrome.access" = [ - "openid" - "email" - "profile" - ]; - "firefly.access" = [ - "openid" - "email" - "profile" - ]; - "radicale.access" = [ - "openid" - "email" - "profile" - ]; - "slink.access" = [ - "openid" - "email" - "profile" - ]; - }; - preferShortUsername = true; - claimMaps.groups = { - joinType = "array"; - valuesByGroup = { - "freshrss.access" = [ "ttrss_access" ]; - "navidrome.access" = [ "navidrome_access" ]; - "firefly.access" = [ "firefly_access" ]; - "radicale.access" = [ "radicale_access" ]; - "slink.access" = [ "slink_access" ]; - }; - }; - }; - }; - }; - }; - }; - }; - - systemd.services = { - ${serviceName}.serviceConfig.RestartSec = "30"; - }; - - nodes.moonside.services.nginx = { - upstreams = { - ${serviceName} = { - servers = { - "${serviceAddress}:${builtins.toString servicePort}" = { }; - }; - }; - }; - virtualHosts = { - "${serviceDomain}" = { - enableACME = true; - forceSSL = true; - acmeRoot = null; - locations = { - "/" = { - proxyPass = "https://${serviceName}"; - }; - }; - extraConfig = '' - proxy_ssl_verify off; + chmod 0644 "${certPath}" + chmod 0600 "${keyPath}" + chown ${serviceUser}:${serviceGroup} "${certPath}" "${keyPath}" + fi ''; + deps = [ "etc" ]; + }; + + services = { + ${serviceName} = { + package = pkgs.kanidmWithSecretProvisioning_1_7; + enableServer = true; + serverSettings = { + domain = serviceDomain; + origin = "https://${serviceDomain}"; + # tls_chain = config.sops.secrets.kanidm-self-signed-crt.path; + tls_chain = certPath; + # tls_key = config.sops.secrets.kanidm-self-signed-key.path; + tls_key = keyPath; + bindaddress = "0.0.0.0:${toString servicePort}"; + 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" = { }; + "firefly.access" = { }; + "radicale.access" = { }; + "slink.access" = { }; + "opkssh.access" = { }; + }; + + inherit (config.repo.secrets.local) persons; + + systems = { + oauth2 = { + immich = { + displayName = "Immich"; + originUrl = [ + "https://${immichDomain}/auth/login" + "https://${immichDomain}/user-settings" + "app.immich:///oauth-callback" + "https://${immichDomain}/api/oauth/mobile-redirect" + ]; + originLanding = "https://${immichDomain}/"; + 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://${paperlessDomain}/accounts/oidc/kanidm/login/callback/"; + originLanding = "https://${paperlessDomain}/"; + basicSecretFile = config.sops.secrets.kanidm-paperless.path; + preferShortUsername = true; + scopeMaps."paperless.access" = [ + "openid" + "email" + "profile" + ]; + }; + forgejo = { + displayName = "Forgejo"; + originUrl = "https://${forgejoDomain}/user/oauth2/kanidm/callback"; + originLanding = "https://${forgejoDomain}/"; + 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://${grafanaDomain}/login/generic_oauth"; + originLanding = "https://${grafanaDomain}/"; + 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://${nextcloudDomain}/apps/sociallogin/custom_oidc/kanidm"; + originLanding = "https://${nextcloudDomain}/"; + 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" ]; + }; + }; + }; + opkssh = { + displayName = "OPKSSH"; + originUrl = [ + "http://localhost:3000" + "http://localhost:3000/login-callback" + "http://localhost:10001/login-callback" + "http://localhost:11110/login-callback" + ]; + originLanding = "http://localhost:3000"; + public = true; + enableLocalhostRedirects = true; + scopeMaps."opkssh.access" = [ + "openid" + "email" + "profile" + ]; + }; + 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" + ]; + "navidrome.access" = [ + "openid" + "email" + "profile" + ]; + "firefly.access" = [ + "openid" + "email" + "profile" + ]; + "radicale.access" = [ + "openid" + "email" + "profile" + ]; + "slink.access" = [ + "openid" + "email" + "profile" + ]; + }; + preferShortUsername = true; + claimMaps.groups = { + joinType = "array"; + valuesByGroup = { + "freshrss.access" = [ "ttrss_access" ]; + "navidrome.access" = [ "navidrome_access" ]; + "firefly.access" = [ "firefly_access" ]; + "radicale.access" = [ "radicale_access" ]; + "slink.access" = [ "slink_access" ]; + }; + }; + }; + }; + }; + }; + }; + }; + + systemd.services = { + ${serviceName}.serviceConfig.RestartSec = "30"; + }; + + nodes.moonside.services.nginx = { + upstreams = { + ${serviceName} = { + servers = { + "${serviceAddress}:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://${serviceName}"; + }; + }; + extraConfig = '' + proxy_ssl_verify off; + ''; + }; }; }; }; - }; -} + } #+end_src **** oauth2-proxy @@ -9738,9 +9757,15 @@ in }; }; - systemd.tmpfiles.rules = [ - "d ${cfg.settings.storage.filesystem_folder} 0750 ${serviceUser} ${serviceGroup} - -" - ]; + systemd.tmpfiles.settings."10-radicale" = { + "${cfg.settings.storage.filesystem_folder}" = { + d = { + group = serviceGroup; + user = serviceUser; + mode = "0750"; + }; + }; + }; networking.firewall.allowedTCPPorts = [ servicePort ]; @@ -10048,13 +10073,25 @@ in ]; }; - systemd.tmpfiles.rules = [ - "d ${serviceDir}/data 0750 1001 root - -" - "d ${serviceDir}/data/cache 0750 1001 root - -" - "d ${serviceDir}/data/locks 0750 1001 root - -" - "d ${serviceDir}/data/log 0750 1001 root - -" - "d ${serviceDir}/data/proxies 0750 1001 root - -" - ]; + systemd.tmpfiles.settings."11-shlink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "1001"; + mode = "0750"; + }; + }; + }) [ + "${serviceDir}/data" + "${serviceDir}/data/cache" + "${serviceDir}/data/locks" + "${serviceDir}/data/log" + "${serviceDir}/data/proxies" + ] + ); networking.firewall.allowedTCPPorts = [ servicePort ]; @@ -10137,10 +10174,22 @@ Deployment notes: ]; }; - systemd.tmpfiles.rules = [ - "d ${serviceDir}/var/data 0750 root root - -" - "d ${serviceDir}/images 0750 root root - -" - ]; + systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "root"; + mode = "0750"; + }; + }; + }) [ + "${serviceDir}/var/data" + "${serviceDir}/images" + ] + ); networking.firewall.allowedTCPPorts = [ servicePort ]; @@ -10326,6 +10375,48 @@ Deployment notes: } #+end_src +**** OPKSSH + +#+begin_src nix-ts :tangle modules/nixos/server/opkssh.nix + { lib, config, globals, ... }: + let + serviceName = "opkssh"; + serviceUser = "opksshuser"; + serviceGroup = serviceUser; + + kanidmDomain = globals.services.kanidm.domain; + + inherit (config.swarselsystems) mainUser; + inherit (config.repo.secrets.local) persons; + in + { + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + services.${serviceName} = { + enable = true; + user = serviceUser; + group = serviceGroup; + providers = { + kanidm = { + lifetime = "oidc"; + issuer = "https://${kanidmDomain}/oauth2/openid/${serviceName}"; + clientId = serviceName; + }; + }; + authorizations = [ + { + user = mainUser; + principal = builtins.head persons.${mainUser}.mailAddresses; + inherit (config.services.opkssh.providers.kanidm) issuer; + } + ]; + }; + + }; + + } +#+end_src *** Darwin :PROPERTIES: :CUSTOM_ID: h:ac0cd8b3-06cf-4dca-ba73-6100c8fedb47 @@ -11286,6 +11377,9 @@ This holds packages that I can use as provided, or with small modifications (as simple-scan cura-appimage + # ssh login using idm + opkssh + # dict (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ])) @@ -14651,9 +14745,15 @@ When setting up a new machine: }; # assure correct permissions - systemd.user.tmpfiles.rules = [ - "d ${homeDir}/.gnupg 700 ${mainUser} users" - ]; + systemd.user.tmpfiles.settings."30-gpgagent".rules = { + "${homeDir}/.gnupg" = { + d = { + group = "users"; + user = mainUser; + mode = "0700"; + }; + }; + }; }; } @@ -15239,6 +15339,41 @@ This service changes the screen hue at night. I am not sure if that really does } #+end_src +**** opkssh + +#+begin_src nix-ts :tangle modules/home/common/opkssh.nix + { lib, config, ... }: + let + moduleName = "opkssh"; + in + { + options.swarselmodules.${moduleName} = lib.mkEnableOption "enable ${moduleName} and settings"; + config = lib.mkIf config.swarselmodules.${moduleName} { + programs.${moduleName} = { + enable = true; + settings = { + default_provider = "kanidm"; + + providers = [ + { + alias = "kanidm"; + issuer = "https://sso.swarsel.win/oauth2/openid/opkssh"; + client_id = "opkssh"; + scopes = "openid email profile"; + redirect_uris = [ + "http://localhost:3000/login-callback" + "http://localhost:10001/login-callback" + "http://localhost:11110/login-callback" + ]; + } + ]; + }; + }; + }; + + } +#+end_src + *** Server :PROPERTIES: :CUSTOM_ID: h:b1a00339-6e9b-4ae4-b5dc-6fd5669a2ddb @@ -18826,6 +18961,7 @@ This holds modules that are to be used on most hosts. These are also the most im obs-studio = lib.mkDefault true; obsidian = lib.mkDefault true; obsidian-tray = lib.mkDefault true; + opkssh = lib.mkDefault true; ownpackages = lib.mkDefault true; packages = lib.mkDefault true; passwordstore = lib.mkDefault true; diff --git a/flake.lock b/flake.lock index edf4925..b90a12e 100644 --- a/flake.lock +++ b/flake.lock @@ -4666,15 +4666,16 @@ ] }, "locked": { - "lastModified": 1762025346, - "narHash": "sha256-6KR4dsNfA3Pqm6uT8j7aKjWydP/KXFqZUhOfMlfP+1E=", - "owner": "nix-community", + "lastModified": 1762257324, + "narHash": "sha256-05SxDx82j23zd1/EMDFZTPYFgvzxviBiByUBVs+860w=", + "owner": "Swarsel", "repo": "home-manager", - "rev": "87044c57222fb485974062e2dd557e7b8abd8fff", + "rev": "d27fabec395885790e7ab666c72522e18e534117", "type": "github" }, "original": { - "owner": "nix-community", + "owner": "Swarsel", + "ref": "main", "repo": "home-manager", "type": "github" } diff --git a/flake.nix b/flake.nix index 1614ccd..543216f 100644 --- a/flake.nix +++ b/flake.nix @@ -21,8 +21,8 @@ swarsel-modules.url = "github:Swarsel/swarsel-modules/main"; swarsel-nix.url = "github:Swarsel/swarsel-nix/main"; home-manager = { - url = "github:nix-community/home-manager"; - # url = "github:Swarsel/home-manager/main"; + # url = "github:nix-community/home-manager"; + url = "github:Swarsel/home-manager/main"; inputs.nixpkgs.follows = "nixpkgs"; }; swarsel.url = "github:Swarsel/.dotfiles"; diff --git a/hosts/nixos/winters/default.nix b/hosts/nixos/winters/default.nix index 38207d8..98d3bc5 100644 --- a/hosts/nixos/winters/default.nix +++ b/hosts/nixos/winters/default.nix @@ -66,6 +66,7 @@ ankisync = lib.mkDefault true; # snipeit = lib.mkDefault false; homebox = lib.mkDefault true; + opkssh = lib.mkDefault true; }; } diff --git a/hosts/nixos/winters/secrets/pii.nix.enc b/hosts/nixos/winters/secrets/pii.nix.enc index 97cae9e..053a465 100644 --- a/hosts/nixos/winters/secrets/pii.nix.enc +++ b/hosts/nixos/winters/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:lDbxvTQBbWAyHGx1a0TPXA6nIJ4crJICZaG/VYdeRl09NbJeF1dO2/UEquQxulE0IR+VkzIbfRiLPJ74kRcEsuFEBQ9LrF+gMEgwocQGCPGRTaWTfl84HfcX4qo+elOB5/W5lx1moOdYKFNFkxKbAj6wN0A8n0YBFqPDW/zeMO0DU6+MLW1XEb8zjG0hERocHb8svHVv2Xhu6sP2AX844vtN9u2xKil/kAR8XLPyuVCutsO+8GNfGkZh4hYdn5H+TYmWcWzzLujBt2H8+7UfdYsU4Q2PYcJ/Uokol8PAqMmPQDt9fF2S/IBbWZBmJY+ZmcxGHgYGY1+FeXZviIR37i8t9DS6isasDmGexDn4WyBq45XI/xo96qy0+GNHbeUoirAWWa+2YhLeqekG8q1luTbMh1uLw1n+ZmT3SexA20ens2sYmwjWdhyCR0cY3lcLhP/NVXgnbmOP61RUl7/RCPq0F3PCZ29LJG7oeDjD3JbJypRhTKnRMF+heEPrvmacWrkCMpEAkR8eB2WQmDxUTFyFTk/tv55GtHDuw5uA77pSmPIqVPppH2CupzDf+nBKyVIqON1jbpyxjlE8bBHtrEWEd0SioiTuC/CcoytzB8IiiEDgNjgoqNHfHYRyxGq0EKFtVcdqvkm3mbD0XyGG0UR3Arl8QSdHhIBSqedN7Y6rZJxef6VfLuxE13FWrZnSVGopAjMb/RTZbXLxjDNeHg6HwdBlrXMT7rps7jxbg39v5sOhGYhphF/L7KdCKVfmi8ubAfDVFZ3XRSuBzcHCW8FkxBWG+7Gnl3mMoNVw8loQwUjT+PbL1VHNQCr2fqS4wrv6ehQZIqOsqqlUF9JH9A3TgmHb4ioQ+jAEyOymTmrMzeaUBzcB9w54epkU+okDTO1LOvwC6gvEOQMoHv6fI2YCsQRCH3Mk3rhNBaotD3lQfuz1Pc3XL8t/9KGrCVwHLAoA+lt9g6o1KZoiBfcp3pFCWsPX3lrmUsJnKZHIVO+S9jOz+aJdOgaX/h5+cHp607bL8Zb296N4yK9yt5U+3mYONKn/hzGAmSghjfb/GTlMRJa5f6J9SerUvKqrjUVXJ2CmMAYojBt30Z1QRXIADRLLeyJglMZy9TwtoNsVr7iD2+rylU5TLJmPpxDbnQFriN0X4yhp1wYYBgjVkXV4SNGElDxjm0HFsBJkBaOpXeijCrcOJ5vmnuGLIhBTw+3qG4dEx/foUYRV1oJFrwH1U/f4oL9cOfdC0B9fv9yXJ/GORywOIlVb4FNLewSbu+PIAiZJLVQcBeBWNKQfoIb892ao2RXXJN6XZxp7IfEIBRBM/sfcfgtDk5ud+nuYPWYf09533t/ZjgbZ1en24MAwsit060IPROVwW3pIh2TSx/j1iMWbJ9ljaabBhivmGVo8CLVErGeSLwLL8eXLQ02D9i+LgY1oQavx3675Z7RDwQbdQxjLCKB5Fq77nPGuf2mzTLE/6NL7l6INCcuyg+Vp2zaxX87NMgh+0q9ZFW/ECL/+QzNeBaUmFJ2tXwMiVJAcm2vGiUvI,iv:emlV4hFKBALvzUiaDtu3CDX0Y8fd8TTQmPaR9WyqN4I=,tag:aFPEYachc1H0F+/q3FbuGA==,type:str]", + "data": "ENC[AES256_GCM,data:i8kqmycc80NLcOirK7D20hxsWWFGNMx5d1SDueaSTmymvTb0rNMFiRoLf44c9Bc5yBySg9zRNCJ8lyc1SZH5YAqIYGN8y4nzOTxyPxT1R+Igtzm49sBdp1dK6MVCW1/xIsjDE/niV19l9LTptN7neCIEuDjB22bSMRhfVOxOiSwd2FKBeH61XACFLAz3AuVC4a+FhqsBkmLOAg7SIG0xdsyi2tjuTOCiMci9eQk8q9XkmLQC3z6vBaIBxPErkIG/fyhNBYt0Hv+37b/TFVbBL9qnGK369Ln9EUm0IarInaq9haxE18DkI5bzlJH5hw4JNjMGFz8av6rOJT5oW52yPor1RBodwUtP6YDQCQsdINIbjmAM9C0HGBY31nyy07fVT+QDcNydjT1fEXLSJTseO5E/MrRCVBioorBpG+5kBBQhRMihGeT8htVGVQpiJC7peARRfSQTUqjNH/esWTPCk0xPOXgApgPIbXBIJFj4RiH2erzN1PKPpuGZfuqbEr63VXCp6t0jttZKDEEjob1ZJMKfiqRubaz+ycg/sxFb1p+l9O21ihW8jM9huiBpVTc+mHU0ENTSstC4uB5LG6HMqV9vmqrKP4OM13vifggpQ6K9VRXebpI/t4tdRETcwWIMo/LRCmhGO4o0+1tNG+ayVDVmY/ORJu/Nrl61TtmUXxGc74Vd1sea+60LuqDHdIEs06thxFLSCVXaxa70gTlQIlVSEJBz9yaAMm7Iw4CSvLbYTXrRg6P/jIZx7rGSyC7I/kFMAPsa2KYxUUkBNd8zSoHyf8paxbgocb3yNcMtHIyNYiOXaLgr56DasJKsXTdjnuic6PLBCd9+U9o9R4wdBw4CGHZ3+3P+LexImkK9InVTm3QD9gyWtgDe66jrQMJxicixtx6hKvP0P0nmjhE6UjhfMMYV2Qu7vx1APhE62KF9y+axIlKju4kYkRhC9BHi2FuZUCFkPiPd/RRWzBTuChnKud9TWTixBtzpBI39/5e3i8f55hcG0XkUT+rMolq/hCObIczq7M2TVrZK4vUGByHXi1Vljk+K2tUZLVGq+Vpa7JUfmyp/qdUBG5FFXBBLUmAA0yjR+XxTtuZ4+M7JdgoYlzXEN8AbyBTEfVps4HJVPfwWMZaXEWqc7fwSVNlkRpIGSkbF8+tzBd0M66FtjbC9hs1P/SwwUr/ZTwJW0D8EXDUOJo2VCGpGtzHZpg5wd7qPTKL20fLVrpdfKIMeBrPtpCkv2YAzSXClAMQqMJ1DtrFr9WNeTAv0ROwkfnGIYwFpDnK3Xy3Z9V1HlZY3K/UYybzb43Dvtl0qWcSmXxKWLK0rqe3r4sWKA/gpOi8UIyCssAv5At0JO2e0yBJv7tXi+WGNZIBEy559EYdnfHty2KZTR5M/PiP5t1PMbuzUkee3voybnAioCZfGsh5Ln04ZihUHbtbxocM4yieaqBd7nryO81b1ZARoZKbASaGMc7y65pgsA1MiNnbtGIZUwlNyg1q3iZxdtkG6qgG2vCY9Z6JNXXl6RoBmbDbCaIzvid8OXvK8z0z4t0BpYQCFVb3/sEAqweUnQffstEFB,iv:+cS1MmSlZWLdRt5Ey31y6WrDAudgjHxsUbfCBUK0/Sg=,tag:byOqNWWPQmlrDWQO1tRRJQ==,type:str]", "sops": { "age": [ { @@ -11,8 +11,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNeGtTZ0ZSV0trWlQrS2dV\nSFo0dytGYXhRTjl6cDZrUU0wZ1IybDVRaFZrCmZmRmxJNmdwS0xodHdEOGU4bldU\nR1JScHAvZHhlVTBJbWExb0VpR0h2MXMKLS0tIDYwQmZpMjdYRmpBeXFNOXArN0h5\nVGN1THljeCtVV0hXenMyRVJkMjlHNEEKm+yZTT48nYr3H0Bd1OKw/CYk1kwnrBzk\nTgSQHsGXhmOyDag9cSZ4wAOmqtqSjA9bouFBuhl2lSbgpjnarvFaXQ==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-09-16T11:26:37Z", - "mac": "ENC[AES256_GCM,data:T87Y33N0Tu8IySeUgrV7lgMzG+8Cl0ezJkjZFHzmeXy7+TJ2JKrnTfy0nY2oXjVCRLC2CBRh6e9n6ITnAnQJxyS3ZDF/FaY1h5LSm4e6bklFxBZRTQhssVJHbRE4dm8VZL+NLli32VM5YG+E8Kn640YE0oUgBlxqS4qmH4/fLl4=,iv:37xHtgBX6nmgGUGhwSpLIItN8OMto5yLL+Xr+KA4h9E=,tag:Y4FU6/HYmu1IgLG4ChutZw==,type:str]", + "lastmodified": "2025-11-04T09:26:35Z", + "mac": "ENC[AES256_GCM,data:T8GqsMxfFB9s1EOeLHNzxoz23FCOnlNsBsbvMxiLq7a78xt5Xw3dVN/IWfkyiCDwfSjo+fVx2yEd5tP/B3fSN7S8WJNSe5ZywLpal/RlsCzv7ARvbVCaBx22S4az97JsR1qQUcGSvoiTH5e/0t2tBtimGJ1witbvbiGkTBp8taw=,iv:Qs26cjeMLtRhTDO91yfBo93wUKJ9zVfUbJ8o6myHGUo=,tag:FbT8emz6q1QnXdxoX6hsYQ==,type:str]", "pgp": [ { "created_at": "2025-08-24T23:36:17Z", @@ -21,6 +21,6 @@ } ], "unencrypted_suffix": "_unencrypted", - "version": "3.10.2" + "version": "3.11.0" } } diff --git a/modules/home/common/gpg-agent.nix b/modules/home/common/gpg-agent.nix index e92711f..247dba4 100644 --- a/modules/home/common/gpg-agent.nix +++ b/modules/home/common/gpg-agent.nix @@ -37,9 +37,15 @@ in }; # assure correct permissions - systemd.user.tmpfiles.rules = [ - "d ${homeDir}/.gnupg 700 ${mainUser} users" - ]; + systemd.user.tmpfiles.settings."30-gpgagent".rules = { + "${homeDir}/.gnupg" = { + d = { + group = "users"; + user = mainUser; + mode = "0700"; + }; + }; + }; }; } diff --git a/modules/home/common/opkssh.nix b/modules/home/common/opkssh.nix new file mode 100644 index 0000000..9d5b86d --- /dev/null +++ b/modules/home/common/opkssh.nix @@ -0,0 +1,30 @@ +{ lib, config, ... }: +let + moduleName = "opkssh"; +in +{ + options.swarselmodules.${moduleName} = lib.mkEnableOption "enable ${moduleName} and settings"; + config = lib.mkIf config.swarselmodules.${moduleName} { + programs.${moduleName} = { + enable = true; + settings = { + default_provider = "kanidm"; + + providers = [ + { + alias = "kanidm"; + issuer = "https://sso.swarsel.win/oauth2/openid/opkssh"; + client_id = "opkssh"; + scopes = "openid email profile"; + redirect_uris = [ + "http://localhost:3000/login-callback" + "http://localhost:10001/login-callback" + "http://localhost:11110/login-callback" + ]; + } + ]; + }; + }; + }; + +} diff --git a/modules/home/common/packages.nix b/modules/home/common/packages.nix index 1695b95..a1c84bf 100644 --- a/modules/home/common/packages.nix +++ b/modules/home/common/packages.nix @@ -22,6 +22,9 @@ simple-scan cura-appimage + # ssh login using idm + opkssh + # dict (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ])) diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index 61495b6..79b1983 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -135,6 +135,7 @@ in "firefly.access" = { }; "radicale.access" = { }; "slink.access" = { }; + "opkssh.access" = { }; }; inherit (config.repo.secrets.local) persons; @@ -229,6 +230,23 @@ in }; }; }; + opkssh = { + displayName = "OPKSSH"; + originUrl = [ + "http://localhost:3000" + "http://localhost:3000/login-callback" + "http://localhost:10001/login-callback" + "http://localhost:11110/login-callback" + ]; + originLanding = "http://localhost:3000"; + public = true; + enableLocalhostRedirects = true; + scopeMaps."opkssh.access" = [ + "openid" + "email" + "profile" + ]; + }; oauth2-proxy = { displayName = "Oauth2-Proxy"; originUrl = "https://${oauth2ProxyDomain}/oauth2/callback"; diff --git a/modules/nixos/server/opkssh.nix b/modules/nixos/server/opkssh.nix new file mode 100644 index 0000000..823102a --- /dev/null +++ b/modules/nixos/server/opkssh.nix @@ -0,0 +1,38 @@ +{ lib, config, globals, ... }: +let + serviceName = "opkssh"; + serviceUser = "opksshuser"; + serviceGroup = serviceUser; + + kanidmDomain = globals.services.kanidm.domain; + + inherit (config.swarselsystems) mainUser; + inherit (config.repo.secrets.local) persons; +in +{ + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + services.${serviceName} = { + enable = true; + user = serviceUser; + group = serviceGroup; + providers = { + kanidm = { + lifetime = "oidc"; + issuer = "https://${kanidmDomain}/oauth2/openid/${serviceName}"; + clientId = serviceName; + }; + }; + authorizations = [ + { + user = mainUser; + principal = builtins.head persons.${mainUser}.mailAddresses; + inherit (config.services.opkssh.providers.kanidm) issuer; + } + ]; + }; + + }; + +} diff --git a/modules/nixos/server/radicale.nix b/modules/nixos/server/radicale.nix index 68a940b..7ad9fe2 100644 --- a/modules/nixos/server/radicale.nix +++ b/modules/nixos/server/radicale.nix @@ -76,9 +76,15 @@ in }; }; - systemd.tmpfiles.rules = [ - "d ${cfg.settings.storage.filesystem_folder} 0750 ${serviceUser} ${serviceGroup} - -" - ]; + systemd.tmpfiles.settings."10-radicale" = { + "${cfg.settings.storage.filesystem_folder}" = { + d = { + group = serviceGroup; + user = serviceUser; + mode = "0750"; + }; + }; + }; networking.firewall.allowedTCPPorts = [ servicePort ]; diff --git a/modules/nixos/server/shlink.nix b/modules/nixos/server/shlink.nix index 5e8ef98..527eaa2 100644 --- a/modules/nixos/server/shlink.nix +++ b/modules/nixos/server/shlink.nix @@ -48,13 +48,25 @@ in ]; }; - systemd.tmpfiles.rules = [ - "d ${serviceDir}/data 0750 1001 root - -" - "d ${serviceDir}/data/cache 0750 1001 root - -" - "d ${serviceDir}/data/locks 0750 1001 root - -" - "d ${serviceDir}/data/log 0750 1001 root - -" - "d ${serviceDir}/data/proxies 0750 1001 root - -" - ]; + systemd.tmpfiles.settings."11-shlink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "1001"; + mode = "0750"; + }; + }; + }) [ + "${serviceDir}/data" + "${serviceDir}/data/cache" + "${serviceDir}/data/locks" + "${serviceDir}/data/log" + "${serviceDir}/data/proxies" + ] + ); networking.firewall.allowedTCPPorts = [ servicePort ]; diff --git a/modules/nixos/server/slink.nix b/modules/nixos/server/slink.nix index 4dfc7cb..4f54b8a 100644 --- a/modules/nixos/server/slink.nix +++ b/modules/nixos/server/slink.nix @@ -29,10 +29,22 @@ in ]; }; - systemd.tmpfiles.rules = [ - "d ${serviceDir}/var/data 0750 root root - -" - "d ${serviceDir}/images 0750 root root - -" - ]; + systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "root"; + mode = "0750"; + }; + }; + }) [ + "${serviceDir}/var/data" + "${serviceDir}/images" + ] + ); networking.firewall.allowedTCPPorts = [ servicePort ]; diff --git a/profiles/home/personal/default.nix b/profiles/home/personal/default.nix index cd554b7..ec37f9d 100644 --- a/profiles/home/personal/default.nix +++ b/profiles/home/personal/default.nix @@ -37,6 +37,7 @@ obs-studio = lib.mkDefault true; obsidian = lib.mkDefault true; obsidian-tray = lib.mkDefault true; + opkssh = lib.mkDefault true; ownpackages = lib.mkDefault true; packages = lib.mkDefault true; passwordstore = lib.mkDefault true;