paris-container/paris-container.nix

349 lines
10 KiB
Nix
Raw Normal View History

2024-05-27 17:30:16 -07:00
{ config, lib, pkgs, ... }:
with lib;
let
2024-05-27 18:05:35 -07:00
cfg = config.fudo.paris-container;
packages = with pkgs; [ rtorrent ];
2024-05-27 17:30:16 -07:00
2024-05-28 11:05:33 -07:00
hostname = config.instance.hostname;
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
2024-06-02 17:02:37 -07:00
parisKeypairs = config.fudo.secrets.files.ssh.host-keypairs.paris;
2024-06-02 16:56:56 -07:00
2024-06-02 18:18:07 -07:00
keypairFilename = keypair: "host-paris-${keypair.key-type}-private-key";
2024-06-02 16:56:56 -07:00
2024-05-27 17:30:16 -07:00
in {
2024-05-28 11:03:45 -07:00
options.fudo.paris-container = with types; {
2024-05-27 17:30:16 -07:00
enable = mkEnableOption "Enable Fudo Paris user server.";
state-directory = mkOption {
type = str;
description = "Directory at which to store server state.";
};
2024-05-28 11:09:29 -07:00
ports = mkOption {
type = listOf port;
description = "List of ports to open to the public internet.";
default = [ ];
};
2024-06-01 09:18:28 -07:00
ssh-keys = mkOption {
type = listOf str;
description = "List of SSH keys to use.";
default = [ ];
};
kerberos = mkOption {
type = nullOr (submodule {
options = {
keytab = mkOption {
type = str;
description = "Location of Paris keytab.";
};
};
});
default = null;
};
2024-05-28 10:27:13 -07:00
ldap = {
image = mkOption {
type = str;
description = "Authentik LDAP outpost Docker image.";
2024-05-28 10:41:07 -07:00
default = "ghcr.io/goauthentik/ldap:latest";
2024-05-28 10:27:13 -07:00
};
listen-ips = mkOption {
2024-06-05 09:37:00 -07:00
type = listOf str;
description = "Address on which to listen for requests.";
};
2024-05-28 11:09:29 -07:00
port = mkOption {
type = port;
description = "Port on which to listen for LDAP requests.";
default = 4389;
};
2024-05-28 10:27:13 -07:00
domain = mkOption {
type = str;
description =
"Domain for which data is served. Only used for internal mapping.";
};
authentik-host = mkOption {
type = str;
description = "Hostname of the LDAP outpost provider.";
};
2024-05-28 10:41:07 -07:00
outpost-token-file = mkOption {
2024-05-28 10:27:13 -07:00
type = str;
2024-05-28 10:41:07 -07:00
description =
"File containing token with which to authenticate to the Authentik host.";
2024-05-28 10:27:13 -07:00
};
bind-dn = mkOption {
type = str;
description = "DN as which to bind with the LDAP server.";
};
bind-token-file = mkOption {
type = str;
description =
"File containing token with which to bind with the LDAP server.";
};
base = mkOption {
type = str;
description = "Base of the LDAP server.";
example = "dc=fudo,dc=org";
};
user-ou = mkOption {
type = str;
description = "Organizational unit containing users.";
default = "ou=users";
};
group-ou = mkOption {
type = str;
description = "Organizational unit containing users.";
default = "ou=groups";
};
};
2024-05-27 17:30:16 -07:00
networking = {
internal = {
interface = mkOption {
type = str;
description =
"Parent host interface on which to listen for internal traffic.";
};
2024-05-27 17:30:16 -07:00
ipv4 = mkOption {
type = nullOr (submodule {
options = {
address = mkOption {
type = str;
description = "IP address.";
};
prefixLength = mkOption {
type = int;
description = "Significant bits in the address.";
};
2024-05-27 17:30:16 -07:00
};
});
default = null;
};
ipv6 = mkOption {
type = nullOr (submodule {
options = {
address = mkOption {
type = str;
description = "IP address.";
};
prefixLength = mkOption {
type = int;
description = "Significant bits in the address.";
};
2024-05-27 17:30:16 -07:00
};
});
default = null;
};
2024-05-27 17:30:16 -07:00
};
external = {
interface = mkOption {
type = str;
description = "Parent host interface on which to listen.";
};
2024-05-27 17:30:16 -07:00
ipv4 = mkOption {
type = nullOr (submodule {
options = {
address = mkOption {
type = str;
description = "IP address.";
};
prefixLength = mkOption {
type = int;
description = "Significant bits in the address.";
};
2024-05-27 17:30:16 -07:00
};
});
default = null;
};
ipv6 = mkOption {
type = nullOr (submodule {
options = {
address = mkOption {
type = str;
description = "IP address.";
};
prefixLength = mkOption {
type = int;
description = "Significant bits in the address.";
};
2024-05-27 17:30:16 -07:00
};
});
default = null;
};
2024-05-27 17:30:16 -07:00
};
};
};
2024-06-01 09:18:28 -07:00
config = mkIf cfg.enable {
2024-05-27 17:30:16 -07:00
systemd.tmpfiles.rules =
[ "d ${cfg.state-directory}/home 0700 root root - -" ];
2024-05-28 10:27:13 -07:00
fudo.secrets.host-secrets."${hostname}" = {
parisLdapEnv = {
source-file = pkgs.writeText "paris-ldap-proxy.env"
(concatStringsSep "\n" [
2024-06-05 10:09:53 -07:00
"AUTHENTIK_HOST=https://${cfg.ldap.authentik-host}"
2024-05-28 10:41:07 -07:00
"AUTHENTIK_TOKEN=${readFile cfg.ldap.outpost-token-file}"
2024-06-05 10:09:53 -07:00
"AUTHENTIK_INSECURE=0"
2024-05-28 10:27:13 -07:00
]);
target-file = "/run/paris/ldap.env";
};
parisSssdEnv = {
source-file = pkgs.writeText "paris-sssd.env"
"LDAP_DEFAULT_AUTHTOKEN=${readFile cfg.ldap.bind-token-file}";
2024-05-28 11:45:05 -07:00
target-file = "/run/paris/sssd.env";
2024-05-28 10:27:13 -07:00
};
2024-06-02 16:56:56 -07:00
} // (listToAttrs (map (keypair:
nameValuePair (keypairFilename keypair) {
source-file = keypair.private-key;
target-file = "/run/paris/openssh/${keypairFilename keypair}";
}) parisKeypairs));
2024-05-28 10:27:13 -07:00
virtualisation.oci-containers.containers.paris-ldap-proxy = {
2024-05-28 10:27:13 -07:00
image = cfg.ldap.image;
autoStart = true;
ports =
map (ip: "${ip}:${toString cfg.ldap.port}:389") cfg.ldap.listen-ips;
2024-05-28 10:27:13 -07:00
environmentFiles = [ hostSecrets.parisLdapEnv.target-file ];
};
2024-05-27 17:30:16 -07:00
containers.paris = {
autoStart = true;
macvlans =
[ cfg.networking.internal.interface cfg.networking.external.interface ];
2024-05-27 17:30:16 -07:00
bindMounts = {
"/home" = {
hostPath = "${cfg.state-directory}/home";
isReadOnly = false;
};
2024-06-05 11:26:37 -07:00
"/run/paris/sssd.env" = {
2024-06-03 11:30:07 -07:00
hostPath = hostSecrets.parisSssdEnv.target-file;
isReadOnly = true;
};
2024-06-02 16:56:56 -07:00
} // (listToAttrs (map (keypair:
nameValuePair "/run/openssh/keys/${keypairFilename keypair}" {
hostPath = "/run/paris/openssh/${keypairFilename keypair}";
isReadOnly = true;
}) parisKeypairs));
2024-05-27 17:30:16 -07:00
additionalCapabilities = [ "CAP_NET_ADMIN" ];
config = {
nixpkgs.pkgs = pkgs;
2024-05-27 18:05:35 -07:00
environment.systemPackages = packages;
2024-05-27 17:30:16 -07:00
2024-06-01 09:18:28 -07:00
services = {
2024-06-02 21:53:53 -07:00
openssh = {
2024-06-01 09:18:28 -07:00
enable = true;
startWhenNeeded = true;
2024-06-02 16:56:56 -07:00
hostKeys = map (keypair: {
path = "/run/openssh/keys/${keypairFilename keypair}";
type = keypair.key-type;
}) parisKeypairs;
2024-06-01 09:18:28 -07:00
settings = {
UseDns = true;
PermitRootLogin = "no";
2024-05-28 10:27:13 -07:00
};
2024-06-01 09:18:28 -07:00
};
2024-05-28 10:27:13 -07:00
2024-06-01 09:18:28 -07:00
sssd = {
enable = true;
kcm = true;
2024-06-05 11:26:37 -07:00
environmentFile = "/run/paris/sssd.env";
2024-06-01 09:18:28 -07:00
config = lib.generators.toINI { } {
sssd = {
config_file_version = 2;
reconnection_retries = 3;
2024-06-05 12:29:04 -07:00
services = concatStringsSep ", " [ "nss" "pam" "ssh" ];
domains = concatStringsSep ", " [ cfg.ldap.domain ];
2024-06-01 09:18:28 -07:00
};
pam = { reconnection_retries = 3; };
nss.reconnection_retries = 3;
2024-06-01 09:18:28 -07:00
"domain/${cfg.ldap.domain}" = {
cache_credentials = true;
id_provider = "ldap";
chpass_provider = "ldap";
auth_provider = "ldap";
access_provider = "ldap";
2024-06-05 10:07:18 -07:00
ldap_uri = "ldap://${head cfg.ldap.listen-ips}:${
toString cfg.ldap.port
}";
2024-06-01 09:18:28 -07:00
ldap_schema = "rfc2307bis";
ldap_search_base = cfg.ldap.base;
ldap_user_search_base = "${cfg.ldap.user-ou},${cfg.ldap.base}";
ldap_group_search_base =
"${cfg.ldap.group-ou},${cfg.ldap.base}";
2024-06-05 12:07:53 -07:00
ldap_user_object_class = "user";
ldap_user_name = "cn";
2024-06-01 09:18:28 -07:00
ldap_group_object_class = "group";
ldap_group_name = "cn";
ldap_default_bind_dn = cfg.ldap.bind-dn;
ldap_default_authtok = "$LDAP_DEFAULT_AUTHTOKEN";
};
2024-05-28 10:27:13 -07:00
};
};
};
networking = let
external = cfg.networking.external;
internal = cfg.networking.internal;
in {
2024-05-27 17:30:16 -07:00
defaultGateway = {
2024-05-27 18:05:35 -07:00
address = config.networking.defaultGateway.address;
interface = "mv-${external.interface}";
2024-05-27 17:30:16 -07:00
};
enableIPv6 = !isNull internal.ipv6 || !isNull external.ipv6;
2024-05-27 17:30:16 -07:00
nameservers = config.networking.nameservers;
firewall = {
enable = true;
allowedTCPPorts = [ 22 ] ++ cfg.ports;
};
2024-06-03 13:44:25 -07:00
interfaces = {
"mv-${external.interface}" = {
ipv4.addresses = optional (!isNull external.ipv4) {
inherit (external.ipv4) address prefixLength;
};
ipv6.addresses = optional (!isNull external.ipv6) {
inherit (external.ipv6) address prefixLength;
};
};
"mv-${internal.interface}" = {
ipv4.addresses = optional (!isNull internal.ipv4) {
inherit (internal.ipv4) address prefixLength;
2024-06-03 13:44:25 -07:00
};
ipv6.addresses = optional (!isNull internal.ipv6) {
inherit (internal.ipv6) address prefixLength;
2024-06-03 13:44:25 -07:00
};
2024-05-27 17:30:16 -07:00
};
};
};
};
};
};
}