feat[work,server,client]: add opkssh
Some checks failed
Flake check / Check flake (push) Has been cancelled

This commit is contained in:
Leon Schwarzäugl 2025-11-04 15:45:52 +01:00
parent 3b368ec8de
commit c9e7e493d8
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
14 changed files with 604 additions and 340 deletions

View file

@ -437,8 +437,8 @@ A short overview over each input and what it does:
swarsel-modules.url = "github:Swarsel/swarsel-modules/main"; swarsel-modules.url = "github:Swarsel/swarsel-modules/main";
swarsel-nix.url = "github:Swarsel/swarsel-nix/main"; swarsel-nix.url = "github:Swarsel/swarsel-nix/main";
home-manager = { home-manager = {
url = "github:nix-community/home-manager"; # url = "github:nix-community/home-manager";
# url = "github:Swarsel/home-manager/main"; url = "github:Swarsel/home-manager/main";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
swarsel.url = "github:Swarsel/.dotfiles"; 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; ankisync = lib.mkDefault true;
# snipeit = lib.mkDefault false; # snipeit = lib.mkDefault false;
homebox = lib.mkDefault true; 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://<kanidmDomain>/oauth2/openid/<clientID>/.well-known/oauth-authorization-server, e.g. https://<kanidmDomain>/oauth2/openid/nextcloud/.well-known/oauth-authorization-server, with clienID being the client name as specified in kanidm. To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clientID>/.well-known/oauth-authorization-server, e.g. https://<kanidmDomain>/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 #+begin_src nix-ts :tangle modules/nixos/server/kanidm.nix
{ self, lib, pkgs, config, globals, ... }: { self, lib, pkgs, config, globals, ... }:
let let
certsSopsFile = self + /secrets/certs/secrets.yaml; certsSopsFile = self + /secrets/certs/secrets.yaml;
inherit (config.swarselsystems) sopsFile; inherit (config.swarselsystems) sopsFile;
servicePort = 8300; servicePort = 8300;
serviceUser = "kanidm"; serviceUser = "kanidm";
serviceGroup = serviceUser; serviceGroup = serviceUser;
serviceName = "kanidm"; serviceName = "kanidm";
serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; serviceDomain = config.repo.secrets.common.services.domains.${serviceName};
serviceAddress = globals.hosts.winters.ipv4; serviceAddress = globals.hosts.winters.ipv4;
oauth2ProxyDomain = globals.services.oauth2Proxy.domain; oauth2ProxyDomain = globals.services.oauth2Proxy.domain;
immichDomain = globals.services.immich.domain; immichDomain = globals.services.immich.domain;
paperlessDomain = globals.services.paperless.domain; paperlessDomain = globals.services.paperless.domain;
forgejoDomain = globals.services.forgejo.domain; forgejoDomain = globals.services.forgejo.domain;
grafanaDomain = globals.services.grafana.domain; grafanaDomain = globals.services.grafana.domain;
nextcloudDomain = globals.services.nextcloud.domain; nextcloudDomain = globals.services.nextcloud.domain;
certBase = "/etc/ssl"; certBase = "/etc/ssl";
certsDir = "${certBase}/certs"; certsDir = "${certBase}/certs";
privateDir = "${certBase}/private"; privateDir = "${certBase}/private";
certPath = "${certsDir}/${serviceName}.crt"; certPath = "${certsDir}/${serviceName}.crt";
keyPath = "${privateDir}/${serviceName}.key"; keyPath = "${privateDir}/${serviceName}.key";
in in
{ {
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} { config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.users.${serviceUser} = { users.users.${serviceUser} = {
group = serviceGroup; group = serviceGroup;
isSystemUser = true; 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"; };
}; };
};
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}" = networking.firewall.allowedTCPPorts = [ servicePort ];
let
daysValid = 3650;
renewBeforeDays = 365;
in
{
text = ''
set -eu
${pkgs.coreutils}/bin/install -d -m 0755 ${certsDir} globals.services.${serviceName}.domain = serviceDomain;
${pkgs.coreutils}/bin/install -d -m 0750 ${privateDir}
need_gen=0 system.activationScripts."generateSSLCert-${serviceName}" =
if [ ! -f "${certPath}" ] || [ ! -f "${keyPath}" ]; then let
need_gen=1 daysValid = 3650;
else renewBeforeDays = 365;
enddate="$(${pkgs.openssl}/bin/openssl x509 -noout -enddate -in "${certPath}" | cut -d= -f2)" in
end_epoch="$(${pkgs.coreutils}/bin/date -d "$enddate" +%s)" {
now_epoch="$(${pkgs.coreutils}/bin/date +%s)" text = ''
seconds_left=$(( end_epoch - now_epoch )) set -eu
days_left=$(( seconds_left / 86400 ))
if [ "$days_left" -lt ${toString renewBeforeDays} ]; then ${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 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
fi
if [ "$need_gen" -eq 1 ]; then if [ "$need_gen" -eq 1 ]; then
${pkgs.openssl}/bin/openssl req -x509 -nodes -days ${toString daysValid} -newkey rsa:4096 -sha256 \ ${pkgs.openssl}/bin/openssl req -x509 -nodes -days ${toString daysValid} -newkey rsa:4096 -sha256 \
-keyout "${keyPath}" \ -keyout "${keyPath}" \
-out "${certPath}" \ -out "${certPath}" \
-subj "/CN=${serviceDomain}" \ -subj "/CN=${serviceDomain}" \
-addext "subjectAltName=DNS:${serviceDomain}" -addext "subjectAltName=DNS:${serviceDomain}"
chmod 0644 "${certPath}" chmod 0644 "${certPath}"
chmod 0600 "${keyPath}" chmod 0600 "${keyPath}"
chown ${serviceUser}:${serviceGroup} "${certPath}" "${keyPath}" chown ${serviceUser}:${serviceGroup} "${certPath}" "${keyPath}"
fi 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;
''; '';
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 #+end_src
**** oauth2-proxy **** oauth2-proxy
@ -9738,9 +9757,15 @@ in
}; };
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."10-radicale" = {
"d ${cfg.settings.storage.filesystem_folder} 0750 ${serviceUser} ${serviceGroup} - -" "${cfg.settings.storage.filesystem_folder}" = {
]; d = {
group = serviceGroup;
user = serviceUser;
mode = "0750";
};
};
};
networking.firewall.allowedTCPPorts = [ servicePort ]; networking.firewall.allowedTCPPorts = [ servicePort ];
@ -10048,13 +10073,25 @@ in
]; ];
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."11-shlink" = builtins.listToAttrs (
"d ${serviceDir}/data 0750 1001 root - -" map
"d ${serviceDir}/data/cache 0750 1001 root - -" (path: {
"d ${serviceDir}/data/locks 0750 1001 root - -" name = "${serviceDir}/${path}";
"d ${serviceDir}/data/log 0750 1001 root - -" value = {
"d ${serviceDir}/data/proxies 0750 1001 root - -" 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 ]; networking.firewall.allowedTCPPorts = [ servicePort ];
@ -10137,10 +10174,22 @@ Deployment notes:
]; ];
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs (
"d ${serviceDir}/var/data 0750 root root - -" map
"d ${serviceDir}/images 0750 root root - -" (path: {
]; name = "${serviceDir}/${path}";
value = {
d = {
group = "root";
user = "root";
mode = "0750";
};
};
}) [
"${serviceDir}/var/data"
"${serviceDir}/images"
]
);
networking.firewall.allowedTCPPorts = [ servicePort ]; networking.firewall.allowedTCPPorts = [ servicePort ];
@ -10326,6 +10375,48 @@ Deployment notes:
} }
#+end_src #+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 *** Darwin
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: h:ac0cd8b3-06cf-4dca-ba73-6100c8fedb47 :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 simple-scan
cura-appimage cura-appimage
# ssh login using idm
opkssh
# dict # dict
(aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ])) (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ]))
@ -14651,9 +14745,15 @@ When setting up a new machine:
}; };
# assure correct permissions # assure correct permissions
systemd.user.tmpfiles.rules = [ systemd.user.tmpfiles.settings."30-gpgagent".rules = {
"d ${homeDir}/.gnupg 700 ${mainUser} users" "${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 #+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 *** Server
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: h:b1a00339-6e9b-4ae4-b5dc-6fd5669a2ddb :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; obs-studio = lib.mkDefault true;
obsidian = lib.mkDefault true; obsidian = lib.mkDefault true;
obsidian-tray = lib.mkDefault true; obsidian-tray = lib.mkDefault true;
opkssh = lib.mkDefault true;
ownpackages = lib.mkDefault true; ownpackages = lib.mkDefault true;
packages = lib.mkDefault true; packages = lib.mkDefault true;
passwordstore = lib.mkDefault true; passwordstore = lib.mkDefault true;

11
flake.lock generated
View file

@ -4666,15 +4666,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762025346, "lastModified": 1762257324,
"narHash": "sha256-6KR4dsNfA3Pqm6uT8j7aKjWydP/KXFqZUhOfMlfP+1E=", "narHash": "sha256-05SxDx82j23zd1/EMDFZTPYFgvzxviBiByUBVs+860w=",
"owner": "nix-community", "owner": "Swarsel",
"repo": "home-manager", "repo": "home-manager",
"rev": "87044c57222fb485974062e2dd557e7b8abd8fff", "rev": "d27fabec395885790e7ab666c72522e18e534117",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "Swarsel",
"ref": "main",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }

View file

@ -21,8 +21,8 @@
swarsel-modules.url = "github:Swarsel/swarsel-modules/main"; swarsel-modules.url = "github:Swarsel/swarsel-modules/main";
swarsel-nix.url = "github:Swarsel/swarsel-nix/main"; swarsel-nix.url = "github:Swarsel/swarsel-nix/main";
home-manager = { home-manager = {
url = "github:nix-community/home-manager"; # url = "github:nix-community/home-manager";
# url = "github:Swarsel/home-manager/main"; url = "github:Swarsel/home-manager/main";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
swarsel.url = "github:Swarsel/.dotfiles"; swarsel.url = "github:Swarsel/.dotfiles";

View file

@ -66,6 +66,7 @@
ankisync = lib.mkDefault true; ankisync = lib.mkDefault true;
# snipeit = lib.mkDefault false; # snipeit = lib.mkDefault false;
homebox = lib.mkDefault true; homebox = lib.mkDefault true;
opkssh = lib.mkDefault true;
}; };
} }

View file

@ -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": { "sops": {
"age": [ "age": [
{ {
@ -11,8 +11,8 @@
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNeGtTZ0ZSV0trWlQrS2dV\nSFo0dytGYXhRTjl6cDZrUU0wZ1IybDVRaFZrCmZmRmxJNmdwS0xodHdEOGU4bldU\nR1JScHAvZHhlVTBJbWExb0VpR0h2MXMKLS0tIDYwQmZpMjdYRmpBeXFNOXArN0h5\nVGN1THljeCtVV0hXenMyRVJkMjlHNEEKm+yZTT48nYr3H0Bd1OKw/CYk1kwnrBzk\nTgSQHsGXhmOyDag9cSZ4wAOmqtqSjA9bouFBuhl2lSbgpjnarvFaXQ==\n-----END AGE ENCRYPTED FILE-----\n" "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", "lastmodified": "2025-11-04T09:26:35Z",
"mac": "ENC[AES256_GCM,data:T87Y33N0Tu8IySeUgrV7lgMzG+8Cl0ezJkjZFHzmeXy7+TJ2JKrnTfy0nY2oXjVCRLC2CBRh6e9n6ITnAnQJxyS3ZDF/FaY1h5LSm4e6bklFxBZRTQhssVJHbRE4dm8VZL+NLli32VM5YG+E8Kn640YE0oUgBlxqS4qmH4/fLl4=,iv:37xHtgBX6nmgGUGhwSpLIItN8OMto5yLL+Xr+KA4h9E=,tag:Y4FU6/HYmu1IgLG4ChutZw==,type:str]", "mac": "ENC[AES256_GCM,data:T8GqsMxfFB9s1EOeLHNzxoz23FCOnlNsBsbvMxiLq7a78xt5Xw3dVN/IWfkyiCDwfSjo+fVx2yEd5tP/B3fSN7S8WJNSe5ZywLpal/RlsCzv7ARvbVCaBx22S4az97JsR1qQUcGSvoiTH5e/0t2tBtimGJ1witbvbiGkTBp8taw=,iv:Qs26cjeMLtRhTDO91yfBo93wUKJ9zVfUbJ8o6myHGUo=,tag:FbT8emz6q1QnXdxoX6hsYQ==,type:str]",
"pgp": [ "pgp": [
{ {
"created_at": "2025-08-24T23:36:17Z", "created_at": "2025-08-24T23:36:17Z",
@ -21,6 +21,6 @@
} }
], ],
"unencrypted_suffix": "_unencrypted", "unencrypted_suffix": "_unencrypted",
"version": "3.10.2" "version": "3.11.0"
} }
} }

View file

@ -37,9 +37,15 @@ in
}; };
# assure correct permissions # assure correct permissions
systemd.user.tmpfiles.rules = [ systemd.user.tmpfiles.settings."30-gpgagent".rules = {
"d ${homeDir}/.gnupg 700 ${mainUser} users" "${homeDir}/.gnupg" = {
]; d = {
group = "users";
user = mainUser;
mode = "0700";
};
};
};
}; };
} }

View file

@ -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"
];
}
];
};
};
};
}

View file

@ -22,6 +22,9 @@
simple-scan simple-scan
cura-appimage cura-appimage
# ssh login using idm
opkssh
# dict # dict
(aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ])) (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ]))

View file

@ -135,6 +135,7 @@ in
"firefly.access" = { }; "firefly.access" = { };
"radicale.access" = { }; "radicale.access" = { };
"slink.access" = { }; "slink.access" = { };
"opkssh.access" = { };
}; };
inherit (config.repo.secrets.local) persons; 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 = { oauth2-proxy = {
displayName = "Oauth2-Proxy"; displayName = "Oauth2-Proxy";
originUrl = "https://${oauth2ProxyDomain}/oauth2/callback"; originUrl = "https://${oauth2ProxyDomain}/oauth2/callback";

View file

@ -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;
}
];
};
};
}

View file

@ -76,9 +76,15 @@ in
}; };
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."10-radicale" = {
"d ${cfg.settings.storage.filesystem_folder} 0750 ${serviceUser} ${serviceGroup} - -" "${cfg.settings.storage.filesystem_folder}" = {
]; d = {
group = serviceGroup;
user = serviceUser;
mode = "0750";
};
};
};
networking.firewall.allowedTCPPorts = [ servicePort ]; networking.firewall.allowedTCPPorts = [ servicePort ];

View file

@ -48,13 +48,25 @@ in
]; ];
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."11-shlink" = builtins.listToAttrs (
"d ${serviceDir}/data 0750 1001 root - -" map
"d ${serviceDir}/data/cache 0750 1001 root - -" (path: {
"d ${serviceDir}/data/locks 0750 1001 root - -" name = "${serviceDir}/${path}";
"d ${serviceDir}/data/log 0750 1001 root - -" value = {
"d ${serviceDir}/data/proxies 0750 1001 root - -" 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 ]; networking.firewall.allowedTCPPorts = [ servicePort ];

View file

@ -29,10 +29,22 @@ in
]; ];
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs (
"d ${serviceDir}/var/data 0750 root root - -" map
"d ${serviceDir}/images 0750 root root - -" (path: {
]; name = "${serviceDir}/${path}";
value = {
d = {
group = "root";
user = "root";
mode = "0750";
};
};
}) [
"${serviceDir}/var/data"
"${serviceDir}/images"
]
);
networking.firewall.allowedTCPPorts = [ servicePort ]; networking.firewall.allowedTCPPorts = [ servicePort ];

View file

@ -37,6 +37,7 @@
obs-studio = lib.mkDefault true; obs-studio = lib.mkDefault true;
obsidian = lib.mkDefault true; obsidian = lib.mkDefault true;
obsidian-tray = lib.mkDefault true; obsidian-tray = lib.mkDefault true;
opkssh = lib.mkDefault true;
ownpackages = lib.mkDefault true; ownpackages = lib.mkDefault true;
packages = lib.mkDefault true; packages = lib.mkDefault true;
passwordstore = lib.mkDefault true; passwordstore = lib.mkDefault true;