Port to NixOS: replace Helm chart with flake-based NixOS config
Replaces the Helm/k3s setup with a declarative NixOS configuration targeting
a Raspberry Pi 4. Services run as podman containers under systemd, with data
on an external HD at /mnt/data. Key components:
- flake.nix: multi-host flake with pi-main (aarch64) and a placeholder for a
second machine
- modules/common.nix: shared system config (nix, podman, sops, SSH)
- modules/storage.nix: external HD mount with per-service subdirs
- modules/caddy.nix: Caddy with cloudflare DNS-01 ACME + authelia forward_auth
- modules/cloudflared.nix: Cloudflare tunnel for remote access
- modules/backup.nix: restic daily backups with NC maintenance mode pre-hook
- modules/services/{openldap,authelia,gitea,nextcloud,phpldapadmin}.nix: core services
- modules/services/{jellyfin,transmission}.nix: media services (disabled by default)
- secrets/: sops-nix scaffold with .sops.yaml age key config
- hosts/pi-main/: hardware config + service selection for the Pi
- PORTING.md: step-by-step migration guide (SD card → data restore → verify)
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
{ config, lib, pkgs, homeyConfig, ... }:
|
||||
|
||||
# Authelia — SSO gateway.
|
||||
#
|
||||
# Connects to OpenLDAP on 127.0.0.1:389.
|
||||
# Exposes port 9091 on localhost; Caddy reverse-proxies it and provides
|
||||
# the forward_auth endpoint for protected vhosts.
|
||||
#
|
||||
# Volume layout:
|
||||
# <dataDir>/authelia/config/ → /config (sqlite db, notification log, etc.)
|
||||
#
|
||||
# The configuration file is rendered by Nix (no Go templates) and written
|
||||
# to a NixOS-managed path, then bind-mounted read-only into the container.
|
||||
#
|
||||
# Secrets consumed from sops:
|
||||
# authelia/jwt_secret
|
||||
# authelia/session_secret
|
||||
# authelia/storage_encryption_key
|
||||
# openldap/ro_password (shared with openldap module)
|
||||
|
||||
let
|
||||
cfg = config.homey.authelia;
|
||||
dataDir = config.homey.storage.mountPoint;
|
||||
domain = homeyConfig.domain;
|
||||
|
||||
# LDAP base DN derived from domain: home.zakobar.com → dc=home,dc=zakobar,dc=com
|
||||
ldapBaseDN = lib.concatStringsSep ","
|
||||
(map (p: "dc=${p}") (lib.splitString "." domain));
|
||||
|
||||
# The authelia config is written as a Nix string so all values are
|
||||
# resolved at build time except for secrets, which are injected at
|
||||
# runtime via a wrapper script (same pattern as openldap).
|
||||
autheliaConfig = ''
|
||||
###############################################################
|
||||
# Authelia configuration #
|
||||
# Generated by NixOS — do not edit by hand #
|
||||
###############################################################
|
||||
theme: "light"
|
||||
log:
|
||||
level: "info"
|
||||
|
||||
# jwt_secret injected at runtime via env var AUTHELIA_JWT_SECRET_FILE
|
||||
authentication_backend:
|
||||
ldap:
|
||||
implementation: "custom"
|
||||
url: "ldap://127.0.0.1:389"
|
||||
timeout: "5s"
|
||||
start_tls: false
|
||||
base_dn: "${ldapBaseDN}"
|
||||
users_filter: "({username_attribute}={input})"
|
||||
username_attribute: "uid"
|
||||
additional_users_dn: "ou=users"
|
||||
groups_filter: "(&(uniquemember=uid={input},ou=users,${ldapBaseDN})(objectclass=groupOfUniqueNames))"
|
||||
group_name_attribute: "cn"
|
||||
additional_groups_dn: "ou=groups"
|
||||
mail_attribute: "mail"
|
||||
display_name_attribute: "uid"
|
||||
permit_referrals: false
|
||||
permit_unauthenticated_bind: false
|
||||
user: "cn=readonly,${ldapBaseDN}"
|
||||
# password injected at runtime via AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE
|
||||
|
||||
totp:
|
||||
issuer: "${domain}"
|
||||
disable: false
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
# secret injected at runtime via AUTHELIA_SESSION_SECRET_FILE
|
||||
expiration: 3600
|
||||
inactivity: 7200
|
||||
domain: "${domain}"
|
||||
|
||||
storage:
|
||||
local:
|
||||
path: "/config/db.sqlite3"
|
||||
# encryption_key injected at runtime via AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE
|
||||
|
||||
access_control:
|
||||
default_policy: "deny"
|
||||
rules:
|
||||
- domain:
|
||||
- "auth.${domain}"
|
||||
policy: "bypass"
|
||||
- domain:
|
||||
- "ldapadmin.${domain}"
|
||||
subject:
|
||||
- "group:admins"
|
||||
policy: "two_factor"
|
||||
- domain:
|
||||
- "ldapadmin.${domain}"
|
||||
policy: "deny"
|
||||
- domain:
|
||||
- "torrent.${domain}"
|
||||
subject:
|
||||
- "group:admins"
|
||||
policy: "two_factor"
|
||||
- domain:
|
||||
- "torrent.${domain}"
|
||||
policy: "deny"
|
||||
- domain:
|
||||
- "git.${domain}"
|
||||
policy: "one_factor"
|
||||
- domain:
|
||||
- "nextcloud.${domain}"
|
||||
policy: "one_factor"
|
||||
- domain:
|
||||
- "jellyfin.${domain}"
|
||||
policy: "one_factor"
|
||||
|
||||
notifier:
|
||||
filesystem:
|
||||
filename: "/config/emails.txt"
|
||||
|
||||
ntp:
|
||||
address: "udp://time.cloudflare.com:123"
|
||||
version: 3
|
||||
max_desync: "3s"
|
||||
disable_startup_check: false
|
||||
disable_failure: true
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options.homey.authelia = {
|
||||
enable = lib.mkEnableOption "Authelia SSO gateway";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "docker.io/authelia/authelia:latest";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 9091;
|
||||
description = "Host port Authelia listens on (bound to 127.0.0.1).";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# -----------------------------------------------------------------------
|
||||
# Secrets
|
||||
# -----------------------------------------------------------------------
|
||||
sops.secrets."authelia/jwt_secret" = { owner = "root"; };
|
||||
sops.secrets."authelia/session_secret" = { owner = "root"; };
|
||||
sops.secrets."authelia/storage_encryption_key" = { owner = "root"; };
|
||||
# openldap/ro_password is declared in openldap.nix; reference it here too
|
||||
# (sops-nix deduplicates identical declarations)
|
||||
sops.secrets."openldap/ro_password" = { owner = "root"; };
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Write the config file into /etc (read-only in the container)
|
||||
# -----------------------------------------------------------------------
|
||||
environment.etc."authelia/configuration.yml" = {
|
||||
text = autheliaConfig;
|
||||
mode = "0444";
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Container
|
||||
# -----------------------------------------------------------------------
|
||||
virtualisation.oci-containers.containers.authelia = {
|
||||
image = cfg.image;
|
||||
|
||||
ports = [ "127.0.0.1:${toString cfg.port}:9091" ];
|
||||
|
||||
environment = {
|
||||
TZ = homeyConfig.timezone;
|
||||
# Tell authelia to read secrets from files (its native mechanism)
|
||||
AUTHELIA_JWT_SECRET_FILE = "/run/secrets/jwt_secret";
|
||||
AUTHELIA_SESSION_SECRET_FILE = "/run/secrets/session_secret";
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = "/run/secrets/storage_encryption_key";
|
||||
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = "/run/secrets/ldap_ro_password";
|
||||
};
|
||||
|
||||
volumes = [
|
||||
"/etc/authelia/configuration.yml:/config/configuration.yml:ro"
|
||||
"${dataDir}/authelia/config:/config"
|
||||
# Mount sops secret files into the container under /run/secrets/
|
||||
"${config.sops.secrets."authelia/jwt_secret".path}:/run/secrets/jwt_secret:ro"
|
||||
"${config.sops.secrets."authelia/session_secret".path}:/run/secrets/session_secret:ro"
|
||||
"${config.sops.secrets."authelia/storage_encryption_key".path}:/run/secrets/storage_encryption_key:ro"
|
||||
"${config.sops.secrets."openldap/ro_password".path}:/run/secrets/ldap_ro_password:ro"
|
||||
];
|
||||
|
||||
extraOptions = [
|
||||
"--network=host"
|
||||
"--hostname=authelia"
|
||||
];
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Systemd — wait for openldap and external HD
|
||||
# -----------------------------------------------------------------------
|
||||
systemd.services."podman-authelia" = {
|
||||
after = lib.mkAfter [ "mnt-data.mount" "podman-openldap.service" ];
|
||||
requires = lib.mkAfter [ "mnt-data.mount" "podman-openldap.service" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user