mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-09-17 02:12:55 +02:00
Working secrets with nix
This commit is contained in:
@@ -1,83 +1,19 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
|
||||||
k8sUpstreamConf = "/run/nginx/k8s-upstream.conf";
|
|
||||||
k8sHttpUpstreamConf = "/run/nginx/k8s-http-upstream.conf";
|
|
||||||
|
|
||||||
updateK8sIp = pkgs.writeShellScriptBin "update-k8s-ip" ''
|
|
||||||
NEW_IP="$SSH_ORIGINAL_COMMAND"
|
|
||||||
|
|
||||||
if ! echo "$NEW_IP" | ${pkgs.gnugrep}/bin/grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
||||||
echo "Invalid IP: $NEW_IP"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CONF="${k8sUpstreamConf}"
|
|
||||||
CURRENT_IP=$(${pkgs.gnugrep}/bin/grep -oP 'server \K[0-9.]+' "$CONF" 2>/dev/null | head -1)
|
|
||||||
|
|
||||||
if [ "$CURRENT_IP" = "$NEW_IP" ]; then
|
|
||||||
echo "IP unchanged: $NEW_IP"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf 'upstream k8s_tls {\n server %s:443;\n}\n\nupstream k8s_ldap {\n server %s:3389;\n}\n' "$NEW_IP" "$NEW_IP" > "$CONF"
|
|
||||||
printf 'upstream k8s_http {\n server %s:80;\n}\n' "$NEW_IP" > ${k8sHttpUpstreamConf}
|
|
||||||
|
|
||||||
${pkgs.nginx}/bin/nginx -t && ${pkgs.systemd}/bin/systemctl reload nginx
|
|
||||||
echo "Updated K8s backend IP to $NEW_IP"
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
# Firewall (replaces UFW)
|
# Firewall
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
25 # SMTP
|
25 # SMTP
|
||||||
465 # SMTP submissions (implicit TLS)
|
465 # SMTP submissions (implicit TLS)
|
||||||
587 # SMTP submission (STARTTLS)
|
587 # SMTP submission (STARTTLS)
|
||||||
993 # IMAP (implicit TLS)
|
993 # IMAP (implicit TLS)
|
||||||
443 # HTTPS
|
443 # HTTPS (webadmin)
|
||||||
80 # HTTP (ACME + redirect)
|
80 # HTTP (ACME)
|
||||||
];
|
];
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
# Sysctl hardening (replaces base role)
|
# ACME / Let's Encrypt
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
boot.kernel.sysctl = {
|
|
||||||
"net.ipv4.conf.all.rp_filter" = 1;
|
|
||||||
"net.ipv4.conf.default.rp_filter" = 1;
|
|
||||||
"net.ipv4.conf.all.accept_redirects" = 0;
|
|
||||||
"net.ipv4.conf.default.accept_redirects" = 0;
|
|
||||||
"net.ipv4.conf.all.send_redirects" = 0;
|
|
||||||
"net.ipv4.conf.default.send_redirects" = 0;
|
|
||||||
"net.ipv4.tcp_syncookies" = 1;
|
|
||||||
"net.ipv4.icmp_echo_ignore_broadcasts" = 1;
|
|
||||||
"net.ipv6.conf.all.accept_redirects" = 0;
|
|
||||||
"net.ipv6.conf.default.accept_redirects" = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Fail2ban
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
services.fail2ban = {
|
|
||||||
enable = true;
|
|
||||||
maxretry = 5;
|
|
||||||
bantime = "1h";
|
|
||||||
|
|
||||||
jails = {
|
|
||||||
sshd = {
|
|
||||||
settings = {
|
|
||||||
enabled = true;
|
|
||||||
port = "ssh";
|
|
||||||
maxretry = 3;
|
|
||||||
bantime = "1h";
|
|
||||||
findtime = "10m";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# ACME / Let's Encrypt (replaces certbot)
|
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
security.acme = {
|
security.acme = {
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
@@ -89,6 +25,26 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/lib/acme/acme-challenge 0755 acme acme -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Serve ACME challenges via nginx on port 80
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."stalwart.rubenhensen.nl" = {
|
||||||
|
listen = [
|
||||||
|
{ addr = "0.0.0.0"; port = 80; }
|
||||||
|
{ addr = "[::]"; port = 80; }
|
||||||
|
];
|
||||||
|
locations."/.well-known/acme-challenge/" = {
|
||||||
|
root = "/var/lib/acme/acme-challenge";
|
||||||
|
};
|
||||||
|
locations."/" = {
|
||||||
|
return = "301 https://$host$request_uri";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
# Stalwart mail server
|
# Stalwart mail server
|
||||||
@@ -98,7 +54,6 @@ in
|
|||||||
settings = {
|
settings = {
|
||||||
server = {
|
server = {
|
||||||
hostname = "stalwart.rubenhensen.nl";
|
hostname = "stalwart.rubenhensen.nl";
|
||||||
max-connections = 8192;
|
|
||||||
listener = {
|
listener = {
|
||||||
smtp = {
|
smtp = {
|
||||||
bind = "[::]:25";
|
bind = "[::]:25";
|
||||||
@@ -119,14 +74,10 @@ in
|
|||||||
tls.implicit = true;
|
tls.implicit = true;
|
||||||
};
|
};
|
||||||
https = {
|
https = {
|
||||||
bind = "127.0.0.1:8443";
|
bind = "[::]:443";
|
||||||
protocol = "http";
|
protocol = "http";
|
||||||
tls.implicit = true;
|
tls.implicit = true;
|
||||||
};
|
};
|
||||||
http = {
|
|
||||||
bind = "127.0.0.1:8080";
|
|
||||||
protocol = "http";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,7 +91,7 @@ in
|
|||||||
fts = "rocksdb";
|
fts = "rocksdb";
|
||||||
blob = "rocksdb";
|
blob = "rocksdb";
|
||||||
lookup = "rocksdb";
|
lookup = "rocksdb";
|
||||||
directory = "ldap";
|
directory = "internal";
|
||||||
};
|
};
|
||||||
|
|
||||||
store.rocksdb = {
|
store.rocksdb = {
|
||||||
@@ -149,22 +100,9 @@ in
|
|||||||
compression = "lz4";
|
compression = "lz4";
|
||||||
};
|
};
|
||||||
|
|
||||||
directory.ldap = {
|
directory.internal = {
|
||||||
type = "ldap";
|
type = "internal";
|
||||||
url = "ldap://127.0.0.1:3389";
|
store = "rocksdb";
|
||||||
base-dn = "DC=ldap,DC=goauthentik,DC=io";
|
|
||||||
bind.dn = "cn=ldapservice,ou=users,DC=ldap,DC=goauthentik,DC=io";
|
|
||||||
bind.secret = "%{file:/run/secrets/stalwart-ldap-password}%";
|
|
||||||
filter.name = "(&(objectClass=user)(cn=?))";
|
|
||||||
filter.email = "(&(objectClass=user)(mail=?))";
|
|
||||||
filter.verify = "(&(objectClass=user)(|(mail=*?*)(cn=*?*)))";
|
|
||||||
filter.expand = "(&(objectClass=group)(cn=?))";
|
|
||||||
attribute.name = "cn";
|
|
||||||
attribute.type = "class";
|
|
||||||
attribute.email = "mail";
|
|
||||||
attribute.secret = "userPassword";
|
|
||||||
attribute.groups = "memberOf";
|
|
||||||
attribute.description = "displayName";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tracer.stdout = {
|
tracer.stdout = {
|
||||||
@@ -174,140 +112,16 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
tracer.log = {
|
|
||||||
type = "log";
|
|
||||||
level = "info";
|
|
||||||
path = "/var/lib/stalwart-mail/logs";
|
|
||||||
prefix = "stalwart.log";
|
|
||||||
rotate = "daily";
|
|
||||||
ansi = false;
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
authentication.fallback-admin = {
|
authentication.fallback-admin = {
|
||||||
user = "admin";
|
user = "admin";
|
||||||
secret = "%{file:/run/secrets/stalwart-admin-password}%";
|
secret = "%{file:/run/credentials/stalwart-mail.service/stalwart-admin-password}%";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Grant stalwart and nginx access to ACME certs
|
systemd.services.stalwart-mail.serviceConfig.LoadCredentialEncrypted = [
|
||||||
|
"stalwart-admin-password:/root/secrets/[%%secrets/stalwart-admin-password%%]"
|
||||||
|
];
|
||||||
|
|
||||||
users.users.stalwart-mail.extraGroups = [ "acme" ];
|
users.users.stalwart-mail.extraGroups = [ "acme" ];
|
||||||
users.users.nginx.extraGroups = [ "stalwart-mail" ];
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Nginx (reverse proxy + stream proxy to K8s)
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
services.nginx = {
|
|
||||||
enable = true;
|
|
||||||
recommendedTlsSettings = true;
|
|
||||||
recommendedOptimisation = true;
|
|
||||||
recommendedGzipSettings = true;
|
|
||||||
recommendedProxySettings = true;
|
|
||||||
eventsConfig = "worker_connections 4096;";
|
|
||||||
|
|
||||||
# HTTP upstream for K8s (included from mutable file)
|
|
||||||
appendHttpConfig = ''
|
|
||||||
include /run/nginx/k8s-http-upstream.conf;
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Stream config for TLS SNI routing + LDAP proxy
|
|
||||||
streamConfig = ''
|
|
||||||
log_format stream '$remote_addr [$time_local] '
|
|
||||||
'$protocol $status $bytes_sent $bytes_received '
|
|
||||||
'$session_time "$ssl_preread_server_name"';
|
|
||||||
access_log /var/log/nginx/stream.log stream;
|
|
||||||
|
|
||||||
map $ssl_preread_server_name $tls_backend {
|
|
||||||
stalwart.rubenhensen.nl local_tls;
|
|
||||||
default k8s_tls;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream local_tls {
|
|
||||||
server 127.0.0.1:8443;
|
|
||||||
}
|
|
||||||
|
|
||||||
include /run/nginx/k8s-upstream.conf;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443;
|
|
||||||
listen [::]:443;
|
|
||||||
ssl_preread on;
|
|
||||||
proxy_pass $tls_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
# LDAP proxy to K8s Authentik LDAP outpost
|
|
||||||
server {
|
|
||||||
listen 127.0.0.1:3389;
|
|
||||||
proxy_pass k8s_ldap;
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Create stream.d directory and initial upstream config
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d /run/secrets 0700 root root -"
|
|
||||||
"d /var/lib/acme/acme-challenge 0755 acme acme -"
|
|
||||||
];
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# K8s IP update script (called via SSH)
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Allow nginx to read/write mutable upstream configs
|
|
||||||
systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/run/nginx" ];
|
|
||||||
systemd.services.nginx.serviceConfig.LimitNOFILE = 65536;
|
|
||||||
systemd.services.nginx.preStart = lib.mkBefore ''
|
|
||||||
mkdir -p /run/nginx
|
|
||||||
test -f /run/nginx/k8s-upstream.conf || printf 'upstream k8s_tls {\n server 127.0.0.1:1;\n}\n\nupstream k8s_ldap {\n server 127.0.0.1:1;\n}\n' > /run/nginx/k8s-upstream.conf
|
|
||||||
test -f /run/nginx/k8s-http-upstream.conf || printf 'upstream k8s_http {\n server 127.0.0.1:80;\n}\n' > /run/nginx/k8s-http-upstream.conf
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Stalwart ACME HTTP-01 challenge
|
|
||||||
services.nginx.virtualHosts."stalwart.rubenhensen.nl" = {
|
|
||||||
listen = [
|
|
||||||
{ addr = "0.0.0.0"; port = 80; }
|
|
||||||
{ addr = "[::]"; port = 80; }
|
|
||||||
];
|
|
||||||
locations."/.well-known/acme-challenge/" = {
|
|
||||||
root = "/var/lib/acme/acme-challenge";
|
|
||||||
};
|
|
||||||
locations."/" = {
|
|
||||||
return = "301 https://$host$request_uri";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Catch-all port 80 — proxy to K8s for ACME challenges + redirect
|
|
||||||
services.nginx.virtualHosts."_" = {
|
|
||||||
default = true;
|
|
||||||
listen = [
|
|
||||||
{ addr = "0.0.0.0"; port = 80; }
|
|
||||||
{ addr = "[::]"; port = 80; }
|
|
||||||
];
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://k8s_http";
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# IP update script (as a proper Nix package)
|
|
||||||
environment.systemPackages = [ updateK8sIp pkgs.openssl ];
|
|
||||||
|
|
||||||
# SSH authorized key for K8s IP updater (add the actual pubkey)
|
|
||||||
users.users.root.openssh.authorizedKeys.keys = [
|
|
||||||
# nix-infra will set the main SSH key via configuration.nix
|
|
||||||
# Add the IP updater key with command restriction:
|
|
||||||
''command="${updateK8sIp}/bin/update-k8s-ip",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5cMc73rlUCn3mS5FXlu3nO+AUeW2L28jRh22VYIPY4 k8s-ip-updater''
|
|
||||||
];
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Automatic updates
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
system.autoUpgrade = {
|
|
||||||
enable = true;
|
|
||||||
allowReboot = false;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user