mirror of
https://github.com/oddlama/nix-config.git
synced 2025-10-10 23:00:39 +02:00
feat: use kanidm secret provisioning
This commit is contained in:
parent
522de920bb
commit
7c48e51320
9 changed files with 126 additions and 105 deletions
92
README.md
92
README.md
|
@ -125,95 +125,3 @@ openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
|
||||||
-keyout selfcert.key -out selfcert.crt -subj \
|
-keyout selfcert.key -out selfcert.crt -subj \
|
||||||
"/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:sub1.example.com,DNS:sub2.example.com,IP:10.0.0.1"
|
"/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:sub1.example.com,DNS:sub2.example.com,IP:10.0.0.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
services.kanidm.provision = {
|
|
||||||
persons.myuser = {
|
|
||||||
legalname = "Full Name";
|
|
||||||
mail = "mail@example.com";
|
|
||||||
groups = ["grafana-access" "grafana-server-admins"];
|
|
||||||
};
|
|
||||||
|
|
||||||
groups.grafana-access = {};
|
|
||||||
groups.grafana-server-admins = {};
|
|
||||||
groups.grafana-admins = {};
|
|
||||||
groups.grafana-editors = {};
|
|
||||||
|
|
||||||
systems.oauth2.grafana = {
|
|
||||||
displayName = "Grafana";
|
|
||||||
originUrl = "https://grafana.${personalDomain}";
|
|
||||||
basicSecretFile = pkgs.writeText "bs" "verygoodsecret";
|
|
||||||
scopeMaps = {
|
|
||||||
grafana-access = ["openid" "email" "profile"];
|
|
||||||
};
|
|
||||||
supplementaryScopeMaps = {
|
|
||||||
grafana-server-admins = ["server_admin"];
|
|
||||||
grafana-admins = ["admin"];
|
|
||||||
grafana-editors = ["editor"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Recover admin account
|
|
||||||
kanidmd recover-account admin
|
|
||||||
> FrEELN4tfyVbUAfhGeuUyZyaKk8cbpFufuDwyCPhY3xhb3X2
|
|
||||||
# Login with recovered root account
|
|
||||||
kanidm login --name admin
|
|
||||||
# Generate new credentials for idm_admin account
|
|
||||||
kanidm service-account credential generate -D admin idm_admin
|
|
||||||
> Yk0W24SQGzkLp97DNxxExCcryDLvA7Q2dR0A7ZuaVQevLR6B
|
|
||||||
# Generate new oauth2 app for grafana
|
|
||||||
kanidm group create grafana-access
|
|
||||||
kanidm group create grafana-server-admins
|
|
||||||
kanidm group create grafana-admins
|
|
||||||
kanidm group create grafana-editors
|
|
||||||
kanidm system oauth2 create grafana "Grafana" https://grafana.${personalDomain}
|
|
||||||
kanidm system oauth2 update-scope-map grafana grafana-access openid email profile
|
|
||||||
kanidm system oauth2 update-sup-scope-map grafana grafana-server-admins server_admin
|
|
||||||
kanidm system oauth2 update-sup-scope-map grafana grafana-admins admin
|
|
||||||
kanidm system oauth2 update-sup-scope-map grafana grafana-editors editor
|
|
||||||
kanidm system oauth2 show-basic-secret grafana
|
|
||||||
# Generate new oauth2 app for proxied webapps
|
|
||||||
kanidm group create web-sentinel-access
|
|
||||||
kanidm group create web-sentinel-adguardhome-access
|
|
||||||
kanidm group create web-sentinel-influxdb-access
|
|
||||||
kanidm system oauth2 create web-sentinel "Web services" https://oauth2.${personalDomain}
|
|
||||||
kanidm system oauth2 update-scope-map web-sentinel web-sentinel-access openid email
|
|
||||||
kanidm system oauth2 update-sup-scope-map web-sentinel web-sentinel-adguardhome-access access_adguardhome
|
|
||||||
kanidm system oauth2 update-sup-scope-map web-sentinel web-sentinel-influxdb-access access_influxdb
|
|
||||||
kanidm system oauth2 show-basic-secret web-sentinel
|
|
||||||
# Generate new oauth2 app for forgejo
|
|
||||||
kanidm group create forgejo-access
|
|
||||||
kanidm group create forgejo-admins
|
|
||||||
kanidm system oauth2 create forgejo "Forgejo" https://git.${personalDomain}
|
|
||||||
kanidm system oauth2 update-scope-map forgejo forgejo-access openid email profile
|
|
||||||
kanidm system oauth2 update-sup-scope-map forgejo forgejo-server-admins server_admin
|
|
||||||
kanidm system oauth2 update-sup-scope-map forgejo forgejo-admins admin
|
|
||||||
kanidm system oauth2 update-sup-scope-map forgejo forgejo-editors editor
|
|
||||||
kanidm system oauth2 show-basic-secret forgejo
|
|
||||||
# Add new user
|
|
||||||
kanidm login --name idm_admin
|
|
||||||
kanidm person create myuser "My User"
|
|
||||||
kanidm person update myuser --legalname "Full Name" --mail "myuser@example.com"
|
|
||||||
kanidm group add-members grafana-access myuser
|
|
||||||
kanidm group add-members grafana-server-admins myuser
|
|
||||||
kanidm group add-members web-sentinel-access myuser
|
|
||||||
kanidm group add-members web-sentinel-adguardhome-access myuser
|
|
||||||
kanidm group add-members web-sentinel-influxdb-access myuser
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
nodes,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
meta.oauth2_proxy = {
|
meta.oauth2_proxy = {
|
||||||
|
@ -11,8 +12,27 @@
|
||||||
# TODO portal redirect to dashboard (in case someone clicks on kanidm "Web services")
|
# TODO portal redirect to dashboard (in case someone clicks on kanidm "Web services")
|
||||||
};
|
};
|
||||||
|
|
||||||
age.secrets.oauth2-proxy-secret = {
|
age.secrets.oauth2-cookie-secret = {
|
||||||
rekeyFile = ./secrets/oauth2-proxy-secret.age;
|
rekeyFile = ./secrets/oauth2-cookie-secret.age;
|
||||||
|
mode = "440";
|
||||||
|
group = "oauth2_proxy";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Mirror the original oauth2 secret, but prepend OAUTH2_PROXY_CLIENT_SECRET=
|
||||||
|
# so it can be used as an EnvironmentFile
|
||||||
|
age.secrets.oauth2-client-secret = {
|
||||||
|
generator.dependencies = [
|
||||||
|
nodes.ward-kanidm.config.age.secrets.kanidm-oauth2-web-sentinel
|
||||||
|
];
|
||||||
|
generator.script = {
|
||||||
|
lib,
|
||||||
|
decrypt,
|
||||||
|
deps,
|
||||||
|
...
|
||||||
|
}: ''
|
||||||
|
echo -n "OAUTH2_PROXY_CLIENT_SECRET="
|
||||||
|
${decrypt} ${lib.escapeShellArg (lib.head deps).file}
|
||||||
|
'';
|
||||||
mode = "440";
|
mode = "440";
|
||||||
group = "oauth2_proxy";
|
group = "oauth2_proxy";
|
||||||
};
|
};
|
||||||
|
@ -26,7 +46,7 @@
|
||||||
redeemURL = "https://${config.networking.providedDomains.kanidm}/oauth2/token";
|
redeemURL = "https://${config.networking.providedDomains.kanidm}/oauth2/token";
|
||||||
validateURL = "https://${config.networking.providedDomains.kanidm}/oauth2/openid/${clientId}/userinfo";
|
validateURL = "https://${config.networking.providedDomains.kanidm}/oauth2/openid/${clientId}/userinfo";
|
||||||
clientID = clientId;
|
clientID = clientId;
|
||||||
keyFile = config.age.secrets.oauth2-proxy-secret.path;
|
keyFile = config.age.secrets.oauth2-cookie-secret.path;
|
||||||
email.domains = ["*"];
|
email.domains = ["*"];
|
||||||
|
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
|
|
10
hosts/sentinel/secrets/oauth2-cookie-secret.age
Normal file
10
hosts/sentinel/secrets/oauth2-cookie-secret.age
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> X25519 KdpmgjrRS0ELGwUakn4bKF56nftZLenn3NB7PYgiNQE
|
||||||
|
52zchN0TRUP3/fdSTQ83aDi+0DZ07zxRRANNBe9i0IY
|
||||||
|
-> piv-p256 xqSe8Q An0xez98f0vVvi2E+pwwGzKOsI4HzQE7cJN59T8yl3n0
|
||||||
|
vvX2Yqergv0XqNOV37Qs4YUbCEGQbIF5O9NxkRpy11Q
|
||||||
|
-> S_J0JSh-grease ]
|
||||||
|
5Wf2tYlp7iszD54QfYkV95WGpcQ3HEeGACA3Y97NTr7uzUck4OPuKJwEwgK6pman
|
||||||
|
AjB3lmIusWODZvwnuAL3fG/X4JEOJ2T21eBp5/Qfg/TsvHGH
|
||||||
|
--- qjh6E4UM8Yd5zl8gOaQQJLk2AH+vDh7dCEv0ig0rO2k
|
||||||
|
P‡]7ã\ú¨¶¿p—˜�(:'3E˜]ºšéÄRw8/²Z&Jz2I¼#“†Koç‚�¦w ™‚qyW‹-«ìÚ/iÐØ)+
Â+fÓÇSFž(Y_ý�4 ¼ŸßÍÒº?ÂjØ2l®0#
|
Binary file not shown.
|
@ -20,6 +20,13 @@ in {
|
||||||
inherit (config.services.gitea) group;
|
inherit (config.services.gitea) group;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Mirror the original oauth2 secret
|
||||||
|
age.secrets.forgejo-oauth2-client-secret = {
|
||||||
|
inherit (nodes.ward-kanidm.config.age.secrets.kanidm-oauth2-forgejo) rekeyFile;
|
||||||
|
mode = "440";
|
||||||
|
inherit (config.services.gitea) group;
|
||||||
|
};
|
||||||
|
|
||||||
nodes.sentinel = {
|
nodes.sentinel = {
|
||||||
networking.providedDomains.forgejo = forgejoDomain;
|
networking.providedDomains.forgejo = forgejoDomain;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,13 @@ in {
|
||||||
group = "grafana";
|
group = "grafana";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Mirror the original oauth2 secret
|
||||||
|
age.secrets.grafana-oauth2-client-secret = {
|
||||||
|
inherit (nodes.ward-kanidm.config.age.secrets.kanidm-oauth2-grafana) rekeyFile;
|
||||||
|
mode = "440";
|
||||||
|
group = "grafana";
|
||||||
|
};
|
||||||
|
|
||||||
nodes.ward-influxdb = {
|
nodes.ward-influxdb = {
|
||||||
# Mirror the original secret on the influx host
|
# Mirror the original secret on the influx host
|
||||||
age.secrets."grafana-influxdb-token-${config.node.name}" = {
|
age.secrets."grafana-influxdb-token-${config.node.name}" = {
|
||||||
|
@ -100,8 +107,7 @@ in {
|
||||||
allow_sign_up = true;
|
allow_sign_up = true;
|
||||||
#auto_login = true;
|
#auto_login = true;
|
||||||
client_id = "grafana";
|
client_id = "grafana";
|
||||||
#client_secret = "$__file{${config.age.secrets.grafana-oauth-client-secret.path}}";
|
client_secret = "$__file{${config.age.secrets.grafana-oauth2-client-secret.path}}";
|
||||||
client_secret = "aZKNCM6KpjBy4RqwKJXMLXzyx9rKH6MZTFk4wYrKWuBqLj6t"; # TODO temporary test not a real secret
|
|
||||||
scopes = "openid email profile";
|
scopes = "openid email profile";
|
||||||
login_attribute_path = "prefered_username";
|
login_attribute_path = "prefered_username";
|
||||||
auth_url = "https://${sentinelCfg.networking.providedDomains.kanidm}/ui/oauth2";
|
auth_url = "https://${sentinelCfg.networking.providedDomains.kanidm}/ui/oauth2";
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
|
inherit (sentinelCfg.repo.secrets.local) personalDomain;
|
||||||
sentinelCfg = nodes.sentinel.config;
|
sentinelCfg = nodes.sentinel.config;
|
||||||
kanidmDomain = "auth.${sentinelCfg.repo.secrets.local.personalDomain}";
|
kanidmDomain = "auth.${personalDomain}";
|
||||||
kanidmPort = 8300;
|
kanidmPort = 8300;
|
||||||
in {
|
in {
|
||||||
meta.wireguard-proxy.sentinel.allowedTCPPorts = [kanidmPort];
|
meta.wireguard-proxy.sentinel.allowedTCPPorts = [kanidmPort];
|
||||||
|
@ -23,6 +24,27 @@ in {
|
||||||
group = "kanidm";
|
group = "kanidm";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
age.secrets.kanidm-oauth2-grafana = {
|
||||||
|
generator.script = "alnum";
|
||||||
|
generator.tags = ["oauth2"];
|
||||||
|
mode = "440";
|
||||||
|
group = "kanidm";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.kanidm-oauth2-forgejo = {
|
||||||
|
generator.script = "alnum";
|
||||||
|
generator.tags = ["oauth2"];
|
||||||
|
mode = "440";
|
||||||
|
group = "kanidm";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.kanidm-oauth2-web-sentinel = {
|
||||||
|
generator.script = "alnum";
|
||||||
|
generator.tags = ["oauth2"];
|
||||||
|
mode = "440";
|
||||||
|
group = "kanidm";
|
||||||
|
};
|
||||||
|
|
||||||
nodes.sentinel = {
|
nodes.sentinel = {
|
||||||
networking.providedDomains.kanidm = kanidmDomain;
|
networking.providedDomains.kanidm = kanidmDomain;
|
||||||
|
|
||||||
|
@ -49,7 +71,6 @@ in {
|
||||||
|
|
||||||
services.kanidm = {
|
services.kanidm = {
|
||||||
enableServer = true;
|
enableServer = true;
|
||||||
# enablePAM = true;
|
|
||||||
serverSettings = {
|
serverSettings = {
|
||||||
domain = kanidmDomain;
|
domain = kanidmDomain;
|
||||||
origin = "https://${kanidmDomain}";
|
origin = "https://${kanidmDomain}";
|
||||||
|
@ -58,18 +79,65 @@ in {
|
||||||
bindaddress = "0.0.0.0:${toString kanidmPort}";
|
bindaddress = "0.0.0.0:${toString kanidmPort}";
|
||||||
trust_x_forward_for = true;
|
trust_x_forward_for = true;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [pkgs.kanidm];
|
|
||||||
|
|
||||||
services.kanidm = {
|
|
||||||
enableClient = true;
|
enableClient = true;
|
||||||
clientSettings = {
|
clientSettings = {
|
||||||
uri = config.services.kanidm.serverSettings.origin;
|
uri = config.services.kanidm.serverSettings.origin;
|
||||||
verify_ca = true;
|
verify_ca = true;
|
||||||
verify_hostnames = true;
|
verify_hostnames = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provision = {
|
||||||
|
inherit (config.secrets.global.kanidm) persons;
|
||||||
|
|
||||||
|
# Grafana
|
||||||
|
groups.grafana = {};
|
||||||
|
groups."grafana.admins" = {};
|
||||||
|
groups."grafana.editors" = {};
|
||||||
|
groups."grafana.server-admins" = {};
|
||||||
|
systems.oauth2.grafana = {
|
||||||
|
displayName = "Grafana";
|
||||||
|
originUrl = "https://${config.networking.providedDomains.grafana}";
|
||||||
|
basicSecretFile = config.age.secrets.kanidm-oauth2-grafana.path;
|
||||||
|
scopeMaps.grafana = ["openid" "email" "profile"];
|
||||||
|
supplementaryScopeMaps = {
|
||||||
|
"grafana.admins" = ["admin"];
|
||||||
|
"grafana.editors" = ["editor"];
|
||||||
|
"grafana.server-admins" = ["server_admin"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Forgejo
|
||||||
|
groups.forgejo = {};
|
||||||
|
groups."forgejo.admins" = {};
|
||||||
|
systems.oauth2.forgejo = {
|
||||||
|
displayName = "Forgejo";
|
||||||
|
originUrl = "https://${config.networking.providedDomains.forgejo}";
|
||||||
|
basicSecretFile = config.age.secrets.kanidm-oauth2-forgejo.path;
|
||||||
|
scopeMaps.forgejo = ["openid" "email" "profile"];
|
||||||
|
supplementaryScopeMaps = {
|
||||||
|
"forgejo.admins" = ["admin"];
|
||||||
|
"forgejo.editors" = ["editor"];
|
||||||
|
"forgejo.server-admins" = ["server_admin"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Web Sentinel
|
||||||
|
groups.web-sentinel = {};
|
||||||
|
groups."web-sentinel.adguardhome" = {};
|
||||||
|
systems.oauth2.web-sentinel = {
|
||||||
|
displayName = "Web Sentinel";
|
||||||
|
originUrl = "https://oauth2.${personalDomain}";
|
||||||
|
basicSecretFile = config.age.secrets.kanidm-oauth2-web-sentinel.path;
|
||||||
|
scopeMaps.web-sentinel = ["openid" "email"];
|
||||||
|
supplementaryScopeMaps = {
|
||||||
|
"web-sentinel.adguardhome" = ["access_adguardhome"];
|
||||||
|
"web-sentinel.influxdb" = ["access_influxdb"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.grafana.serviceConfig.RestartSec = "60"; # Retry every minute
|
environment.systemPackages = [pkgs.kanidm];
|
||||||
|
systemd.services.kanidm.serviceConfig.RestartSec = "60"; # Retry every minute
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ in {
|
||||||
networking.providedDomains = mergeFromOthers ["networking" "providedDomains"];
|
networking.providedDomains = mergeFromOthers ["networking" "providedDomains"];
|
||||||
services.nginx.upstreams = mergeFromOthers ["services" "nginx" "upstreams"];
|
services.nginx.upstreams = mergeFromOthers ["services" "nginx" "upstreams"];
|
||||||
services.nginx.virtualHosts = mergeFromOthers ["services" "nginx" "virtualHosts"];
|
services.nginx.virtualHosts = mergeFromOthers ["services" "nginx" "virtualHosts"];
|
||||||
services.influxdb2.provision.organizations = mergeFromOthers ["services" "influxdb2" "organizations"];
|
services.influxdb2.provision.organizations = mergeFromOthers ["services" "influxdb2" "provision" "organizations"];
|
||||||
|
services.kanidm.provision.groups = mergeFromOthers ["services" "kanidm" "provision" "groups"];
|
||||||
|
services.kanidm.provision.systems.oauth2 = mergeFromOthers ["services" "kanidm" "provision" "systems" "oauth2"];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue