208 lines
6 KiB
Nix
208 lines
6 KiB
Nix
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
mkIf
|
|
mkOption
|
|
mkMerge
|
|
attrNames
|
|
mapAttrsToList
|
|
;
|
|
inherit (lib.types)
|
|
str
|
|
nullOr
|
|
submodule
|
|
listOf
|
|
attrsOf
|
|
;
|
|
cfg = config.xyno.services.oauth2Proxy;
|
|
kanidmCfg = config.xyno.services.kanidm;
|
|
oauth2ProxyInternalHostPort = "127.0.0.4:4180";
|
|
oauth2ProxyInternalHostPortMetrics = "127.0.0.4:4181";
|
|
in
|
|
{
|
|
options.xyno.services.oauth2Proxy = {
|
|
domain = mkOption {
|
|
default = "oauth.xyno.systems";
|
|
type = str;
|
|
};
|
|
hosts = mkOption {
|
|
type = attrsOf (
|
|
submodule (
|
|
{ name, ... }:
|
|
{
|
|
options.allowedGroups = mkOption {
|
|
type = nullOr (listOf str);
|
|
default = null;
|
|
};
|
|
options.allowed_email_domains = mkOption {
|
|
type = nullOr (listOf str);
|
|
default = null;
|
|
};
|
|
options.allowed_emails = mkOption {
|
|
type = nullOr (listOf str);
|
|
default = null;
|
|
};
|
|
options.middlewares = mkOption {
|
|
type = listOf str;
|
|
description = "add to your service";
|
|
default = [
|
|
"oauth-errors"
|
|
"oauth-host-${name}"
|
|
];
|
|
};
|
|
}
|
|
)
|
|
);
|
|
example = {
|
|
"navidrome.xyno.systems" = {
|
|
allowedGroups = [ "navidrome_access@idm.xyno.systems" ];
|
|
};
|
|
};
|
|
default = { };
|
|
};
|
|
};
|
|
config = mkIf (cfg.enable && config.xyno.services.kanidm.enable) {
|
|
services.kanidm.provision = {
|
|
groups = {
|
|
proxy_users.members = [ "application_admins" ];
|
|
};
|
|
systems.oauth2.oauth2_proxy = {
|
|
displayName = "oauth2 proxy";
|
|
originUrl = [
|
|
"https://${cfg.domain}/oauth2/callback"
|
|
]
|
|
++ (mapAttrsToList (n: v: "https://${n}/oauth2/callback") cfg.hosts);
|
|
originLanding = "https://${cfg.domain}/oauth2/sign_in";
|
|
preferShortUsername = true;
|
|
claimMaps = {
|
|
"proxy_group" = {
|
|
joinType = "array";
|
|
valuesByGroup = {
|
|
"proxy_users" = [
|
|
"proxy_users"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
scopeMaps."proxy_users" = [
|
|
"email"
|
|
"openid"
|
|
];
|
|
};
|
|
};
|
|
xyno.services.kanidm.templates.oauth2_proxy = {
|
|
wantedBy = [
|
|
"oauth2-proxy.service"
|
|
];
|
|
text = p: ''
|
|
OAUTH2_PROXY_CLIENT_ID=${p.clientId}
|
|
OAUTH2_PROXY_CLIENT_SECRET=${p.basicSecret}
|
|
OAUTH2_PROXY_COOKIE_SECRET=${p.env "COOKIE_SECRET"}
|
|
OAUTH2_PROXY_OIDC_ISSUER_URL=https://${kanidmCfg.domain}/oauth2/openid/${p.clientId}
|
|
'';
|
|
environmentFiles = [ config.sops.templates.oauth2ProxyEnv.path ];
|
|
};
|
|
sops.secrets."oauth2Proxy/cookieSecret" = {
|
|
sopsFile = ../../../instances/${config.networking.hostName}/secrets/kanidm.yaml;
|
|
};
|
|
sops.templates."oauth2ProxyEnv" = {
|
|
restartUnits = [ "generate-kanidm-template-oauth2_proxy.service" ];
|
|
content = ''
|
|
COOKIE_SECRET=${config.sops.placeholder."oauth2Proxy/cookieSecret"}
|
|
'';
|
|
};
|
|
|
|
xyno.services.monitoring.exporters.oauth2Proxy = "http://${oauth2ProxyInternalHostPortMetrics}";
|
|
|
|
systemd.services.oauth2Proxy.after = [ "traefik.service" ];
|
|
xyno.services.oauth2Proxy = {
|
|
environmentFiles = [ kanidmCfg.templates.oauth2_proxy.path ];
|
|
settings = mkMerge [
|
|
{
|
|
provider = "oidc";
|
|
scope = "openid email";
|
|
oidc_groups_claim = "proxy_group";
|
|
allowed_groups = [ "proxy_users" ];
|
|
|
|
http_address = "${oauth2ProxyInternalHostPort}";
|
|
https_address = "";
|
|
whitelist_domains = attrNames cfg.hosts;
|
|
email_domains = "*";
|
|
skip_provider_button = true;
|
|
code_challenge_method = "S256";
|
|
set_xauthrequest = true;
|
|
}
|
|
(mkIf config.xyno.services.monitoring.enable {
|
|
metrics_address = "http://${oauth2ProxyInternalHostPortMetrics}";
|
|
|
|
})
|
|
];
|
|
};
|
|
|
|
xyno.services.traefik.simpleProxy = mkMerge (
|
|
[
|
|
{
|
|
oauth = {
|
|
rule = "Host(`${cfg.domain}`) && PathPrefix(`/oauth2`)";
|
|
internal = "http://${oauth2ProxyInternalHostPort}";
|
|
middlewares = [ "auth-headers" ];
|
|
host = cfg.domain;
|
|
};
|
|
}
|
|
]
|
|
++ (mapAttrsToList (n: v: {
|
|
"oauth-host-${n}" = {
|
|
rule = "Host(`${n}`) && PathPrefix(`/oauth2`)";
|
|
internal = "http://${oauth2ProxyInternalHostPort}";
|
|
middlewares = [ "auth-headers" ];
|
|
host = n;
|
|
};
|
|
}) cfg.hosts)
|
|
);
|
|
services.traefik.dynamicConfigOptions.http.middlewares = mkMerge (
|
|
(mapAttrsToList (n: v: {
|
|
"oauth-host-${n}" =
|
|
let
|
|
maybeQueryArg =
|
|
name: value:
|
|
if name == "middlewares" || value == null then
|
|
null
|
|
else
|
|
"${name}=${lib.concatStringsSep "," (builtins.map lib.escapeURL value)}";
|
|
allArgs = lib.mapAttrsToList maybeQueryArg v;
|
|
cleanArgs = builtins.filter (x: x != null) allArgs;
|
|
cleanArgsStr = lib.concatStringsSep "&" cleanArgs;
|
|
in
|
|
{
|
|
forwardAuth = {
|
|
address = "https://${cfg.domain}/oauth2/auth?${cleanArgsStr}";
|
|
authResponseHeaders = [
|
|
"X-Auth-Request-User"
|
|
"X-Auth-Request-Groups"
|
|
"X-Auth-Request-Email"
|
|
"X-Auth-Request-Preferred-Username"
|
|
];
|
|
trustForwardHeader = true;
|
|
};
|
|
};
|
|
}) cfg.hosts)
|
|
++ [
|
|
{
|
|
auth-headers.headers = {
|
|
frameDeny = true;
|
|
contentTypeNosniff = true;
|
|
};
|
|
oauth-errors.errors = {
|
|
status = [ "401-403" ];
|
|
service = config.xyno.services.traefik.simpleProxy.oauth.serviceName;
|
|
query = "/oauth2/sign_in?rd={url}";
|
|
};
|
|
}
|
|
]
|
|
);
|
|
};
|
|
}
|