Merged with upstream
This commit is contained in:
commit
c8492616db
|
@ -82,12 +82,21 @@ in {
|
||||||
default = 53;
|
default = 53;
|
||||||
};
|
};
|
||||||
|
|
||||||
listen-addresses = mkOption {
|
listen-v4-addresses = mkOption {
|
||||||
type = with types; listOf str;
|
type = with types; listOf str;
|
||||||
description = "IP addresses on which to listen for dns requests.";
|
description = "IPv4 addresses on which to listen for dns requests.";
|
||||||
default = [ "0.0.0.0" ];
|
default = [ "0.0.0.0" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
listen-v6-addresses = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
description = "IPv6 addresses on which to listen for dns requests.";
|
||||||
|
example = [
|
||||||
|
"[abcd::1]"
|
||||||
|
];
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
|
||||||
required-services = mkOption {
|
required-services = mkOption {
|
||||||
type = with types; listOf str;
|
type = with types; listOf str;
|
||||||
description = "A list of services required before the DNS server can start.";
|
description = "A list of services required before the DNS server can start.";
|
||||||
|
@ -152,7 +161,8 @@ in {
|
||||||
|
|
||||||
backplane-powerdns = let
|
backplane-powerdns = let
|
||||||
configDir = pkgs.writeTextDir "pdns.conf" ''
|
configDir = pkgs.writeTextDir "pdns.conf" ''
|
||||||
local-address=${lib.concatStringsSep ", " cfg.listen-addresses}
|
local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses}
|
||||||
|
local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses}
|
||||||
local-port=${toString cfg.port}
|
local-port=${toString cfg.port}
|
||||||
launch=
|
launch=
|
||||||
include-dir=${powerdns-conf-dir}/
|
include-dir=${powerdns-conf-dir}/
|
||||||
|
|
|
@ -86,6 +86,19 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.backplane-dns-client-pw-file = {
|
||||||
|
enable = true;
|
||||||
|
requiredBy = [ "backplane-dns-client.services" ];
|
||||||
|
reloadIfChanged = true;
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
chmod 600 ${cfg.password-file}
|
||||||
|
chown ${cfg.user} ${cfg.password-file}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
services.backplane-dns-client = {
|
services.backplane-dns-client = {
|
||||||
enable = true;
|
enable = true;
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
|
@ -94,6 +107,7 @@ in {
|
||||||
User = cfg.user;
|
User = cfg.user;
|
||||||
};
|
};
|
||||||
path = [ pkgs.openssh ];
|
path = [ pkgs.openssh ];
|
||||||
|
reloadIfChanged = true;
|
||||||
script = ''
|
script = ''
|
||||||
${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString cfg.sshfp "-f"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file}
|
${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString cfg.sshfp "-f"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file}
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -5,8 +5,6 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.fudo.dns;
|
cfg = config.fudo.dns;
|
||||||
|
|
||||||
ip = import ../../lib/ip.nix { lib = lib; };
|
|
||||||
|
|
||||||
join-lines = concatStringsSep "\n";
|
join-lines = concatStringsSep "\n";
|
||||||
|
|
||||||
hostOpts = { host, ...}: {
|
hostOpts = { host, ...}: {
|
||||||
|
@ -32,6 +30,19 @@ let
|
||||||
description = ''
|
description = ''
|
||||||
A list of DNS SSHFP records for this host.
|
A list of DNS SSHFP records for this host.
|
||||||
'';
|
'';
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
description = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = "Description of this host for a TXT record.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
rp = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = "Responsible person.";
|
||||||
|
default = null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -71,7 +82,11 @@ let
|
||||||
description = "A map of hostname to { host_attributes }.";
|
description = "A map of hostname to { host_attributes }.";
|
||||||
};
|
};
|
||||||
|
|
||||||
dnssec = mkEnableOption "Enable DNSSEC security for this zone.";
|
dnssec = mkOption {
|
||||||
|
type = bool;
|
||||||
|
description = "Enable DNSSEC security for this zone.";
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
mx = mkOption {
|
mx = mkOption {
|
||||||
type = listOf str;
|
type = listOf str;
|
||||||
|
@ -125,9 +140,12 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
hostARecords = host: data:
|
hostRecords = host: data:
|
||||||
join-lines ((map (ip: "${host} IN A ${ip}") data.ip-addresses) ++
|
join-lines ((map (ip: "${host} IN A ${ip}") data.ip-addresses) ++
|
||||||
(map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses));
|
(map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses) ++
|
||||||
|
(map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints) ++
|
||||||
|
(optional (data.rp != null) "${host} IN RP ${data.rp}") ++
|
||||||
|
(optional (data.description != null) "${host} IN TXT ${data.description}"));
|
||||||
|
|
||||||
makeSrvRecords = protocol: type: records:
|
makeSrvRecords = protocol: type: records:
|
||||||
join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${toString record.host}.")
|
join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${toString record.host}.")
|
||||||
|
@ -137,8 +155,6 @@ let
|
||||||
|
|
||||||
cnameRecord = alias: host: "${alias} IN CNAME ${host}";
|
cnameRecord = alias: host: "${alias} IN CNAME ${host}";
|
||||||
|
|
||||||
hostSshFpRecords = host: data: join-lines (map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints);
|
|
||||||
|
|
||||||
mxRecords = mxs:
|
mxRecords = mxs:
|
||||||
concatStringsSep "\n"
|
concatStringsSep "\n"
|
||||||
(map (mx: "@ IN MX 10 ${mx}.") mxs);
|
(map (mx: "@ IN MX 10 ${mx}.") mxs);
|
||||||
|
@ -147,9 +163,7 @@ let
|
||||||
optionalString (dmarc-email != null)
|
optionalString (dmarc-email != null)
|
||||||
''_dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"'';
|
''_dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"'';
|
||||||
|
|
||||||
nsRecords = ns-hosts:
|
nsRecords = dom: ns-hosts: join-lines (mapAttrsToList (host: _: "@ IN NS ${host}.${dom}.") ns-hosts);
|
||||||
join-lines ((mapAttrsToList (host: _: "@ IN NS ${host}.") ns-hosts) ++
|
|
||||||
(mapAttrsToList (host: ip: "${host} IN A ${ip}") ns-hosts));
|
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
|
||||||
|
@ -157,10 +171,21 @@ in {
|
||||||
enable = mkEnableOption "Enable master DNS services.";
|
enable = mkEnableOption "Enable master DNS services.";
|
||||||
|
|
||||||
# FIXME: This should allow for AAAA addresses too...
|
# FIXME: This should allow for AAAA addresses too...
|
||||||
dns-hosts = mkOption {
|
nameservers = mkOption {
|
||||||
type = loaOf str;
|
type = loaOf (submodule hostOpts);
|
||||||
description = "Map of domain nameserver FQDNs to IP.";
|
description = "Map of domain nameserver FQDNs to IP.";
|
||||||
example = { "ns1.domain.com" = "1.1.1.1"; };
|
example = {
|
||||||
|
"ns1.domain.com" = {
|
||||||
|
ip-addresses = [ "1.1.1.1" ];
|
||||||
|
ipv6-addresses = [];
|
||||||
|
description = "my fancy dns server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
identity = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "The identity (CH TXT ID.SERVER) of this host.";
|
||||||
};
|
};
|
||||||
|
|
||||||
domains = mkOption {
|
domains = mkOption {
|
||||||
|
@ -179,7 +204,7 @@ in {
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
services.nsd = {
|
services.nsd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
identity = "procul.informis.land";
|
identity = cfg.identity;
|
||||||
interfaces = cfg.listen-ips;
|
interfaces = cfg.listen-ips;
|
||||||
zones = mapAttrs' (dom: dom-cfg:
|
zones = mapAttrs' (dom: dom-cfg:
|
||||||
nameValuePair "${dom}." {
|
nameValuePair "${dom}." {
|
||||||
|
@ -202,13 +227,13 @@ in {
|
||||||
|
|
||||||
$TTL 6h
|
$TTL 6h
|
||||||
|
|
||||||
${nsRecords cfg.dns-hosts}
|
${nsRecords dom cfg.nameservers}
|
||||||
|
${join-lines (mapAttrsToList hostRecords cfg.nameservers)}
|
||||||
|
|
||||||
${dmarcRecord dom-cfg.dmarc-report-address}
|
${dmarcRecord dom-cfg.dmarc-report-address}
|
||||||
|
|
||||||
${join-lines (mapAttrsToList makeSrvProtocolRecords dom-cfg.srv-records)}
|
${join-lines (mapAttrsToList makeSrvProtocolRecords dom-cfg.srv-records)}
|
||||||
${join-lines (mapAttrsToList hostARecords dom-cfg.hosts)}
|
${join-lines (mapAttrsToList hostRecords dom-cfg.hosts)}
|
||||||
${join-lines (mapAttrsToList hostSshFpRecords dom-cfg.hosts)}
|
|
||||||
${join-lines (mapAttrsToList cnameRecord dom-cfg.aliases)}
|
${join-lines (mapAttrsToList cnameRecord dom-cfg.aliases)}
|
||||||
${join-lines dom-cfg.extra-dns-records}
|
${join-lines dom-cfg.extra-dns-records}
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -7,9 +7,6 @@ let
|
||||||
|
|
||||||
join-lines = concatStringsSep "\n";
|
join-lines = concatStringsSep "\n";
|
||||||
|
|
||||||
ip = import ../../lib/ip.nix { inherit lib; };
|
|
||||||
dns = import ../../lib/dns.nix { inherit lib; };
|
|
||||||
|
|
||||||
hostOpts = { hostname, ... }: {
|
hostOpts = { hostname, ... }: {
|
||||||
options = {
|
options = {
|
||||||
ip-address = mkOption {
|
ip-address = mkOption {
|
||||||
|
|
|
@ -4,29 +4,44 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.fudo.vpn;
|
cfg = config.fudo.vpn;
|
||||||
|
|
||||||
peerOpts = { peer-name, ... }: {
|
generate-pubkey-pkg = name: privkey:
|
||||||
options = with types; {
|
pkgs.runCommand "wireguard-${name}-pubkey" {
|
||||||
public-key = mkOption {
|
WIREGUARD_PRIVATE_KEY = privkey;
|
||||||
type = str;
|
} ''
|
||||||
description = "Peer public key.";
|
mkdir $out
|
||||||
};
|
PUBKEY=$(echo $WIREGUARD_PRIVATE_KEY | ${pkgs.wireguard-tools}/bin/wg pubkey)
|
||||||
|
echo $PUBKEY > $out/pubkey.key
|
||||||
|
'';
|
||||||
|
|
||||||
allowed-ips = mkOption {
|
generate-client-config = privkey-file: server-pubkey: network: server-ip: listen-port: dns-servers: ''
|
||||||
type = listOf str;
|
[Interface]
|
||||||
description = "List of allowed IP ranges from which this peer can connect.";
|
Address = ${ip.networkMinIp network}
|
||||||
example = [ "10.100.0.0/16" ];
|
PrivateKey = ${fileContents privkey-file}
|
||||||
default = [];
|
ListenPort = ${toString listen-port}
|
||||||
};
|
DNS = ${concatStringsSep ", " dns-servers}
|
||||||
};
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ${server-pubkey}
|
||||||
|
Endpoint = ${server-ip}:${toString listen-port}
|
||||||
|
AllowedIps = 0.0.0.0/0, ::/0
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
'';
|
||||||
|
|
||||||
|
generate-peer-entry = peer-name: peer-privkey-path: peer-allowed-ips: let
|
||||||
|
peer-pkg = generate-pubkey-pkg "client-${peer-name}" (fileContents peer-privkey-path);
|
||||||
|
pubkey-path = "${peer-pkg}/pubkey.key";
|
||||||
|
in {
|
||||||
|
publicKey = fileContents pubkey-path;
|
||||||
|
allowedIPs = peer-allowed-ips;
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
in {
|
||||||
options.fudo.vpn = with types; {
|
options.fudo.vpn = with types; {
|
||||||
enable = mkEnableOption "Enable Fudo VPN";
|
enable = mkEnableOption "Enable Fudo VPN";
|
||||||
|
|
||||||
ips = mkOption {
|
network = mkOption {
|
||||||
type = str;
|
type = str;
|
||||||
description = "IP range to assign this interface.";
|
description = "Network range to assign this interface.";
|
||||||
default = "10.100.0.0/16";
|
default = "10.100.0.0/16";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,30 +57,68 @@ in {
|
||||||
default = 51820;
|
default = 51820;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dns-servers = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
description = "A list of dns servers to pass to clients.";
|
||||||
|
default = ["1.1.1.1" "8.8.8.8"];
|
||||||
|
};
|
||||||
|
|
||||||
|
server-ip = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "IP of this WireGuard server.";
|
||||||
|
};
|
||||||
|
|
||||||
peers = mkOption {
|
peers = mkOption {
|
||||||
type = loaOf (submodule peerOpts);
|
type = loaOf str;
|
||||||
description = "A list of peers allowed to connect.";
|
description = "A map of peers to shared private keys.";
|
||||||
default = {};
|
default = {};
|
||||||
example = {
|
example = {
|
||||||
peer0 = {
|
peer0 = "/path/to/priv.key";
|
||||||
public-key = "xyz";
|
|
||||||
allowed-ips = ["10.100.1.0/24"];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
environment.etc = let
|
||||||
|
peer-data = imap1 (i: peer:{
|
||||||
|
name = peer.name;
|
||||||
|
privkey-path = peer.privkey-path;
|
||||||
|
network-range = let
|
||||||
|
base = ip.intToIpv4
|
||||||
|
((ip.ipv4ToInt (ip.getNetworkBase cfg.network)) + (i * 256));
|
||||||
|
in "${base}/24";
|
||||||
|
}) (mapAttrsToList (name: privkey-path: {
|
||||||
|
name = name;
|
||||||
|
privkey-path = privkey-path;
|
||||||
|
}) cfg.peers);
|
||||||
|
|
||||||
|
server-pubkey-pkg = generate-pubkey-pkg "server-pubkey" (fileContents cfg.private-key-file);
|
||||||
|
|
||||||
|
server-pubkey = fileContents "${server-pubkey-pkg}/pubkey.key";
|
||||||
|
|
||||||
|
in listToAttrs
|
||||||
|
(map (peer: nameValuePair "wireguard/clients/${peer.name}.conf" {
|
||||||
|
mode = "0400";
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
text = generate-client-config
|
||||||
|
peer.privkey-path
|
||||||
|
server-pubkey
|
||||||
|
peer.network-range
|
||||||
|
cfg.server-ip
|
||||||
|
cfg.listen-port
|
||||||
|
cfg.dns-servers;
|
||||||
|
}) peer-data);
|
||||||
|
|
||||||
networking.wireguard = {
|
networking.wireguard = {
|
||||||
enable = true;
|
enable = true;
|
||||||
interfaces.wgtun0 = {
|
interfaces.wgtun0 = {
|
||||||
generatePrivateKeyFile = false;
|
generatePrivateKeyFile = false;
|
||||||
ips = [ cfg.ips ];
|
ips = [ cfg.network ];
|
||||||
listenPort = cfg.listen-port;
|
listenPort = cfg.listen-port;
|
||||||
peers = mapAttrsToList (peer-name: peer-config: {
|
peers = mapAttrsToList
|
||||||
publicKey = peer-config.public-key;
|
(name: private-key: generate-peer-entry name private-key ["0.0.0.0/0" "::/0"])
|
||||||
allowedIPs = peer-config.allowed-ips;
|
cfg.peers;
|
||||||
}) cfg.peers;
|
|
||||||
privateKeyFile = cfg.private-key-file;
|
privateKeyFile = cfg.private-key-file;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Ref: https://learnxinyminutes.com/docs/nix/
|
# Ref: https://learnxinyminutes.com/docs/nix/
|
||||||
|
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
@ -9,6 +9,12 @@
|
||||||
./config/local.nix
|
./config/local.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
lib = {
|
||||||
|
buildLisp = import ./lib/buildLisp.nix;
|
||||||
|
ip = import ./lib/ip.nix;
|
||||||
|
dns = import ./lib/dns.nix;
|
||||||
|
};
|
||||||
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
security.acme.acceptTerms = true;
|
security.acme.acceptTerms = true;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
dnssec = true;
|
||||||
|
|
||||||
|
mx = ["mail.fudo.org"];
|
||||||
|
|
||||||
|
hosts = {
|
||||||
|
cisco = {
|
||||||
|
ip-addresses = [ "198.163.150.211" ];
|
||||||
|
description = "\"allbran\" \"converge\"";
|
||||||
|
};
|
||||||
|
cisco-int = {
|
||||||
|
ip-addresses = [ "10.73.77.10" ];
|
||||||
|
description = "\"fruitloops\" \"aironet\"";
|
||||||
|
};
|
||||||
|
cupid = {
|
||||||
|
ip-addresses = [ "208.38.36.100" ];
|
||||||
|
};
|
||||||
|
docker = {
|
||||||
|
ip-addresses = [ "208.81.3.126" ];
|
||||||
|
};
|
||||||
|
france = {
|
||||||
|
ip-addresses = [ "208.81.3.117" ];
|
||||||
|
ssh-fingerprints = [
|
||||||
|
"4 1 c95a198f504a589fc62893a95424b12f0b24732d"
|
||||||
|
"4 2 3e7dad879d6cab7f7fb6769e156d7988d0c01281618d03b793834eea2f09bc96"
|
||||||
|
"1 1 1b6d62dafae9ebc59169dfb4ef828582a5450d94"
|
||||||
|
"1 2 079e7a57873542541095bf3d2f97b7350bb457d027b423a6fb56f7f6aa84ac80"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
frankfurt = {
|
||||||
|
ip-addresses = [ "208.81.3.120" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe8c:9738" ];
|
||||||
|
ssh-fingerprints = [
|
||||||
|
"2 1 4b9e4ed16a6b3fe6d41ed0f5cdeed853cc101e12"
|
||||||
|
"2 2 286ce32326874fe8aa15e3fd60b176b906ebd87306109f7c250d077db4ba85c5"
|
||||||
|
"3 1 3531dfd2f240ce0cd548b748462f78451df3f081"
|
||||||
|
"3 2 338809345ed38eb6808fd468067a74b2a8000fd8cc3bc016b9f977050bf1bba8"
|
||||||
|
"1 1 fb9ba707daa78243f8a8801f024fe790516b99a7"
|
||||||
|
"1 2 407f9692fedbd83449f0daf1cf795258b561a7e9c7e8072577cc84ffc0c84130"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
germany = {
|
||||||
|
ip-addresses = [ "208.81.3.116" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d200:1:78d9:d8ff:fe0f:dd88" ];
|
||||||
|
ssh-fingerprints = [
|
||||||
|
"2 1 5609a728a91d7e52a6060ea7f3a7790005ba5e81"
|
||||||
|
"2 2 520a8eb3b9013837ac3ab4b28254f96b7718f9613e751a20dc488bf7d967b485"
|
||||||
|
"3 1 ee5b49888a36a34e7d4ee0d18626c82a16c2fcdf"
|
||||||
|
"3 2 d5e44cf2d85032638d49c030a9ccbff6638198c354efcb11bf173017d1257f49"
|
||||||
|
"1 1 9915d2515d7acdb38924d8829925113d5ce80b88"
|
||||||
|
"1 2 a7c866306e9661b8b568b2de282367c84065301d6228e58e57e6c4d3d33e3051"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
hanover = {
|
||||||
|
ip-addresses = [ "208.81.1.130" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d100:1:5054:ff:fe61:ac8b" ];
|
||||||
|
};
|
||||||
|
localhost = {
|
||||||
|
ip-addresses = [ "127.0.0.1" ];
|
||||||
|
};
|
||||||
|
lsbb-gba = {
|
||||||
|
ip-addresses = [ "199.101.56.34" ];
|
||||||
|
};
|
||||||
|
lsbb-abg = {
|
||||||
|
ip-addresses = [ "199.101.56.38" ];
|
||||||
|
};
|
||||||
|
lsbb-hwd = {
|
||||||
|
ip-addresses = [ "199.101.56.106" ];
|
||||||
|
};
|
||||||
|
lsbb-hcl = {
|
||||||
|
ip-addresses = [ "199.101.56.110" ];
|
||||||
|
};
|
||||||
|
procul = {
|
||||||
|
ip-addresses = [ "172.86.179.18" ];
|
||||||
|
};
|
||||||
|
prunel = {
|
||||||
|
ip-addresses = [ "208.81.3.123" ];
|
||||||
|
};
|
||||||
|
mbix = {
|
||||||
|
ip-addresses = [ "208.81.7.146" ];
|
||||||
|
};
|
||||||
|
ns3-fudo = {
|
||||||
|
ip-addresses = [ "208.75.74.205" ];
|
||||||
|
};
|
||||||
|
ns3-dair = {
|
||||||
|
ip-addresses = [ "208.75.74.205" ];
|
||||||
|
};
|
||||||
|
ns4-fudo = {
|
||||||
|
ip-addresses = [ "208.75.75.157" ];
|
||||||
|
};
|
||||||
|
ns4-dair = {
|
||||||
|
ip-addresses = [ "208.75.75.157" ];
|
||||||
|
};
|
||||||
|
paris = {
|
||||||
|
ip-addresses = [ "208.81.3.125" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe67:d0c1" ];
|
||||||
|
ssh-fingerprints = [
|
||||||
|
"2 1 9fe9e689a36316831ffafffc22c85913748670a6"
|
||||||
|
"2 2 f2ce57bf470c907604b79b6ef031c928a64a81031e78892fd475bbcf65ae728b"
|
||||||
|
"3 1 5c56e93a20868886ffe76e1fab012989ce8e995f"
|
||||||
|
"3 2 af4f383cb349fc3b2496a0bf0911da3a09f98a6d4d2a3c81bb0fb23a45bde950"
|
||||||
|
"4 1 71a1d35c32b4445b98ce339696f155e1d4c39bd5"
|
||||||
|
"4 2 a9e4810a24bd52082c9bb2b1019a9de7d7983246fecb454dd8d918ac5a11af81"
|
||||||
|
"1 1 18e8dd7cac48f1ac6103ec21c279e339d8690be1"
|
||||||
|
"1 2 72e4aa05c733441da57c464e6540486f5306b6768d784dd97c666e16629d77a0"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
probe = {
|
||||||
|
ip-addresses = [ "208.81.3.119" ];
|
||||||
|
};
|
||||||
|
tours = {
|
||||||
|
ip-addresses = [ "208.81.3.121" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe95:34e5" ];
|
||||||
|
ssh-fingerprints = [
|
||||||
|
"2 2 41cddf1457880c7e86fa3838eabdbbe7cf803f98998ed406319ba3e43036964c"
|
||||||
|
"3 1 89b72a740ef6ef7ad9aaf5fe2178d356cdc7ee5b"
|
||||||
|
"3 2 c39346def56817aaf4c64db5667ccc6aeb400ff1166125fe630b63b5eab0ef29"
|
||||||
|
"4 1 049b1e6ef1d338d35e97baf312d8a371a266b7d1"
|
||||||
|
"4 2 1a889e43148ea1ded9f8bc60799ccf1bc32cb084946c8815abed6cc31f212594"
|
||||||
|
"1 1 bae37560759ec8dba35755473fbb346f9dc4e333"
|
||||||
|
"1 2 3d0d5efe2da329ea19b191f227c3aaad45271c651717ec3315cda131e992bbcf"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
default-host = "208.81.3.117";
|
||||||
|
|
||||||
|
srv-records = {
|
||||||
|
tcp = {
|
||||||
|
domain = [
|
||||||
|
{
|
||||||
|
host = "ns1.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns2.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns3.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns4.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ssh = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 22;
|
||||||
|
}];
|
||||||
|
smtp = [{
|
||||||
|
host = "mail.fudo.org";
|
||||||
|
port = 25;
|
||||||
|
}];
|
||||||
|
submission = [{
|
||||||
|
host = "mail.fudo.org";
|
||||||
|
port = 587;
|
||||||
|
}];
|
||||||
|
kerberos = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 88;
|
||||||
|
}];
|
||||||
|
imaps = [{
|
||||||
|
host = "mail.fudo.org";
|
||||||
|
port = 993;
|
||||||
|
}];
|
||||||
|
ldap = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 389;
|
||||||
|
}];
|
||||||
|
ldaps = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 636;
|
||||||
|
}];
|
||||||
|
pop3s = [{
|
||||||
|
host = "mail.fudo.org";
|
||||||
|
port = 995;
|
||||||
|
}];
|
||||||
|
http = [{
|
||||||
|
host = "wiki.fudo.org";
|
||||||
|
port = 80;
|
||||||
|
}];
|
||||||
|
https = [{
|
||||||
|
host = "wiki.fudo.org";
|
||||||
|
port = 80;
|
||||||
|
}];
|
||||||
|
xmpp-server = [{
|
||||||
|
host = "fudo.im";
|
||||||
|
port = 5269;
|
||||||
|
}];
|
||||||
|
xmpp-client = [{
|
||||||
|
host = "fudo.im";
|
||||||
|
port = 5222;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
udp = {
|
||||||
|
domain = [
|
||||||
|
{
|
||||||
|
host = "ns1.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns2.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns3.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
host = "ns4.fudo.org";
|
||||||
|
port = 53;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
kerberos = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 88;
|
||||||
|
}];
|
||||||
|
kerberos-master = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 88;
|
||||||
|
}];
|
||||||
|
kpasswd = [{
|
||||||
|
host = "france.fudo.org";
|
||||||
|
port = 464;
|
||||||
|
}];
|
||||||
|
xmpp-server = [{
|
||||||
|
host = "fudo.im";
|
||||||
|
port = 5269;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases = {
|
||||||
|
pop = "mail.fudo.org.";
|
||||||
|
smtp = "mail.fudo.org.";
|
||||||
|
imap = "mail.fudo.org.";
|
||||||
|
webmail = "france.fudo.org.";
|
||||||
|
|
||||||
|
archiva = "france.fudo.org.";
|
||||||
|
auth = "france.fudo.org.";
|
||||||
|
backplane = "france.fudo.org.";
|
||||||
|
chat = "france.fudo.org.";
|
||||||
|
de = "germany.fudo.org.";
|
||||||
|
fr = "france.fudo.org.";
|
||||||
|
git = "france.fudo.org.";
|
||||||
|
metrics = "france.fudo.org.";
|
||||||
|
minecraft = "france.fudo.org.";
|
||||||
|
monitor = "france.fudo.org.";
|
||||||
|
user = "paris.fudo.org.";
|
||||||
|
u = "user.fudo.org.";
|
||||||
|
w = "www.fudo.org.";
|
||||||
|
ww = "www.fudo.org.";
|
||||||
|
www = "hanover.fudo.org.";
|
||||||
|
wiki = "hanover.fudo.org.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extra-dns-records = [
|
||||||
|
''_kerberos IN TXT "FUDO.ORG"''
|
||||||
|
''@ IN TXT "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"''
|
||||||
|
''@ IN SPF "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"''
|
||||||
|
];
|
||||||
|
|
||||||
|
dmarc-report-address = "dmarc-report@fudo.org";
|
||||||
|
}
|
|
@ -75,7 +75,8 @@
|
||||||
uid = 10035;
|
uid = 10035;
|
||||||
group = "selby";
|
group = "selby";
|
||||||
common-name = "Ken Selby";
|
common-name = "Ken Selby";
|
||||||
hashed-password = "{SSHA}X8DxUcwH2Fzel5UKbGVNhC5B2vg0Prsc";
|
hashed-password = "{SSHA}wUGV/9dr8inz/HyqSF/OWKxy0DCy5AI3";
|
||||||
|
# hashed-password = "{SSHA}X8DxUcwH2Fzel5UKbGVNhC5B2vg0Prsc";
|
||||||
};
|
};
|
||||||
|
|
||||||
reaper = {
|
reaper = {
|
||||||
|
@ -390,7 +391,8 @@
|
||||||
uid = 10108;
|
uid = 10108;
|
||||||
group = "selby";
|
group = "selby";
|
||||||
common-name = "Lauren Hotel";
|
common-name = "Lauren Hotel";
|
||||||
hashed-password = "{SSHA}DKnhrycmXSu4HKWFPeBXA9xvZ0ytgXIpZA10tg==";
|
hashed-password = "{SSHA}1q/MC5LKROlIT1nDrKrMvcFAXFtcQXIR";
|
||||||
|
# hashed-password = "{SSHA}DKnhrycmXSu4HKWFPeBXA9xvZ0ytgXIpZA10tg==";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Used to send alerts from grafana
|
# Used to send alerts from grafana
|
||||||
|
|
|
@ -7,7 +7,7 @@ let
|
||||||
mail-hostname = "mail.${domain}";
|
mail-hostname = "mail.${domain}";
|
||||||
host_ipv4 = "208.81.3.117";
|
host_ipv4 = "208.81.3.117";
|
||||||
# Use a special IP for git.fudo.org, since it needs to be SSH-able
|
# Use a special IP for git.fudo.org, since it needs to be SSH-able
|
||||||
git_ipv4 = "208.81.3.126";
|
link_ipv4 = "208.81.3.126";
|
||||||
all-hostnames = [];
|
all-hostnames = [];
|
||||||
|
|
||||||
acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem";
|
acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem";
|
||||||
|
@ -40,6 +40,7 @@ in {
|
||||||
lxd
|
lxd
|
||||||
multipath-tools
|
multipath-tools
|
||||||
nix-prefetch-docker
|
nix-prefetch-docker
|
||||||
|
powerdns
|
||||||
tshark
|
tshark
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -185,22 +186,44 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# fudo.dns = {
|
fudo.dns = {
|
||||||
# enable = true;
|
enable = true;
|
||||||
|
|
||||||
# dns-hosts = {
|
identity = "france.fudo.org";
|
||||||
# "ns1.fudo.org" = host_ipv4;
|
|
||||||
# "ns2.fudo.org" = "";
|
|
||||||
# };
|
|
||||||
|
|
||||||
# listen-ips = [host_ipv4];
|
nameservers = {
|
||||||
|
ns1 = {
|
||||||
|
ip-addresses = [ "208.81.3.117" ];
|
||||||
|
ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe8c:9738" ];
|
||||||
|
description = "Nameserver 1, france, in Winnipeg, MB, CA";
|
||||||
|
rp = "reaper reaper.rp";
|
||||||
|
};
|
||||||
|
ns2 = {
|
||||||
|
ip-addresses = [ "209.117.102.102" ];
|
||||||
|
ipv6-addresses = [ "2001:470:1f16:40::2" ];
|
||||||
|
description = "Nameserver 2, musashi, in Winnipeg, MB, CA";
|
||||||
|
rp = "reaper reaper.rp";
|
||||||
|
};
|
||||||
|
ns3 = {
|
||||||
|
ip-addresses = [ "104.131.53.95" ];
|
||||||
|
ipv6-addresses = [ "2604:a880:800:10::8:7001" ];
|
||||||
|
description = "Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
|
||||||
|
rp = "reaper reaper.rp";
|
||||||
|
};
|
||||||
|
ns4 = {
|
||||||
|
ip-addresses = [ "204.42.254.5" ];
|
||||||
|
ipv6-addresses = [ "2001:418:3f4::5" ];
|
||||||
|
description = "Nameserver 4, puck.nether.net, in Chicago, IL, US";
|
||||||
|
rp = "reaper reaper.rp";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# domains = {
|
listen-ips = [host_ipv4];
|
||||||
# "selby.ca" = import ../fudo.org/selby.ca.nix {
|
|
||||||
# inherit host_ipv4 config;
|
domains = {
|
||||||
# };
|
"fudo.org" = import ../fudo/fudo.org.nix { inherit config; };
|
||||||
# };
|
};
|
||||||
# };
|
};
|
||||||
|
|
||||||
# Not all users need access to france; don't allow LDAP-user access.
|
# Not all users need access to france; don't allow LDAP-user access.
|
||||||
fudo.authentication.enable = false;
|
fudo.authentication.enable = false;
|
||||||
|
@ -259,6 +282,15 @@ in {
|
||||||
# TODO: not used yet
|
# TODO: not used yet
|
||||||
fudo.acme.hostnames = all-hostnames;
|
fudo.acme.hostnames = all-hostnames;
|
||||||
|
|
||||||
|
fudo.client.dns = {
|
||||||
|
enable = true;
|
||||||
|
ipv4 = true;
|
||||||
|
ipv6 = true;
|
||||||
|
user = "fudo-client";
|
||||||
|
external-interface = "extif0";
|
||||||
|
password-file = "/srv/client/secure/client.passwd";
|
||||||
|
};
|
||||||
|
|
||||||
fudo.mail-server = import ../fudo/email.nix { inherit config; } // {
|
fudo.mail-server = import ../fudo/email.nix { inherit config; } // {
|
||||||
enableContainer = true;
|
enableContainer = true;
|
||||||
debug = true;
|
debug = true;
|
||||||
|
@ -392,7 +424,7 @@ in {
|
||||||
repository-dir = /srv/git/repo;
|
repository-dir = /srv/git/repo;
|
||||||
state-dir = /srv/git/state;
|
state-dir = /srv/git/state;
|
||||||
ssh = {
|
ssh = {
|
||||||
listen-ip = git_ipv4;
|
listen-ip = link_ipv4;
|
||||||
listen-port = 2222;
|
listen-port = 2222;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -438,7 +470,7 @@ in {
|
||||||
macAddress = "02:6d:e2:e1:ad:ca";
|
macAddress = "02:6d:e2:e1:ad:ca";
|
||||||
ipv4.addresses = [
|
ipv4.addresses = [
|
||||||
{
|
{
|
||||||
address = git_ipv4;
|
address = link_ipv4;
|
||||||
prefixLength = 28;
|
prefixLength = 28;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -58,8 +58,12 @@ in {
|
||||||
databases = {
|
databases = {
|
||||||
backplane_dns = {
|
backplane_dns = {
|
||||||
access = "CONNECT";
|
access = "CONNECT";
|
||||||
|
# entity-access = {
|
||||||
|
# "ALL TABLES IN SCHEMA public" = "SELECT";
|
||||||
|
# };
|
||||||
entity-access = {
|
entity-access = {
|
||||||
"ALL TABLES IN SCHEMA public" = "SELECT";
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -70,7 +74,7 @@ in {
|
||||||
backplane_dns = {
|
backplane_dns = {
|
||||||
access = "CONNECT";
|
access = "CONNECT";
|
||||||
entity-access = {
|
entity-access = {
|
||||||
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE";
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -87,8 +91,8 @@ in {
|
||||||
|
|
||||||
backplane.dns = {
|
backplane.dns = {
|
||||||
enable = true;
|
enable = true;
|
||||||
port = 353;
|
listen-v4-addresses = [ "208.81.3.126" ];
|
||||||
listen-addresses = [ "208.81.3.117" ];
|
listen-v6-addresses = [ "[2605:e200:d200:1:6d:e2ff:fee1:adca]" ];
|
||||||
required-services = [ "fudo-passwords.target" ];
|
required-services = [ "fudo-passwords.target" ];
|
||||||
user = "backplane-dns";
|
user = "backplane-dns";
|
||||||
group = "backplane-dns";
|
group = "backplane-dns";
|
||||||
|
|
|
@ -4,6 +4,9 @@ with lib;
|
||||||
let
|
let
|
||||||
backplane-auth = "/etc/nixos/static/backplane-auth.scm";
|
backplane-auth = "/etc/nixos/static/backplane-auth.scm";
|
||||||
|
|
||||||
|
host-passwd-file = "/srv/jabber/secret/hosts-passwd.scm";
|
||||||
|
service-passwd-file = "/srv/jabber/secret/services-passwd.scm";
|
||||||
|
|
||||||
cert-basedir = "/var/lib/ejabberd/certs";
|
cert-basedir = "/var/lib/ejabberd/certs";
|
||||||
|
|
||||||
target-certs = ["key" "cert" "chain" "fullchain"];
|
target-certs = ["key" "cert" "chain" "fullchain"];
|
||||||
|
@ -50,30 +53,67 @@ in {
|
||||||
security.acme.certs."fudo.im".email = "admin@fudo.org";
|
security.acme.certs."fudo.im".email = "admin@fudo.org";
|
||||||
security.acme.certs."backplane.fudo.org".email = "admin@fudo.org";
|
security.acme.certs."backplane.fudo.org".email = "admin@fudo.org";
|
||||||
|
|
||||||
systemd.services = {
|
systemd = {
|
||||||
ejabberd-generate-certs = {
|
services = {
|
||||||
enable = true;
|
ejabberd-generate-certs = {
|
||||||
description = "Generate required SSL certs for ejabberd.";
|
enable = true;
|
||||||
wantedBy = [ "ejabberd.service" ];
|
description = "Generate required SSL certs for ejabberd.";
|
||||||
after = [
|
wantedBy = [ "ejabberd.service" ];
|
||||||
"acme-backplane.fudo.org.service"
|
after = [
|
||||||
"acme-fudo.im.service"
|
"acme-backplane.fudo.org.service"
|
||||||
];
|
"acme-fudo.im.service"
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${move-server-certs ["fudo.im" "backplane.fudo.org"]}";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStop = remove-server-certs;
|
||||||
|
StandardOutput = "journal";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
serviceConfig = {
|
ejabberd = {
|
||||||
Type = "oneshot";
|
requires = [ "ejabberd-generate-certs.service" ];
|
||||||
ExecStart = "${move-server-certs ["fudo.im" "backplane.fudo.org"]}";
|
environment = {
|
||||||
RemainAfterExit = true;
|
FUDO_HOST_PASSWD_FILE = host-passwd-file;
|
||||||
ExecStop = remove-server-certs;
|
FUDO_SERVICE_PASSWD_FILE = service-passwd-file;
|
||||||
StandardOutput = "journal";
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ejabberd-hostfile-watcher = {
|
||||||
|
description = "Watch the ejabberd host file and restart if changes occur.";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
after = [ "ejabberd.service" ];
|
||||||
|
script = ''
|
||||||
|
SYSCTL=${pkgs.systemd}/bin/systemctl
|
||||||
|
if $SYSCTL is-active --quiet ejabberd.service; then
|
||||||
|
echo "restarting ejabberd.service because hostfile has changed."
|
||||||
|
$SYSCTL restart ejabberd.service
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
ejabberd-servicefile-watcher = {
|
||||||
|
description = "Watch the ejabberd service file and restart if changes occur.";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
after = [ "ejabberd.service" ];
|
||||||
|
script = ''
|
||||||
|
SYSCTL=${pkgs.systemd}/bin/systemctl
|
||||||
|
if $SYSCTL is-active --quiet ejabberd.service; then
|
||||||
|
echo "restarting ejabberd.service because servicefile has changed."
|
||||||
|
$SYSCTL restart ejabberd.service
|
||||||
|
fi
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ejabberd = {
|
paths = {
|
||||||
requires = [ "ejabberd-generate-certs.service" ];
|
ejabberd-hostfile-watcher = {
|
||||||
environment = {
|
pathConfig.PathChanged = host-passwd-file;
|
||||||
FUDO_HOST_PASSWD_FILE = "/srv/jabber/secret/hosts-passwd.scm";
|
};
|
||||||
FUDO_SERVICE_PASSWD_FILE = "/srv/jabber/secret/services-passwd.scm";
|
|
||||||
|
ejabberd-servicefile-watcher = {
|
||||||
|
pathConfig.PathChanged = service-passwd-file;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,10 +31,6 @@ in {
|
||||||
../informis/users.nix
|
../informis/users.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
multipath-tools
|
|
||||||
];
|
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = hostname;
|
hostName = hostname;
|
||||||
|
|
||||||
|
@ -110,6 +106,15 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
client.dns = {
|
||||||
|
enable = true;
|
||||||
|
ipv4 = true;
|
||||||
|
ipv6 = true;
|
||||||
|
user = "fudo-client";
|
||||||
|
external-interface = "extif0";
|
||||||
|
password-file = "/srv/client/secure/client.passwd";
|
||||||
|
};
|
||||||
|
|
||||||
# Not all users need access to procul; don't allow LDAP-user access.
|
# Not all users need access to procul; don't allow LDAP-user access.
|
||||||
authentication.enable = false;
|
authentication.enable = false;
|
||||||
|
|
||||||
|
@ -214,15 +219,23 @@ in {
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
gituser = {
|
gituser = {
|
||||||
password = fileContents "/srv/git/secure/db.passwd";
|
password-file = "/srv/git/secure/db.passwd";
|
||||||
databases = {
|
databases = {
|
||||||
git = "ALL PRIVILEGES";
|
git = {
|
||||||
|
access = "CONNECT";
|
||||||
|
entity-access = {
|
||||||
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT, UPDATE";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
databases = {
|
databases = {
|
||||||
git = ["niten"];
|
git = {
|
||||||
|
users = ["niten"];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -273,22 +286,15 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
fudo.vpn = {
|
fudo.vpn = {
|
||||||
enable = true;
|
# fer some fuckin reason this sets the default gw to the vpn interface
|
||||||
ips = "10.100.0.0/16";
|
enable = false;
|
||||||
|
network = "10.100.0.0/16";
|
||||||
|
server-ip = host_ipv4;
|
||||||
private-key-file = "/srv/wireguard/secure/secret.key";
|
private-key-file = "/srv/wireguard/secure/secret.key";
|
||||||
peers = {
|
peers = {
|
||||||
peter = {
|
peter = "/srv/wireguard/clients/peter.key";
|
||||||
allowed-ips = [ "10.100.1.0/24" ];
|
ken = "/srv/wireguard/clients/ken.key";
|
||||||
public-key = "d1NfRFWRkcKq2gxvqfMy7Oe+JFYf5DjomnsTyisvgB4=";
|
helen = "/srv/wireguard/clients/helen.key";
|
||||||
};
|
|
||||||
ken = {
|
|
||||||
allowed-ips = [ "10.100.2.0/24" ];
|
|
||||||
public-key = "y294rTCK0iSRhA6EIOErPzEuqzJMuYAG4XbHasySMVU=";
|
|
||||||
};
|
|
||||||
helen = {
|
|
||||||
allowed-ips = [ "10.100.3.0/24" ];
|
|
||||||
public-key = "7Hdko6RibhIYdoPLWXGwmElY5vKvZ+rURmqFTDUfC2w=";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
# buildLisp provides Nix functions to build Common Lisp packages,
|
||||||
|
# targeting SBCL.
|
||||||
|
#
|
||||||
|
# buildLisp is designed to enforce conventions and do away with the
|
||||||
|
# free-for-all of existing Lisp build systems.
|
||||||
|
|
||||||
|
{ pkgs ? import <nixpkgs> {}, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins) map elemAt match filter;
|
||||||
|
inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Internal helper definitions
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'genLoadLisp' generates Lisp code that instructs SBCL to load all
|
||||||
|
# the provided Lisp libraries.
|
||||||
|
genLoadLisp = deps: lib.concatStringsSep "\n"
|
||||||
|
(map (lib: "(load \"${lib}/${lib.lispName}.fasl\")") (allDeps deps));
|
||||||
|
|
||||||
|
# 'genCompileLisp' generates a Lisp file that instructs SBCL to
|
||||||
|
# compile the provided list of Lisp source files to $out.
|
||||||
|
genCompileLisp = srcs: deps: writeText "compile.lisp" ''
|
||||||
|
;; This file compiles the specified sources into the Nix build
|
||||||
|
;; directory, creating one FASL file for each source.
|
||||||
|
(require 'sb-posix)
|
||||||
|
|
||||||
|
${genLoadLisp deps}
|
||||||
|
|
||||||
|
(defun nix-compile-lisp (file srcfile)
|
||||||
|
(let ((outfile (make-pathname :type "fasl"
|
||||||
|
:directory (or (sb-posix:getenv "NIX_BUILD_TOP")
|
||||||
|
(error "not running in a Nix build"))
|
||||||
|
:name (substitute #\- #\/ srcfile))))
|
||||||
|
(multiple-value-bind (_outfile _warnings-p failure-p)
|
||||||
|
(compile-file srcfile :output-file outfile)
|
||||||
|
(if failure-p (sb-posix:exit 1)
|
||||||
|
(progn
|
||||||
|
;; For the case of multiple files belonging to the same
|
||||||
|
;; library being compiled, load them in order:
|
||||||
|
(load outfile)
|
||||||
|
|
||||||
|
;; Write them to the FASL list in the same order:
|
||||||
|
(format file "cat ~a~%" (namestring outfile)))))))
|
||||||
|
|
||||||
|
(let ((*compile-verbose* t)
|
||||||
|
;; FASL files are compiled into the working directory of the
|
||||||
|
;; build and *then* moved to the correct out location.
|
||||||
|
(pwd (sb-posix:getcwd)))
|
||||||
|
|
||||||
|
(with-open-file (file "cat_fasls"
|
||||||
|
:direction :output
|
||||||
|
:if-does-not-exist :create)
|
||||||
|
|
||||||
|
;; These forms were inserted by the Nix build:
|
||||||
|
${
|
||||||
|
lib.concatStringsSep "\n" (map (src: "(nix-compile-lisp file \"${src}\")") srcs)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 'genTestLisp' generates a Lisp file that loads all sources and deps and
|
||||||
|
# executes expression
|
||||||
|
genTestLisp = name: srcs: deps: expression: writeText "${name}.lisp" ''
|
||||||
|
;; Dependencies
|
||||||
|
${genLoadLisp deps}
|
||||||
|
|
||||||
|
;; Sources
|
||||||
|
${lib.concatStringsSep "\n" (map (src: "(load \"${src}\")") srcs)}
|
||||||
|
|
||||||
|
;; Test expression
|
||||||
|
(unless ${expression}
|
||||||
|
(exit :code 1))
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 'dependsOn' determines whether Lisp library 'b' depends on 'a'.
|
||||||
|
dependsOn = a: b: builtins.elem a b.lispDeps;
|
||||||
|
|
||||||
|
# 'allDeps' flattens the list of dependencies (and their
|
||||||
|
# dependencies) into one ordered list of unique deps.
|
||||||
|
allDeps = deps: (lib.toposort dependsOn (lib.unique (
|
||||||
|
lib.flatten (deps ++ (map (d: d.lispDeps) deps))
|
||||||
|
))).result;
|
||||||
|
|
||||||
|
# 'allNative' extracts all native dependencies of a dependency list
|
||||||
|
# to ensure that library load paths are set correctly during all
|
||||||
|
# compilations and program assembly.
|
||||||
|
allNative = native: deps: lib.unique (
|
||||||
|
lib.flatten (native ++ (map (d: d.lispNativeDeps) deps))
|
||||||
|
);
|
||||||
|
|
||||||
|
# 'genDumpLisp' generates a Lisp file that instructs SBCL to dump
|
||||||
|
# the currently loaded image as an executable to $out/bin/$name.
|
||||||
|
#
|
||||||
|
# TODO(tazjin): Compression is currently unsupported because the
|
||||||
|
# SBCL in nixpkgs is, by default, not compiled with zlib support.
|
||||||
|
genDumpLisp = name: main: deps: writeText "dump.lisp" ''
|
||||||
|
(require 'sb-posix)
|
||||||
|
|
||||||
|
${genLoadLisp deps}
|
||||||
|
|
||||||
|
(let* ((bindir (concatenate 'string (sb-posix:getenv "out") "/bin"))
|
||||||
|
(outpath (make-pathname :name "${name}"
|
||||||
|
:directory bindir)))
|
||||||
|
(save-lisp-and-die outpath
|
||||||
|
:executable t
|
||||||
|
:toplevel (function ${main})
|
||||||
|
:purify t))
|
||||||
|
;;
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Add an `overrideLisp` attribute to a function result that works
|
||||||
|
# similar to `overrideAttrs`, but is used specifically for the
|
||||||
|
# arguments passed to Lisp builders.
|
||||||
|
makeOverridable = f: orig: (f orig) // {
|
||||||
|
overrideLisp = new: makeOverridable f (orig // (new orig));
|
||||||
|
};
|
||||||
|
|
||||||
|
# 'testSuite' builds a Common Lisp test suite that loads all of srcs and deps,
|
||||||
|
# and then executes expression to check its result
|
||||||
|
testSuite = { name, expression, srcs, deps ? [], native ? [] }:
|
||||||
|
let
|
||||||
|
lispNativeDeps = allNative native deps;
|
||||||
|
lispDeps = allDeps deps;
|
||||||
|
in runCommandNoCC name {
|
||||||
|
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
||||||
|
LANG = "C.UTF-8";
|
||||||
|
} ''
|
||||||
|
echo "Running test suite ${name}"
|
||||||
|
|
||||||
|
${sbcl}/bin/sbcl --script ${genTestLisp name srcs deps expression} \
|
||||||
|
| tee $out
|
||||||
|
|
||||||
|
echo "Test suite ${name} succeeded"
|
||||||
|
'';
|
||||||
|
|
||||||
|
#
|
||||||
|
# Public API functions
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'library' builds a list of Common Lisp files into a single FASL
|
||||||
|
# which can then be loaded into SBCL.
|
||||||
|
library =
|
||||||
|
{ name
|
||||||
|
, srcs
|
||||||
|
, deps ? []
|
||||||
|
, native ? []
|
||||||
|
, tests ? null
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
lispNativeDeps = (allNative native deps);
|
||||||
|
lispDeps = allDeps deps;
|
||||||
|
testDrv = if ! isNull tests
|
||||||
|
then testSuite {
|
||||||
|
name = tests.name or "${name}-test";
|
||||||
|
srcs = srcs ++ (tests.srcs or []);
|
||||||
|
deps = deps ++ (tests.deps or []);
|
||||||
|
expression = tests.expression;
|
||||||
|
}
|
||||||
|
else null;
|
||||||
|
in lib.fix (self: runCommandNoCC "${name}-cllib" {
|
||||||
|
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
||||||
|
LANG = "C.UTF-8";
|
||||||
|
} ''
|
||||||
|
${if ! isNull testDrv
|
||||||
|
then "echo 'Test ${testDrv} succeeded'"
|
||||||
|
else "echo 'No tests run'"}
|
||||||
|
${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps}
|
||||||
|
|
||||||
|
echo "Compilation finished, assembling FASL files"
|
||||||
|
|
||||||
|
# FASL files can be combined by simply concatenating them
|
||||||
|
# together, but it needs to be in the compilation order.
|
||||||
|
mkdir $out
|
||||||
|
|
||||||
|
chmod +x cat_fasls
|
||||||
|
./cat_fasls > $out/${name}.fasl
|
||||||
|
'' // {
|
||||||
|
inherit lispNativeDeps lispDeps;
|
||||||
|
lispName = name;
|
||||||
|
lispBinary = false;
|
||||||
|
tests = testDrv;
|
||||||
|
sbcl = sbclWith [ self ];
|
||||||
|
});
|
||||||
|
|
||||||
|
# 'program' creates an executable containing a dumped image of the
|
||||||
|
# specified sources and dependencies.
|
||||||
|
program =
|
||||||
|
{ name
|
||||||
|
, main ? "${name}:main"
|
||||||
|
, srcs
|
||||||
|
, deps ? []
|
||||||
|
, native ? []
|
||||||
|
, tests ? null
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
lispDeps = allDeps deps;
|
||||||
|
libPath = lib.makeLibraryPath (allNative native lispDeps);
|
||||||
|
selfLib = library {
|
||||||
|
inherit name srcs native;
|
||||||
|
deps = lispDeps;
|
||||||
|
};
|
||||||
|
testDrv = if ! isNull tests
|
||||||
|
then testSuite {
|
||||||
|
name = tests.name or "${name}-test";
|
||||||
|
srcs =
|
||||||
|
(
|
||||||
|
srcs ++ (tests.srcs or []));
|
||||||
|
deps = deps ++ (tests.deps or []);
|
||||||
|
expression = tests.expression;
|
||||||
|
}
|
||||||
|
else null;
|
||||||
|
in lib.fix (self: runCommandNoCC "${name}" {
|
||||||
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
LD_LIBRARY_PATH = libPath;
|
||||||
|
LANG = "C.UTF-8";
|
||||||
|
} ''
|
||||||
|
${if ! isNull testDrv
|
||||||
|
then "echo 'Test ${testDrv} succeeded'"
|
||||||
|
else ""}
|
||||||
|
mkdir -p $out/bin
|
||||||
|
|
||||||
|
${sbcl}/bin/sbcl --script ${
|
||||||
|
genDumpLisp name main ([ selfLib ] ++ lispDeps)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}"
|
||||||
|
'' // {
|
||||||
|
lispName = name;
|
||||||
|
lispDeps = [ selfLib ] ++ (tests.deps or []);
|
||||||
|
lispNativeDeps = native;
|
||||||
|
lispBinary = true;
|
||||||
|
tests = testDrv;
|
||||||
|
sbcl = sbclWith [ self ];
|
||||||
|
});
|
||||||
|
|
||||||
|
# 'bundled' creates a "library" that calls 'require' on a built-in
|
||||||
|
# package, such as any of SBCL's sb-* packages.
|
||||||
|
bundled = name: (makeOverridable library) {
|
||||||
|
inherit name;
|
||||||
|
srcs = lib.singleton (builtins.toFile "${name}.lisp" "(require '${name})");
|
||||||
|
};
|
||||||
|
|
||||||
|
# 'sbclWith' creates an image with the specified libraries /
|
||||||
|
# programs loaded.
|
||||||
|
sbclWith = deps:
|
||||||
|
let lispDeps = filter (d: !d.lispBinary) (allDeps deps);
|
||||||
|
in writeShellScriptBin "sbcl" ''
|
||||||
|
export LD_LIBRARY_PATH="${lib.makeLibraryPath (allNative [] lispDeps)}"
|
||||||
|
export LANG="C.UTF-8"
|
||||||
|
exec ${sbcl}/bin/sbcl ${lib.optionalString (deps != []) "--load ${writeText "load.lisp" (genLoadLisp lispDeps)}"} $@
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
library = makeOverridable library;
|
||||||
|
program = makeOverridable program;
|
||||||
|
sbclWith = makeOverridable sbclWith;
|
||||||
|
bundled = makeOverridable bundled;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{ lib }:
|
{ pkgs ? import <nixpkgs> {}, ... }:
|
||||||
|
|
||||||
with lib;
|
with pkgs.lib;
|
||||||
let
|
let
|
||||||
join-lines = concatStringsSep "\n";
|
join-lines = concatStringsSep "\n";
|
||||||
|
|
||||||
|
|
49
lib/ip.nix
49
lib/ip.nix
|
@ -1,45 +1,32 @@
|
||||||
{ lib }:
|
{ pkgs ? import <nixpkgs> {}, ... }:
|
||||||
|
|
||||||
with lib;
|
with pkgs.lib;
|
||||||
let
|
let
|
||||||
joinString = lib.concatStringsSep;
|
joinString = concatStringsSep;
|
||||||
|
|
||||||
pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1));
|
pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1));
|
||||||
|
|
||||||
in rec {
|
|
||||||
|
|
||||||
generateNBits = n: let
|
generateNBits = n: let
|
||||||
helper = n: c: if (c == n) then pow 2 c else (pow 2 c) + (helper n (c + 1));
|
helper = n: c: if (c == n) then pow 2 c else (pow 2 c) + (helper n (c + 1));
|
||||||
in if (n <= 0) then throw "Can't generate 0 or fewer bits" else helper (n - 1) 0;
|
in if (n <= 0) then throw "Can't generate 0 or fewer bits" else helper (n - 1) 0;
|
||||||
|
|
||||||
|
rightPadBits = int: bits: bitOr int (generateNBits bits);
|
||||||
|
|
||||||
reverseIpv4 = ip: joinString "." (reverseList (splitString "." ip));
|
reverseIpv4 = ip: joinString "." (reverseList (splitString "." ip));
|
||||||
|
|
||||||
intToBinaryList = int: let
|
intToBinaryList = int: let
|
||||||
helper = int: cur: let
|
helper = int: cur: let
|
||||||
curExp = pow 2 cur;
|
curExp = pow 2 cur;
|
||||||
in if (curExp > int) then
|
in if (curExp > int) then
|
||||||
[]
|
[]
|
||||||
else
|
else
|
||||||
[(if ((bitAnd curExp int) > 0) then 1 else 0)] ++ (helper int (cur + 1));
|
[(if ((bitAnd curExp int) > 0) then 1 else 0)] ++ (helper int (cur + 1));
|
||||||
in reverseList (helper int 0);
|
in reverseList (helper int 0);
|
||||||
|
|
||||||
leftShift = int: n: int * (pow 2 n);
|
leftShift = int: n: int * (pow 2 n);
|
||||||
|
|
||||||
rightShift = int: n: int / (pow 2 n);
|
rightShift = int: n: int / (pow 2 n);
|
||||||
|
|
||||||
ipv4ToInt = ip: let
|
|
||||||
els = map toInt (reverseList (splitString "." ip));
|
|
||||||
in foldr (a: b: a + b) 0 (imap0 (i: el: (leftShift el (i * 8))) els);
|
|
||||||
|
|
||||||
intToIpv4 = int: joinString "." (map (i: toString (bitAnd (rightShift int (i * 8)) 255)) [ 3 2 1 0 ]);
|
|
||||||
|
|
||||||
rightPadBits = int: bits: bitOr int (generateNBits bits);
|
|
||||||
|
|
||||||
maskFromV32Network = network: let
|
|
||||||
fullMask = ipv4ToInt "255.255.255.255";
|
|
||||||
insignificantBits = 32 - (getNetworkMask network);
|
|
||||||
in intToIpv4 (leftShift (rightShift fullMask insignificantBits) insignificantBits);
|
|
||||||
|
|
||||||
getNetworkMask = network: toInt (elemAt (splitString "/" network) 1);
|
getNetworkMask = network: toInt (elemAt (splitString "/" network) 1);
|
||||||
|
|
||||||
getNetworkBase = network: let
|
getNetworkBase = network: let
|
||||||
|
@ -47,10 +34,30 @@ in rec {
|
||||||
insignificantBits = 32 - (getNetworkMask network);
|
insignificantBits = 32 - (getNetworkMask network);
|
||||||
in intToIpv4 (leftShift (rightShift (ipv4ToInt ip) insignificantBits) insignificantBits);
|
in intToIpv4 (leftShift (rightShift (ipv4ToInt ip) insignificantBits) insignificantBits);
|
||||||
|
|
||||||
|
in rec {
|
||||||
|
|
||||||
|
ipv4ToInt = ip: let
|
||||||
|
els = map toInt (reverseList (splitString "." ip));
|
||||||
|
in foldr (a: b: a + b) 0 (imap0 (i: el: (leftShift el (i * 8))) els);
|
||||||
|
|
||||||
|
intToIpv4 = int: joinString "." (map (i: toString (bitAnd (rightShift int (i * 8)) 255)) [ 3 2 1 0 ]);
|
||||||
|
|
||||||
|
maskFromV32Network = network: let
|
||||||
|
fullMask = ipv4ToInt "255.255.255.255";
|
||||||
|
insignificantBits = 32 - (getNetworkMask network);
|
||||||
|
in intToIpv4 (leftShift (rightShift fullMask insignificantBits) insignificantBits);
|
||||||
|
|
||||||
networkMinIp = network: intToIpv4 (1 + (ipv4ToInt (getNetworkBase network)));
|
networkMinIp = network: intToIpv4 (1 + (ipv4ToInt (getNetworkBase network)));
|
||||||
|
|
||||||
networkMaxIp = network: intToIpv4 (rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network)));
|
networkMaxIp = network: intToIpv4 (rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network)));
|
||||||
|
|
||||||
# To avoid broadcast IP...
|
# To avoid broadcast IP...
|
||||||
networkMaxButOneIp = network: intToIpv4 ((rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network))) - 1);
|
networkMaxButOneIp = network: intToIpv4 ((rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network))) - 1);
|
||||||
|
|
||||||
|
ipv4OnNetwork = ip: network: let
|
||||||
|
ip-int = ipv4ToInt ip;
|
||||||
|
net-min = networkMinIp network;
|
||||||
|
net-max = networkMaxIp network;
|
||||||
|
in
|
||||||
|
(ip-int >= networkMinIp) && (ip-int <= networkMaxIp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ in stdenv.mkDerivation {
|
||||||
|
|
||||||
src = fetchgit {
|
src = fetchgit {
|
||||||
url = url;
|
url = url;
|
||||||
rev = "543df72f3962cf91b0e0508d15cdc083a3cd7ed4";
|
rev = "c552394e55816541a9426974c5f8e6f1f83bf195";
|
||||||
sha256 = "0hda1wjf9wd4rvxchdlxw0af3i2cvl5plg37ric3ckma6gfzkmm0";
|
sha256 = "0r61bwj5a2dvzl41cwdf2pdnhdsmp3kzfyxa5x5hsg67al6s7vi8";
|
||||||
fetchSubmodules = false;
|
fetchSubmodules = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
(format (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%"))
|
(format (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%"))
|
||||||
(exit 1))
|
(exit 1))
|
||||||
|
|
||||||
(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)")
|
(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)$")
|
||||||
(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)")
|
(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)$")
|
||||||
|
|
||||||
(define (make-verifier passwd-file)
|
(define (make-verifier passwd-file)
|
||||||
(let ((passwds (load passwd-file)))
|
(let ((passwds (load passwd-file)))
|
||||||
|
|
Loading…
Reference in New Issue