This commit is contained in:
commit
fef2377502
174 changed files with 7423 additions and 0 deletions
6
nixos-modules/qois/backup-client/README.md
Normal file
6
nixos-modules/qois/backup-client/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Backup Module
|
||||
|
||||
This module creates a host-based backup job `system-${target-hostname}` (currently with borg).
|
||||
The module has sensible defaults for a whole system, note however that individual services/paths must be included or excluded added manually.
|
||||
|
||||
Target hosts should use the [Backup Server Module](../backup-server).
|
103
nixos-modules/qois/backup-client/default.nix
Normal file
103
nixos-modules/qois/backup-client/default.nix
Normal file
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.qois.backup-client;
|
||||
defaultIncludePaths = [
|
||||
"/etc"
|
||||
"/home"
|
||||
"/root"
|
||||
];
|
||||
defaultExcludePaths = [
|
||||
"/root/.cache"
|
||||
"/root/.config/borg"
|
||||
];
|
||||
defaultSopsPasswordFile = "system/backup/password";
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
options.qois.backup-client =
|
||||
let
|
||||
pathsType = with types; listOf str;
|
||||
in
|
||||
{
|
||||
enable = mkEnableOption "Enable this host to execute backups.";
|
||||
|
||||
targets = mkOption {
|
||||
type = with types; listOf (enum (attrNames config.qois.meta.hosts));
|
||||
default = [
|
||||
"cyprianspitz"
|
||||
];
|
||||
description = "Target hosts to make backups to. Must be configured to receive backups in the backplane network.";
|
||||
};
|
||||
|
||||
includePaths = mkOption {
|
||||
type = pathsType;
|
||||
default = [ ];
|
||||
description = "Paths that are included in backup. The backup module always includes: ${concatStringsSep ", " defaultIncludePaths}";
|
||||
};
|
||||
|
||||
excludePaths = mkOption {
|
||||
type = pathsType;
|
||||
default = [ ];
|
||||
description = "Paths that are excluded in backup. The backup module always excludes: ${concatStringsSep ", " defaultExcludePaths}";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = "config.sops.secrets.${defaultSopsPasswordFile}.path";
|
||||
description = "Path to password file. Taken from sops host secret ${defaultSopsPasswordFile} by default, must be randomly generated per host.";
|
||||
};
|
||||
|
||||
networkName = mkOption {
|
||||
type = types.enum (attrNames config.qois.meta.network.virtual);
|
||||
default = "backplane";
|
||||
description = "Name of virtual network through which the backups should be done";
|
||||
};
|
||||
};
|
||||
|
||||
config.services.borgbackup.jobs = mkIf cfg.enable (
|
||||
builtins.listToAttrs (
|
||||
map (backupHost: {
|
||||
name = "system-${backupHost}";
|
||||
value = {
|
||||
repo = "borg@${config.qois.meta.network.virtual.${cfg.networkName}.hosts.${backupHost}.v4.ip}:.";
|
||||
environment.BORG_RSH = "ssh -i /etc/ssh/ssh_host_ed25519_key";
|
||||
|
||||
paths = defaultIncludePaths ++ cfg.includePaths;
|
||||
exclude = defaultExcludePaths ++ cfg.excludePaths;
|
||||
|
||||
doInit = true;
|
||||
encryption = {
|
||||
mode = "repokey";
|
||||
passCommand =
|
||||
let
|
||||
passFile =
|
||||
if cfg.passwordFile != null then
|
||||
cfg.passwordFile
|
||||
else
|
||||
config.sops.secrets.${defaultSopsPasswordFile}.path;
|
||||
in
|
||||
"cat ${passFile}";
|
||||
};
|
||||
|
||||
startAt = "07:06";
|
||||
persistentTimer = true;
|
||||
};
|
||||
}) cfg.targets
|
||||
)
|
||||
);
|
||||
|
||||
config.sops.secrets = mkIf (cfg.enable && cfg.passwordFile == null) {
|
||||
${defaultSopsPasswordFile} = {
|
||||
restartUnits = map (target: "borgbackup-job-system-${target}.service") cfg.targets;
|
||||
};
|
||||
};
|
||||
}
|
3
nixos-modules/qois/backup-server/README.md
Normal file
3
nixos-modules/qois/backup-server/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Backup Server Module
|
||||
|
||||
This backup module creates borg repositories for all the hosts configured with hosts.
|
53
nixos-modules/qois/backup-server/default.nix
Normal file
53
nixos-modules/qois/backup-server/default.nix
Normal file
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.qois.backup-server or { };
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
options.qois.backup-server = {
|
||||
enable = mkEnableOption "Enable backup hosting";
|
||||
|
||||
backupStorageRoot = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = "/mnt/backup";
|
||||
example = "/mnt/nas/backup";
|
||||
description = "Path where backups are stored if this host is used as a backup target.";
|
||||
};
|
||||
|
||||
hosts = options.qois.meta.hosts // {
|
||||
default = config.qois.meta.hosts;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.borgbackup.repos =
|
||||
let
|
||||
hasSshKey = hostName: cfg.hosts.${hostName}.sshKey != null;
|
||||
mkRepo =
|
||||
hostName:
|
||||
(
|
||||
let
|
||||
name = "system-${hostName}";
|
||||
in
|
||||
{
|
||||
inherit name;
|
||||
value = {
|
||||
path = "${cfg.backupStorageRoot}/${name}";
|
||||
authorizedKeys = [ cfg.hosts.${hostName}.sshKey ];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
hostsWithSshKeys = lib.filter hasSshKey (lib.attrNames cfg.hosts);
|
||||
in
|
||||
lib.listToAttrs (map mkRepo hostsWithSshKeys);
|
||||
};
|
||||
}
|
10
nixos-modules/qois/default.nix
Normal file
10
nixos-modules/qois/default.nix
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
imports = inputs.self.lib.loadSubmodulesFrom ./.;
|
||||
}
|
8
nixos-modules/qois/git-ci-runner/README.md
Normal file
8
nixos-modules/qois/git-ci-runner/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Git CI Runner
|
||||
|
||||
Runner for the [Forgejo git instance](../git/README.md).
|
||||
Currently registers a default runner with ubuntu OS.
|
||||
|
||||
## Create Secret Token
|
||||
|
||||
To create a new token for registration, follow the steps outlined in the [Forgejo documentation](https://forgejo.org/docs/latest/user/actions/#forgejo-runner).
|
52
nixos-modules/qois/git-ci-runner/default.nix
Normal file
52
nixos-modules/qois/git-ci-runner/default.nix
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.qois.git-ci-runner;
|
||||
defaultInstanceName = "default";
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
options.qois.git-ci-runner = {
|
||||
enable = mkEnableOption "Enable qois git ci-runner service";
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = "git.qo.is";
|
||||
description = "Domain, under which the service is served.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
sops.secrets."forgejo/runner-token/${defaultInstanceName}".restartUnits = [
|
||||
"gitea-runner-${defaultInstanceName}.service"
|
||||
];
|
||||
|
||||
services.gitea-actions-runner = {
|
||||
package = pkgs.forgejo-runner;
|
||||
instances.${defaultInstanceName} = {
|
||||
enable = true;
|
||||
name = "${config.networking.hostName}-${defaultInstanceName}";
|
||||
url = "https://${cfg.domain}";
|
||||
tokenFile = config.sops.secrets."forgejo/runner-token/${defaultInstanceName}".path;
|
||||
labels = [
|
||||
"ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"
|
||||
"ubuntu-22.04:docker://ghcr.io/catthehacker/ubuntu:act-22.04"
|
||||
"docker:docker://code.forgejo.org/oci/alpine:3.20"
|
||||
];
|
||||
settings = {
|
||||
log.level = "warn";
|
||||
runner = {
|
||||
capacity = 30;
|
||||
};
|
||||
cache.enable = true; # TODO: This should probably be a central cache server?
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
44
nixos-modules/qois/git/README.md
Normal file
44
nixos-modules/qois/git/README.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Git
|
||||
|
||||
## Configuration for Git Clients
|
||||
|
||||
### Authentication
|
||||
|
||||
To use oauth authentication, your git configuration should have something like:
|
||||
|
||||
```ini
|
||||
[credential]
|
||||
helper = "libsecret"
|
||||
helper = "cache --timeout 21600"
|
||||
helper = "/usr/bin/git-credential-oauth" # See https://github.com/hickford/git-credential-oauth
|
||||
```
|
||||
|
||||
On NixOS with HomeManager, this can be achieved by following home-manager config:
|
||||
|
||||
```nix
|
||||
programs.git.extraConfig.credential.helper = [ "libsecret" "cache --timeout 21600" ];
|
||||
programs.git-credential-oauth.enable = true;
|
||||
```
|
||||
|
||||
## Administration
|
||||
|
||||
### Create Accounts
|
||||
|
||||
Accounts can be created by an admin in the [administrator area](https://git.qo.is/admin).
|
||||
|
||||
- use their full `firstname.lastname@qo.is` email so users may be connected to a LDAP database in the future
|
||||
- Username should be in form of "firstnamelastname" (Forgejo doesn't support usernames with dots)
|
||||
|
||||
To create a new admin user from the commandline, run:
|
||||
|
||||
```bash
|
||||
sudo -u forgejo 'nix run nixpkgs#forgejo -- admin user create --config ~custom/conf/app.ini --admin --email "xy.z@qo.is" --username firstnamelastname --password Chur7000'
|
||||
```
|
||||
|
||||
## Backup / Restore
|
||||
|
||||
1. `systemctl stop forgejo.service`
|
||||
2. Import Postgresql Database Backup
|
||||
3. Restore `/var/lib/forgejo`
|
||||
4. `systemctl start forgejo.service`
|
||||
|
81
nixos-modules/qois/git/default.nix
Normal file
81
nixos-modules/qois/git/default.nix
Normal file
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.qois.git;
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
options.qois.git = {
|
||||
enable = mkEnableOption "Enable qois git service";
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = "git.qo.is";
|
||||
description = "Domain, under which the service is served.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
qois.postgresql.enable = true;
|
||||
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
database.type = "postgres";
|
||||
|
||||
lfs.enable = true;
|
||||
|
||||
settings = {
|
||||
DEFAULT.APP_NAME = cfg.domain;
|
||||
server = {
|
||||
DOMAIN = cfg.domain;
|
||||
ROOT_URL = "https://${cfg.domain}";
|
||||
PROTOCOL = "http+unix";
|
||||
DISABLE_SSH = true;
|
||||
};
|
||||
"ssh.minimum_key_sizes".RSA = 2047;
|
||||
session.COOKIE_SECURE = true;
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
mailer = {
|
||||
ENABLED = true;
|
||||
PROTOCOL = "sendmail";
|
||||
FROM = "git@qo.is";
|
||||
SENDMAIL_PATH = "${pkgs.msmtp}/bin/sendmail";
|
||||
# Note: The sendmail passwordeval has to use the coreutil cat (that is in the services path)
|
||||
# instead of the busybox one due to filtered syscalls.
|
||||
SENDMAIL_ARGS = "--passwordeval 'cat ${config.sops.secrets."msmtp/password".path}'";
|
||||
};
|
||||
log.LEVEL = "Warn";
|
||||
};
|
||||
};
|
||||
|
||||
qois.backup-client.includePaths = [ config.services.forgejo.stateDir ];
|
||||
|
||||
users.users.forgejo.extraGroups = [ "postdrop" ];
|
||||
systemd.services.forgejo.serviceConfig.ReadOnlyPaths = [
|
||||
config.sops.secrets."msmtp/password".path
|
||||
];
|
||||
|
||||
networking.hosts."127.0.0.1" = [ cfg.domain ];
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
virtualHosts.${cfg.domain} = {
|
||||
kTLS = true;
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
extraConfig = ''
|
||||
client_max_body_size 512M;
|
||||
'';
|
||||
locations."/" = {
|
||||
proxyPass = "http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
169
nixos-modules/qois/loadbalancer/default.nix
Normal file
169
nixos-modules/qois/loadbalancer/default.nix
Normal file
|
@ -0,0 +1,169 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
let
|
||||
# We assume that all static pages are hosted on lindberg-webapps
|
||||
staticPages = pipe config.qois.static-page.pages [
|
||||
(mapAttrsToList (name: { domain, domainAliases, ... }: [ domain ] ++ domainAliases))
|
||||
flatten
|
||||
(map (name: {
|
||||
inherit name;
|
||||
value = "lindberg-webapps";
|
||||
}))
|
||||
listToAttrs
|
||||
];
|
||||
defaultDomains = staticPages // {
|
||||
"cloud.qo.is" = "lindberg-nextcloud";
|
||||
|
||||
"build.qo.is" = "lindberg-build";
|
||||
"gitlab-runner.qo.is" = "lindberg-build";
|
||||
"nixpkgs-cache.qo.is" = "lindberg-build";
|
||||
"attic.qo.is" = "lindberg-build";
|
||||
|
||||
"vault.qo.is" = "lindberg-webapps";
|
||||
"git.qo.is" = "lindberg-webapps";
|
||||
|
||||
"kokus.raphael.li" = "lindberg-rzimmermann";
|
||||
"auth.raphael.li" = "lindberg-rzimmermann";
|
||||
"toolia.raphael.li" = "lindberg-rzimmermann";
|
||||
"ha.raphael.li" = "lindberg-rzimmermann";
|
||||
"www.raphael.li" = "lindberg-rzimmermann";
|
||||
|
||||
"vpn.qo.is" = "cyprianspitz-headscale";
|
||||
};
|
||||
getBackplaneIp = hostname: config.qois.meta.network.virtual.backplane.hosts.${hostname}.v4.ip;
|
||||
defaultHostmap =
|
||||
lib.pipe
|
||||
[
|
||||
"lindberg-nextcloud"
|
||||
"lindberg-build"
|
||||
"lindberg-webapps"
|
||||
]
|
||||
[
|
||||
(map (name: {
|
||||
inherit name;
|
||||
value = getBackplaneIp name;
|
||||
}))
|
||||
lib.listToAttrs
|
||||
];
|
||||
defaultExtraConfig =
|
||||
let
|
||||
headscalePort = toString 46084;
|
||||
rzimmermannIp = "10.247.0.113";
|
||||
in
|
||||
''
|
||||
# lindberg-rzimmermann (uses send-proxy-v2)
|
||||
backend lindberg-rzimmermann-https
|
||||
mode tcp
|
||||
server s1 ${rzimmermannIp}:443 send-proxy-v2
|
||||
|
||||
backend lindberg-rzimmermann-http
|
||||
mode http
|
||||
server s1 ${rzimmermannIp}:80
|
||||
|
||||
# cyprianspitz headscale
|
||||
backend cyprianspitz-headscale-http
|
||||
mode http
|
||||
server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}
|
||||
|
||||
backend cyprianspitz-headscale-https
|
||||
mode tcp
|
||||
server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}
|
||||
'';
|
||||
cfg = config.qois.loadbalancer;
|
||||
in
|
||||
{
|
||||
|
||||
options.qois.loadbalancer = with lib; {
|
||||
enable = mkEnableOption "Enable services http+s loadbalancing";
|
||||
|
||||
domains = mkOption {
|
||||
description = "Domain to hostname mappings";
|
||||
type = with lib.types; attrsOf str;
|
||||
default = defaultDomains;
|
||||
};
|
||||
|
||||
hostmap = mkOption {
|
||||
description = "Hostname to IP mappings for TLS-TCP and http forwarding";
|
||||
type = with lib.types; attrsOf str;
|
||||
default = defaultHostmap;
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
description = "Additional haproxy mapping configs. Amended to services.haproxy.config. Make sure indentations are correct.";
|
||||
type = types.nullOr types.lines;
|
||||
default = defaultExtraConfig;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config =
|
||||
with lib;
|
||||
mkIf cfg.enable {
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
|
||||
services.haproxy =
|
||||
let
|
||||
domainMappingFile = pipe cfg.domains [
|
||||
(mapAttrsToList (host: backend: "${host} ${backend}"))
|
||||
concatLines
|
||||
(pkgs.writeText "haproxy_backend_map")
|
||||
];
|
||||
genHttpBackend = hostName: ip: ''
|
||||
|
||||
# Mapping for ${hostName}
|
||||
backend ${hostName}-https
|
||||
mode tcp
|
||||
server s1 ${ip}:443
|
||||
|
||||
backend ${hostName}-http
|
||||
mode http
|
||||
server s1 ${ip}:80
|
||||
'';
|
||||
httpBackends = pipe cfg.hostmap [
|
||||
(mapAttrsToList genHttpBackend)
|
||||
concatLines
|
||||
];
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
config = ''
|
||||
defaults
|
||||
mode http
|
||||
retries 3
|
||||
maxconn 2000
|
||||
timeout connect 5000
|
||||
timeout client 50000
|
||||
timeout server 50000
|
||||
|
||||
frontend http
|
||||
mode http
|
||||
bind *:80
|
||||
use_backend %[req.hdr(host),lower,map_dom(${domainMappingFile})]-http
|
||||
|
||||
frontend https
|
||||
bind *:443
|
||||
mode tcp
|
||||
tcp-request inspect-delay 5s
|
||||
tcp-request content accept if { req_ssl_hello_type 1 }
|
||||
|
||||
use_backend %[req.ssl_sni,lower,map_dom(${domainMappingFile})]-https
|
||||
|
||||
## Generated Backends:
|
||||
${httpBackends}
|
||||
|
||||
## extraConfig
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
6
nixos-modules/qois/static-page/README.md
Normal file
6
nixos-modules/qois/static-page/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Static Pages
|
||||
|
||||
This module enables static nginx sites, with data served from "/var/lib/nginx/$domain/root".
|
||||
|
||||
To deploy the site, a user `nginx-$domain` is added, of which a `root` profile in the home folder can be deployed, e.g. with deploy-rs.
|
||||
|
26
nixos-modules/qois/static-page/default-pages.nix
Normal file
26
nixos-modules/qois/static-page/default-pages.nix
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
qois.static-page.pages = {
|
||||
"fabianhauser.ch" = {
|
||||
domainAliases = [
|
||||
"www.fabianhauser.ch"
|
||||
"fabianhauser.nl"
|
||||
"www.fabianhauser.nl"
|
||||
"www.fh2.ch"
|
||||
"fh2.ch"
|
||||
];
|
||||
authorizedKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsSCoClNpgW7x6YngP/CEFbyR8GEJ3V8NdUFvZ/6lj6 ci@git.qo.is"
|
||||
];
|
||||
};
|
||||
"docs-ops.qo.is".authorizedKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBS65v7n5ozOUjYGuO/dgLC9C5MUGL5kTnQnvWAYP5B3 ci@git.qo.is"
|
||||
];
|
||||
};
|
||||
}
|
145
nixos-modules/qois/static-page/default.nix
Normal file
145
nixos-modules/qois/static-page/default.nix
Normal file
|
@ -0,0 +1,145 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.qois.static-page;
|
||||
in
|
||||
with lib;
|
||||
{
|
||||
imports = [ ./default-pages.nix ];
|
||||
|
||||
options.qois.static-page =
|
||||
let
|
||||
pageType =
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = ''
|
||||
Primary domain, under which the site is served.
|
||||
Only ASCII Domains are supported at this time.
|
||||
Note that changing this changes the root folder of the vhost in /var/lib/nginx-$domain/root and the ssh user to "nginx-$domain".
|
||||
'';
|
||||
};
|
||||
|
||||
domainAliases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Domain aliases which are forwarded to the primary domain";
|
||||
};
|
||||
|
||||
authorizedKeys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "SSH keys for deployment";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
;
|
||||
in
|
||||
{
|
||||
enable = mkEnableOption "Enable static-page hosting";
|
||||
pages = mkOption {
|
||||
type = types.attrsOf (types.submodule (pageType));
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (
|
||||
let
|
||||
pageConfigs = concatMapAttrs (
|
||||
name: page:
|
||||
let
|
||||
home = "/var/lib/nginx-${page.domain}";
|
||||
in
|
||||
{
|
||||
"${page.domain}" = page // {
|
||||
inherit home;
|
||||
user = "${config.services.nginx.user}-${page.domain}";
|
||||
root = "${home}/root";
|
||||
};
|
||||
}
|
||||
) cfg.pages;
|
||||
|
||||
in
|
||||
{
|
||||
networking.hosts."127.0.0.1" = pipe pageConfigs [
|
||||
attrValues
|
||||
(map (page: [ page.domain ] ++ page.domainAliases))
|
||||
flatten
|
||||
];
|
||||
|
||||
users = {
|
||||
groups = concatMapAttrs (
|
||||
name:
|
||||
{ user, ... }:
|
||||
{
|
||||
"${user}" = { };
|
||||
}
|
||||
) pageConfigs;
|
||||
users =
|
||||
{
|
||||
${config.services.nginx.user}.extraGroups = mapAttrsToList (domain: getAttr "user") pageConfigs;
|
||||
}
|
||||
// (concatMapAttrs (
|
||||
name:
|
||||
{
|
||||
user,
|
||||
home,
|
||||
authorizedKeys,
|
||||
...
|
||||
}:
|
||||
{
|
||||
${user} = {
|
||||
inherit home;
|
||||
isSystemUser = true;
|
||||
useDefaultShell = true;
|
||||
homeMode = "750";
|
||||
createHome = true;
|
||||
group = user;
|
||||
openssh.authorizedKeys.keys = authorizedKeys;
|
||||
};
|
||||
}
|
||||
) pageConfigs);
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts =
|
||||
let
|
||||
defaultVhostConfig = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
kTLS = true;
|
||||
};
|
||||
mkVhost =
|
||||
{ root, ... }:
|
||||
defaultVhostConfig
|
||||
// {
|
||||
inherit root;
|
||||
};
|
||||
mkAliasVhost =
|
||||
{ domainAliases, domain, ... }:
|
||||
if (domainAliases == [ ]) then
|
||||
{ }
|
||||
else
|
||||
({
|
||||
"${head domainAliases}" = defaultVhostConfig // {
|
||||
serverAliases = tail domainAliases;
|
||||
globalRedirect = domain;
|
||||
};
|
||||
});
|
||||
aliasVhosts = concatMapAttrs (name: mkAliasVhost) pageConfigs;
|
||||
|
||||
in
|
||||
aliasVhosts // (mapAttrs (name: mkVhost) pageConfigs);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
136
nixos-modules/qois/vpn-server/default.nix
Normal file
136
nixos-modules/qois/vpn-server/default.nix
Normal file
|
@ -0,0 +1,136 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
cfg = config.qois.vpn-server;
|
||||
cfgLoadbalancer = config.qois.loadbalancer;
|
||||
defaultDnsRecords = mapAttrs (
|
||||
name: value: mkIf (cfgLoadbalancer.hostmap ? ${value}) cfgLoadbalancer.hostmap.${value}
|
||||
) cfgLoadbalancer.domains;
|
||||
in
|
||||
{
|
||||
|
||||
options.qois.vpn-server = {
|
||||
enable = mkEnableOption "Enable vpn server services";
|
||||
dnsRecords = mkOption {
|
||||
description = "DNS records to add to Hosts";
|
||||
type = with types; attrsOf str;
|
||||
default = defaultDnsRecords;
|
||||
};
|
||||
wheelUsers = mkOption {
|
||||
description = "Usernames that can change configurations";
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable ({
|
||||
|
||||
environment.systemPackages = [ pkgs.headscale ];
|
||||
|
||||
qois.backup-client.includePaths =
|
||||
with config.services.headscale.settings;
|
||||
(
|
||||
[
|
||||
db_path
|
||||
private_key_path
|
||||
noise.private_key_path
|
||||
]
|
||||
++ derp.paths
|
||||
);
|
||||
|
||||
networking.firewall.checkReversePath = "loose";
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
41641
|
||||
];
|
||||
services.headscale =
|
||||
let
|
||||
vnet = config.qois.meta.network.virtual;
|
||||
vpnNet = vnet.vpn;
|
||||
vpnNetPrefix = "${vpnNet.v4.id}/${builtins.toString vpnNet.v4.prefixLength}";
|
||||
backplaneNetPrefix = "${vnet.backplane.v4.id}/${builtins.toString vnet.backplane.v4.prefixLength}";
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
address = vnet.backplane.hosts.cyprianspitz.v4.ip;
|
||||
port = 46084;
|
||||
settings = {
|
||||
server_url = "https://${vpnNet.domain}:443";
|
||||
|
||||
tls_letsencrypt_challenge_type = "TLS-ALPN-01";
|
||||
tls_letsencrypt_hostname = vpnNet.domain;
|
||||
|
||||
dns_config = {
|
||||
nameservers = [ vnet.backplane.hosts.calanda.v4.ip ];
|
||||
domains = [
|
||||
vpnNet.domain
|
||||
vnet.backplane.domain
|
||||
];
|
||||
magic_dns = true;
|
||||
base_domain = vpnNet.domain;
|
||||
extra_records = pipe cfg.dnsRecords [
|
||||
attrsToList
|
||||
(map (val: val // { type = "A"; }))
|
||||
];
|
||||
};
|
||||
|
||||
ip_prefixes = [ vpnNetPrefix ];
|
||||
|
||||
acl_policy_path = pkgs.writeTextFile {
|
||||
name = "acls";
|
||||
text = builtins.toJSON {
|
||||
hosts = {
|
||||
"clients" = vpnNetPrefix;
|
||||
};
|
||||
groups = {
|
||||
"group:wheel" = cfg.wheelUsers;
|
||||
};
|
||||
tagOwners = {
|
||||
"tag:srv" = [ "srv" ]; # srv tag ist owned by srv user
|
||||
};
|
||||
autoApprovers = {
|
||||
exitNode = [
|
||||
"tag:srv"
|
||||
"group:wheel"
|
||||
];
|
||||
routes = {
|
||||
${backplaneNetPrefix} = [ "tag:srv" ];
|
||||
};
|
||||
};
|
||||
|
||||
acls = [
|
||||
# Allow all communication from and to srv tagged hosts
|
||||
{
|
||||
action = "accept";
|
||||
src = [
|
||||
"tag:srv"
|
||||
"srv"
|
||||
];
|
||||
dst = [ "*:*" ];
|
||||
}
|
||||
{
|
||||
action = "accept";
|
||||
src = [ "*" ];
|
||||
dst = [
|
||||
"tag:srv:*"
|
||||
"srv:*"
|
||||
];
|
||||
}
|
||||
|
||||
# Allow access to all connected hosts for wheels
|
||||
{
|
||||
action = "accept";
|
||||
src = [ "group:wheel" ];
|
||||
dst = [ "*:*" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue