diff --git a/modules/homelab/reverse-proxy.nix b/modules/homelab/reverse-proxy.nix index 7dd7019e..f7c3f0c6 100644 --- a/modules/homelab/reverse-proxy.nix +++ b/modules/homelab/reverse-proxy.nix @@ -63,6 +63,7 @@ in "FR" "CH" "US" + "BE" ]; in { diff --git a/modules/homelab/security/authelia/lib.nix b/modules/homelab/security/authelia/lib.nix index 0f425bfc..74be2f34 100644 --- a/modules/homelab/security/authelia/lib.nix +++ b/modules/homelab/security/authelia/lib.nix @@ -7,7 +7,16 @@ let lib.unique ( lib.concatLists ( lib.mapAttrsToList ( - _: c: map (uri: lib.head (lib.match "(https?://[^/]+).*" uri)) c.redirectUris + _: c: + lib.concatLists ( + map ( + uri: + let + match = lib.match "(https?://[^/]+).*" uri; + in + lib.optional (match != null) (lib.head match) + ) c.redirectUris + ) ) clients ) ); diff --git a/modules/homelab/services/immich/default.nix b/modules/homelab/services/immich/default.nix index e692bdd4..f1b18d7f 100644 --- a/modules/homelab/services/immich/default.nix +++ b/modules/homelab/services/immich/default.nix @@ -15,7 +15,6 @@ let immichPort = 2283; immichDbUser = "postgres"; immichDbName = "immich"; - immichOidcClientId = config.flake.meta.oidc-clients.immich.clientId; immichConfig = { storageTemplate = { @@ -25,17 +24,28 @@ let }; oauth = { enabled = true; - issuerUrl = "https://${hosts.auth}"; - clientId = immichOidcClientId; - clientSecret = "PLACEHOLDER"; - scope = "openid profile email"; - autoLaunch = true; + # Set to true once SSO is confirmed working. + autoLaunch = false; autoRegister = true; - mobileOverrideEnabled = true; - mobileRedirectUri = "https://${hosts.immich}/api/oauth/mobile-redirect"; + buttonText = "Login with Authelia"; + clientId = config.flake.meta.oidc-clients.immich.clientId; + clientSecret = ""; + defaultStorageQuota = 0; + issuerUrl = "https://${hosts.auth}"; + mobileOverrideEnabled = false; + mobileRedirectUri = ""; + profileSigningAlgorithm = "none"; + roleClaim = "immich_role"; + scope = "openid email profile"; signingAlgorithm = "RS256"; + storageLabelClaim = "preferred_username"; + storageQuotaClaim = "immich_quota"; + timeout = 30000; tokenEndpointAuthMethod = "client_secret_post"; }; + passwordLogin = { + enabled = true; + }; }; in { @@ -62,9 +72,15 @@ in redirectUris = [ "https://${hosts.immich}/auth/login" "https://${hosts.immich}/user-settings" - "https://${hosts.immich}/api/oauth/mobile-redirect" + "app.immich:///oauth-callback" ]; secretName = "autheliaImmichOidcSecret"; + extraYamlLines = [ + ''token_endpoint_auth_method: "client_secret_post"'' + "require_pkce: false" + ''id_token_signed_response_alg: "RS256"'' + ''userinfo_signed_response_alg: "RS256"'' + ]; }; flake.modules.nixos.homelab-immich = { @@ -93,6 +109,7 @@ in "d ${immichAppDir}/ml-models 0750 ${immichUser} ${immichGroup} -" "d ${immichAppDir}/ml-dotcache 0750 ${immichUser} ${immichGroup} -" "d ${immichAppDir}/ml-config 0750 ${immichUser} ${immichGroup} -" + "d ${immichAppDir}/config 0750 ${immichUser} ${immichGroup} -" "d ${immichDataDir} 0750 ${immichUser} ${immichGroup} -" "d ${immichDataDir}/postgresql 0750 ${immichUser} ${immichGroup} -" "d ${immichDataDir}/postgresql/data 0750 ${immichUser} ${immichGroup} -" @@ -179,7 +196,12 @@ in }; flake.modules.homeManager.homelab-immich = - { osConfig, pkgs, ... }: + { + osConfig, + pkgs, + lib, + ... + }: let sharedEnv = { DB_HOSTNAME = "immich-db"; @@ -190,32 +212,32 @@ in REDIS_HOSTNAME = "immich-redis"; REDIS_PORT = "6379"; TZ = osConfig.time.timeZone; - IMMICH_CONFIG_FILE = "/tmp/immich.json"; }; sharedSecrets = { DB_PASSWORD = osConfig.services.onepassword-secrets.secretPaths.immichDbPassword; - IMMICH_OAUTH_CLIENT_SECRET = - osConfig.services.onepassword-secrets.secretPaths.immichOidcClientSecret; }; - immichConfigFile = pkgs.writeText "immich-config.json" (builtins.toJSON immichConfig); - - entrypointScript = pkgs.writeTextFile { - name = "immich-entrypoint"; - executable = true; - text = '' - #!/bin/sh - set -e - node -e " - const fs = require('fs'); - const config = JSON.parse(fs.readFileSync('/config/immich.json', 'utf8')); - config.oauth.clientSecret = process.env.IMMICH_OAUTH_CLIENT_SECRET; - fs.writeFileSync('/tmp/immich.json', JSON.stringify(config)); - " - exec "$@" - ''; - }; + immichBaseConfigFile = pkgs.writeText "immich-config-base.json" (builtins.toJSON immichConfig); + + immichRuntimeConfigPath = "${immichAppDir}/config/immich.json"; + + immichGenerateConfig = lib.getExe ( + pkgs.writeShellApplication { + name = "immich-generate-config"; + runtimeInputs = [ + pkgs.coreutils + pkgs.jq + ]; + text = '' + install -D -m 600 /dev/null "${immichRuntimeConfigPath}" + ${lib.getExe pkgs.jq} \ + --arg clientSecret "$(<${osConfig.services.onepassword-secrets.secretPaths.immichOidcClientSecret})" \ + '.oauth.clientSecret = $clientSecret' \ + "${immichBaseConfigFile}" > "${immichRuntimeConfigPath}" + ''; + } + ); in { config = { @@ -247,13 +269,13 @@ in volumes = [ "${immichAppDir}/photos:/data" "/etc/localtime:/etc/localtime:ro" - "${immichConfigFile}:/config/immich.json:ro" - "${entrypointScript}:/entrypoint.sh:ro" + "${immichRuntimeConfigPath}:/config/immich.json:ro" ]; environment = sharedEnv // { - # Set to "false" after initial admin registration to disable /auth/admin-sign-up - IMMICH_ALLOW_SETUP = "false"; + IMMICH_CONFIG_FILE = "/config/immich.json"; + # TODO: Set to "false" after creating the admin account. + IMMICH_ALLOW_SETUP = "true"; IMMICH_TRUSTED_PROXIES = "10.89.0.0/16"; }; @@ -264,12 +286,14 @@ in LogDriver = "journald"; SecurityLabelDisable = false; NoNewPrivileges = true; - Entrypoint = [ "/entrypoint.sh" ]; # HealthCmd = "wget --no-verbose --tries=1 --spider http://localhost:2283/api/server/ping || exit 1"; # HealthInterval = "30s"; # HealthTimeout = "10s"; # HealthRetries = 3; }; + Service = { + ExecStartPre = [ "${immichGenerateConfig}" ]; + }; }; }; diff --git a/modules/homelab/services/job-ops/README.md b/modules/homelab/services/job-ops/README.md index f9612488..70082520 100644 --- a/modules/homelab/services/job-ops/README.md +++ b/modules/homelab/services/job-ops/README.md @@ -8,9 +8,9 @@ AI-powered job application assistant running in Podman. services.job-ops = { enable = true; port = 3001; # default: 3001 - model = "deepseek-v4-flash-free"; # default + model = "deepseek-v4-flash"; # default llmProvider = "openai_compatible"; # default - llmBaseUrl = "https://opencode.ai/zen/v1/chat/completions"; + llmBaseUrl = "https://opencode.ai/zen/go/v1/chat/completions"; llmApiKeyFile = /path/to/api/key; publicBaseUrl = "https://jobs.example.com"; diff --git a/modules/homelab/services/job-ops/default.nix b/modules/homelab/services/job-ops/default.nix index 712c8a79..59d0598f 100644 --- a/modules/homelab/services/job-ops/default.nix +++ b/modules/homelab/services/job-ops/default.nix @@ -15,9 +15,9 @@ let jobOpsImage = "ghcr.io/dakheera47/job-ops:v0.11.0"; jobOpsPort = 3001; - jobOpsModel = "deepseek-v4-flash-free"; + jobOpsModel = "deepseek-v4-flash"; jobOpsLlmProvider = "openai_compatible"; - jobOpsLlmBaseUrl = "https://opencode.ai/zen/v1/chat/completions"; + jobOpsLlmBaseUrl = "https://opencode.ai/zen/go/v1/chat/completions"; jobOpsPublicBaseUrl = "https://${hosts.jobs}"; jobOpsBasicAuthUser = "admin"; diff --git a/modules/homelab/services/sure-finance/default.nix b/modules/homelab/services/sure-finance/default.nix index 84a8ca94..d6494849 100644 --- a/modules/homelab/services/sure-finance/default.nix +++ b/modules/homelab/services/sure-finance/default.nix @@ -210,8 +210,8 @@ in OIDC_ISSUER = "https://${hosts.auth}"; OIDC_REDIRECT_URI = "https://${hosts.finance}/auth/openid_connect/callback"; OIDC_BUTTON_LABEL = "Sign in with Authelia"; - OPENAI_MODEL = "mimo-v2.5-free"; - OPENAI_URI_BASE = "https://opencode.ai/zen/v1"; + OPENAI_MODEL = "mimo-v2.5"; + OPENAI_URI_BASE = "https://opencode.ai/zen/go/v1"; ONBOARDING_STATE = "closed"; POSTGRES_USER = sureFinanceDbUser; POSTGRES_DB = sureFinanceDbName;