infrastructure/nixos-modules/qois/vpn-server/default.nix

158 lines
4.4 KiB
Nix
Raw Permalink Normal View History

2024-10-02 15:52:04 +02:00
{
config,
pkgs,
lib,
...
}:
with lib;
let
cfg = config.qois.vpn-server;
cfgLoadbalancer = config.qois.loadbalancer;
2024-12-11 12:01:22 +01:00
defaultDnsRecords =
(mapAttrs (
name: value: mkIf (cfgLoadbalancer.hostmap ? ${value}) cfgLoadbalancer.hostmap.${value}
) cfgLoadbalancer.domains)
// {
"vpn.qo.is" = config.services.headscale.address;
};
2024-10-02 15:52:04 +02:00
in
{
options.qois.vpn-server = {
enable = mkEnableOption "Enable vpn server services";
2024-12-06 18:08:11 +01:00
domain = mkOption {
description = "Domain for the VPN admin server";
type = types.str;
default = "vpn.qo.is";
};
2024-10-02 15:52:04 +02:00
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 ];
2024-12-11 12:01:22 +01:00
# We bind to the backplane vpn IP, so wait for the wireguard net to be available
2024-12-06 18:08:11 +01:00
systemd.services.headscale.after = [ "wireguard-wg-backplane.service" ];
2024-10-02 15:52:04 +02:00
qois.backup-client.includePaths =
with config.services.headscale.settings;
(
[
2024-12-06 18:08:11 +01:00
database.sqlite.path
derp.server.private_key_path
2024-10-02 15:52:04 +02:00
noise.private_key_path
]
++ derp.paths
);
networking.firewall.checkReversePath = "loose";
2024-12-11 12:11:42 +01:00
networking.firewall.allowedTCPPorts = [ config.services.headscale.port ];
2024-10-02 15:52:04 +02:00
networking.firewall.allowedUDPPorts = [
41641
];
services.headscale =
let
vnet = config.qois.meta.network.virtual;
vpnNet = vnet.vpn;
vpnNetPrefix = "${vpnNet.v4.id}/${toString vpnNet.v4.prefixLength}";
2024-10-02 15:52:04 +02:00
backplaneNetPrefix = "${vnet.backplane.v4.id}/${builtins.toString vnet.backplane.v4.prefixLength}";
in
{
enable = true;
2024-12-11 12:01:22 +01:00
address = vnet.backplane.hosts.cyprianspitz.v4.ip;
2024-10-02 15:52:04 +02:00
port = 46084;
settings = {
2024-12-06 18:08:11 +01:00
server_url = "https://${cfg.domain}:443";
2024-10-02 15:52:04 +02:00
tls_letsencrypt_challenge_type = "TLS-ALPN-01";
tls_letsencrypt_hostname = vpnNet.domain;
2024-12-06 18:08:11 +01:00
dns = {
base_domain = vpnNet.domain;
magic_dns = true;
2024-12-11 14:25:47 +01:00
nameservers.global = [ "127.0.0.1" ];
2024-12-06 18:08:11 +01:00
search_domains = [
2024-12-11 14:25:47 +01:00
# First is base_domain by default with magic_dns
2024-10-02 15:52:04 +02:00
vnet.backplane.domain
];
extra_records = pipe cfg.dnsRecords [
attrsToList
(map (val: val // { type = "A"; }))
];
};
ip_prefixes = [ vpnNetPrefix ];
2024-12-06 18:08:11 +01:00
policy =
let
# Note: headscale has limited acl support currently. This might change in the future.
aclPolicy = {
hosts = {
"clients" = vpnNetPrefix;
2024-10-02 15:52:04 +02:00
};
2024-12-06 18:08:11 +01:00
groups = {
"group:wheel" = cfg.wheelUsers;
};
tagOwners = {
"tag:srv" = [ "srv" ]; # srv tag ist owned by srv user
};
autoApprovers = {
exitNode = [
2024-10-02 15:52:04 +02:00
"tag:srv"
2024-12-06 18:08:11 +01:00
"group:wheel"
2024-10-02 15:52:04 +02:00
];
2024-12-06 18:08:11 +01:00
routes = {
${backplaneNetPrefix} = [ "tag:srv" ];
};
};
2024-10-02 15:52:04 +02:00
2024-12-06 18:08:11 +01:00
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 = [ "*:*" ];
}
];
};
in
{
mode = "file";
path = pkgs.writeTextFile {
name = "acls";
text = builtins.toJSON aclPolicy;
};
2024-10-02 15:52:04 +02:00
};
};
};
});
}