diff --git a/hosts/koi/configuration.nix b/hosts/koi/configuration.nix index 668167d..a9d8f78 100755 --- a/hosts/koi/configuration.nix +++ b/hosts/koi/configuration.nix @@ -33,13 +33,15 @@ ./containers/navidrome ./containers/conduwuit ./containers/zond - ./containers/kanidm + ./containers/zitadel ./containers/siyuan ./containers/memos ./containers/wakapi + ./containers/outline ./containers/teisu.nix ./containers/bots/pcre-sub-bot.nix ./containers/bots/channel-logger-bot.nix + ./containers/bots/bsky-crossposter ./vms/hass.nix ./vms/bnuuy.nix # ./vms/windows.nix @@ -80,6 +82,8 @@ "8.8.8.8" "8.8.4.4" ]; + + firewall.logRefusedConnections = false; }; virtualisation.libvirtd = { @@ -102,7 +106,7 @@ }]; 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; services.desu-deploy = { diff --git a/hosts/koi/containers/kanidm/default.nix b/hosts/koi/containers/kanidm/default.nix deleted file mode 100644 index 947a041..0000000 --- a/hosts/koi/containers/kanidm/default.nix +++ /dev/null @@ -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; - }; - }; -} \ No newline at end of file diff --git a/hosts/koi/containers/kanidm/fish.png b/hosts/koi/containers/kanidm/fish.png deleted file mode 100644 index 05db6c1..0000000 Binary files a/hosts/koi/containers/kanidm/fish.png and /dev/null differ diff --git a/hosts/koi/containers/kanidm/note.md b/hosts/koi/containers/kanidm/note.md deleted file mode 100644 index 6fc45eb..0000000 --- a/hosts/koi/containers/kanidm/note.md +++ /dev/null @@ -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=... -``` \ No newline at end of file diff --git a/hosts/koi/containers/kanidm/server.toml b/hosts/koi/containers/kanidm/server.toml deleted file mode 100644 index 6c857e6..0000000 --- a/hosts/koi/containers/kanidm/server.toml +++ /dev/null @@ -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" \ No newline at end of file diff --git a/hosts/koi/containers/kanidm/style.css b/hosts/koi/containers/kanidm/style.css deleted file mode 100644 index 0f8a9d0..0000000 --- a/hosts/koi/containers/kanidm/style.css +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/hosts/koi/containers/siyuan/default.nix b/hosts/koi/containers/siyuan/default.nix index b08efa2..39df5ba 100644 --- a/hosts/koi/containers/siyuan/default.nix +++ b/hosts/koi/containers/siyuan/default.nix @@ -33,7 +33,7 @@ in { desu.secrets.siyuan-teidesu-proxy-env.owner = "siyuan-teidesu"; desu.openid-proxy.services.siyuan-teidesu = { - clientId = "teidesu-siyuan"; + clientId = "299749237216837638"; domain = "siyuan.tei.su"; upstream = "http://siyuan-teidesu.docker:6806"; envSecret = "siyuan-teidesu-proxy-env"; diff --git a/hosts/koi/containers/torrent.nix b/hosts/koi/containers/torrent.nix index 95e92b2..93fb909 100644 --- a/hosts/koi/containers/torrent.nix +++ b/hosts/koi/containers/torrent.nix @@ -76,7 +76,7 @@ in ]; desu.openid-proxy.services.torrent = { - clientId = "torrent"; + clientId = "299749111337385990"; domain = "torrent.stupid.fish"; upstream = "http://torrent.containers"; envSecret = "torrent-proxy-env"; diff --git a/hosts/koi/containers/zitadel/default.nix b/hosts/koi/containers/zitadel/default.nix new file mode 100644 index 0000000..2936a22 --- /dev/null +++ b/hosts/koi/containers/zitadel/default.nix @@ -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; + }; + }; +} \ No newline at end of file diff --git a/hosts/koi/containers/zitadel/init.sql b/hosts/koi/containers/zitadel/init.sql new file mode 100644 index 0000000..887eb3d --- /dev/null +++ b/hosts/koi/containers/zitadel/init.sql @@ -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) +); \ No newline at end of file diff --git a/hosts/koi/containers/kanidm/proxy.nix b/hosts/koi/containers/zitadel/proxy.nix similarity index 96% rename from hosts/koi/containers/kanidm/proxy.nix rename to hosts/koi/containers/zitadel/proxy.nix index 919d10b..e34b8c0 100644 --- a/hosts/koi/containers/kanidm/proxy.nix +++ b/hosts/koi/containers/zitadel/proxy.nix @@ -61,7 +61,7 @@ in { "--client-id=${service.clientId}" "--upstream=${service.upstream}" "--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; }; }) (builtins.attrNames cfg.services) diff --git a/hosts/koi/vms/hass.nix b/hosts/koi/vms/hass.nix index 627015f..7d0821d 100644 --- a/hosts/koi/vms/hass.nix +++ b/hosts/koi/vms/hass.nix @@ -24,7 +24,7 @@ in desu.secrets.hass-proxy-env = {}; desu.openid-proxy.services.hass = { - clientId = "hass"; + clientId = "299748893099360262"; domain = "hass.stupid.fish"; upstream = "http://10.42.0.3:8123"; envSecret = "hass-proxy-env"; diff --git a/secrets/hass-proxy-env.age b/secrets/hass-proxy-env.age index cffd679..dc7d7d7 100644 Binary files a/secrets/hass-proxy-env.age and b/secrets/hass-proxy-env.age differ diff --git a/secrets/sftpgo-env.age b/secrets/sftpgo-env.age index 06a18d4..12a40a2 100644 Binary files a/secrets/sftpgo-env.age and b/secrets/sftpgo-env.age differ diff --git a/secrets/siyuan-teidesu-proxy-env.age b/secrets/siyuan-teidesu-proxy-env.age index 559f107..a42cd26 100644 Binary files a/secrets/siyuan-teidesu-proxy-env.age and b/secrets/siyuan-teidesu-proxy-env.age differ diff --git a/secrets/torrent-proxy-env.age b/secrets/torrent-proxy-env.age index 5c7e43d..85e51fc 100644 Binary files a/secrets/torrent-proxy-env.age and b/secrets/torrent-proxy-env.age differ diff --git a/secrets/zitadel-env.age b/secrets/zitadel-env.age new file mode 100644 index 0000000..3bb19dd --- /dev/null +++ b/secrets/zitadel-env.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> ssh-ed25519 sj88Xw KHqbr2MMzik5ygZw4RC65UAX6QB8/y8m761UtWLaLFo +WWW15wcaj1HDWy/T9TQyp+MAR+pzfX41sCWq4Rj6THs +--- MJrS0sFAtch6GH83x5r1yJ9ogvYti0drZR8Pdx1Y60U +s4Aɰw wLE' >\.opT6~qMz:)iS%"<_cƍ߭cr;@Mu %U^04QNeQ+)N=+ArfbWr ++}e'9!9Jځ 0tl^S=Uܳr"_7 \ No newline at end of file