Compare commits
No commits in common. "6a1cc7877ce1e59dd199776e893c8f91d338225e" and "fa6b537a3c828ca5a90668fa6eb9797e9bec24fb" have entirely different histories.
6a1cc7877c
...
fa6b537a3c
3 changed files with 93 additions and 160 deletions
|
|
@ -77,7 +77,7 @@ let
|
||||||
(builtins.readFile ./../bin/secret-translator.jl);
|
(builtins.readFile ./../bin/secret-translator.jl);
|
||||||
|
|
||||||
# Submodule type for each service's secrets configuration
|
# Submodule type for each service's secrets configuration
|
||||||
serviceSecretsType = types.submodule ({ config, name, ... }: {
|
serviceSecretsType = types.submodule {
|
||||||
options = {
|
options = {
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
|
@ -97,28 +97,18 @@ let
|
||||||
'';
|
'';
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
ref = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description =
|
|
||||||
"Reference to the systemd service name for declaring dependencies";
|
|
||||||
readOnly = true;
|
|
||||||
default = "podman-secrets-${name}.service";
|
|
||||||
example = "podman-secrets-caddy.service";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
# Generate a systemd user service for each configured service
|
# Generate a systemd service for each configured service
|
||||||
mkSecretsService = name: serviceCfg:
|
mkSecretsService = name: serviceCfg:
|
||||||
nameValuePair "podman-secrets-${name}" {
|
nameValuePair "podman-secrets-${name}" {
|
||||||
description = "Podman secrets converter service for ${name}";
|
description = "Podman secrets converter service for ${name}";
|
||||||
wantedBy = [ "default.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
unitConfig.ConditionUser = "${serviceCfg.user}";
|
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
|
User = serviceCfg.user;
|
||||||
ProtectProc = "invisible";
|
ProtectProc = "invisible";
|
||||||
ExecStart = "${secret-translator}/bin/secret-translator ${
|
ExecStart = "${secret-translator}/bin/secret-translator ${
|
||||||
lib.concatStringsSep " " serviceCfg.secrets-files
|
lib.concatStringsSep " " serviceCfg.secrets-files
|
||||||
|
|
@ -138,8 +128,8 @@ in {
|
||||||
type = types.attrsOf serviceSecretsType;
|
type = types.attrsOf serviceSecretsType;
|
||||||
description = ''
|
description = ''
|
||||||
Per-service Podman secrets configuration.
|
Per-service Podman secrets configuration.
|
||||||
Each attribute creates a separate systemd user service that translates TOML secrets
|
Each attribute creates a separate systemd service that translates TOML secrets
|
||||||
files into Podman secrets.
|
files into Podman secrets for the specified user.
|
||||||
'';
|
'';
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
{
|
{
|
||||||
|
|
@ -162,6 +152,6 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf (enabledServices != { }) {
|
config = mkIf (enabledServices != { }) {
|
||||||
systemd.user.services = lib.mapAttrs' mkSecretsService enabledServices;
|
systemd.services = lib.mapAttrs' mkSecretsService enabledServices;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ let
|
||||||
port = "9000";
|
port = "9000";
|
||||||
|
|
||||||
in {
|
in {
|
||||||
# Secrets are translated in the system NixOS scope, but performed by the user,
|
# Secrets are translated in the system scope, but performed by the user,
|
||||||
# via a user systemd unit, so are available in the user's Podman secrets
|
# so are available in the user's Podman secrets
|
||||||
age.secrets = {
|
age.secrets = {
|
||||||
"authentik.toml" = {
|
"authentik.toml" = {
|
||||||
file = ./../secrets/authentik.toml.age;
|
file = ./../secrets/authentik.toml.age;
|
||||||
|
|
@ -19,15 +19,7 @@ in {
|
||||||
secrets-files = [ config.age.secrets."authentik.toml".path ];
|
secrets-files = [ config.age.secrets."authentik.toml".path ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Podman, unlike Docker apparently, does not automatically create mount points
|
systemd.tmpfiles.rules = [ "d ${state-directory} 1775 ${user} ${user} -" ];
|
||||||
# within folders, so every mounted folder needs to be specified here.
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d ${state-directory} 1775 ${user} ${user} -"
|
|
||||||
"d ${state-directory}/database 1775 ${user} ${user} -"
|
|
||||||
"d ${state-directory}/media 1775 ${user} ${user} -"
|
|
||||||
"d ${state-directory}/certs 1775 ${user} ${user} -"
|
|
||||||
"d ${state-directory}/custom-templates 1775 ${user} ${user} -"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.caddy.virtualHosts."auth.millironx.com".extraConfig = ''
|
services.caddy.virtualHosts."auth.millironx.com".extraConfig = ''
|
||||||
reverse_proxy http://127.0.0.1:${port}
|
reverse_proxy http://127.0.0.1:${port}
|
||||||
|
|
@ -35,33 +27,21 @@ in {
|
||||||
|
|
||||||
# Create a dedicated user for this service
|
# Create a dedicated user for this service
|
||||||
users.users."${user}" = {
|
users.users."${user}" = {
|
||||||
# Group is technically not mandatory, but NixOS complains that an unset
|
|
||||||
# group is "unsafe." The group has to be declared below
|
|
||||||
group = "${user}";
|
group = "${user}";
|
||||||
|
isSystemUser = true;
|
||||||
# System users don't have a shell. For security purposes, that *would* be
|
|
||||||
# superior, but that means that, e.g. borgmatic can't `sudo` into the
|
|
||||||
# account to access Podman commands
|
|
||||||
isNormalUser = true;
|
|
||||||
|
|
||||||
# A home directory is mandatory in order to allow systemd user units to be
|
|
||||||
# created and run
|
|
||||||
home = "${state-directory}";
|
home = "${state-directory}";
|
||||||
createHome = true;
|
createHome = true;
|
||||||
|
|
||||||
# Settings for running containers while a login shell is not active
|
|
||||||
linger = true;
|
linger = true;
|
||||||
autoSubUidGidRange = true;
|
autoSubUidGidRange = true;
|
||||||
};
|
};
|
||||||
users.groups."${user}" = { };
|
users.groups."${user}" = { };
|
||||||
|
|
||||||
home-manager.users."${user}" = { config, osConfig, ... }: {
|
home-manager.users."${user}" = { config, ... }: {
|
||||||
imports = [ home-manager-quadlet-nix ];
|
imports = [ home-manager-quadlet-nix ];
|
||||||
|
|
||||||
home.stateVersion = "25.05";
|
home.stateVersion = "25.05";
|
||||||
|
|
||||||
virtualisation.quadlet = {
|
virtualisation.quadlet.containers = {
|
||||||
containers = {
|
|
||||||
authentik-db = {
|
authentik-db = {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
containerConfig = {
|
containerConfig = {
|
||||||
|
|
@ -71,43 +51,14 @@ in {
|
||||||
POSTGRES_USER = "${user}";
|
POSTGRES_USER = "${user}";
|
||||||
};
|
};
|
||||||
secrets = [
|
secrets = [
|
||||||
# POSTGRES_PASSWORD is used by the container to setup the
|
|
||||||
# database, PGPASSWORD is used by pg_dump to authenticate for
|
|
||||||
# backup purposes
|
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=POSTGRES_PASSWORD"
|
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=POSTGRES_PASSWORD"
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=PGPASSWORD"
|
|
||||||
];
|
];
|
||||||
|
healthCmd = "pg_isready -d \${POSTGRES_DB} -U \${POSTGRES_USER}";
|
||||||
# Double dollarsigns are required for use of *container* environment
|
|
||||||
# variables, Nix escaping creates the weird $\${} syntax
|
|
||||||
healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}";
|
|
||||||
healthInterval = "30s";
|
healthInterval = "30s";
|
||||||
healthRetries = 5;
|
healthRetries = 5;
|
||||||
healthStartPeriod = "20s";
|
healthStartPeriod = "20s";
|
||||||
|
volumes = [ "${state-directory}/database:/var/lib/postgresql/data" ];
|
||||||
# Volumes have to be bound with the :U label to allow for username
|
|
||||||
# remapping in rootless containers. :Z/:z is not needed b/c NixOS
|
|
||||||
# does not support SELinux
|
|
||||||
volumes =
|
|
||||||
[ "${state-directory}/database:/var/lib/postgresql/data:U" ];
|
|
||||||
|
|
||||||
# A network is actually required for these containers to talk to one
|
|
||||||
# another. I suppose the more idiomatic way would be for these
|
|
||||||
# related containers to be in a "pod," but I'm still struggling
|
|
||||||
# learning the idiosyncrasies of quadlet/rootless/podman, so we'll
|
|
||||||
# stick with this for now
|
|
||||||
networks =
|
|
||||||
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Allowing this container to start before the secrets are translated
|
|
||||||
# will lead to errors. Use osConfig b/c secrets are declared in the
|
|
||||||
# system NixOS scope, even though it is a user process.
|
|
||||||
unitConfig.Requires =
|
|
||||||
[ osConfig.millironx.podman-secrets.authentik.ref ];
|
|
||||||
unitConfig.After =
|
|
||||||
[ osConfig.millironx.podman-secrets.authentik.ref ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
authentik-worker = {
|
authentik-worker = {
|
||||||
|
|
@ -125,17 +76,19 @@ in {
|
||||||
"AUTHENTIK_SECRET_KEY,type=env"
|
"AUTHENTIK_SECRET_KEY,type=env"
|
||||||
];
|
];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${state-directory}/media:/media:U"
|
"${state-directory}/media:/media"
|
||||||
"${state-directory}/custom-templates:/templates:U"
|
"${state-directory}/custom-templates:/templates"
|
||||||
"${state-directory}/certs:/certs:U"
|
"${state-directory}/certs:/certs"
|
||||||
];
|
];
|
||||||
networks =
|
|
||||||
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
|
|
||||||
};
|
};
|
||||||
unitConfig.Requires =
|
unitConfig.Requires = [
|
||||||
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
|
config.virtualisation.quadlet.containers.authentik-db.ref
|
||||||
unitConfig.After =
|
"network-online.target"
|
||||||
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
|
];
|
||||||
|
unitConfig.After = [
|
||||||
|
config.virtualisation.quadlet.containers.authentik-db.ref
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
authentik = {
|
authentik = {
|
||||||
|
|
@ -147,7 +100,6 @@ in {
|
||||||
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
||||||
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
||||||
};
|
};
|
||||||
exec = "server";
|
|
||||||
secrets = [
|
secrets = [
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
|
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
|
||||||
"AUTHENTIK_SECRET_KEY,type=env"
|
"AUTHENTIK_SECRET_KEY,type=env"
|
||||||
|
|
@ -158,30 +110,21 @@ in {
|
||||||
"AUTHENTIK_EMAIL__USE_SSL,type=env"
|
"AUTHENTIK_EMAIL__USE_SSL,type=env"
|
||||||
"AUTHENTIK_EMAIL__FROM,type=env"
|
"AUTHENTIK_EMAIL__FROM,type=env"
|
||||||
];
|
];
|
||||||
|
|
||||||
# Change from Traefik: publish ports to localhost only via 127.0.0.1
|
|
||||||
# and then reverse proxy to that port. Authentik does not appear to
|
|
||||||
# have a way to configure the port, so we will use the default of
|
|
||||||
# 9000 for non-secured traffic.
|
|
||||||
publishPorts = [ "127.0.0.1:${port}:${port}" ];
|
publishPorts = [ "127.0.0.1:${port}:${port}" ];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${state-directory}/media:/media:U"
|
"${state-directory}/media:/media"
|
||||||
"${state-directory}/custom-templates:/templates:U"
|
"${state-directory}/custom-templates:/templates"
|
||||||
];
|
];
|
||||||
networks =
|
|
||||||
[ config.virtualisation.quadlet.networks.authentik-net.ref ];
|
|
||||||
};
|
};
|
||||||
unitConfig.Requires =
|
unitConfig.Requires = [
|
||||||
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
|
config.virtualisation.quadlet.containers.authentik-db.ref
|
||||||
unitConfig.After =
|
"network-online.target"
|
||||||
[ config.virtualisation.quadlet.containers.authentik-db.ref ];
|
];
|
||||||
|
unitConfig.After = [
|
||||||
|
config.virtualisation.quadlet.containers.authentik-db.ref
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
networks.authentik-net = { };
|
|
||||||
|
|
||||||
# One of the main advantages of using Quadlet
|
|
||||||
autoUpdate.enable = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
millironx = {
|
millironx = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
description = "Thomas A. Christensen II";
|
description = "Thomas A. Christensen II";
|
||||||
extraGroups = [ "adm" "wheel" ];
|
extraGroups = [ "wheel" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue