Compare commits
No commits in common. "2b0684863245e19fff61f3f9790ff69ec2852e56" and "1730970935d170d806fc82eebd1ab68b8db40dd3" have entirely different histories.
2b06848632
...
1730970935
7 changed files with 0 additions and 324 deletions
|
|
@ -1,30 +0,0 @@
|
||||||
#!/usr/bin/env julia
|
|
||||||
using TOML: TOML
|
|
||||||
|
|
||||||
function remove_podman_secrets()
|
|
||||||
run(`podman secret rm --all`)
|
|
||||||
return nothing
|
|
||||||
end #function
|
|
||||||
|
|
||||||
function create_podman_secret(name, secret)
|
|
||||||
run(
|
|
||||||
Cmd(
|
|
||||||
`podman secret create --env=true --replace $name SECRET`;
|
|
||||||
env = Dict("SECRET" => secret),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return nothing
|
|
||||||
end #function
|
|
||||||
|
|
||||||
function parse_toml_secrets(file)
|
|
||||||
foreach(f -> create_podman_secret(f[1], f[2]), TOML.parsefile(file))
|
|
||||||
return nothing
|
|
||||||
end #function
|
|
||||||
|
|
||||||
function main()
|
|
||||||
remove_podman_secrets()
|
|
||||||
foreach(parse_toml_secrets, ARGS)
|
|
||||||
return 0
|
|
||||||
end #function
|
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
@ -138,13 +138,9 @@
|
||||||
|
|
||||||
"mcentire" = nixpkgs.lib.nixosSystem {
|
"mcentire" = nixpkgs.lib.nixosSystem {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
specialArgs = {
|
|
||||||
home-manager-quadlet-nix = quadlet-nix.homeManagerModules.quadlet;
|
|
||||||
};
|
|
||||||
modules = [
|
modules = [
|
||||||
./systems/linux/mcentire.nix
|
./systems/linux/mcentire.nix
|
||||||
agenix.nixosModules.default
|
agenix.nixosModules.default
|
||||||
home-manager.nixosModules.home-manager
|
|
||||||
quadlet-nix.nixosModules.quadlet
|
quadlet-nix.nixosModules.quadlet
|
||||||
crowdsec.nixosModules.crowdsec
|
crowdsec.nixosModules.crowdsec
|
||||||
crowdsec.nixosModules.crowdsec-firewall-bouncer
|
crowdsec.nixosModules.crowdsec-firewall-bouncer
|
||||||
|
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
# Warning to my future self: This module was "vibe coded" by Claude Sonnet 4.5.
|
|
||||||
# I originally had a hand-written module in here that did the secrets
|
|
||||||
# translation as the root user. I knew that I would want to support multiple
|
|
||||||
# secrets files and multiple users, and thought I should be able to create an
|
|
||||||
# arbitrary number of them, similar to how you can have an arbitrary number
|
|
||||||
# of `programs.firefox.profiles`. Unfortunately, even after looking at lots of
|
|
||||||
# example modules, I could not figure out the syntax (Nix has a serious lack
|
|
||||||
# of minimum working examples), so I broke down and asked Claude to rewrite it
|
|
||||||
# for me. Based on everything that I read, this seems to be exactly what I asked
|
|
||||||
# for.
|
|
||||||
#
|
|
||||||
# Here is the prompt I used to get here:
|
|
||||||
# [@Per service isolation on NixOS with Traefik](zed:///agent/thread/94cb8a22-ff0c-4772-a1a1-018b0107f334?name=Per+service+isolation+on+NixOS+with+Traefik)
|
|
||||||
#
|
|
||||||
# [@podman-secrets.nix](file:///home/millironx/.config/home-manager/modules/podman-secrets.nix)
|
|
||||||
#
|
|
||||||
# I originally wrote this module assuming that I would use Podman as root for
|
|
||||||
# all containers. I would like to fix the module to have as many secrets files
|
|
||||||
# processed as needed with services setup for each secret that is run as the
|
|
||||||
# appropriate user. Ideally, the syntax for working with secrets to be
|
|
||||||
# translated would be
|
|
||||||
#
|
|
||||||
# ```nix
|
|
||||||
# { config, ... }: {
|
|
||||||
# age.secrets = {
|
|
||||||
# "caddy.toml" = {
|
|
||||||
# file = ./../secrets/caddy.toml.age;
|
|
||||||
# owner = "caddy";
|
|
||||||
# group = "caddy";
|
|
||||||
# };
|
|
||||||
#
|
|
||||||
# "authentik.toml" = {
|
|
||||||
# file = ./../secrets/authentik.toml.age;
|
|
||||||
# owner = "authentik";
|
|
||||||
# group = "authentik";
|
|
||||||
# };
|
|
||||||
#
|
|
||||||
# "freshrss.toml" = {
|
|
||||||
# file = ./../secrets/freshrss.toml.age;
|
|
||||||
# owner = "freshrss";
|
|
||||||
# group = "freshrss";
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
#
|
|
||||||
# millironx.podman-secrets = with config; {
|
|
||||||
# caddy = {
|
|
||||||
# user = "caddy";
|
|
||||||
# secrets-files = [
|
|
||||||
# ./../not-really-secret.toml
|
|
||||||
# age.secrets."caddy.toml".path
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# authentik = {
|
|
||||||
# user = "authentik";
|
|
||||||
# secrets-files = [
|
|
||||||
# age.secrets."authentik.toml".path
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# freshrss = {
|
|
||||||
# user = "freshrss";
|
|
||||||
# secrets-files = [
|
|
||||||
# age.secrets."freshrss.toml".path
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# }
|
|
||||||
# ```
|
|
||||||
#
|
|
||||||
# Can you help me rewrite the module to accomplish this?
|
|
||||||
#
|
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
with lib;
|
|
||||||
let
|
|
||||||
cfg = config.millironx.podman-secrets;
|
|
||||||
|
|
||||||
secret-translator = pkgs.writeScriptBin "secret-translator"
|
|
||||||
(builtins.readFile ./../bin/secret-translator.jl);
|
|
||||||
|
|
||||||
# Submodule type for each service's secrets configuration
|
|
||||||
serviceSecretsType = types.submodule {
|
|
||||||
options = {
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "User account to run the secrets translation service as";
|
|
||||||
example = "caddy";
|
|
||||||
};
|
|
||||||
|
|
||||||
secrets-files = mkOption {
|
|
||||||
type = types.listOf (types.either types.path types.string);
|
|
||||||
description =
|
|
||||||
"List of TOML files containing secrets to translate to Podman secrets";
|
|
||||||
example = literalExpression ''
|
|
||||||
[
|
|
||||||
"/run/agenix/caddy.toml"
|
|
||||||
./../not-really-secret.toml
|
|
||||||
]
|
|
||||||
'';
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Generate a systemd service for each configured service
|
|
||||||
mkSecretsService = name: serviceCfg:
|
|
||||||
nameValuePair "podman-secrets-${name}" {
|
|
||||||
description = "Podman secrets converter service for ${name}";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = serviceCfg.user;
|
|
||||||
ExecStart = "${secret-translator}/bin/secret-translator ${
|
|
||||||
lib.concatStringsSep " " serviceCfg.secrets-files
|
|
||||||
}";
|
|
||||||
Path = [ "${pkgs.podman}/bin" "${pkgs.julia-lts-bin}/bin" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Filter out services with no secrets-files
|
|
||||||
enabledServices =
|
|
||||||
lib.filterAttrs (name: serviceCfg: serviceCfg.secrets-files != [ ]) cfg;
|
|
||||||
|
|
||||||
in {
|
|
||||||
options.millironx.podman-secrets = mkOption {
|
|
||||||
type = types.attrsOf serviceSecretsType;
|
|
||||||
description = ''
|
|
||||||
Per-service Podman secrets configuration.
|
|
||||||
Each attribute creates a separate systemd service that translates TOML secrets
|
|
||||||
files into Podman secrets for the specified user.
|
|
||||||
'';
|
|
||||||
example = literalExpression ''
|
|
||||||
{
|
|
||||||
caddy = {
|
|
||||||
user = "caddy";
|
|
||||||
secrets-files = [
|
|
||||||
./../not-really-secret.toml
|
|
||||||
config.age.secrets."caddy.toml".path
|
|
||||||
];
|
|
||||||
};
|
|
||||||
authentik = {
|
|
||||||
user = "authentik";
|
|
||||||
secrets-files = [
|
|
||||||
config.age.secrets."authentik.toml".path
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
default = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf (enabledServices != { }) {
|
|
||||||
systemd.services = lib.mapAttrs' mkSecretsService enabledServices;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -26,8 +26,6 @@ let
|
||||||
|
|
||||||
in {
|
in {
|
||||||
"secrets/ansible-vault-password.age".publicKeys = system-administrators;
|
"secrets/ansible-vault-password.age".publicKeys = system-administrators;
|
||||||
"secrets/authentik.toml.age".publicKeys = system-administrators
|
|
||||||
++ [ mcentire-host ];
|
|
||||||
"secrets/borgmatic-passphrase.age".publicKeys = system-administrators
|
"secrets/borgmatic-passphrase.age".publicKeys = system-administrators
|
||||||
++ [ mcentire-host ];
|
++ [ mcentire-host ];
|
||||||
"secrets/borgmatic-ssh-config.age".publicKeys = system-administrators
|
"secrets/borgmatic-ssh-config.age".publicKeys = system-administrators
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,128 +0,0 @@
|
||||||
{ config, home-manager-quadlet-nix, ... }:
|
|
||||||
let
|
|
||||||
user = "authentik";
|
|
||||||
state-directory = "/var/lib/authentik";
|
|
||||||
port = "9000";
|
|
||||||
|
|
||||||
in {
|
|
||||||
# Secrets are translated in the system scope, but performed by the user,
|
|
||||||
# so are available in the user's Podman secrets
|
|
||||||
age.secrets = {
|
|
||||||
"authentik.toml" = {
|
|
||||||
file = ./../secrets/authentik.toml.age;
|
|
||||||
owner = "${user}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
millironx.podman-secrets.authentik = {
|
|
||||||
user = "${user}";
|
|
||||||
secrets-files = [ config.age.secrets."authentik.toml".path ];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [ "d ${state-directory} 1775 ${user} ${user} -" ];
|
|
||||||
|
|
||||||
services.caddy.virtualHosts."auth.millironx.com".extraConfig = ''
|
|
||||||
reverse_proxy http://127.0.0.1:${port}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Create a dedicated user for this service
|
|
||||||
users.users."${user}" = {
|
|
||||||
group = "${user}";
|
|
||||||
isSystemUser = true;
|
|
||||||
linger = true;
|
|
||||||
autoSubUidGidRange = true;
|
|
||||||
};
|
|
||||||
users.groups."${user}" = {};
|
|
||||||
|
|
||||||
home-manager.users."${user}" = { config, ... }: {
|
|
||||||
imports = [ home-manager-quadlet-nix ];
|
|
||||||
|
|
||||||
home.stateVersion = "25.05";
|
|
||||||
|
|
||||||
virtualisation.quadlet.containers = {
|
|
||||||
authentik-db = {
|
|
||||||
autoStart = true;
|
|
||||||
containerConfig = {
|
|
||||||
image = "docker.io/library/postgres:16";
|
|
||||||
environments = {
|
|
||||||
POSTGRES_DB = "${user}";
|
|
||||||
POSTGRES_USER = "${user}";
|
|
||||||
};
|
|
||||||
secrets = [
|
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=POSTGRES_PASSWORD"
|
|
||||||
];
|
|
||||||
healthCmd = "pg_isready -d \${POSTGRES_DB} -U \${POSTGRES_USER}";
|
|
||||||
healthInterval = "30s";
|
|
||||||
healthRetries = 5;
|
|
||||||
healthStartPeriod = "20s";
|
|
||||||
volumes = [ "${state-directory}/database:/var/lib/postgresql/data" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
authentik-worker = {
|
|
||||||
autoStart = true;
|
|
||||||
containerConfig = {
|
|
||||||
image = "ghcr.io/goauthentik/server:2025.10.2";
|
|
||||||
environments = {
|
|
||||||
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
|
|
||||||
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
|
||||||
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
|
||||||
};
|
|
||||||
exec = "worker";
|
|
||||||
secrets = [
|
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
|
|
||||||
"AUTHENTIK_SECRET_KEY,type=env"
|
|
||||||
];
|
|
||||||
volumes = [
|
|
||||||
"${state-directory}/media:/media"
|
|
||||||
"${state-directory}/custom-templates:/templates"
|
|
||||||
"${state-directory}/certs:/certs"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
unitConfig.Requires = [
|
|
||||||
config.virtualisation.quadlet.containers.authentik-db.ref
|
|
||||||
"network-online.target"
|
|
||||||
];
|
|
||||||
unitConfig.After = [
|
|
||||||
config.virtualisation.quadlet.containers.authentik-db.ref
|
|
||||||
"network-online.target"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
authentik = {
|
|
||||||
autoStart = true;
|
|
||||||
containerConfig = {
|
|
||||||
image = "ghcr.io/goauthentik/server:2025.10.2";
|
|
||||||
environments = {
|
|
||||||
AUTHENTIK_POSTGRESQL__HOST = "authentik-db";
|
|
||||||
AUTHENTIK_POSTGRESQL__NAME = "${user}";
|
|
||||||
AUTHENTIK_POSTGRESQL__USER = "${user}";
|
|
||||||
};
|
|
||||||
secrets = [
|
|
||||||
"AUTHENTIK_POSTGRESQL__PASSWORD,type=env"
|
|
||||||
"AUTHENTIK_SECRET_KEY,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__HOST,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__PORT,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__USERNAME,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__PASSWORD,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__USE_SSL,type=env"
|
|
||||||
"AUTHENTIK_EMAIL__FROM,type=env"
|
|
||||||
];
|
|
||||||
publishPorts = [ "127.0.0.1:${port}:${port}" ];
|
|
||||||
volumes = [
|
|
||||||
"${state-directory}/media:/media"
|
|
||||||
"${state-directory}/custom-templates:/templates"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
unitConfig.Requires = [
|
|
||||||
config.virtualisation.quadlet.containers.authentik-db.ref
|
|
||||||
"network-online.target"
|
|
||||||
];
|
|
||||||
unitConfig.After = [
|
|
||||||
config.virtualisation.quadlet.containers.authentik-db.ref
|
|
||||||
"network-online.target"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -3,11 +3,9 @@
|
||||||
{
|
{
|
||||||
imports = [ # Include the results of the hardware scan.
|
imports = [ # Include the results of the hardware scan.
|
||||||
./hardware-configuration/mcentire.nix
|
./hardware-configuration/mcentire.nix
|
||||||
./../../modules/podman-secrets.nix
|
|
||||||
./../../services/nixos-update.nix
|
./../../services/nixos-update.nix
|
||||||
./../../services/borgmatic.nix
|
./../../services/borgmatic.nix
|
||||||
./../../services/crowdsec.nix
|
./../../services/crowdsec.nix
|
||||||
./../../services/authentik.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# Use the GRUB 2 boot loader.
|
# Use the GRUB 2 boot loader.
|
||||||
|
|
@ -18,7 +16,6 @@
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
interfaces.eth0.useDHCP = true;
|
interfaces.eth0.useDHCP = true;
|
||||||
hostName = "mcentire"; # Define your hostname.
|
hostName = "mcentire"; # Define your hostname.
|
||||||
firewall.allowedTCPPorts = [ 80 443 ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Set your time zone.
|
# Set your time zone.
|
||||||
|
|
@ -58,11 +55,8 @@
|
||||||
services = {
|
services = {
|
||||||
openssh.enable = true;
|
openssh.enable = true;
|
||||||
tailscale.enable = true;
|
tailscale.enable = true;
|
||||||
caddy.enable = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation.quadlet.enable = true;
|
|
||||||
|
|
||||||
system.stateVersion = "25.05"; # Did you read the comment?
|
system.stateVersion = "25.05"; # Did you read the comment?
|
||||||
nix = { extraOptions = "experimental-features = nix-command flakes"; };
|
nix = { extraOptions = "experimental-features = nix-command flakes"; };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue