Misc changes, add local-network option

This commit is contained in:
niten 2022-02-27 15:27:10 -08:00
parent 5411c53022
commit c40aba6133
16 changed files with 305 additions and 56 deletions

View File

@ -46,6 +46,8 @@ with lib; {
./users.nix
./vpn.nix
./webmail.nix
# ./wireguard.nix
# ./wireguard-client.nix
./wireless-networks.nix
./zones.nix
];

View File

@ -91,6 +91,25 @@ let
default = null;
};
wireguard = {
gateway = mkOption {
type = str;
description = "Host serving as WireGuard gateway for this domain.";
};
network = mkOption {
type = str;
description = "IP subnet used for WireGuard clients.";
default = "172.16.0.0/16";
};
routed-network = mkOption {
type = nullOr str;
description = "Subnet of larger network for which we NAT traffic.";
default = "172.16.16.0/20";
};
};
gssapi-realm = mkOption {
type = str;
description = "GSSAPI (i.e. Kerberos) realm of this domain.";

View File

@ -150,6 +150,8 @@ in {
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${cfg.hostname}" = {
@ -158,14 +160,6 @@ in {
locations."/" = {
proxyPass = "http://127.0.0.1:${toString cfg.local-port}";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
};

View File

@ -220,19 +220,35 @@ in {
};
extraOptions = mkIf (cfg.ldap != null) (let
base = cfg.ldap.base-dn;
config-file = pkgs.writeText "grafana-ldap.toml" ''
[[servers]]
host = "${concatStringsSep " " cfg.ldap.hosts}"
port = 389
start_tls = true
bind_dn = "${cfg.ldap.bind-dn}"
bind_password = "''${GRAFANA_LDAP_BIND_PASSWD}"
bind_dn = "uid=%s,ou=members,${base}"
search_filter = "(uid=%s)"
search_base_dns = [ "${cfg.ldap.base-dn}" ]
search_base_dns = [ "ou=members,${base}" ]
group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"
group_search_base_dns = ["ou=groups,${base}"]
group_search_filter_user_attribute = "uid"
[[servers.group_mappings]]
group_dn = "cn=admin,ou=groups,${base}"
org_role = "Admin"
grafana_admin = true
[[servers.group_mappings]]
group_dn = "cn=*,ou=groups,${base}"
org_role = "Viewer"
'';
in {
AUTH_LDAP_ENABLED = "true";
AUTH_LDAP_ALLOW_SIGN_UP = "false";
AUTH_LDAP_ALLOW_SIGN_UP = "true";
AUTH_LDAP_CONFIG_FILE = config-file;
});

View File

@ -392,31 +392,13 @@ in {
# olcRootDN = "cn=admin,${cfg.base}";
# olcRootPW = FIXME; # NOTE: this should be hashed...
olcDbDirectory = "${cfg.state-directory}/database";
olcDbIndex = [ "objectClass eq" "uid pres,eq" ];
olcDbIndex = [ "objectClass eq" "uid pres,eq" "memberUid eq"];
olcAccess = makeAccess {
"attrs=userPassword,shadowLastChange" = {
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"dn.exact=cn=auth_reader,${cfg.base}" = "read";
"*" = "auth";
};
# "dn=cn=admin,ou=groups,${cfg.base}" = {
# # "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
# "anonymous" = "auth";
# "*" = "read";
# };
# "dn.subtree=ou=groups,${cfg.base} attrs=memberUid" = {
# # "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
# # "dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write";
# "anonymous" = "auth";
# "*" = "read";
# };
# "dn.subtree=ou=members,${cfg.base} attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber" = {
# # "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
# "anonymous" = "auth";
# "*" = "read";
# };
"*" = {
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"anonymous" = "auth";
"*" = "read";
};

View File

@ -23,7 +23,7 @@ in rec {
# Disable postfix on this host--it'll be run in the container instead
services.postfix.enable = false;
services.nginx = mkIf cfg.monitoring {
services.nginx = mkIf cfg.monitoring.enable {
enable = true;
virtualHosts = let
@ -45,7 +45,7 @@ in rec {
forceSSL = true;
locations."/metrics/postfix" = {
proxyPass = "http://127.0.0.1:9154/metrics";
proxyPass = "http://127.0.0.1:${cfg.monitoring.postfix-listen-port}/metrics";
extraConfig = ''
${proxy-headers}
@ -55,7 +55,7 @@ in rec {
};
locations."/metrics/dovecot" = {
proxyPass = "http://127.0.0.1:9166/metrics";
proxyPass = "http://127.0.0.1:${cfg.monitoring.dovecot-listen-port}/metrics";
extraConfig = ''
${proxy-headers}
@ -65,7 +65,7 @@ in rec {
};
locations."/metrics/rspamd" = {
proxyPass = "http://127.0.0.1:7980/metrics";
proxyPass = "http://127.0.0.1:${cfg.monitoring.rspamd-listen-port}/metrics";
extraConfig = ''
${proxy-headers}
@ -178,7 +178,7 @@ in rec {
domain = cfg.domain;
debug = cfg.debug;
monitoring = cfg.monitoring;
monitoring = cfg.monitoring.enable;
state-directory = container-statedir;
mail-directory = container-maildir;

View File

@ -32,7 +32,27 @@ in {
example = "ldaps://auth.fudo.org/";
};
monitoring = mkEnableOption "Enable monitoring for the mail server.";
monitoring = {
enable = mkEnableOption "Enable monitoring for the mail server.";
dovecot-listen-port = mkOption {
type = port;
description = "Port on which to serve Postfix metrics.";
default = 9166;
};
postfix-listen-port = mkOption {
type = port;
description = "Port on which to serve Postfix metrics.";
default = 9154;
};
rspamd-listen-port = mkOption {
type = port;
description = "Port on which to serve Postfix metrics.";
default = 7980;
};
};
mail-user = mkOption {
type = str;
@ -50,6 +70,7 @@ in {
mail-user-id = mkOption {
type = int;
description = "UID of mail-user.";
default = 525;
};
local-domains = mkOption {

View File

@ -130,11 +130,11 @@ in {
config = mkIf cfg.enable {
services.prometheus.exporters.dovecot = mkIf cfg.monitoring {
services.prometheus.exporters.dovecot = mkIf cfg.monitoring.enable {
enable = true;
scopes = ["user" "global"];
listenAddress = "127.0.0.1";
port = 9166;
port = cfg.monitoring.dovecot-listen-port;
socketPath = "/var/run/dovecot2/old-stats";
};
@ -159,13 +159,13 @@ in {
sieveScripts = {
after = builtins.toFile "spam.sieve" ''
require "fileinto";
require "fileinto";
if header :is "X-Spam" "Yes" {
fileinto "Junk";
stop;
}
'';
if header :is "X-Spam" "Yes" {
fileinto "Junk";
stop;
}
'';
};
mailboxes = cfg.mailboxes;
@ -173,7 +173,7 @@ in {
extraConfig = ''
#Extra Config
${optionalString cfg.monitoring ''
${optionalString cfg.monitoring.enable ''
# The prometheus exporter still expects an older style of metrics
mail_plugins = $mail_plugins old_stats
service old-stats {

View File

@ -97,12 +97,14 @@ in {
config = mkIf cfg.enable {
services.prometheus.exporters.postfix = mkIf cfg.monitoring {
services.prometheus.exporters.postfix = mkIf cfg.monitoring.enable {
enable = true;
systemd.enable = true;
showqPath = "/var/lib/postfix/queue/public/showq";
user = config.services.postfix.user;
group = config.services.postfix.group;
port = cfg.monitoring.postfix-listen-port;
listenAddress = "127.0.0.1";
};
services.postfix = {

View File

@ -6,7 +6,11 @@ let
in {
config = mkIf cfg.enable {
services.prometheus.exporters.rspamd.enable = true;
services.prometheus.exporters.rspamd = mkIf cfg.monitoring.enable {
enable = true;
listenAddress = "127.0.0.1";
port = cfg.monitoring.rspamd-listen-port;
};
services.rspamd = {

View File

@ -135,9 +135,8 @@ in {
enable = if (cfg.push-url != null) then true else false;
web = {
external-url = if cfg.push-url == null then
cfg.push-address
else
cfg.push-url;
cfg.push-address else
cfg.push-url;
listen-address = cfg.push-address;
};
};

View File

@ -22,6 +22,12 @@ let
description = "Network to be treated as local.";
};
private-network = mkOption {
type = nullOr str;
description = "Network of the local site.";
default = null;
};
dynamic-network = mkOption {
type = nullOr str;
description = "Network to be allocated by DHCP.";
@ -60,9 +66,6 @@ let
default = [ ];
};
enable-monitoring =
mkEnableOption "Enable site-wide monitoring with prometheus.";
nameservers = mkOption {
type = listOf str;
description = "List of nameservers to be used by hosts at this site.";

View File

@ -0,0 +1,73 @@
{ config, lib, pkgs, ... }:
with lib;
let
in {
options.fudo.wireguard-client = with types; {
enable = mkEnableOption "Enable WireGuard client on this host.";
server = {
ip = {
type = str;
description = "IP address of the WireGuard server.";
};
port = {
type = port;
description = "Port on which to contact WireGuard server.";
default = 51820;
};
public-key = {
type = str;
description = "Server public key.";
};
};
assigned-ip = mkOption {
type = str;
description = "IP assigned to the local host.";
};
private-key-file = mkOption {
type = str;
description = "Path (on the host) to the host WireGuard private key.";
};
listen-port = mkOption {
type = port;
description = "Port on which to listen for incoming connections.";
default = 51820;
};
wireguard-interface = mkOption {
type = str;
description = "Name of the created WireGuard interface.";
default = "wgclient0";
};
managed-subnet = mkOption {
type = str;
description = "Subnet to route to WireGuard. 0.0.0.0/0 will send all traffic to it.";
};
};
config = {
networking = {
firewall.allowedUDPPorts = [ cfg.listen-port ];
wireguard.interfaces.${cfg.wireguard-interface} = {
ips = [ "${cfg.assigned-ip}/32" ];
listen-port = cfg.listen-port;
private-key-file = cfg.private-key-file;
peers = [{
publicKey = cfg.server.public-key;
allowedIPs = [ cfg.managed-subnet ];
endpoint = "${cfg.server.ip}:${cfg.server.port}";
persistentKeepalive = 25;
}];
};
};
};
}

95
lib/fudo/wireguard.nix Normal file
View File

@ -0,0 +1,95 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fudo.wireguard;
peerOpts = { name, ... }: {
options = {
public-key = mkOption {
type = str;
description = "Peer public key.";
};
assigned-ip = mkOption {
type = str;
description = "IP assigned to this peer.";
};
};
};
in {
options.fudo.wireguard = with types; {
enable = mkEnableOption "Enable WireGuard server.";
network = mkOption {
type = str;
description = "WireGuard managed IP subnet.";
default = "172.16.0.0/16";
};
routed-network = mkOption {
type = str;
description = "Subnet of larger network for which we act as a gateway.";
default = "172.16.16.0/20";
};
peers = mkOption {
type = attrsOf (submodule peerOpts);
description = "Map of peer to peer options.";
default = { };
};
listen-port = mkOption {
type = port;
description = "Port on which to listen for incoming connections.";
default = 51820;
};
private-key-file = mkOption {
type = str;
description = "Path (on the host) to the host WireGuard private key.";
};
wireguard-interface = mkOption {
type = str;
description = "Name of the created WireGuard interface.";
default = "wgnet0";
};
external-interface = mkOption {
type = str;
description = "Name of the host public-facing interface.";
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = all (peerOpts:
pkgs.lib.ip.ipv4OnNetwork peerOpts.assigned-ip cfg.network)
(attrValues cfg.peers);
message = "Peer IPs must be on the assigned network.";
}
];
networking = {
firewall.allowedUDPPorts = [ cfg.listen-port ];
wireguard.interfaces.${cfg.wireguard-interface} = {
ips = [ cfg.network ];
listenPort = cfg.listen-port;
postSetup =
"${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE";
postShutdown =
"${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE";
privateKeyFile = cfg.private-key-file;
peers = mapAttrs (_: peerOpts: {
public-key = peerOpts.public-key;
allowedIPs = [ "${peerOpts.assigned-ip}/32" ];
}) cfg.peers;
};
};
};
}

View File

@ -2,11 +2,11 @@
with pkgs.lib;
let
get-basename = filename:
get-basename-without-hash = filename:
head (builtins.match "^[a-zA-Z0-9]+-(.+)$" (baseNameOf filename));
format-json-file = filename: pkgs.stdenv.mkDerivation {
name = "formatted-${get-basename filename}";
name = "formatted-${get-basename-without-hash filename}";
phases = [ "installPhase" ];
buildInputs = with pkgs; [ python ];
installPhase = "python -mjson.tool ${filename} > $out";

View File

@ -264,6 +264,45 @@ in rec {
default = false;
};
wireguard = let
clientOpts = {
options = {
ip = mkOption {
type = nullOr str;
description = "IP address assigned to this host in the WireGuard network.";
};
bound = mkOption {
type = bool;
description = "Whether to route all traffic from this host.";
default = false;
};
};
};
wireguardOpts = {
options = {
private-key-file = mkOption {
type = str;
description = "WireGuard private key file of the host.";
};
public-key = mkOption {
type = str;
description = "WireGuard public key.";
};
client = mkOption {
type = nullOr (submodule clientOpts);
default = null;
};
};
};
in mkOption {
type = nullOr (submodule wireguardOpts);
default = null;
};
initrd-network = let
keypair-type = { ... }: {
options = {