Initial proposal for momo infrastructure setup

This commit is contained in:
Benjamin Bädorf 2023-02-25 04:24:27 +01:00
parent b1d2bfef98
commit f291e88d86
No known key found for this signature in database
GPG key ID: 4406E80E13CD656C
15 changed files with 402 additions and 210 deletions

View file

@ -16,7 +16,6 @@ in {
./triton-vmtools.nix
./caddy.nix
./drone.nix
./keycloak.nix
./gitea.nix
./mailman.nix
@ -27,11 +26,18 @@ in {
"${latestModulesPath}/services/misc/gitea.nix"
];
disabledModules = [
"services/misc/gitea.nix"
];
config = {
age.secrets.mailing-password = {
file = "${self}/secrets/gitea-database-password.age";
mode = "700";
owner = "root";
};
# # #
# # # pub.solar options
# # #
@ -41,6 +47,16 @@ in {
lite = true;
};
pub-solar.infra-node = {
mailing = {
type = "smtp";
user = "admin@momo.koeln";
host = "mx2.greenbaum.cloud:465";
from = ''"pub.solar git server" <gitea@pub.solar>'';
passwordFile = config.age.secrets.mailing-password.path;
};
};
# Allow sudo without a password for the barkeeper user
security.sudo.extraRules = [
{
@ -55,19 +71,17 @@ in {
];
# Machine user for CI pipelines
users.users.hakkonaut = {
description = "CI and automation user";
users.users.www-user = {
description = "user";
home = "/var/nix/iso-cache";
useDefaultShell = true;
uid = 998;
group = "hakkonaut";
uid = 10001;
group = "www-user";
isSystemUser = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGP5MvCwNRtCcP1pSDrn0XZTNlpOqYnjHDm9/OI4hECW hakkonaut@flora-6"
];
openssh.authorizedKeys.keys = [];
};
users.groups.hakkonaut = {};
users.groups.www-user = {};
# # #
# # # Triton host specific options

55
hosts/0001/caddy.nix Normal file
View file

@ -0,0 +1,55 @@
{
config,
lib,
pkgs,
self,
...
}: {
services.caddy = {
enable = lib.mkForce true;
group = "www-user";
email = "admins@pub.solar";
globalConfig = lib.mkForce "";
virtualHosts = {
"momo.koeln" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
# website
handle {
root * /srv/www/momo.koeln
try_files {path}.html {path}
file_server
}
# minimal error handling, respond with status code and text
handle_errors {
respond "{http.error.status_code} {http.error.status_text}"
}
'';
};
"www.momo.koeln" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir https://momo.koeln{uri}
'';
};
"list.momo.koeln" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
handle_path /static/* {
root * /var/lib/mailman/web
file_server
}
reverse_proxy :8000
'';
};
};
};
networking.firewall.allowedTCPPorts = [80 443];
}

View file

@ -1,5 +1,5 @@
{...}: {
imports = [
./flora-6.nix
./0001.nix
];
}

120
hosts/0001/gitea.nix Normal file
View file

@ -0,0 +1,120 @@
{
config,
lib,
pkgs,
self,
...
}: let
hostAddress = "10.10.42.1";
serviceAddress = "10.10.42.3";
hostname = "git.momo.koeln";
dbUserName = "gitea";
hostStateDir = "/mnt/internal/gitea";
containerStateDir = "/var/lib/gitea";
in {
age.secrets.gitea-database-password = {
file = "${self}/secrets/gitea-database-password.age";
mode = "600";
owner = "gitea";
};
age.secrets.gitea-mailer-password = {
file = "${self}/secrets/gitea-mailer-password.age";
mode = "600";
owner = "gitea";
};
services.caddy.virtualHosts.${hostname} = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir /user/login /user/oauth2/${config.containers.keycloak.config.services.keycloak.settings.hostname} temporary
reverse_proxy ${serviceAddress}:8080
'';
};
containers."gitea" = {
privateNetwork = true;
hostAddress = hostAddress;
localAddress = serviceAddress;
bindMounts."${containerStateDir}" = {
hostPath = hostStateDir;
isReadOnly = false;
};
bindMounts."${config.age.secrets.gitea-database-password.path}" = {
hostPath = config.age.secrets.gitea-database-password.path;
isReadOnly = true;
};
bindMounts."${config.age.secrets.gitea-mailer-password.path}" = {
hostPath = config.age.secrets.gitea-mailer-password.path;
isReadOnly = true;
};
config = {
config,
pkgs,
...
}: {
# gitea
services.gitea = {
enable = true;
appName = "pub.solar git server";
database = {
type = "postgres";
passwordFile = config.age.secrets.gitea-database-password.path;
};
domain = domain;
httpAddress = "0.0.0.0";
httpPort = 3000;
lfs.enable = true;
mailerPasswordFile = config.pub-solar.infra-node.mailing.passwordFile;
rootUrl = "https://git.pub.solar";
settings = {
mailer = mkIf config.pub-solar.infra-node.mailing.enabled {
ENABLED = true;
MAILER_TYPE = config.pub-solar.infra-node.mailing.type;
HOST = config.pub-solar.infra-node.mailing.host;
FROM = config.pub-solar.infra-node.mailing.from;
USER = config.pub-solar.infra-node.mailing.user;
};
# currently broken, gpg core dumps
#"repository.signing" = {
# SIGNING_KEY = "default";
# MERGES = "always";
#};
openid = {
ENABLE_OPENID_SIGNIN = true;
ENABLE_OPENID_SIGNUP = true;
};
# uncomment after initial deployment, first user is admin user
# required to setup SSO (oauth openid-connect, keycloak auth provider)
service.ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
session.COOKIE_SECURE = lib.mkForce true;
};
};
# Required for gitea server side gpg signatures
# configured / setup manually in
# /var/lib/gitea/data/home/.gitconfig and
# /var/lib/gitea/data/home/.gnupg/
programs.gnupg.agent = {
enable = true;
pinentryFlavor = "curses";
};
# Required to make gpg work without a graphical environment?
# otherwise generating a new gpg key fails with this error:
# gpg: agent_genkey failed: No pinentry
# see: https://github.com/NixOS/nixpkgs/issues/97861#issuecomment-827951675
environment.variables = {
GPG_TTY = "$(tty)";
};
};
};
}

73
hosts/0001/keycloak.nix Normal file
View file

@ -0,0 +1,73 @@
{
config,
lib,
inputs,
pkgs,
self,
...
}: let
hostAddress = "10.10.42.1";
serviceAddress = "10.10.42.1";
hostname = "auth.momo.koeln";
dbUserName = "keycloak";
hostStateDir = "/mnt/internal/keycloak";
containerStateDir = "/var/lib/keycloak";
in {
age.secrets.keycloak-database-password = {
file = "${self}/secrets/keycloak-database-password.age";
mode = "700";
#owner = "keycloak";
};
services.caddy.virtualHosts.${hostname} = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir / /realms/momo.koeln/account temporary
reverse_proxy ${serviceAddress}:8080
'';
};
containers."keycloak" = {
privateNetwork = true;
hostAddress = hostAddress;
localAddress = serviceAddress;
bindMounts."${containerStateDir}" = {
hostPath = hostStateDir;
isReadOnly = false;
};
bindMounts."${config.age.secrets.keycloak-database-password.path}" = {
hostPath = config.age.secrets.keycloak-database-password.path;
isReadOnly = true;
};
config = {
config,
pkgs,
...
}: {
# keycloak
services.keycloak = {
enable = true;
database.passwordFile = config.age.secrets.keycloak-database-password.path;
settings = {
hostname = domain;
http-host = "0.0.0.0";
http-port = 8080;
proxy = "edge";
};
# themes = {
# "momo.koeln" = inputs.keycloak-theme-pub-solar.legacyPackages.${pkgs.system}.keycloak-theme-pub-solar;
# };
};
};
};
}

88
hosts/0001/nextcloud.nix Normal file
View file

@ -0,0 +1,88 @@
{
config,
lib,
inputs,
pkgs,
self,
...
}: let
hostAddress = "10.10.42.1";
serviceAddress = "10.10.42.2";
hostname = "cloud.momo.koeln";
dbUserName = "nextcloud";
hostStateDir = "/mnt/internal/nextcloud";
containerStateDir = "/var/lib/nextcloud";
in {
age.secrets.nextcloud-db-password = {
file = "${self}/secrets/nextcloud-db-password.age";
mode = "700";
owner = "nextcloud";
};
age.secrets.nextcloud-admin-password = {
file = "${self}/secrets/nextcloud-admin-password";
mode = "700";
owner = "nextcloud";
};
services.caddy.virtualHosts.${hostname} = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
reverse_proxy ${serviceAddress}:80
'';
};
containers."nextcloud" = {
privateNetwork = true;
hostAddress = hostAddress;
localAddress = serviceAddress;
bindMounts."${containerStateDir}" = {
hostPath = hostStateDir;
isReadOnly = false;
};
config = {
config,
pkgs,
...
}: {
networking.firewall.allowedTCPPorts = [80];
# nextcloud
services.nextcloud = {
enable = true;
hostName = hostname;
home = containerStateDir;
config = {
dbuser = dbUserName;
dbtype = "pgsql";
dbport = 5432;
dbpassFile = config.age.secrets.nextcloud-db-password.path;
adminUser = "admin";
adminpassFile = config.age.secrets.nextcloud-admin-password.path;
};
};
services.postgresql = {
enable = true;
ensureUsers = [
{
name = dbUserName;
ensurePermissions = {
"DATABASE nextcloud" = "ALL PRIVILEGES";
};
}
];
ensureDatabases = ["nextcloud"];
};
};
};
}

View file

@ -1,99 +0,0 @@
{
config,
lib,
pkgs,
self,
...
}: {
services.caddy = {
enable = lib.mkForce true;
group = "hakkonaut";
email = "admins@pub.solar";
globalConfig = lib.mkForce "";
virtualHosts = {
"pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
# PubSolarOS images
handle /os/download/* {
root * /srv/www
file_server /os/download/* browse
}
# serve base domain pub.solar for mastodon.pub.solar
# https://masto.host/mastodon-usernames-different-from-the-domain-used-for-installation/
handle /.well-known/host-meta {
redir https://mastodon.pub.solar{uri}
}
# pub.solar website
handle {
root * /srv/www/pub.solar
try_files {path}.html {path}
file_server
}
# minimal error handling, respond with status code and text
handle_errors {
respond "{http.error.status_code} {http.error.status_text}"
}
'';
};
"www.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir https://pub.solar{uri}
'';
};
"auth.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir / /realms/pub.solar/account temporary
reverse_proxy :8080
'';
};
"git.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
redir /user/login /user/oauth2/keycloak temporary
reverse_proxy :3000
'';
};
"ci.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
reverse_proxy :4000
'';
};
"list.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
handle_path /static/* {
root * /var/lib/mailman/web
file_server
}
reverse_proxy :8000
'';
};
"obs-portal.pub.solar" = {
logFormat = lib.mkForce ''
output discard
'';
extraConfig = ''
reverse_proxy obs-portal.svc.e5756d08-36fd-424b-f8bc-acdb92ca7b82.lev-1.int.greenbaum.zone:3000
'';
};
};
};
networking.firewall.allowedTCPPorts = [80 443];
}

View file

@ -1,72 +0,0 @@
{
config,
lib,
pkgs,
self,
...
}: {
age.secrets.gitea-database-password = {
file = "${self}/secrets/gitea-database-password.age";
mode = "600";
owner = "gitea";
};
age.secrets.gitea-mailer-password = {
file = "${self}/secrets/gitea-mailer-password.age";
mode = "600";
owner = "gitea";
};
# gitea
services.gitea = {
enable = true;
appName = "pub.solar git server";
database = {
type = "postgres";
passwordFile = config.age.secrets.gitea-database-password.path;
};
domain = "git.pub.solar";
httpAddress = "127.0.0.1";
httpPort = 3000;
lfs.enable = true;
mailerPasswordFile = config.age.secrets.gitea-mailer-password.path;
rootUrl = "https://git.pub.solar";
settings = {
mailer = {
ENABLED = true;
MAILER_TYPE = "smtp";
HOST = "mx2.greenbaum.cloud:465";
FROM = ''"pub.solar git server" <gitea@pub.solar>'';
USER = "admins@pub.solar";
};
# currently broken, gpg core dumps
#"repository.signing" = {
# SIGNING_KEY = "default";
# MERGES = "always";
#};
openid = {
ENABLE_OPENID_SIGNIN = true;
ENABLE_OPENID_SIGNUP = true;
};
# uncomment after initial deployment, first user is admin user
# required to setup SSO (oauth openid-connect, keycloak auth provider)
service.ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
session.COOKIE_SECURE = lib.mkForce true;
};
};
# Required for gitea server side gpg signatures
# configured / setup manually in
# /var/lib/gitea/data/home/.gitconfig and
# /var/lib/gitea/data/home/.gnupg/
programs.gnupg.agent = {
enable = true;
pinentryFlavor = "curses";
};
# Required to make gpg work without a graphical environment?
# otherwise generating a new gpg key fails with this error:
# gpg: agent_genkey failed: No pinentry
# see: https://github.com/NixOS/nixpkgs/issues/97861#issuecomment-827951675
environment.variables = {
GPG_TTY = "$(tty)";
};
}

View file

@ -1,29 +0,0 @@
{
config,
lib,
inputs,
pkgs,
self,
...
}: {
age.secrets.keycloak-database-password = {
file = "${self}/secrets/keycloak-database-password.age";
mode = "700";
#owner = "keycloak";
};
# keycloak
services.keycloak = {
enable = true;
database.passwordFile = config.age.secrets.keycloak-database-password.path;
settings = {
hostname = "auth.pub.solar";
http-host = "127.0.0.1";
http-port = 8080;
proxy = "edge";
};
themes = {
"pub.solar" = inputs.keycloak-theme-pub-solar.legacyPackages.${pkgs.system}.keycloak-theme-pub-solar;
};
};
}

View file

@ -0,0 +1,42 @@
{
lib,
config,
pkgs,
...
}:
with lib; let
cfg = config.pub-solar;
in {
options.pub-solar = {
infra-node = {
mailing = {
enabled = mkEnableOption "Whether to enable mailing for services on the host";
type = mkOption {
description = "Mail server type";
type = types.nullOr types.str;
default = "smtp";
};
host = mkOption {
description = "Mailing server host";
type = types.nullOr types.str;
default = null;
};
from = mkOption {
description = "Mailing server from";
type = types.nullOr types.str;
default = null;
};
user = mkOption {
description = "Mailing server user";
type = types.listOf types.str;
default = [];
};
passwordFile = mkOption {
description = "Mailing server passwordFile";
type = types.nullOr types.str;
default = null;
};
};
};
};
}