chore(koi): kanidm -> zitadel

This commit is contained in:
alina 🌸 2024-12-26 11:25:39 +03:00
parent 7832342642
commit 6f3fb407cb
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
17 changed files with 208 additions and 325 deletions

View file

@ -33,13 +33,15 @@
./containers/navidrome ./containers/navidrome
./containers/conduwuit ./containers/conduwuit
./containers/zond ./containers/zond
./containers/kanidm ./containers/zitadel
./containers/siyuan ./containers/siyuan
./containers/memos ./containers/memos
./containers/wakapi ./containers/wakapi
./containers/outline
./containers/teisu.nix ./containers/teisu.nix
./containers/bots/pcre-sub-bot.nix ./containers/bots/pcre-sub-bot.nix
./containers/bots/channel-logger-bot.nix ./containers/bots/channel-logger-bot.nix
./containers/bots/bsky-crossposter
./vms/hass.nix ./vms/hass.nix
./vms/bnuuy.nix ./vms/bnuuy.nix
# ./vms/windows.nix # ./vms/windows.nix
@ -80,6 +82,8 @@
"8.8.8.8" "8.8.8.8"
"8.8.4.4" "8.8.4.4"
]; ];
firewall.logRefusedConnections = false;
}; };
virtualisation.libvirtd = { virtualisation.libvirtd = {
@ -102,7 +106,7 @@
}]; }];
boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
boot.kernelParams = [ "panic=5" "mitigations=off" ]; boot.kernelParams = [ "panic=5" "panic_on_oops=1" "mitigations=off" ];
boot.kernelPackages = pkgs.linuxPackages_latest; boot.kernelPackages = pkgs.linuxPackages_latest;
services.desu-deploy = { services.desu-deploy = {

View file

@ -1,49 +0,0 @@
{ config, ... }:
let
UID = 1111;
in {
imports = [
./proxy.nix
];
desu.secrets.kanidm-tls-key.owner = "kanidm";
desu.secrets.kanidm-tls-cert.owner = "kanidm";
users.users.kanidm = {
isNormalUser = true;
uid = UID;
};
virtualisation.oci-containers.containers.kanidm = {
image = "kanidm/server:1.4.2";
volumes = [
# "/srv/kanidm/data:/data/db"
"${./server.toml}:/data/server.toml"
"${./style.css}:/hpkg/style.css"
"${./fish.png}:/hpkg/img/fish.png"
];
user = "${builtins.toString UID}";
extraOptions = [
"--mount=type=bind,source=/srv/kanidm/data,target=/data/db"
"--mount=type=bind,source=${config.desu.secrets.kanidm-tls-key.path},target=/data/key.pem,readonly"
"--mount=type=bind,source=${config.desu.secrets.kanidm-tls-cert.path},target=/data/chain.pem,readonly"
];
};
systemd.tmpfiles.rules = [
"d /srv/kanidm/data 0700 ${builtins.toString UID} ${builtins.toString UID} -"
];
services.nginx.virtualHosts."id.stupid.fish" = {
forceSSL = true;
useACMEHost = "stupid.fish";
locations."/" = {
proxyPass = "https://kanidm.docker:8443$request_uri";
proxyWebsockets = true;
};
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View file

@ -1,22 +0,0 @@
notes for self:
## creating an oauth2 app:
```bash
kanidm system oauth2 create myapp myapp_display_name https://url.to.app
kanidm system oauth2 warning-insecure-client-disable-pkce myapp # optional, for oauth2-proxy
kanidm system oauth2 prefer-short-username myapp # optional
kanidm system oauth2 show-basic-secret myapp
kanidm system oauth2 add-redirect-url myapp https://url.to.app/oauth2/callback # the default path for oauth2-proxy
# adding users to the app
kanidm group create myapp_users
kanidm group add-members myapp_users teidesu
kanidm system oauth2 update-scope-map myapp myapp_users email openid profile
```
## oauth2 proxy env:
```bash
OAUTH2_PROXY_COOKIE_SECRET=...
OAUTH2_PROXY_CLIENT_SECRET=...
```

View file

@ -1,9 +0,0 @@
bindaddress = "0.0.0.0:8443"
adminbindpath = "/tmp/kanidm.sock"
trust_x_forward_for = true
db_path = "/data/db/kanidm.db"
tls_chain = "/data/chain.pem"
tls_key = "/data/key.pem"
domain = "id.stupid.fish"
origin = "https://id.stupid.fish"

View file

@ -1,239 +0,0 @@
:root {
--totp-width-and-height: 30px;
--totp-stroke-width: 60px;
}
html,
body {
height: 100%;
}
.form-cred-reset-body {
max-width: 500px;
}
#settings-window .form-cred-reset-body {
max-width: unset;
}
.form-signin {
max-width: 680px;
}
/*
* Sidebar
*/
.side-menu {
min-width: 180px;
}
.side-menu-item {
--icon-size: 24px;
padding: .4rem .7rem;
text-decoration: none;
&.active {
font-weight: 600;
}
&:hover,
&.active {
background-color: var(--bs-gray-300);
}
.icon-container img {
filter: invert(40%);
width: 100%;
height: 100%;
object-fit: contain;
}
}
/*
* Personal Settings sidemenu
*/
/*
* Navbar
*/
.kanidm_logo {
width: 12em;
height: 12em;
}
.identity-verification-container {
display: flex;
flex-direction: column;
max-width: fit-content;
align-items: center;
margin: auto;
}
.totp-display-container {
padding: 5px 10px;
display: flex;
flex-direction: row;
max-width: fit-content;
align-items: center;
margin: auto;
border-radius: 15px;
background: #21252915;
box-shadow: -5px -5px 11px #ededed, 5px 5px 11px #ffffff;
margin: 15px;
}
.totp-display {
font-size: 35px;
margin: 10px;
}
.totp-timer {
margin: 10px;
position: relative;
height: var(--totp-width-and-height);
width: var(--totp-width-and-height);
}
/* Removes SVG styling that would hide the time label */
.totp-timer__circle {
fill: none;
stroke: none;
}
.totp-timer__path-remaining {
stroke-width: var(--totp-stroke-width);
/* Makes sure the animation starts at the top of the circle */
transform: rotate(90deg);
transform-origin: center;
/* One second aligns with the speed of the countdown timer */
transition: 1s linear all;
stroke: currentColor;
}
.totp-timer__svg {
transform: scaleX(-1);
}
.totp-timer__path-remaining.green {
color: rgb(65, 184, 131);
}
.totp-timer__path-remaining.orange {
color: orange;
}
.totp-timer__path-remaining.red {
color: red;
}
.totp-timer__path-remaining.no-transition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
.card>a {
height: 150px;
}
.oauth2-img {
max-width: 100%;
max-height: 90%;
padding: 10px;
height: 100%;
}
.btn-tiny {
--bs-btn-padding-y: .05rem;
--bs-btn-padding-x: .4rem;
--bs-btn-font-size: .75rem;
}
#cred-update-commit-bar {
display: block;
/*
position: fixed;
bottom: .5rem;
left: 50%;
transform: translateX(-50%);
*/
background: white;
}
.icon-container {
padding: 2px;
width: var(--icon-size);
height: var(--icon-size);
}
/* stupid.fish customizations */
.kanidm_logo {
width: 0;
height: 0;
background: url(/pkg/img/fish.png) no-repeat;
background-size: contain;
padding: 4em 8em;
}
.footer .text-muted::after {
content: ' and some stupidity';
}
.form-signin h3 {
margin-bottom: 1em;
}
/* ayu mirage */
@media (prefers-color-scheme: dark) {
body {
background: #242936;
color: #cccac2;
--bs-body-bg: #242936;
--bs-body-color: #cccac2;
--bs-border-color: #707a8c45;
}
.form-control {
background-color: #1f2430;
}
.bg-light,
.bg-dark {
background-color: #1f2430 !important;
}
.text-muted {
color: #b8cfe680 !important;
}
.btn-dark {
--bs-btn-color: #cccac2;
--bs-btn-bg: #1f2430;
--bs-btn-border-color: #1f2430;
--bs-btn-hover-color: #cccac2;
--bs-btn-hover-bg: #2e3037;
--bs-btn-hover-border-color: #2e3037;
--bs-btn-focus-shadow-rgb: 66, 70, 73;
--bs-btn-active-color: #cccac2;
--bs-btn-active-bg: #2e3037;
--bs-btn-active-border-color: #2e3037;
--bs-btn-active-shadow: none;
--bs-btn-disabled-color: #b8cfe680;
--bs-btn-disabled-bg: #1f2430;
--bs-btn-disabled-border-color: #1f2430;
}
.alert-light {
--bs-alert-color: #cccac2;
--bs-alert-bg: #1f2430;
--bs-alert-border-color: transparent;
--bs-alert-link-color: #cccac2;
}
}

View file

@ -33,7 +33,7 @@ in {
desu.secrets.siyuan-teidesu-proxy-env.owner = "siyuan-teidesu"; desu.secrets.siyuan-teidesu-proxy-env.owner = "siyuan-teidesu";
desu.openid-proxy.services.siyuan-teidesu = { desu.openid-proxy.services.siyuan-teidesu = {
clientId = "teidesu-siyuan"; clientId = "299749237216837638";
domain = "siyuan.tei.su"; domain = "siyuan.tei.su";
upstream = "http://siyuan-teidesu.docker:6806"; upstream = "http://siyuan-teidesu.docker:6806";
envSecret = "siyuan-teidesu-proxy-env"; envSecret = "siyuan-teidesu-proxy-env";

View file

@ -76,7 +76,7 @@ in
]; ];
desu.openid-proxy.services.torrent = { desu.openid-proxy.services.torrent = {
clientId = "torrent"; clientId = "299749111337385990";
domain = "torrent.stupid.fish"; domain = "torrent.stupid.fish";
upstream = "http://torrent.containers"; upstream = "http://torrent.containers";
envSecret = "torrent-proxy-env"; envSecret = "torrent-proxy-env";

View file

@ -0,0 +1,55 @@
{ pkgs, config, ... }:
let
UID = 1122;
in {
imports = [
./proxy.nix
];
users.users.zitadel = {
isNormalUser = true;
uid = UID;
};
services.postgresql.ensureUsers = [
{ name = "zitadel"; ensureDBOwnership = true; }
];
services.postgresql.ensureDatabases = [ "zitadel" ];
desu.postgresql.ensurePasswords.zitadel = "zitadel";
desu.secrets.zitadel-env.owner = "zitadel";
virtualisation.oci-containers.containers.zitadel = {
image = "ghcr.io/zitadel/zitadel:v2.66.1";
cmd = [ "start-from-setup" "--masterkeyFromEnv" "--tlsMode" "external" ];
environment = {
"ZITADEL_DATABASE_POSTGRES_HOST" = "172.17.0.1";
"ZITADEL_DATABASE_POSTGRES_PORT" = "5432";
"ZITADEL_DATABASE_POSTGRES_DATABASE" = "zitadel";
"ZITADEL_DATABASE_POSTGRES_USER_USERNAME" = "zitadel";
"ZITADEL_DATABASE_POSTGRES_USER_PASSWORD" = "zitadel";
"ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE" = "disable";
"ZITADEL_EXTERNALSECURE" = "true";
"ZITADEL_EXTERNALDOMAIN" = "id.stupid.fish";
"ZITADEL_EXTERNALPORT" = "443";
"ZITADEL_TLS_ENABLED" = "false";
"ZITADEL_WEBAUTHNNAME" = "stupid.fish";
};
environmentFiles = [
config.desu.secrets.zitadel-env.path
];
user = builtins.toString UID;
};
systemd.services.docker-zitadel.requires = [ "postgresql.service" ];
services.nginx.virtualHosts."id.stupid.fish" = {
forceSSL = true;
useACMEHost = "stupid.fish";
locations."/" = {
proxyPass = "http://zitadel.docker:8080$request_uri";
proxyWebsockets = true;
};
};
}

View file

@ -0,0 +1,137 @@
CREATE SCHEMA IF NOT EXISTS eventstore;
CREATE SCHEMA IF NOT EXISTS projections;
CREATE SCHEMA IF NOT EXISTS system;
CREATE TABLE IF NOT EXISTS system.encryption_keys (
id TEXT NOT NULL
, key TEXT NOT NULL
, PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS eventstore.events2 (
instance_id TEXT NOT NULL
, aggregate_type TEXT NOT NULL
, aggregate_id TEXT NOT NULL
, event_type TEXT NOT NULL
, "sequence" BIGINT NOT NULL
, revision SMALLINT NOT NULL
, created_at TIMESTAMPTZ NOT NULL
, payload JSONB
, creator TEXT NOT NULL
, "owner" TEXT NOT NULL
, "position" DECIMAL NOT NULL
, in_tx_order INTEGER NOT NULL
, PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence")
);
CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id);
CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type);
CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position");
-- represents an event to be created.
DO $$ BEGIN
CREATE TYPE eventstore.command AS (
instance_id TEXT
, aggregate_type TEXT
, aggregate_id TEXT
, command_type TEXT
, revision INT2
, payload JSONB
, creator TEXT
, owner TEXT
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE OR REPLACE FUNCTION eventstore.commands_to_events(commands eventstore.command[]) RETURNS SETOF eventstore.events2 VOLATILE AS $$
SELECT
c.instance_id
, c.aggregate_type
, c.aggregate_id
, c.command_type AS event_type
, cs.sequence + ROW_NUMBER() OVER (PARTITION BY c.instance_id, c.aggregate_type, c.aggregate_id ORDER BY c.in_tx_order) AS sequence
, c.revision
, NOW() AS created_at
, c.payload
, c.creator
, cs.owner
, EXTRACT(EPOCH FROM NOW()) AS position
, c.in_tx_order
FROM (
SELECT
c.instance_id
, c.aggregate_type
, c.aggregate_id
, c.command_type
, c.revision
, c.payload
, c.creator
, c.owner
, ROW_NUMBER() OVER () AS in_tx_order
FROM
UNNEST(commands) AS c
) AS c
JOIN (
SELECT
cmds.instance_id
, cmds.aggregate_type
, cmds.aggregate_id
, CASE WHEN (e.owner IS NOT NULL OR e.owner <> '') THEN e.owner ELSE command_owners.owner END AS owner
, COALESCE(MAX(e.sequence), 0) AS sequence
FROM (
SELECT DISTINCT
instance_id
, aggregate_type
, aggregate_id
, owner
FROM UNNEST(commands)
) AS cmds
LEFT JOIN eventstore.events2 AS e
ON cmds.instance_id = e.instance_id
AND cmds.aggregate_type = e.aggregate_type
AND cmds.aggregate_id = e.aggregate_id
JOIN (
SELECT
DISTINCT ON (
instance_id
, aggregate_type
, aggregate_id
)
instance_id
, aggregate_type
, aggregate_id
, owner
FROM
UNNEST(commands)
) AS command_owners ON
cmds.instance_id = command_owners.instance_id
AND cmds.aggregate_type = command_owners.aggregate_type
AND cmds.aggregate_id = command_owners.aggregate_id
GROUP BY
cmds.instance_id
, cmds.aggregate_type
, cmds.aggregate_id
, 4 -- owner
) AS cs
ON c.instance_id = cs.instance_id
AND c.aggregate_type = cs.aggregate_type
AND c.aggregate_id = cs.aggregate_id
ORDER BY
in_tx_order;
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION eventstore.push(commands eventstore.command[]) RETURNS SETOF eventstore.events2 VOLATILE AS $$
INSERT INTO eventstore.events2
SELECT * FROM eventstore.commands_to_events(commands)
RETURNING *
$$ LANGUAGE SQL;
CREATE SEQUENCE IF NOT EXISTS eventstore.system_seq;
CREATE TABLE IF NOT EXISTS eventstore.unique_constraints (
instance_id TEXT,
unique_type TEXT,
unique_field TEXT,
PRIMARY KEY (instance_id, unique_type, unique_field)
);

View file

@ -61,7 +61,7 @@ in {
"--client-id=${service.clientId}" "--client-id=${service.clientId}"
"--upstream=${service.upstream}" "--upstream=${service.upstream}"
"--redirect-url=https://${service.domain}/oauth2/callback" "--redirect-url=https://${service.domain}/oauth2/callback"
"--oidc-issuer-url=https://id.stupid.fish/oauth2/openid/${service.clientId}" "--oidc-issuer-url=https://id.stupid.fish"
] ++ service.extra; ] ++ service.extra;
}; };
}) (builtins.attrNames cfg.services) }) (builtins.attrNames cfg.services)

View file

@ -24,7 +24,7 @@ in
desu.secrets.hass-proxy-env = {}; desu.secrets.hass-proxy-env = {};
desu.openid-proxy.services.hass = { desu.openid-proxy.services.hass = {
clientId = "hass"; clientId = "299748893099360262";
domain = "hass.stupid.fish"; domain = "hass.stupid.fish";
upstream = "http://10.42.0.3:8123"; upstream = "http://10.42.0.3:8123";
envSecret = "hass-proxy-env"; envSecret = "hass-proxy-env";

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

6
secrets/zitadel-env.age Normal file
View file

@ -0,0 +1,6 @@
age-encryption.org/v1
-> ssh-ed25519 sj88Xw KHqbr2MMzik5ygZw4RC65UAX6QB8/y8m761UtWLaLFo
WWW15wcaj1HDWy/T9TQyp+MAR+pzfX41sCWq4Rj6THs
--- MJrS0sFAtch6GH83x5r1yJ9ogvYti0drZR8Pdx1Y60U
s4ÊAÉ°wìÐ «wLæEžî'¿ç  >\<5C>Ð.×öoªpT6~<7E>©Ûº¦<1D>¼qMzô:ÞÐÃö‡ìÕ)iSš%œÿ"<œñÿæ_¥ÍcýäÆ<C3A4>ß­cr¯;µœÌ@Mu Öä%U^0À€¡µ¡ƒò­4µ£ºÂQNe”¨Q¡ßÉ+ù)N=â+AÐÕþrfbWœážÚr»
+}e9¤Çâ¿!9„JÚÚ<C39A> …0tl½•½^î¦S†=ÊUܳ¥r"âÑ_7