treewide: move nixos modules

This commit is contained in:
Charlotte Van Petegem 2024-07-18 15:04:18 +02:00
parent d84be7c616
commit 8eff4c5e4f
73 changed files with 62 additions and 62 deletions

View file

@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.accentor.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.accentor.enable {
services.postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
};
services.accentor = {
enable = true;
home = "${config.chvp.dataPrefix}/var/lib/accentor";
hostname = "accentor.vanpetegem.me";
environmentFile = config.age.secrets."passwords/services/accentor".path;
rescanTimer = {
enable = true;
dates = "00:00";
};
nginx = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
};
};
security.doas.extraRules = [{
users = [ "charlotte" ];
noPass = true;
cmd = "accentor-console";
runAs = "accentor";
}];
age.secrets."passwords/services/accentor" = {
file = ../../../../secrets/passwords/services/accentor.age;
owner = "accentor";
};
};
}

View file

@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.containers = {
enable = lib.mkOption {
default = false;
example = true;
};
externalInterface = lib.mkOption {
example = "eno3";
};
};
config = {
networking.nat = lib.mkIf config.chvp.services.containers.enable {
enable = true;
enableIPv6 = true;
internalInterfaces = [ "ve-+" ];
externalInterface = config.chvp.services.containers.externalInterface;
};
};
}

View file

@ -0,0 +1,46 @@
{ pkgs, ... }:
{
users.users.data = {
isNormalUser = true;
home = "/home/data";
description = "Data Access";
uid = 1000;
group = "users";
hashedPasswordFile = "/run/secrets/password_file";
};
users.users.readonly = {
isNormalUser = true;
home = "/home/readonly";
description = "Readonly data access";
uid = 1001;
group = "sftponly";
hashedPasswordFile = "/run/secrets/readonly_password_file";
};
users.groups.sftponly = { gid = 10000; };
environment.systemPackages = [ pkgs.rsync pkgs.mktorrent (pkgs.writeShellScriptBin "create_torrent" ". /run/secrets/create_torrent") ];
security.sudo.enable = false;
services.openssh = {
enable = true;
hostKeys = [
{ bits = 4096; path = "/run/secrets/ssh_host_rsa_key"; type = "rsa"; }
{ path = "/run/secrets/ssh_host_ed25519_key"; type = "ed25519"; }
];
settings = {
HostKeyAlgorithms = "+ssh-rsa";
Macs = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha2-256-etm@openssh.com" "umac-128-etm@openssh.com" "hmac-sha2-512" ];
PermitRootLogin = "no";
};
extraConfig = ''
Match group sftponly
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
ForceCommand internal-sftp
Match user data
PasswordAuthentication no
KbdInteractiveAuthentication no
'';
authorizedKeysFiles = [ "/run/secrets/%u_authorized_keys" ];
};
}

View file

@ -0,0 +1,122 @@
{ config, lib, ... }:
{
options.chvp.services.data-access.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.data-access.enable {
chvp.services = {
containers.enable = true;
nginx.hosts = [
{
fqdn = "data.vanpetegem.me";
options = {
default = true;
basicAuthFile = config.age.secrets."passwords/services/data-basic-auth".path;
root = "/srv/data";
locations = {
"/".extraConfig = ''
autoindex on;
'';
"/public".extraConfig = ''
autoindex on;
auth_basic off;
'';
};
};
}
];
};
networking.firewall.allowedTCPPorts = [ 2002 ];
containers.data-access = {
ephemeral = true;
autoStart = true;
bindMounts = {
"/home/data/data" = {
hostPath = "/srv/data";
isReadOnly = false;
};
"/home/readonly/data" = {
hostPath = "/srv/data";
isReadOnly = true;
};
"/run/secrets" = {
hostPath = "/run/data-access";
isReadOnly = true;
};
};
forwardPorts = [{
containerPort = 22;
hostPort = 2002;
protocol = "tcp";
}];
privateNetwork = true;
hostAddress = "192.168.100.10";
hostAddress6 = "fc00::1";
localAddress = "192.168.100.11";
localAddress6 = "fc00::2";
config = { ... }: {
system.stateVersion = config.chvp.stateVersion;
imports = [ ./config.nix ];
};
};
age.secrets."data-access/ssh_host_rsa_key" = {
file = ../../../../secrets/data-access/ssh_host_rsa_key.age;
path = "/run/data-access/ssh_host_rsa_key";
symlink = false;
};
age.secrets."data-access/ssh_host_rsa_key.pub" = {
file = ../../../../secrets/data-access/ssh_host_rsa_key.pub.age;
path = "/run/data-access/ssh_host_rsa_key.pub";
symlink = false;
};
age.secrets."data-access/ssh_host_ed25519_key" = {
file = ../../../../secrets/data-access/ssh_host_ed25519_key.age;
path = "/run/data-access/ssh_host_ed25519_key";
symlink = false;
};
age.secrets."data-access/ssh_host_ed25519_key.pub" = {
file = ../../../../secrets/data-access/ssh_host_ed25519_key.pub.age;
path = "/run/data-access/ssh_host_ed25519_key.pub";
symlink = false;
};
age.secrets."data-access/password_file" = {
file = ../../../../secrets/data-access/password_file.age;
path = "/run/data-access/password_file";
symlink = false;
};
age.secrets."data-access/readonly_password_file" = {
file = ../../../../secrets/data-access/readonly_password_file.age;
path = "/run/data-access/readonly_password_file";
symlink = false;
};
age.secrets."data-access/authorized_keys" = {
file = ../../../../secrets/data-access/authorized_keys.age;
owner = "charlotte";
path = "/run/data-access/data_authorized_keys";
symlink = false;
};
age.secrets."data-access/readonly_authorized_keys" = {
file = ../../../../secrets/data-access/readonly_authorized_keys.age;
owner = "1001";
group = "65534";
path = "/run/data-access/readonly_authorized_keys";
symlink = false;
};
age.secrets."data-access/create_torrent" = {
file = ../../../../secrets/data-access/create_torrent.age;
owner = "charlotte";
path = "/run/data-access/create_torrent";
symlink = false;
};
age.secrets."passwords/services/data-basic-auth" = {
file = ../../../../secrets/passwords/services/data-basic-auth.age;
owner = "nginx";
};
};
}

View file

@ -0,0 +1,20 @@
{ config, pkgs, ... }:
{
imports = [
./accentor
./containers
./data-access
./garmin-scraper
./git
./grafana
./mail
./mastodon
./matrix
./nextcloud
./nginx
./torrents
];
services.postgresql.package = pkgs.postgresql_15;
}

View file

@ -0,0 +1,39 @@
{ config, lib, pkgs, ... }:
let
garmin2influx = pkgs.writers.writePython3Bin "garmin2influx"
{
libraries = with pkgs.python3Packages; [ garminconnect influxdb-client ];
}
(builtins.readFile ./garmin2influx.py);
in
{
options.chvp.services.garmin-scraper.enable = lib.mkEnableOption "garmin scraper";
config = lib.mkIf config.chvp.services.garmin-scraper.enable {
# Install in environment to allow manual data collection
environment.systemPackages = [ garmin2influx ];
systemd = {
services.garmin2influx = {
description = "Garmin health data importer";
restartIfChanged = false;
unitConfig.X-StopOnRemoval = false;
serviceConfig = {
EnvironmentFile = config.age.secrets."passwords/services/garmin2influx-env".path;
Type = "oneshot";
User = "charlotte";
Group = "users";
ExecStart = "${garmin2influx}/bin/garmin2influx";
RestartSec = "5s";
Restart = "on-failure";
};
startAt = "02/4:00";
};
timers.garmin2influx.timerConfig.RandomizedDelaySec = "30min";
};
age.secrets."passwords/services/garmin2influx-env" = {
file = ../../../../secrets/passwords/services/garmin2influx-env.age;
owner = "charlotte";
};
};
}

View file

@ -0,0 +1,88 @@
import os
import sys
from datetime import date, datetime, timedelta, timezone
from garminconnect import (
Garmin,
GarminConnectConnectionError,
GarminConnectTooManyRequestsError,
GarminConnectAuthenticationError,
)
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
email = os.getenv('EMAIL')
password = os.getenv('PASSWORD')
token = os.getenv('TOKEN')
org = 'default'
bucket = 'default'
def hr2point(time, val):
return Point("health") \
.field("heart_rate", val) \
.time(
datetime.fromtimestamp(time / 1000, timezone.utc),
WritePrecision.S
)
def stress2point(time, val):
return Point("health") \
.field("stress", max(val, 0)) \
.time(
datetime.fromtimestamp(time / 1000, timezone.utc),
WritePrecision.S
)
def hr_for_date(api, date_to_fetch):
return api.get_heart_rates(date_to_fetch.isoformat())['heartRateValues']
def stress_for_date(api, date_to_fetch):
return api.get_stress_data(date_to_fetch.isoformat())['stressValuesArray']
date_to_fetch = date.today().isoformat()
if len(sys.argv) > 1:
date_to_fetch = sys.argv[1]
date_to_fetch = date.fromisoformat(date_to_fetch)
try:
api = Garmin(email, password)
api.login()
hr_points = list(map(
lambda p: hr2point(*p),
hr_for_date(api, date_to_fetch - timedelta(days=1))
))
stress_points = list(map(
lambda p: stress2point(*p),
stress_for_date(api, date_to_fetch - timedelta(days=1))
))
hr_points += list(map(
lambda p: hr2point(*p),
hr_for_date(api, date_to_fetch)
))
stress_points += list(map(
lambda p: stress2point(*p),
stress_for_date(api, date_to_fetch)
))
with InfluxDBClient(
url="https://stats.chvp.be:8086",
token=token,
org=org
) as client:
write_api = client.write_api(write_options=SYNCHRONOUS)
write_api.write(bucket, org, hr_points)
write_api.write(bucket, org, stress_points)
except (
GarminConnectConnectionError,
GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError,
) as err:
print(
f'Error occured during Garmin Connect communication: {err}',
file=sys.stderr
)
sys.exit(1)

View file

@ -0,0 +1,85 @@
{ config, lib, pkgs, ... }:
{
imports = [ ./runner.nix ];
options.chvp.services.git.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.git.enable {
chvp.services.nginx.hosts = [{
fqdn = "git.chvp.be";
options = {
locations."/" = {
proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
extraConfig = ''
client_max_body_size 50M;
'';
};
};
}];
users = {
users = {
git = {
uid = lib.mkForce 963;
group = "git";
isSystemUser = true;
useDefaultShell = true;
};
nginx.extraGroups = [ "git" ];
};
groups.git.gid = lib.mkForce 963;
};
services.openssh.settings.AcceptEnv = "GIT_PROTOCOL";
services.gitlab = {
enable = true;
statePath = "/var/lib/git/state";
backup.path = "/var/lib/git/backup";
databaseCreateLocally = true;
databaseUsername = "git";
databaseName = "git";
user = "git";
group = "git";
host = "git.chvp.be";
port = 443;
https = true;
initialRootEmail = "charlotte@vanpetegem.be";
initialRootPasswordFile = config.age.secrets."passwords/services/git/initial-root-password".path;
# Hack, https://github.com/NixOS/nixpkgs/pull/135926 broke stuff
pages.settings.pages-domain = "not.actually.enabled";
secrets = {
dbFile = config.age.secrets."passwords/services/git/db".path;
jwsFile = config.age.secrets."passwords/services/git/jws".path;
otpFile = config.age.secrets."passwords/services/git/otp".path;
secretFile = config.age.secrets."passwords/services/git/secret".path;
};
smtp = {
enable = true;
enableStartTLSAuto = false;
};
};
age.secrets."passwords/services/git/initial-root-password" = {
file = ../../../../secrets/passwords/services/git/initial-root-password.age;
owner = "git";
};
age.secrets."passwords/services/git/db" = {
file = ../../../../secrets/passwords/services/git/db.age;
owner = "git";
};
age.secrets."passwords/services/git/jws" = {
file = ../../../../secrets/passwords/services/git/jws.age;
owner = "git";
};
age.secrets."passwords/services/git/otp" = {
file = ../../../../secrets/passwords/services/git/otp.age;
owner = "git";
};
age.secrets."passwords/services/git/secret" = {
file = ../../../../secrets/passwords/services/git/secret.age;
owner = "git";
};
};
}

View file

@ -0,0 +1,57 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.git.runner.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.git.runner.enable {
services.gitlab-runner = {
enable = true;
settings.concurrent = 8;
services = {
nix = {
authenticationTokenConfigFile = config.age.secrets."passwords/services/gitlab-runner/registration".path;
dockerImage = "alpine";
dockerVolumes = [
"/nix/store:/nix/store:ro"
"/nix/var/nix/db:/nix/var/nix/db:ro"
"/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket:ro"
"/etc/nix/nix.conf:/etc/nix/nix.conf:ro"
];
preBuildScript = pkgs.writeScript "setup-container" ''
mkdir -p -m 0755 /nix/var/log/nix/drvs
mkdir -p -m 0755 /nix/var/nix/gcroots
mkdir -p -m 0755 /nix/var/nix/profiles
mkdir -p -m 0755 /nix/var/nix/temproots
mkdir -p -m 0755 /nix/var/nix/userpool
mkdir -p -m 1777 /nix/var/nix/gcroots/per-user
mkdir -p -m 1777 /nix/var/nix/profiles/per-user
mkdir -p -m 0755 /nix/var/nix/profiles/per-user/root
mkdir -p -m 0700 "$HOME/.nix-defexpr"
. ${pkgs.nix}/etc/profile.d/nix.sh
${pkgs.nix}/bin/nix-env -i ${lib.concatStringsSep " " (with pkgs; [ nix cacert git openssh ])}
'';
environmentVariables = {
ENV = "/etc/profile";
USER = "root";
NIX_REMOTE = "daemon";
PATH = "/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin";
NIX_SSL_CERT_FILE = "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt";
};
requestConcurrency = 4;
};
};
};
virtualisation.docker = {
enable = true;
storageDriver = "zfs";
};
age.secrets."passwords/services/gitlab-runner/registration" = {
file = ../../../../secrets/passwords/services/gitlab-runner/registration.age;
};
};
}

View file

@ -0,0 +1,97 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.grafana.enable = lib.mkEnableOption "grafana";
config = lib.mkIf config.chvp.services.grafana.enable {
chvp.services.nginx.hosts = [{
fqdn = "stats.chvp.be";
options.locations."/" = {
proxyPass = "http://grafana";
proxyWebsockets = true;
};
}];
users.users = {
influxdb2.extraGroups = [ "acme" ];
nginx.extraGroups = [ "grafana" ];
};
networking.firewall.allowedTCPPorts = [ 8086 ];
services = {
nginx.upstreams.grafana.servers = { "unix:/run/grafana/grafana.sock" = { }; };
influxdb2 = {
enable = true;
settings = {
reporting-disabled = true;
tls-cert = "${config.security.acme.certs."vanpetegem.me".directory}/fullchain.pem";
tls-key = "${config.security.acme.certs."vanpetegem.me".directory}/key.pem";
};
};
grafana = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/grafana";
settings = {
analytics.reporting_enabled = false;
"auth.anonymous" = {
enabled = "true";
org_name = "Van Petegem";
};
database = {
user = "grafana";
type = "postgres";
host = "/run/postgresql/";
name = "grafana";
};
security = {
admin_user = "chvp";
admin_password = "$__file{${config.age.secrets."passwords/services/grafana/admin-password".path}}";
secret_key = "$__file{${config.age.secrets."passwords/services/grafana/secret-key".path}}";
};
server = {
domain = "stats.chvp.be";
http_port = 3000;
protocol = "socket";
root_url = "https://stats.chvp.be";
socket = "/run/grafana/grafana.sock";
};
smtp = {
enabled = true;
host = "mail.vanpetegem.me:25";
user = "noreply@vanpetegem.me";
from_address = "noreply@vanpetegem.me";
password = "$__file{${config.age.secrets."passwords/services/grafana/smtp".path}}";
};
users = {
default_theme = "light";
allow_sign_up = false;
};
};
};
grafana-image-renderer = {
enable = true;
provisionGrafana = true;
chromium = pkgs.ungoogled-chromium;
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [ "grafana" ];
ensureUsers = [{
name = "grafana";
ensureDBOwnership = true;
}];
};
};
age.secrets."passwords/services/grafana/smtp" = {
file = ../../../../secrets/passwords/services/grafana/smtp.age;
owner = "grafana";
};
age.secrets."passwords/services/grafana/admin-password" = {
file = ../../../../secrets/passwords/services/grafana/admin-password.age;
owner = "grafana";
};
age.secrets."passwords/services/grafana/secret-key" = {
file = ../../../../secrets/passwords/services/grafana/secret-key.age;
owner = "grafana";
};
};
}

View file

@ -0,0 +1,166 @@
{ config, lib, pkgs, ... }:
let
keyFile = "${config.security.acme.certs."vanpetegem.me".directory}/key.pem";
certFile = "${config.security.acme.certs."vanpetegem.me".directory}/fullchain.pem";
in
{
options.chvp.services.mail.enable = lib.mkEnableOption "mail";
config = lib.mkIf config.chvp.services.mail.enable {
chvp.base.zfs.systemLinks = [
{ path = "/var/lib/dhparams"; type = "cache"; }
{ path = "/var/lib/dovecot"; type = "cache"; }
{ path = "/var/lib/opendkim"; type = "cache"; }
{ path = "/var/lib/postfix"; type = "cache"; }
{ path = "/var/lib/redis-rspamd"; type = "cache"; }
{ path = "/var/lib/rspamd"; type = "cache"; }
{ path = "/var/sieve"; type = "data"; }
];
mailserver = {
enable = true;
enableManageSieve = true;
fqdn = "mail.vanpetegem.me";
domains = [
"accentor.tech"
"chvp.be"
"cvpetegem.be"
"robbe.be"
"robbevanpetegem.be"
"robbevp.be"
"toekomstlabo.be"
"vanpetegem.be"
"vanpetegem.me"
];
localDnsResolver = false;
loginAccounts = {
"charlotte@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/charlotte@vanpetegem.be".path;
aliases = [ "@chvp.be" "@cvpetegem.be" "charlotte@vanpetegem.me" ];
};
"huis@vanpetegem.me".hashedPasswordFile = config.age.secrets."passwords/services/mail/huis@vanpetegem.me".path;
"noreply@vanpetegem.me" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/noreply@vanpetegem.me".path;
sendOnly = true;
};
"peter@vanpetegem.me".hashedPasswordFile = config.age.secrets."passwords/services/mail/peter@vanpetegem.me".path;
"postbot@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/postbot@vanpetegem.be".path;
aliases = [ "@vanpetegem.me" "@vanpetegem.be" ];
};
"robbe@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/robbe@vanpetegem.be".path;
aliases = [ "robbe.nb@vanpetegem.me" "robbe@vanpetegem.me" ];
};
"robbe@robbevanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/robbe@robbevanpetegem.be".path;
aliases = [ "@robbevanpetegem.be" ];
};
"hallo@robbe.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/hallo@robbe.be".path;
aliases = [ "@robbe.be" "@robbevp.be" ];
};
"webmaster@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/webmaster@vanpetegem.be".path;
aliases = [ "webmaster@vanpetegem.me" ];
};
};
indexDir = "${config.chvp.cachePrefix}/var/lib/dovecot/indices";
fullTextSearch = {
enable = false;
memoryLimit = 4000;
};
lmtpSaveToDetailMailbox = "no";
mailboxes = {
Trash = {
auto = "no";
specialUse = "Trash";
autoexpunge = "60d";
};
Junk = {
auto = "subscribe";
specialUse = "Junk";
autoexpunge = "60d";
};
Drafts = {
auto = "subscribe";
specialUse = "Drafts";
};
Sent = {
auto = "subscribe";
specialUse = "Sent";
};
};
extraVirtualAliases = {
"team@accentor.tech" = [ "charlotte@vanpetegem.be" "robbe@vanpetegem.be" ];
};
forwards = {
"info@toekomstlabo.be" = "robbe+toekomstlabo@robbevanpetegem.be";
};
rejectRecipients = [
"cindy@vanpetegem.be"
"contact@vanpetegem.be"
"info@vanpetegem.be"
"isabelle@vanpetegem.be"
"marierose@vanpetegem.be"
"rudy@vanpetegem.be"
"sarina@vanpetegem.be"
"sabrina@vanpetegem.be"
];
rejectSender = [
"junjunggaming07@gmail.com"
"censysnetbackup@gmail.com"
"vitor.carvalheiro@escola.pr.gov.br"
"spam@vuztc.ru"
"mofos@theportalnetworks.com" # Someone registered my f-droid contact address on a porn site :(
];
mailDirectory = "${config.chvp.dataPrefix}/var/vmail";
useFsLayout = false;
certificateScheme = "manual";
certificateFile = certFile;
keyFile = keyFile;
dkimKeyDirectory = "${config.chvp.dataPrefix}/var/dkim";
};
services.dovecot2.sieve = {
extensions = [ "editheader" ];
scripts.after2 = pkgs.writeText "custom-spam.sieve" ''
require ["fileinto", "regex"];
if anyof(
# Freshdesk is often used to sent spam from emails like `support@info5813.freshdesk.com`
address :regex "From" "[a-z\d]+@[a-z\d]+\.freshdesk\.com",
header :contains "From" ["jakubbielec", "Jakub Bielec"],
# Stop any mail pretending to be from itsme not from their official domains
allof(
address :contains "From" "itsme",
not address :matches :domain "from" ["*itsme.be", "*itsme-id.com"]
)
) {
fileinto "Junk";
stop;
}
'';
};
services.rspamd.extraConfig = ''
actions {
reject = null;
add_header = 6;
greylist = 4;
}
'';
age.secrets = {
"passwords/services/mail/charlotte@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/charlotte_at_vanpetegem.be.age;
"passwords/services/mail/hallo@robbe.be".file = ../../../../secrets/passwords/services/mail/hallo_at_robbe.be.age;
"passwords/services/mail/huis@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/huis_at_vanpetegem.me.age;
"passwords/services/mail/noreply@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/noreply_at_vanpetegem.me.age;
"passwords/services/mail/peter@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/peter_at_vanpetegem.me.age;
"passwords/services/mail/postbot@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/postbot_at_vanpetegem.be.age;
"passwords/services/mail/robbe@robbevanpetegem.be".file = ../../../../secrets/passwords/services/mail/robbe_at_robbevanpetegem.be.age;
"passwords/services/mail/robbe@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/robbe_at_vanpetegem.be.age;
"passwords/services/mail/webmaster@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/webmaster_at_vanpetegem.be.age;
};
};
}

View file

@ -0,0 +1,75 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.mastodon.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.mastodon.enable {
chvp.services.nginx.hosts = [{
fqdn = "social.chvp.be";
options = {
root = "${pkgs.mastodon}/public/";
locations = {
"/system/".alias = "/var/lib/mastodon/public-system/";
"/".tryFiles = "$uri @proxy";
"@proxy" = {
proxyPass = "http://unix:/run/mastodon-web/web.socket";
proxyWebsockets = true;
};
"/api/v1/streaming" = {
proxyPass = "http://unix:/run/mastodon-streaming/streaming.socket";
proxyWebsockets = true;
};
};
};
}];
users = {
users = {
mastodon.uid = 989;
nginx.extraGroups = [ "mastodon" ];
};
groups.mastodon.gid = 985;
};
services.mastodon = {
enable = true;
configureNginx = false;
localDomain = "social.chvp.be";
enableUnixSocket = true;
streamingProcesses = 4;
database.createLocally = true;
redis.createLocally = true;
smtp = {
fromAddress = "social@chvp.be";
createLocally = false;
};
extraConfig = {
SMTP_OPENSSL_VERIFY_MODE = "none";
};
otpSecretFile = config.age.secrets."passwords/services/mastodon/otp".path;
secretKeyBaseFile = config.age.secrets."passwords/services/mastodon/key".path;
vapidPublicKeyFile = config.age.secrets."passwords/services/mastodon/vapid-public".path;
vapidPrivateKeyFile = config.age.secrets."passwords/services/mastodon/vapid-private".path;
};
age.secrets."passwords/services/mastodon/vapid-public" = {
file = ../../../../secrets/passwords/services/mastodon/vapid-public.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/vapid-private" = {
file = ../../../../secrets/passwords/services/mastodon/vapid-private.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/key" = {
file = ../../../../secrets/passwords/services/mastodon/key.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/otp" = {
file = ../../../../secrets/passwords/services/mastodon/otp.age;
owner = "mastodon";
};
};
}

View file

@ -0,0 +1,229 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.matrix.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.matrix.enable {
chvp.base.zfs.systemLinks = [{ path = "/var/lib/matrix-hookshot"; type = "data"; }];
chvp.services.nginx.hosts = [
{
fqdn = "matrix.vanpetegem.me";
options.locations = {
"/" = {
proxyPass = "http://127.0.0.1:8448";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
proxy_read_timeout 600;
client_max_body_size 10M;
'';
};
"/_slack" = {
proxyPass = "http://127.0.0.1:9898";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'';
};
"~ ^/_hookshot/(.*)" = {
proxyPass = "http://127.0.0.1:9000/$1";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'';
};
};
}
{
fqdn = "matrix-sync.vanpetegem.me";
basicProxy = "http://localhost:8009";
}
];
services = {
matrix-synapse = {
enable = true;
settings = {
server_name = "vanpetegem.me";
public_baseurl = "https://matrix.vanpetegem.me";
listeners = [{
port = 8448;
bind_addresses = [ "::1" "127.0.0.1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [
{ names = [ "client" ]; compress = true; }
{ names = [ "federation" ]; compress = false; }
];
}];
url_preview_enabled = true;
enable_metrics = false;
enable_registration = false;
report_stats = false;
allow_guest_access = false;
suppress_key_server_warning = true;
app_service_config_files = [
config.age.secrets."files/services/matrix-synapse/whatsapp-registration.yml".path
config.age.secrets."files/services/matrix-synapse/slack-registration.yml".path
config.age.secrets."files/services/matrix-synapse/hookshot-registration.yml".path
];
};
extraConfigFiles = [
config.age.secrets."files/services/matrix-synapse/config.yml".path
];
dataDir = "${config.chvp.dataPrefix}/var/lib/matrix-synapse";
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [
"matrix-synapse"
"matrix_appservice_slack"
"mautrix_whatsapp"
];
ensureUsers = [
{
name = "matrix_appservice_slack";
ensureDBOwnership = true;
}
{
name = "mautrix_whatsapp";
ensureDBOwnership = true;
}
{
name = "matrix-synapse";
ensureDBOwnership = true;
}
];
};
matrix-sliding-sync = {
enable = true;
settings = {
SYNCV3_SERVER = "https://matrix.vanpetegem.me";
};
environmentFile = config.age.secrets."files/servers/matrix-sliding-sync/env".path;
createDatabase = true;
};
};
systemd.services = {
matrix-appservice-slack = {
description = "Matrix <-> Slack bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" "matrix-synapse.service" ];
requires = [ "postgresql.service" "matrix-synapse.service" ];
script = "${pkgs.matrix-appservice-slack}/bin/matrix-appservice-slack --config ${config.age.secrets."files/services/matrix-appservice-slack/config.yml".path} --file ${config.age.secrets."files/services/matrix-appservice-slack/registration.yml".path}";
serviceConfig = {
User = "matrix_appservice_slack";
Group = "matrix_appservice_slack";
};
};
matrix-synapse = {
requires = [ "postgresql.service" ];
};
mautrix-whatsapp = {
description = "Matrix <-> WhatsApp bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" "matrix-synapse.service" ];
requires = [ "postgresql.service" "matrix-synapse.service" ];
script = "${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp --config ${config.age.secrets."files/services/mautrix-whatsapp/config.yml".path}";
serviceConfig = {
User = "mautrix_whatsapp";
Group = "mautrix_whatsapp";
};
};
matrix-hookshot = {
description = "Matrix <-> Services bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "matrix-synapse.service" ];
requires = [ "matrix-synapse.service" ];
script = "${pkgs.matrix-hookshot}/bin/matrix-hookshot ${config.age.secrets."files/services/matrix-hookshot/config.yml".path} ${config.age.secrets."files/services/matrix-hookshot/registration.yml".path}";
serviceConfig = {
User = "matrix_hookshot";
Group = "matrix_hookshot";
WorkingDirectory = "/var/lib/matrix-hookshot";
};
};
};
users = {
users = {
matrix_appservice_slack = {
uid = 998;
group = "matrix_appservice_slack";
isSystemUser = true;
};
mautrix_whatsapp = {
uid = 997;
group = "mautrix_whatsapp";
isSystemUser = true;
};
matrix_hookshot = {
uid = 979;
group = "matrix_hookshot";
home = "/var/lib/matrix-hookshot";
isSystemUser = true;
};
};
groups = {
matrix_appservice_slack = {
gid = 998;
};
mautrix_whatsapp = {
gid = 997;
};
matrix_hookshot = {
gid = 979;
};
};
};
age.secrets."files/services/matrix-appservice-slack/config.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/config.yml.age;
owner = "matrix_appservice_slack";
};
age.secrets."files/services/matrix-appservice-slack/registration.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/registration.yml.age;
owner = "matrix_appservice_slack";
};
age.secrets."files/services/matrix-hookshot/config.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/config.yml.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/matrix-hookshot/registration.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/registration.yml.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/matrix-hookshot/passkey.pem" = {
path = "/var/lib/matrix-hookshot/passkey.pem";
file = ../../../../secrets/files/services/matrix-hookshot/passkey.pem.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/mautrix-whatsapp/config.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/config.yml.age;
owner = "mautrix_whatsapp";
};
age.secrets."files/services/mautrix-whatsapp/registration.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/registration.yml.age;
owner = "mautrix_whatsapp";
};
age.secrets."files/services/matrix-synapse/config.yml" = {
file = ../../../../secrets/files/services/matrix-synapse/config.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/slack-registration.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/whatsapp-registration.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/hookshot-registration.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/servers/matrix-sliding-sync/env".file = ../../../../secrets/files/services/matrix-sliding-sync/env.age;
};
}

View file

@ -0,0 +1,68 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.nextcloud.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.nextcloud.enable {
services = {
nextcloud = {
home = "${config.chvp.dataPrefix}/var/lib/nextcloud";
https = true;
hostName = "nextcloud.vanpetegem.me";
enable = true;
autoUpdateApps.enable = true;
package = pkgs.nextcloud29;
caching.redis = true;
config = {
dbuser = "nextcloud";
dbname = "nextcloud";
dbtype = "pgsql";
dbhost = "/run/postgresql";
adminuser = "admin";
adminpassFile = config.age.secrets."passwords/services/nextcloud-admin".path;
};
settings.redis = {
host = "127.0.0.1";
port = 31638;
dbindex = 0;
timeout = 1.5;
};
};
nginx.virtualHosts."nextcloud.vanpetegem.me" = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
extraConfig = ''
fastcgi_connect_timeout 10m;
fastcgi_read_timeout 10m;
fastcgi_send_timeout 10m;
'';
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [ "nextcloud" ];
ensureUsers = [{
name = "nextcloud";
ensureDBOwnership = true;
}];
};
redis.servers.nextcloud = {
enable = true;
port = 31638;
bind = "127.0.0.1";
};
};
age.secrets."passwords/services/nextcloud-admin" = {
file = ../../../../secrets/passwords/services/nextcloud-admin.age;
owner = "nextcloud";
};
systemd.services."nextcloud-setup" = {
requires = [ "postgresql.service" ];
after = [ "postgresql.service" ];
};
users.users.nextcloud.uid = 996;
users.groups.nextcloud.gid = 996;
};
}

View file

@ -0,0 +1,88 @@
{ config, lib, ... }:
{
options.chvp.services.nginx = {
enable = lib.mkOption {
readOnly = true;
default = (builtins.length config.chvp.services.nginx.hosts) > 0;
};
hosts = lib.mkOption {
default = [ ];
example = [
{
fqdn = "data.vanpetegem.me";
options = {
default = true;
root = "/srv/data";
locations = {
"/".extraConfig = ''
autoindex on;
'';
"/public".extraConfig = ''
autoindex on;
auth_basic off;
'';
};
};
}
];
};
};
config = lib.mkIf config.chvp.services.nginx.enable {
networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme = {
certs."vanpetegem.me" = {
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."passwords/services/acme".path;
extraDomainNames = [
"*.vanpetegem.me"
"cvpetegem.be"
"*.cvpetegem.be"
"chvp.be"
"*.chvp.be"
"vanpetegem.be"
"*.vanpetegem.be"
];
};
defaults.email = "webmaster@vanpetegem.me";
acceptTerms = true;
preliminarySelfsigned = false;
};
age.secrets."passwords/services/acme" = {
file = ../../../../secrets/passwords/services/acme.age;
owner = "acme";
};
chvp.base.zfs.systemLinks = [
{ type = "data"; path = "/var/lib/acme"; }
];
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = builtins.listToAttrs
(map
(elem: {
name = elem.fqdn;
value = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
locations."/" = lib.mkIf (builtins.hasAttr "basicProxy" elem) {
proxyPass = elem.basicProxy;
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'' + (elem.extraProxySettings or "");
};
} // (elem.options or { });
})
config.chvp.services.nginx.hosts);
};
users.users = {
nginx.extraGroups = [ "acme" ];
acme.uid = 999;
};
users.groups.acme.gid = 999;
};
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.torrents = {
enable = lib.mkOption {
default = false;
example = true;
};
};
config = lib.mkIf config.chvp.services.torrents.enable {
chvp.services.nginx.hosts = [{ fqdn = "transmission.vanpetegem.me"; basicProxy = "http://localhost:9091"; }];
services.transmission = {
enable = true;
package = pkgs.transmission_4;
user = "charlotte";
group = "users";
home = "/data/var/lib/transmission";
openRPCPort = false;
openPeerPorts = true;
credentialsFile = config.age.secrets."files/programs/transmission/config.json".path;
settings = {
umask = 18;
download-dir = "/srv/data";
incomplete-dir = "/srv/data/.incomplete";
rpc-authentication-required = true;
rpc-bind-address = "0.0.0.0";
rpc-enabled = true;
rpc-host-whitelist-enabled = false;
rpc-whitelist-enabled = false;
speed-limit-down = 51200;
speed-limit-down-enabled = true;
};
};
age.secrets."files/programs/transmission/config.json" = {
file = ../../../../secrets/files/programs/transmission/config.json.age;
owner = "charlotte";
};
};
}