Merge branch 'master' into france
This commit is contained in:
@@ -6,26 +6,28 @@ with lib;
|
||||
let
|
||||
cfg = config.fudo.acme;
|
||||
|
||||
wwwRoot = hostname:
|
||||
pkgs.writeTextFile {
|
||||
name = "index.html";
|
||||
# wwwRoot = hostname:
|
||||
# pkgs.writeTextFile {
|
||||
# name = "index.html";
|
||||
|
||||
text = ''
|
||||
<html>
|
||||
<head>
|
||||
<title>${hostname}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${hostname}</title>
|
||||
</body>
|
||||
</html>
|
||||
'';
|
||||
destination = "/www";
|
||||
};
|
||||
# text = ''
|
||||
# <html>
|
||||
# <head>
|
||||
# <title>${hostname}</title>
|
||||
# </head>
|
||||
# <body>
|
||||
# <h1>${hostname}</title>
|
||||
# </body>
|
||||
# </html>
|
||||
# '';
|
||||
# destination = "/www";
|
||||
# };
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.acme = {
|
||||
enable = mkEnableOption "Fetch ACME certs for supplied local hostnames.";
|
||||
|
||||
hostnames = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of hostnames mapping to this host, for which to acquire SSL certificates.";
|
||||
@@ -35,9 +37,15 @@ in {
|
||||
"alt.hostname.com"
|
||||
];
|
||||
};
|
||||
|
||||
admin-address = mkOption {
|
||||
type = types.str;
|
||||
description = "The admin address in charge of these addresses.";
|
||||
default = "admin@fudo.org";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
@@ -49,13 +57,13 @@ in {
|
||||
{
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
root = (wwwRoot hostname) + ("/" + "www");
|
||||
# root = (wwwRoot hostname) + ("/" + "www");
|
||||
})
|
||||
cfg.hostnames);
|
||||
};
|
||||
|
||||
security.acme.certs = listToAttrs
|
||||
(map (hostname: nameValuePair hostname { email = "admin@fudo.org"; })
|
||||
(map (hostname: nameValuePair hostname { email = cfg.admin-address; })
|
||||
cfg.hostnames);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ in {
|
||||
};
|
||||
|
||||
smtp-password-file = mkOption {
|
||||
type = types.path;
|
||||
type = types.str;
|
||||
description = "Path to a file containing the password to use while connecting to the SMTP server.";
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ in {
|
||||
};
|
||||
|
||||
password-file = mkOption {
|
||||
type = types.path;
|
||||
type = types.str;
|
||||
description = "Path to file containing database password.";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ with lib;
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Domain of the
|
||||
Domain of the local network.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -52,5 +52,7 @@ with lib;
|
||||
description = "Email for administrator of this system.";
|
||||
default = "admin@fudo.org";
|
||||
};
|
||||
|
||||
enable-gui = mkEnableOption "Install desktop GUI software.";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.fudo.dns;
|
||||
|
||||
ip = import ../../lib/ip.nix { lib = lib; };
|
||||
|
||||
join-lines = concatStringsSep "\n";
|
||||
|
||||
hostOpts = { host, ...}: {
|
||||
options = {
|
||||
ip-addresses = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = ''
|
||||
A list of IPv4 addresses assigned to this host.
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
ipv6-addresses = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = ''
|
||||
A list of IPv6 addresses assigned to this host.
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
ssh-fingerprints = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = ''
|
||||
A list of DNS SSHFP records for this host.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
srvRecordOpts = with types; {
|
||||
options = {
|
||||
weight = mkOption {
|
||||
type = int;
|
||||
description = "Weight relative to other records.";
|
||||
default = 1;
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = int;
|
||||
description = "Priority to give this record.";
|
||||
default = 0;
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port to use while connecting to this service.";
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "Host that provides this service.";
|
||||
example = "my-host.my-domain.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
domainOpts = { domain, ... }: with types; {
|
||||
options = {
|
||||
hosts = mkOption {
|
||||
type = loaOf (submodule hostOpts);
|
||||
default = {};
|
||||
description = "A map of hostname to { host_attributes }.";
|
||||
};
|
||||
|
||||
dnssec = mkEnableOption "Enable DNSSEC security for this zone.";
|
||||
|
||||
mx = mkOption {
|
||||
type = listOf str;
|
||||
description = "A list of mail servers serving this domain.";
|
||||
default = [];
|
||||
};
|
||||
|
||||
srv-records = mkOption {
|
||||
type = attrsOf (attrsOf (listOf (submodule srvRecordOpts)));
|
||||
description = "Map of traffic type to srv records.";
|
||||
default = {};
|
||||
example = {
|
||||
tcp = {
|
||||
kerberos = {
|
||||
port = 88;
|
||||
host = "auth-host.my-domain.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
aliases = mkOption {
|
||||
type = loaOf str;
|
||||
default = {};
|
||||
description = "A mapping of host-alias => hostnames to add to DNS.";
|
||||
example = {
|
||||
"music" = "host.dom.com.";
|
||||
"mail" = "hostname";
|
||||
};
|
||||
};
|
||||
|
||||
extra-dns-records = mkOption {
|
||||
type = listOf str;
|
||||
description = "Records to be inserted verbatim into the DNS zone.";
|
||||
example = ["some-host IN CNAME base-host"];
|
||||
default = [];
|
||||
};
|
||||
|
||||
dmarc-report-address = mkOption {
|
||||
type = nullOr str;
|
||||
description = "The email to use to recieve DMARC reports, if any.";
|
||||
example = "admin-user@domain.com";
|
||||
default = null;
|
||||
};
|
||||
|
||||
default-host = mkOption {
|
||||
type = nullOr str;
|
||||
description = "IP of the host which will act as the default server for this domain, if any.";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hostARecords = host: data:
|
||||
join-lines ((map (ip: "${host} IN A ${ip}") data.ip-addresses) ++
|
||||
(map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses));
|
||||
|
||||
makeSrvRecords = protocol: type: records:
|
||||
join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${toString record.host}.")
|
||||
records);
|
||||
|
||||
makeSrvProtocolRecords = protocol: types: join-lines (mapAttrsToList (makeSrvRecords protocol) types);
|
||||
|
||||
cnameRecord = alias: host: "${alias} IN CNAME ${host}";
|
||||
|
||||
hostSshFpRecords = host: data: join-lines (map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints);
|
||||
|
||||
mxRecords = mxs:
|
||||
concatStringsSep "\n"
|
||||
(map (mx: "@ IN MX 10 ${mx}.") mxs);
|
||||
|
||||
dmarcRecord = dmarc-email:
|
||||
optionalString (dmarc-email != null)
|
||||
''_dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"'';
|
||||
|
||||
nsRecords = ns-hosts:
|
||||
join-lines ((mapAttrsToList (host: _: "@ IN NS ${host}.") ns-hosts) ++
|
||||
(mapAttrsToList (host: ip: "${host} IN A ${ip}") ns-hosts));
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.dns = with types; {
|
||||
enable = mkEnableOption "Enable master DNS services.";
|
||||
|
||||
# FIXME: This should allow for AAAA addresses too...
|
||||
dns-hosts = mkOption {
|
||||
type = loaOf str;
|
||||
description = "Map of domain nameserver FQDNs to IP.";
|
||||
example = { "ns1.domain.com" = "1.1.1.1"; };
|
||||
};
|
||||
|
||||
domains = mkOption {
|
||||
type = loaOf (submodule domainOpts);
|
||||
default = {};
|
||||
description = "A map of domain to domain options.";
|
||||
};
|
||||
|
||||
listen-ips = mkOption {
|
||||
type = listOf str;
|
||||
description = "A list of IPs on which to listen for DNS queries.";
|
||||
example = ["1.2.3.4"];
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.nsd = {
|
||||
enable = true;
|
||||
identity = "procul.informis.land";
|
||||
interfaces = cfg.listen-ips;
|
||||
zones = mapAttrs' (dom: dom-cfg:
|
||||
nameValuePair "${dom}." {
|
||||
dnssec = dom-cfg.dnssec;
|
||||
|
||||
data = ''
|
||||
$ORIGIN ${dom}.
|
||||
$TTL 12h
|
||||
|
||||
@ IN SOA ns1.${dom}. hostmaster.${dom}. (
|
||||
${toString builtins.currentTime}
|
||||
5m
|
||||
2m
|
||||
6w
|
||||
5m)
|
||||
|
||||
${optionalString (dom-cfg.default-host != null) "@ IN A ${dom-cfg.default-host}"}
|
||||
|
||||
${mxRecords dom-cfg.mx}
|
||||
|
||||
$TTL 6h
|
||||
|
||||
${nsRecords cfg.dns-hosts}
|
||||
|
||||
${dmarcRecord dom-cfg.dmarc-report-address}
|
||||
|
||||
${join-lines (mapAttrsToList makeSrvProtocolRecords dom-cfg.srv-records)}
|
||||
${join-lines (mapAttrsToList hostARecords dom-cfg.hosts)}
|
||||
${join-lines (mapAttrsToList hostSshFpRecords dom-cfg.hosts)}
|
||||
${join-lines (mapAttrsToList cnameRecord dom-cfg.aliases)}
|
||||
${join-lines dom-cfg.extra-dns-records}
|
||||
'';
|
||||
}) cfg.domains;
|
||||
};
|
||||
};
|
||||
}
|
||||
+228
-48
@@ -1,96 +1,276 @@
|
||||
# UNFINISHED!
|
||||
#
|
||||
# The plan is to bootstrap a local network config: DNS, DHCP, etc.
|
||||
|
||||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
hostOpts = { config, ... }: {
|
||||
options = {
|
||||
ipv6Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V6 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
cfg = config.fudo.local-network;
|
||||
|
||||
ipv4Address = mkOption {
|
||||
join-lines = concatStringsSep "\n";
|
||||
|
||||
ip = import ../../lib/ip.nix { lib = lib; };
|
||||
|
||||
hostOpts = { hostname, ... }: {
|
||||
options = {
|
||||
ip-address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V4 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
|
||||
macAddress = mkOption {
|
||||
mac-address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The MAC address of a given host, if desired for IP reservation.
|
||||
'';
|
||||
};
|
||||
|
||||
ssh-fingerprints = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of DNS SSHFP records for this host.";
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
localNameServerOpts = { config, ... }: {
|
||||
traceout = out: builtins.trace out out;
|
||||
|
||||
srvRecordOpts = with types; {
|
||||
options = {
|
||||
ipv6Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V6 IP of this nameserver, if any.
|
||||
'';
|
||||
weight = mkOption {
|
||||
type = int;
|
||||
description = "Weight relative to other records.";
|
||||
default = 1;
|
||||
};
|
||||
|
||||
ipv4Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V4 IP of this nameserver, if any.
|
||||
'';
|
||||
priority = mkOption {
|
||||
type = int;
|
||||
description = "Priority to give this record.";
|
||||
default = 0;
|
||||
};
|
||||
|
||||
ipv4ReverseDomain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The domain of the IPv4 address range for which this nameserver is responsible.
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port to use when connecting.";
|
||||
};
|
||||
|
||||
Eg: 0.10.in-addr.arpa
|
||||
'';
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "Host to contact for this service.";
|
||||
example = "my-host.my-domain.com.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
options = {
|
||||
options.fudo.local-network = {
|
||||
|
||||
fudo.localNetwork.hosts = mkOption {
|
||||
type = types.listOf (submodule hostOpts);
|
||||
enable = mkEnableOption "Enable local network configuration (DHCP & DNS).";
|
||||
|
||||
hosts = mkOption {
|
||||
type = with types; loaOf (submodule hostOpts);
|
||||
default = {};
|
||||
description = ''
|
||||
A map of hostname => { host_attributes }.
|
||||
'';
|
||||
description = "A map of hostname => { host_attributes }.";
|
||||
};
|
||||
|
||||
fudo.localNetwork.domain = mkOption {
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "The domain to use for the local network.";
|
||||
};
|
||||
|
||||
dns-servers = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of domain name server to use for the local network.";
|
||||
};
|
||||
|
||||
dhcp-interfaces = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of interfaces on which to serve DHCP.";
|
||||
};
|
||||
|
||||
dns-serve-ips = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of IPs on which to server DNS queries.";
|
||||
};
|
||||
|
||||
gateway = mkOption {
|
||||
type = types.str;
|
||||
description = "The gateway to use for the local network.";
|
||||
};
|
||||
|
||||
aliases = mkOption {
|
||||
type = with types; loaOf str;
|
||||
default = {};
|
||||
description = "A mapping of host-alias => hostname to use on the local network.";
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "Network to treat as local.";
|
||||
};
|
||||
|
||||
enable-reverse-mappings = mkOption {
|
||||
type = types.bool;
|
||||
description = "Genereate PTR reverse lookup records.";
|
||||
default = false;
|
||||
};
|
||||
|
||||
dhcp-dynamic-network = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The domain to use for the local network.
|
||||
The network from which to dynamically allocate IPs via DHCP.
|
||||
|
||||
Must be a subnet of <network>.
|
||||
'';
|
||||
};
|
||||
|
||||
fudo.localNetwork.hostAliases = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
recursive-resolver = mkOption {
|
||||
type = types.str;
|
||||
description = "DNS nameserver to use for recursive resolution.";
|
||||
};
|
||||
|
||||
server-ip = mkOption {
|
||||
type = types.str;
|
||||
description = "IP of the DNS server.";
|
||||
};
|
||||
|
||||
extra-dns-records = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "Records to be inserted verbatim into the DNS zone.";
|
||||
example = ["some-host IN CNAME other-host"];
|
||||
default = [];
|
||||
};
|
||||
|
||||
srv-records = mkOption {
|
||||
type = with types; attrsOf (attrsOf (listOf (submodule srvRecordOpts)));
|
||||
description = "Map of traffic type to srv records.";
|
||||
default = {};
|
||||
description = ''
|
||||
A mapping of hostAlias => hostName to use on the local network.
|
||||
example = {
|
||||
tcp = {
|
||||
kerberos = {
|
||||
port = 88;
|
||||
host = "auth-host.my-domain.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
search-domains = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of domains to search for DNS names.";
|
||||
example = ["my-domain.com" "other-domain.com"];
|
||||
default = [];
|
||||
};
|
||||
|
||||
# TODO: srv records
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.dhcpd4 = {
|
||||
enable = true;
|
||||
|
||||
machines = mapAttrsToList (hostname: hostOpts: {
|
||||
ethernetAddress = hostOpts.mac-address;
|
||||
hostName = hostname;
|
||||
ipAddress = hostOpts.ip-address;
|
||||
}) cfg.hosts;
|
||||
|
||||
interfaces = cfg.dhcp-interfaces;
|
||||
|
||||
extraConfig = ''
|
||||
subnet ${ip.getNetworkBase cfg.network} netmask ${ip.maskFromV32Network cfg.network} {
|
||||
authoritative;
|
||||
option subnet-mask ${ip.maskFromV32Network cfg.network};
|
||||
option broadcast-address ${ip.networkMaxIp cfg.network};
|
||||
option routers ${cfg.gateway};
|
||||
option domain-name-servers ${concatStringsSep " " cfg.dns-servers};
|
||||
option domain-name "${cfg.domain}";
|
||||
option domain-search ${join-lines (map (dom: "\"${dom}\"") ([cfg.domain] ++ cfg.search-domains))};
|
||||
range ${ip.networkMinIp cfg.dhcp-dynamic-network} ${ip.networkMaxButOneIp cfg.dhcp-dynamic-network};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
fudo.localNetwork.localNameServer = mkOption {
|
||||
type = (submodule localNameServerOpts);
|
||||
description = ''
|
||||
The master nameserver of the local network.
|
||||
'';
|
||||
services.bind = let
|
||||
blockHostsToZone = block: hosts-data: {
|
||||
master = true;
|
||||
name = "${block}.in-addr.arpa";
|
||||
file = let
|
||||
# We should add these...but need a domain to assign them to.
|
||||
# ip-last-el = ip: toInt (last (splitString "." ip));
|
||||
# used-els = map (host-data: ip-last-el host-data.ip-address) hosts-data;
|
||||
# unused-els = subtractLists used-els (map toString (range 1 255));
|
||||
|
||||
in pkgs.writeText "db.${block}-zone" ''
|
||||
$ORIGIN ${block}.in-addr.arpa.
|
||||
$TTL 1h
|
||||
|
||||
@ IN SOA ns1.${cfg.domain}. hostmaster.${cfg.domain}. (
|
||||
${toString builtins.currentTime}
|
||||
1800
|
||||
900
|
||||
604800
|
||||
1800)
|
||||
|
||||
@ IN NS ns1.${cfg.domain}.
|
||||
|
||||
${join-lines (map hostPtrRecord hosts-data)}
|
||||
'';
|
||||
};
|
||||
|
||||
ipToBlock = ip: concatStringsSep "." (reverseList (take 3 (splitString "." ip)));
|
||||
compactHosts = mapAttrsToList (host: data: data // { host = host; }) cfg.hosts;
|
||||
hostsByBlock = groupBy (host-data: ipToBlock host-data.ip-address) compactHosts;
|
||||
hostPtrRecord = host-data:
|
||||
"${last (splitString "." host-data.ip-address)} IN PTR ${host-data.host}.${cfg.domain}.";
|
||||
|
||||
blockZones = mapAttrsToList blockHostsToZone hostsByBlock;
|
||||
|
||||
hostARecord = host: data: "${host} IN A ${data.ip-address}";
|
||||
hostSshFpRecords = host: data: join-lines (map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints);
|
||||
cnameRecord = alias: host: "${alias} IN CNAME ${host}";
|
||||
|
||||
makeSrvRecords = protocol: type: records:
|
||||
join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${record.host}.")
|
||||
records);
|
||||
|
||||
makeSrvProtocolRecords = protocol: types: join-lines (mapAttrsToList (makeSrvRecords protocol) types);
|
||||
|
||||
in {
|
||||
enable = true;
|
||||
cacheNetworks = [ cfg.network "localhost" "localnets" ];
|
||||
forwarders = [ cfg.recursive-resolver ];
|
||||
listenOn = cfg.dns-serve-ips;
|
||||
zones = [
|
||||
{
|
||||
master = true;
|
||||
name = cfg.domain;
|
||||
file = pkgs.writeText "${cfg.domain}-zone" ''
|
||||
@ IN SOA ns1.${cfg.domain}. hostmaster.${cfg.domain}. (
|
||||
${toString builtins.currentTime}
|
||||
5m
|
||||
2m
|
||||
6w
|
||||
5m)
|
||||
|
||||
$TTL 1h
|
||||
|
||||
@ IN NS ns1.${cfg.domain}.
|
||||
|
||||
$ORIGIN ${cfg.domain}.
|
||||
|
||||
$TTL 30m
|
||||
|
||||
ns1 IN A ${cfg.server-ip}
|
||||
${join-lines (mapAttrsToList hostARecord cfg.hosts)}
|
||||
${join-lines (mapAttrsToList hostSshFpRecords cfg.hosts)}
|
||||
${join-lines (mapAttrsToList cnameRecord cfg.aliases)}
|
||||
${join-lines cfg.extra-dns-records}
|
||||
${join-lines (mapAttrsToList makeSrvProtocolRecords cfg.srv-records)}
|
||||
'';
|
||||
}
|
||||
] ++ blockZones;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -160,21 +160,21 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users = {
|
||||
${container-mail-user} = {
|
||||
isSystemUser = true;
|
||||
uid = container-mail-user-id;
|
||||
group = "mailer";
|
||||
};
|
||||
};
|
||||
# users = {
|
||||
# users = {
|
||||
# ${container-mail-user} = {
|
||||
# isSystemUser = true;
|
||||
# uid = container-mail-user-id;
|
||||
# group = "mailer";
|
||||
# };
|
||||
# };
|
||||
|
||||
groups = {
|
||||
${container-mail-group} = {
|
||||
members = ["mailer"];
|
||||
};
|
||||
};
|
||||
};
|
||||
# groups = {
|
||||
# ${container-mail-group} = {
|
||||
# members = ["mailer"];
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
fudo.mail-server = {
|
||||
enable = true;
|
||||
@@ -193,10 +193,12 @@ in rec {
|
||||
dovecot = {
|
||||
ssl-certificate = "/etc/${container-dovecot-cert}";
|
||||
ssl-private-key = "/etc/dovecot-certs/key.pem";
|
||||
ldap-ca = "/etc/${container-fudo-ca-cert}";
|
||||
ldap-urls = cfg.dovecot.ldap-urls;
|
||||
ldap-reader-dn = cfg.dovecot.ldap-reader-dn;
|
||||
ldap-reader-passwd = cfg.dovecot.ldap-reader-passwd;
|
||||
ldap = {
|
||||
# ca = "/etc/${container-fudo-ca-cert}";
|
||||
server-urls = cfg.dovecot.ldap.server-urls;
|
||||
reader-dn = cfg.dovecot.ldap.reader-dn;
|
||||
reader-passwd = cfg.dovecot.ldap.reader-passwd;
|
||||
};
|
||||
};
|
||||
|
||||
local-domains = cfg.local-domains;
|
||||
|
||||
@@ -104,6 +104,7 @@ in {
|
||||
user-aliases = mkOption {
|
||||
type = with types; loaOf(listOf str);
|
||||
description = "A map of real user to list of aliases.";
|
||||
default = {};
|
||||
example = {
|
||||
someuser = ["alias0" "alias1"];
|
||||
};
|
||||
@@ -167,4 +168,22 @@ in {
|
||||
./mail/rspamd.nix
|
||||
./mail/clamav.nix
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users = {
|
||||
users = {
|
||||
mailuser = {
|
||||
isSystemUser = true;
|
||||
uid = cfg.mail-user-id;
|
||||
group = "mailgroup";
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
mailgroup = {
|
||||
members = ["mailuser"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,57 +23,85 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
ldap-conf = filename: uris:
|
||||
pkgs.writeText filename ''
|
||||
uris = ${concatStringsSep " " uris}
|
||||
ldap-conf = filename: config:
|
||||
let
|
||||
ssl-config = if config.ca == null then ''
|
||||
tls = no
|
||||
tls_require_cert = try
|
||||
'' else ''
|
||||
tls_ca_cert_file = ${config.ca}
|
||||
tls = yes
|
||||
tls_require_cert = try
|
||||
'';
|
||||
|
||||
in
|
||||
pkgs.writeText filename ''
|
||||
uris = ${concatStringsSep " " config.server-urls}
|
||||
ldap_version = 3
|
||||
dn = ${cfg.dovecot.ldap-reader-dn}
|
||||
dnpass = ${cfg.dovecot.ldap-reader-passwd}
|
||||
dn = ${config.reader-dn}
|
||||
dnpass = ${config.reader-passwd}
|
||||
auth_bind = yes
|
||||
auth_bind_userdn = uid=%u,ou=members,dc=fudo,dc=org
|
||||
base = dc=fudo,dc=org
|
||||
# tls_ca_cert_file = ${cfg.dovecot.ldap-ca}
|
||||
# FIXME: turn back on when certs work
|
||||
tls = no
|
||||
tls_require_cert = try
|
||||
${ssl-config}
|
||||
'';
|
||||
|
||||
ldap-passwd-entry = ldap-config: ''
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${ldap-conf "ldap-passdb.conf" ldap-config}
|
||||
}
|
||||
'';
|
||||
|
||||
ldapOpts = {
|
||||
options = with types; {
|
||||
ca = mkOption {
|
||||
type = nullOr str;
|
||||
description = "The path to the CA cert used to sign the LDAP server certificate.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
server-urls = mkOption {
|
||||
type = listOf str;
|
||||
description = "A list of LDAP server URLs used for authentication.";
|
||||
};
|
||||
|
||||
reader-dn = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
DN to use for reading user information. Needs access to homeDirectory,
|
||||
uidNumber, gidNumber, and uid, but not password attributes.
|
||||
'';
|
||||
};
|
||||
|
||||
reader-passwd = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
Password for the user specified in ldap-reader-dn.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dovecot-user = config.services.dovecot2.user;
|
||||
|
||||
in {
|
||||
options.fudo.mail-server.dovecot = {
|
||||
options.fudo.mail-server.dovecot = with types; {
|
||||
ssl-private-key = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Location of the server SSL private key.";
|
||||
};
|
||||
|
||||
ssl-certificate = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
description = "Location of the server SSL certificate.";
|
||||
};
|
||||
|
||||
ldap-ca = mkOption {
|
||||
type = types.str;
|
||||
description = "The path to the CA cert used to sign the LDAP server certificate.";
|
||||
};
|
||||
|
||||
ldap-urls = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "The urls of LDAP servers.";
|
||||
};
|
||||
|
||||
ldap-reader-dn = mkOption {
|
||||
type = types.str;
|
||||
ldap = mkOption {
|
||||
type = nullOr (submodule ldapOpts);
|
||||
default = null;
|
||||
description = ''
|
||||
DN to use for reading user information. Needs access to homeDirectory,
|
||||
uidNumber, gidNumber, and uid, but not password attributes.
|
||||
'';
|
||||
};
|
||||
|
||||
ldap-reader-passwd = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Password for the user specified in ldap-reader-dn.
|
||||
LDAP auth server configuration. If omitted, the server will use local authentication.
|
||||
'';
|
||||
};
|
||||
};
|
||||
@@ -93,8 +121,7 @@ in {
|
||||
enableImap = true;
|
||||
enableLmtp = true;
|
||||
enablePop3 = true;
|
||||
enablePAM = false;
|
||||
|
||||
enablePAM = cfg.dovecot.ldap == null;
|
||||
|
||||
createMailUser = true;
|
||||
|
||||
@@ -124,14 +151,16 @@ in {
|
||||
extraConfig = ''
|
||||
#Extra Config
|
||||
|
||||
# The prometheus exporter still expects an older style of metrics
|
||||
mail_plugins = $mail_plugins old_stats
|
||||
service old-stats {
|
||||
unix_listener old-stats {
|
||||
user = dovecot-exporter
|
||||
group = dovecot-exporter
|
||||
${optionalString cfg.monitoring ''
|
||||
# The prometheus exporter still expects an older style of metrics
|
||||
mail_plugins = $mail_plugins old_stats
|
||||
service old-stats {
|
||||
unix_listener old-stats {
|
||||
user = dovecot-exporter
|
||||
group = dovecot-exporter
|
||||
}
|
||||
}
|
||||
}
|
||||
''}
|
||||
|
||||
${lib.optionalString cfg.debug ''
|
||||
mail_debug = yes
|
||||
@@ -170,15 +199,15 @@ in {
|
||||
}
|
||||
|
||||
# Drop privs, since all mail is owned by one user
|
||||
user = ${cfg.mail-user}
|
||||
group = ${cfg.mail-group}
|
||||
# user = ${cfg.mail-user}
|
||||
# group = ${cfg.mail-group}
|
||||
user = root
|
||||
}
|
||||
|
||||
auth_mechanisms = login plain
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${ldap-conf "ldap-passdb.conf" cfg.dovecot.ldap-urls}
|
||||
}
|
||||
|
||||
${optionalString (cfg.dovecot.ldap != null)
|
||||
(ldap-passwd-entry cfg.dovecot.ldap)}
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=${toString cfg.mail-user-id} home=${cfg.mail-directory}/%u
|
||||
@@ -189,8 +218,18 @@ in {
|
||||
unix_listener auth {
|
||||
mode = 0660
|
||||
user = "${config.services.postfix.user}"
|
||||
group = ${config.services.postfix.group}
|
||||
group = ${cfg.mail-group}
|
||||
}
|
||||
|
||||
unix_listener auth-userdb {
|
||||
mode = 0660
|
||||
user = "${config.services.postfix.user}"
|
||||
group = ${cfg.mail-group}
|
||||
}
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
user = root
|
||||
}
|
||||
|
||||
namespace inbox {
|
||||
@@ -236,7 +275,8 @@ in {
|
||||
done
|
||||
chown -R '${dovecot-user}:${cfg.mail-group}' '${state-directory}/imap_sieve'
|
||||
|
||||
chown ${cfg.mail-user}:${cfg.mail-group} ${cfg.mail-directory}
|
||||
chown '${cfg.mail-user}:${cfg.mail-group}' ${cfg.mail-directory}
|
||||
chmod g+w ${cfg.mail-directory}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@ let
|
||||
|
||||
cfg = config.fudo.mail-server;
|
||||
|
||||
# The final newline is important
|
||||
write-entries = filename: entries:
|
||||
let
|
||||
entries-string = (concatStringsSep "\n" entries);
|
||||
in builtins.toFile filename ''
|
||||
${entries-string}
|
||||
'';
|
||||
|
||||
make-user-aliases = entries:
|
||||
concatStringsSep "\n"
|
||||
(mapAttrsToList (user: aliases:
|
||||
@@ -39,25 +47,22 @@ let
|
||||
/^User-Agent:/ IGNORE
|
||||
/^X-Enigmail:/ IGNORE
|
||||
'');
|
||||
|
||||
blacklist-postfix-entry = sender: "${sender} REJECT";
|
||||
blacklist-postfix-file = entries:
|
||||
concatStringsSep "\n" (map blacklist-postfix-entry entries);
|
||||
sender-blacklist-file = builtins.toFile "reject_senders"
|
||||
(blacklist-postfix-file cfg.sender-blacklist);
|
||||
recipient-blacklist-file = builtins.toFile "reject_recipients"
|
||||
(blacklist-postfix-file cfg.recipient-blacklist);
|
||||
blacklist-postfix-file = filename: entries:
|
||||
write-entries filename entries;
|
||||
sender-blacklist-file = blacklist-postfix-file "reject_senders"
|
||||
(map blacklist-postfix-entry cfg.sender-blacklist);
|
||||
recipient-blacklist-file = blacklist-postfix-file "reject_recipients"
|
||||
(map blacklist-postfix-entry cfg.recipient-blacklist);
|
||||
|
||||
# A list of domains for which we accept mail
|
||||
virtual-mailbox-map-file = builtins.toFile "virtual_mailbox_map"
|
||||
(concatStringsSep "\n"
|
||||
(map (domain: "@${domain} OK") (cfg.local-domains ++ [cfg.domain])));
|
||||
virtual-mailbox-map-file = write-entries "virtual_mailbox_map"
|
||||
(map (domain: "@${domain} OK") (cfg.local-domains ++ [cfg.domain]));
|
||||
|
||||
sender-login-map-file = let
|
||||
escapeDot = (str: replaceStrings ["."] ["\\."] str);
|
||||
in builtins.toFile "sender_login_maps"
|
||||
(concatStringsSep "\n"
|
||||
(map (domain: "/^(.*)@${escapeDot domain}$/ \${1}") (cfg.local-domains ++ [cfg.domain])));
|
||||
in write-entries "sender_login_maps"
|
||||
(map (domain: "/^(.*)@${escapeDot domain}$/ \${1}") (cfg.local-domains ++ [cfg.domain]));
|
||||
|
||||
mapped-file = name: "hash:/var/lib/postfix/conf/${name}";
|
||||
|
||||
@@ -106,9 +111,8 @@ in {
|
||||
origin = cfg.domain;
|
||||
hostname = cfg.hostname;
|
||||
destination = ["localhost" "localhost.localdomain"];
|
||||
# destination = ["localhost" "localhost.localdomain"] ++
|
||||
# (map (domain: "localhost.${domain}") cfg.local-domains) ++
|
||||
# cfg.local-domains;
|
||||
# destination = ["localhost" "localhost.localdomain" cfg.hostname] ++
|
||||
# cfg.local-domains;;
|
||||
|
||||
enableHeaderChecks = true;
|
||||
enableSmtp = true;
|
||||
@@ -133,7 +137,7 @@ in {
|
||||
sslKey = cfg.postfix.ssl-private-key;
|
||||
|
||||
config = {
|
||||
virtual_mailbox_domains = builtins.toFile "domain-list" (concatStringsSep "\n" cfg.local-domains);
|
||||
virtual_mailbox_domains = cfg.local-domains ++ [cfg.domain];
|
||||
# virtual_mailbox_base = "${cfg.mail-directory}/";
|
||||
virtual_mailbox_maps = mapped-file "virtual_mailbox_map";
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
{ lib, pkgs, config, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.secure-dns-proxy;
|
||||
|
||||
in {
|
||||
options.fudo.secure-dns-proxy = {
|
||||
enable = mkEnableOption "Enable a DNS server using an encrypted upstream source.";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "Port on which to listen for DNS queries.";
|
||||
default = 53;
|
||||
};
|
||||
|
||||
upstream-dns = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = ''
|
||||
The upstream DNS services to use, in a format useable by dnsproxy.
|
||||
|
||||
See: https://github.com/AdguardTeam/dnsproxy
|
||||
'';
|
||||
default = ["https://cloudflare-dns.com/dns-query"];
|
||||
};
|
||||
|
||||
bootstrap-dns = mkOption {
|
||||
type = types.str;
|
||||
description = "A simple DNS server from which HTTPS DNS can be bootstrapped, if necessary.";
|
||||
default = "1.1.1.1";
|
||||
};
|
||||
|
||||
listen-ips = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of local IP addresses on which to listen.";
|
||||
default = ["0.0.0.0"];
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
dnsproxy
|
||||
];
|
||||
|
||||
systemd.services.secure-dns-proxy = {
|
||||
enable = true;
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
description = "DNS Proxy for secure DNS lookups";
|
||||
serviceConfig = let
|
||||
upstreams = map (upstream: "-u ${upstream}") cfg.upstream-dns;
|
||||
upstream-line = concatStringsSep " " upstreams;
|
||||
listen-line = concatStringsSep " "
|
||||
(map (listen: "-l ${listen}") cfg.listen-ips);
|
||||
cmd = "${pkgs.dnsproxy}/bin/dnsproxy -p ${toString cfg.port} ${upstream-line} ${listen-line} -b ${cfg.bootstrap-dns}";
|
||||
|
||||
in {
|
||||
ExecStart = cmd;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.slynk;
|
||||
|
||||
initScript = port: load-paths: let
|
||||
load-path-string =
|
||||
concatStringsSep " " (map (path: "\"${path}\"") load-paths);
|
||||
in pkgs.writeText "slynk.lisp" ''
|
||||
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
|
||||
(ql:quickload :slynk)
|
||||
(setf asdf:*central-registry*
|
||||
(append asdf:*central-registry*
|
||||
(list ${load-path-string})))
|
||||
(slynk:create-server :port ${toString port} :dont-close t)
|
||||
(dolist (var '("LD_LIBRARY_PATH"))
|
||||
(format t "~S: ~S~%" var (sb-unix::posix-getenv var)))
|
||||
|
||||
(loop (sleep 60))
|
||||
'';
|
||||
|
||||
lisp-libs = with pkgs.lispPackages; [
|
||||
alexandria
|
||||
asdf-package-system
|
||||
asdf-system-connections
|
||||
cl_plus_ssl
|
||||
cl-ppcre
|
||||
quicklisp
|
||||
quri
|
||||
uiop
|
||||
usocket
|
||||
];
|
||||
|
||||
in {
|
||||
options.fudo.slynk = {
|
||||
enable = mkEnableOption "Enable Slynk emacs common lisp server.";
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "Port on which to open a Slynk server.";
|
||||
default = 4005;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.user.services.slynk = {
|
||||
description = "Slynk Common Lisp server.";
|
||||
|
||||
serviceConfig = let
|
||||
load-paths = (map (pkg: "${pkg}/lib/common-lisp/") lisp-libs);
|
||||
in {
|
||||
ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init";
|
||||
ExecStart = "${pkgs.sbcl}/bin/sbcl --load ${initScript cfg.port load-paths}";
|
||||
Restart = "on-failure";
|
||||
PIDFile = "/run/slynk.$USERNAME.pid";
|
||||
};
|
||||
|
||||
path = with pkgs; [
|
||||
gcc
|
||||
glibc # for getent
|
||||
file
|
||||
];
|
||||
|
||||
environment = {
|
||||
LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -186,7 +186,7 @@ let
|
||||
};
|
||||
|
||||
password-file = mkOption {
|
||||
type = types.path;
|
||||
type = types.str;
|
||||
description = "Password to use when connecting to the database.";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.informis.cl-gemini;
|
||||
|
||||
lisp-libs = with pkgs.lispPackages; [
|
||||
asdf-package-system
|
||||
asdf-system-connections
|
||||
alexandria
|
||||
asdf-package-system
|
||||
asdf-system-connections
|
||||
cl_plus_ssl
|
||||
cl-ppcre
|
||||
quicklisp
|
||||
quri
|
||||
uiop
|
||||
usocket
|
||||
];
|
||||
|
||||
launchServer = ip: port: root: public-dir: key: cert: slynk-port: feeds-string: textfiles-archive:
|
||||
pkgs.writeText "launch-server.lisp" ''
|
||||
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
|
||||
(ql:quickload :slynk)
|
||||
(ql:quickload :cl-gemini)
|
||||
${optionalString (slynk-port != null) "(slynk:create-server :port ${toString slynk-port} :dont-close t)"}
|
||||
${feeds-string}
|
||||
(cl-gemini:start-gemini-server "${ip}" "${key}" "${cert}"
|
||||
:port ${toString port}
|
||||
:document-root "${root}"
|
||||
:textfiles-root "${textfiles-archive}"
|
||||
:file-cmd "${pkgs.file}/bin/file"
|
||||
:log-stream *standard-output*
|
||||
:threaded t
|
||||
:separate-thread t)
|
||||
(loop (sleep 60))
|
||||
'';
|
||||
|
||||
sbcl-with-ssl = pkgs.sbcl.overrideAttrs (oldAttrs: rec {
|
||||
extraLibs = with pkgs; [
|
||||
openssl_1_1.dev
|
||||
];
|
||||
});
|
||||
|
||||
feedOpts = with types; {
|
||||
options = {
|
||||
url = mkOption {
|
||||
type = str;
|
||||
description = "Base URI of the feed, i.e. the URI corresponding to the feed path.";
|
||||
example = "gemini://my.server/path/to/feedfiles";
|
||||
};
|
||||
|
||||
title = mkOption {
|
||||
type = str;
|
||||
description = "Title of given feed.";
|
||||
example = "My Fancy Feed";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = str;
|
||||
description = "Path to Gemini files making up the feed.";
|
||||
example = "/path/to/feed";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
register-feed = name: opts: ''
|
||||
(cl-gemini:register-feed :name "${name}" :title "${opts.title}" :path "${opts.path}" :base-uri "${opts.url}")'';
|
||||
|
||||
register-feeds = feeds:
|
||||
concatStringsSep "\n"
|
||||
(mapAttrsToList register-feed feeds);
|
||||
|
||||
|
||||
in {
|
||||
options.informis.cl-gemini = with types; {
|
||||
enable = mkEnableOption "Enable the cl-gemini server.";
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port on which to serve Gemini traffic.";
|
||||
default = 1965;
|
||||
};
|
||||
|
||||
server-ip = mkOption {
|
||||
type = str;
|
||||
description = "IP on which to serve Gemini traffic.";
|
||||
example = "1.2.3.4";
|
||||
};
|
||||
|
||||
document-root = mkOption {
|
||||
type = str;
|
||||
description = "Root at which to look for gemini files.";
|
||||
example = "/my/gemini/root";
|
||||
};
|
||||
|
||||
user-public = mkOption {
|
||||
type = str;
|
||||
description = "Subdirectory of user homes to check for gemini files.";
|
||||
default = "gemini-public";
|
||||
};
|
||||
|
||||
ssl-private-key = mkOption {
|
||||
type = path;
|
||||
description = "Path to the pem-encoded server private key.";
|
||||
example = /path/to/secret/key.pem;
|
||||
};
|
||||
|
||||
ssl-certificate = mkOption {
|
||||
type = path;
|
||||
description = "Path to the pem-encoded server public certificate.";
|
||||
example = /path/to/cert.pem;
|
||||
};
|
||||
|
||||
slynk-port = mkOption {
|
||||
type = nullOr port;
|
||||
description = "Port on which to open a slynk server, if any.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
feeds = mkOption {
|
||||
type = loaOf (submodule feedOpts);
|
||||
description = "Feeds to generate and make available (as eg. /feed/name.xml).";
|
||||
example = {
|
||||
diary = {
|
||||
title = "My Diary";
|
||||
path = "/path/to/my/gemfiles/";
|
||||
url = "gemini://my.host/blog-path/";
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
textfiles-archive = mkOption {
|
||||
type = str;
|
||||
description = "A path containing only gemini & text files.";
|
||||
example = "/path/to/textfiles/";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
cl-gemini
|
||||
];
|
||||
|
||||
users.users = {
|
||||
cl-gemini = {
|
||||
isSystemUser = true;
|
||||
group = "nogroup";
|
||||
createHome = true;
|
||||
home = "/var/lib/cl-gemini";
|
||||
};
|
||||
};
|
||||
|
||||
environment.etc = {
|
||||
"cl-gemini/key.pem" = {
|
||||
mode = "0400";
|
||||
user = "cl-gemini";
|
||||
source = cfg.ssl-private-key;
|
||||
};
|
||||
|
||||
"cl-gemini/cert.pem" = {
|
||||
mode = "0444";
|
||||
user = "cl-gemini";
|
||||
source = cfg.ssl-certificate;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.cl-gemini = {
|
||||
description = "cl-gemini Gemini server (https://gemini.circumlunar.space/)";
|
||||
|
||||
serviceConfig = let
|
||||
feed-registrations = register-feeds cfg.feeds;
|
||||
in {
|
||||
ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init";
|
||||
ExecStart = "${sbcl-with-ssl}/bin/sbcl --load ${
|
||||
launchServer
|
||||
cfg.server-ip
|
||||
cfg.port
|
||||
cfg.document-root
|
||||
cfg.user-public
|
||||
"/etc/cl-gemini/key.pem"
|
||||
"/etc/cl-gemini/cert.pem"
|
||||
cfg.slynk-port
|
||||
feed-registrations
|
||||
cfg.textfiles-archive
|
||||
}";
|
||||
Restart = "on-failure";
|
||||
PIDFile = "/run/cl-gemini.$USERNAME.uid";
|
||||
User = "cl-gemini";
|
||||
};
|
||||
|
||||
environment = {
|
||||
LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
|
||||
CL_SOURCE_REGISTRY = concatStringsSep ":"
|
||||
(["${config.users.users.cl-gemini.home}/quicklisp/quicklisp"] ++
|
||||
(map
|
||||
(pkg: "${pkg}//")
|
||||
(lisp-libs ++ [pkgs.cl-gemini])));
|
||||
};
|
||||
|
||||
path = with pkgs; [
|
||||
gcc
|
||||
file
|
||||
getent
|
||||
];
|
||||
|
||||
wantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -7,19 +7,25 @@ with lib;
|
||||
./fudo/authentication.nix
|
||||
./fudo/chat.nix
|
||||
./fudo/common.nix
|
||||
./fudo/dns.nix
|
||||
./fudo/git.nix
|
||||
./fudo/grafana.nix
|
||||
./fudo/kdc.nix
|
||||
./fudo/ldap.nix
|
||||
./fudo/local-network.nix
|
||||
./fudo/mail.nix
|
||||
./fudo/mail-container.nix
|
||||
./fudo/minecraft-server.nix
|
||||
./fudo/node-exporter.nix
|
||||
./fudo/postgres.nix
|
||||
./fudo/prometheus.nix
|
||||
./fudo/secure-dns-proxy.nix
|
||||
./fudo/slynk.nix
|
||||
./fudo/system.nix
|
||||
./fudo/webmail.nix
|
||||
|
||||
./informis/cl-gemini.nix
|
||||
|
||||
../fudo/profiles
|
||||
../fudo/sites
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user