infra/nix/os/containers/webserver.nix
Stefan Junker 1533077234 sj-srv1,containers: debug and streamline networking config; update and track forgejo here
after an update to nixpkgs on sj-srv1 the networking for the `webserver`
container wasn't working. this caused me to debug the situation and
changing lots of things around. the culprit was most likely some impure
state file on the server that caused the `ve-webserver` interface not to
persist its IP. after renaming the webserver container the problem went
away.

i reverted all the IP changes and am keeping the other changes as opporunistic
improvements
2024-07-26 18:02:52 +02:00

334 lines
9 KiB
Nix

{
specialArgs,
hostAddress,
localAddress,
httpPort,
httpsPort,
forgejoSshPort,
autoStart ? false,
}: let
domain = "www.stefanjunker.de";
in {
inherit specialArgs;
config = {
config,
pkgs,
lib,
repoFlake,
nodeFlake,
...
}: {
system.stateVersion = "22.05"; # Did you read the comment?
disabledModules = [
"services/misc/forgejo.nix"
];
imports = [
"${nodeFlake.inputs.nixpkgs-unstable}/nixos/modules/services/misc/forgejo.nix"
../profiles/containers/configuration.nix
repoFlake.inputs.sops-nix.nixosModules.sops
];
sops.defaultSopsFile = ./webserver_secrets.yaml;
networking.firewall.allowedTCPPorts = [
httpPort
httpsPort
forgejoSshPort
];
sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"];
sops.secrets.hedgedoc_environment_file = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.hedgedoc.name;
};
services.caddy = {
enable = true;
logFormat = ''
level ERROR
'';
virtualHosts."${domain}" = {
extraConfig = ''
redir /hedgedoc* https://hedgedoc.${domain}
file_server /*/* {
browse
root /var/www/stefanjunker.de/htdocs/caddy
pass_thru
}
# respond "Hi"
# respond (not /*/*) "Hi"
'';
};
virtualHosts."hedgedoc.${domain}" = {
extraConfig = ''
reverse_proxy http://[::1]:3000
'';
};
virtualHosts."authelia.${domain}" = {
extraConfig = ''
reverse_proxy http://127.0.0.1:${builtins.toString config.services.authelia.instances.default.settings.server.port}
'';
};
virtualHosts."lldap.${domain}" = {
extraConfig = ''
reverse_proxy http://127.0.0.1:${builtins.toString config.services.lldap.settings.http_port}
'';
};
virtualHosts."forgejo.${domain}" = {
extraConfig = ''
reverse_proxy http://127.0.0.1:${builtins.toString config.services.forgejo.settings.server.HTTP_PORT}
'';
};
};
services.hedgedoc = {
enable = true;
settings = {
domain = "hedgedoc.${domain}";
urlPath = "";
protocolUseSSL = true;
db = {
dialect = "sqlite";
storage = "/var/lib/hedgedoc/db.hedgedoc.sqlite";
};
allowAnonymous = false;
allowAnonymousEdits = false;
allowGravatar = false;
allowFreeURL = false;
defaultPermission = "private";
allowEmailRegister = false;
email = false;
ldap = {
url = "ldap://127.0.0.1:${builtins.toString config.services.lldap.settings.ldap_port}";
bindDn = "uid=admin,ou=people,dc=stefanjunker,dc=de";
# these are set via the `environmentFile`
bindCredentials = "$LDAP_ADMIN_PASSWORD";
searchBase = "ou=people,dc=stefanjunker,dc=de";
searchFilter = "(&(memberOf=cn=hedgedoc,ou=groups,dc=stefanjunker,dc=de)(uid={{username}}))";
useridField = "uid";
};
uploadsPath = "/var/lib/hedgedoc/uploads";
};
environmentFile = config.sops.secrets.hedgedoc_environment_file.path;
};
services.jitsi-meet = {
enable = false;
hostName = "meet.${domain}";
config = {
prejoinPageEnabled = true;
};
caddy.enable = true;
nginx.enable = false;
};
sops.secrets.authelia_storageEncryptionKey = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.authelia-default.name;
};
sops.secrets.authelia_jwtSecret = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.authelia-default.name;
};
services.authelia.instances.default = let
baseDir = "/var/lib/authelia-default";
in {
enable = true;
secrets.storageEncryptionKeyFile = config.sops.secrets.authelia_storageEncryptionKey.path;
secrets.jwtSecretFile = config.sops.secrets.authelia_jwtSecret.path;
settings = {
theme = "auto";
default_2fa_method = "totp";
log.level = "debug";
server = {
disable_healthcheck = true;
host = "127.0.0.1";
port = 9091;
# path = "authelia";
};
storage = {
local.path = "${baseDir}/authelia.sqlite";
};
authentication_backend = {
file.path = "${baseDir}/first_factor.yaml";
file.search.email = true;
file.search.case_insensitive = false;
};
access_control = {
default_policy = "one_factor";
};
session.domain = "stefanjunker.de";
notifier = {
disable_startup_check = true;
filesystem.filename = "${baseDir}/notification.txt";
};
};
};
users.groups.lldap = {};
users.users.lldap = {
isSystemUser = true;
group = "lldap";
};
sops.secrets.lldap_jwtSecret = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.lldap.name;
};
sops.secrets.lldap_adminPassword = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.lldap.name;
};
sops.secrets.lldap_environmentFile = {
sopsFile = ./webserver_secrets.yaml;
owner = config.users.users.lldap.name;
};
services.lldap = {
enable = true;
environment = {
LLDAP_JWT_SECRET_FILE = config.sops.secrets.lldap_jwtSecret.path;
LLDAP_LDAP_USER_PASS_FILE = config.sops.secrets.lldap_adminPassword.path;
};
environmentFile = config.sops.secrets.lldap_environmentFile.path;
settings = {
verbose = true;
ldap_base_dn = "dc=stefanjunker,dc=de";
http_url = "https://lldap.${domain}";
## Options to configure SMTP parameters, to send password reset emails.
## To set these options from environment variables, use the following format
## (example with "password"): LLDAP_SMTP_OPTIONS__PASSWORD
smtp_options = {
## Whether to enabled password reset via email, from LLDAP.
enable_password_reset = true;
# port = 465;
## How the connection is encrypted, either "NONE" (no encryption), "TLS" or "STARTTLS".
# smtp_encryption = "TLS";
};
# database_url = "sqlite:///var/lib/lldap/users.db?mode=rwc";
};
};
sops.secrets.FORGEJO_JWT_SECRET = {};
sops.secrets.FORGEJO_INTERNAL_TOKEN = {};
sops.secrets.FORGEJO_SECRET_KEY = {};
services.forgejo = {
enable = true;
package = nodeFlake.inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.forgejo;
settings = {
service.DISABLE_REGISTRATION = true;
server.HTTP_ADDR = "127.0.0.1";
server.START_SSH_SERVER = true;
server.SSH_PORT = forgejoSshPort;
server.ROOT_URL = "https://forgejo.${domain}";
server.HTTP_PORT = 3001;
# TODO: how do i get a 3072 length SSH key with the yubikey?
"ssh.minimum_key_sizes".RSA = 2048;
};
secrets = {
oauth2.JWT_SECRET = lib.mkForce config.sops.secrets.FORGEJO_JWT_SECRET.path;
security.INTERNAL_TOKEN = lib.mkForce config.sops.secrets.FORGEJO_INTERNAL_TOKEN.path;
security.SECRET_KEY = lib.mkForce config.sops.secrets.FORGEJO_SECRET_KEY.path;
};
};
systemd.services.lldap.serviceConfig.User = config.users.users.lldap.name;
systemd.services.lldap.serviceConfig.Group = config.users.groups.lldap.name;
systemd.services.lldap.serviceConfig.DynamicUser = lib.mkForce false;
};
inherit autoStart;
bindMounts = {
# FIXME/REMINDER: this is used so that the container can decrypt the secrets that are deployed to the host
"/etc/ssh/ssh_host_ed25519_key".isReadOnly = true;
"/etc/ssh/ssh_host_ed25519_key.pub".isReadOnly = true;
"/var/www" = {
hostPath = "/var/lib/container-volumes/webserver/var-www";
isReadOnly = false;
};
"/var/lib/mysql" = {
hostPath = "/var/lib/container-volumes/webserver/var-lib-mysql";
isReadOnly = false;
};
"/var/lib/hedgedoc" = {
hostPath = "/var/lib/container-volumes/webserver/var-lib-hedgedoc";
isReadOnly = false;
};
"/var/lib/authelia-default" = {
hostPath = "/var/lib/container-volumes/webserver/var-lib-authelia-default";
isReadOnly = false;
};
"/var/lib/lldap" = {
hostPath = "/var/lib/container-volumes/webserver/var-lib-lldap";
isReadOnly = false;
};
"/var/lib/forgejo" = {
hostPath = "/var/lib/container-volumes/webserver/var-lib-forgejo";
isReadOnly = false;
};
};
privateNetwork = true;
forwardPorts = [
{
# http
containerPort = 80;
hostPort = httpPort;
protocol = "tcp";
}
{
# https
containerPort = 443;
hostPort = httpsPort;
protocol = "tcp";
}
{
# forgejo ssh
containerPort = forgejoSshPort;
hostPort = forgejoSshPort;
protocol = "tcp";
}
];
inherit hostAddress localAddress;
}