Merge branch 'master' into france

This commit is contained in:
root 2020-07-22 00:26:56 -05:00
commit 7cc059c342
38 changed files with 2471 additions and 517 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
configuration.nix
hardware-configuration.nix
*~

View File

@ -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);
};
}

View File

@ -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.";
};
};

View File

@ -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.";
};
}

218
config/fudo/dns.nix Normal file
View File

@ -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;
};
};
}

View File

@ -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;
};
};
}

View File

@ -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;

View File

@ -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"];
};
};
};
};
}

View File

@ -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}
'';
};
}

View File

@ -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";

View File

@ -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;
};
};
};
}

70
config/fudo/slynk.nix Normal file
View File

@ -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";
};
};
};
}

View File

@ -186,7 +186,7 @@ let
};
password-file = mkOption {
type = types.path;
type = types.str;
description = "Password to use when connecting to the database.";
};
};

View File

@ -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" ];
};
};
}

View File

@ -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
];

View File

@ -1 +0,0 @@
./hosts/france.nix

View File

@ -18,6 +18,7 @@
autoconf
automake
bash
boot
bind
binutils
btrfs-progs
@ -28,8 +29,11 @@
certbot
clang
curl
dpkg
emacs
enca
fail2ban
file
fortune
gcc
git
@ -44,6 +48,7 @@
jdk
kerberos
libisofs
libstdcxxHook
lispPackages.alexandria
lispPackages.cl-ppcre
lispPackages.clx
@ -51,6 +56,8 @@
lshw
mkpasswd
ncurses5
nix-index
nix-prefetch-git
nmap
oidentd
openldap
@ -58,6 +65,7 @@
openssl_1_1
openssh_gssapi
pciutils
pinentry.curses
pv
pwgen
racket
@ -73,19 +81,28 @@
unzip
vim
wget
yubikey-manager
yubikey-personalization
];
system.stateVersion = "19.09";
system.stateVersion = "20.03";
system.autoUpgrade.enable = true;
environment.etc.current-nixos-config.source = ./.;
krb5.enable = true;
krb5.libdefaults.default_realm = "FUDO.ORG";
krb5.kerberos = pkgs.heimdalFull;
console.keyMap = "dvp";
services.xserver = {
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
};
console = {
useXkbConfig = true;
};
i18n = {
defaultLocale = "en_US.UTF-8";
@ -95,6 +112,8 @@
mosh.enable = true;
ssh = {
startAgent = false;
extraConfig = ''
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
@ -129,8 +148,21 @@
GSSAPICleanupCredentials yes
'';
};
pcscd = {
enable = true;
};
udev.packages = with pkgs; [
yubikey-personalization
];
};
environment.shellInit = ''
gpg-connect-agent /bye
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
'';
security.pam = {
enableSSHAgentAuth = true;
# TODO: add yubico?
@ -167,6 +199,9 @@
group = "users";
home = "/home/niten";
hashedPassword = "$6$a1q2Duoe35hd5$IaZGXPfqyGv9uq5DQm7DZq0vIHsUs39sLktBiBBqMiwl/f/Z4jSvNZLJp9DZJYe5u2qGBYh1ca.jsXvQA8FPZ/";
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDoWkjyeIfgwm0b78weToVYOQSD0RQ0qbNzpsN5NokbIFv2/980kLtnYrQEgIJ/JwMLlT3uJYacbCT5/a6Fb8oLxNpj0AF1EKaWZ3Rrlg72Sq+9SEwJwWWmZizX83sovMwUBMaUp6jWLhAhPpzBW5pfc5YWoc89wxGbELSwzgt5EgHbSJgvDnaHSp3fVaY01wfDXbL/oO160iNe7wv2HLMZu/FkWBkIjz6HmoGJJzYM89bUpHbyYG28lmCHB/8UPog5/BsjOn3/qupgf4zh6mMdMsXLvbR2jVwVjxcEMj9N5nCvc+Y3oi7Mij6VNrWbhkaAJMEzeMhWYrF3/pFQxUqG37aK3d0gw9kp5tMDLIlAPX4y1lfA87pIzoa0+Alql0CJQA1IJvp9SFG7lBmSthWQLmZvwwfoGg/ZjF6rOgsVoZ8TizpQnydWJDr6NboU9LL9Oa64OM5Rs0AU3cR2UbOF4QIcWFJ/7oDe3dOnfZ8QYqx9eXJyxoAUpDanaaTHYBiAKkeOBwQU+MVLKCcONKw9FZclf/1TpDB5b3/JeUFANjHQTv0UXA4YYU7iCx6H7XB4qwwtU9O19CGQYYfCfULX12/fRpYJw6VJaQWyyU4Bn5dk/dcB2nGI36jwbLMfhbUTIApujioAnd/GQIMakHEZ1+syPhMx9BxMkZb99B0A1Q== openpgp:0x4EC95B64"
];
};
reaper = {
isNormalUser = true;

179
fudo/profiles/common-ui.nix Normal file
View File

@ -0,0 +1,179 @@
{ config, lib, pkgs, ... }:
with lib;
let
profile = config.fudo.common.profile;
common-packages = with pkgs; [
ffmpeg-full
libfixposix
mono
nomacs
python37Packages.youtube-dl
sqlite
system-config-printer
];
gui-packages = with pkgs; [
cool-retro-term
corefonts
chrome-gnome-shell
chromium
evince
firefox
gimp
glxinfo
gnome3.gnome-shell
gnome3.gnome-session
google-chrome
gtk2
gtk2-x11
gtk3
gtkimageview
i3lock
mplayer
mpv
pdftk
redshift
rhythmbox
shotwell
spotify
(steam.override {
nativeOnly = true;
extraPkgs = pkgs: [
mono
fmodex
gtk3
gtk3-x11
libgdiplus
zlib
];
withJava = true;
}).run
virtmanager
xorg.xev
xzgv
virtmanager-qt
];
cfg = config.fudo.common;
in mkIf ((profile == "desktop") || (profile == "laptop")) {
environment.systemPackages =
common-packages ++ (if cfg.enable-gui then gui-packages else []);
nixpkgs.config.allowBroken = true;
services.avahi = {
enable = true;
browseDomains = [config.fudo.common.domain];
domainName = config.fudo.common.domain;
};
# splash screen
boot.plymouth.enable = false;
boot.tmpOnTmpfs = true;
services.xserver = if cfg.enable-gui then {
enable = true;
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
desktopManager.gnome3.enable = true;
displayManager.gdm.enable = true;
displayManager.defaultSession = "gnome";
windowManager.session = pkgs.lib.singleton {
name = "stumpwm";
start = ''
${pkgs.lispPackages.stumpwm}/bin/stumpwm &
waidPID=$!
'';
};
} else {
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
};
services.gnome3 = mkIf cfg.enable-gui {
evolution-data-server.enable = pkgs.lib.mkForce false;
gnome-user-share.enable = pkgs.lib.mkForce false;
};
services.dbus.socketActivated = true;
sound.enable = true;
hardware.pulseaudio.enable = true;
fonts = mkIf cfg.enable-gui {
enableFontDir = true;
#fontconfig.antialias = true;
fontconfig.enable = true;
#fontconfig.penultimate.enable = true;
#fontconfig.subpixel.lcdfilter = "default";
fonts = with pkgs; [
cantarell_fonts
dejavu_fonts
dina-font
dosemu_fonts
fira-code
fira-code-symbols
freefont_ttf
liberation_ttf
mplus-outline-fonts
#nerdfonts
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
proggyfonts
terminus_font
ubuntu_font_family
ucsFonts
ultimate-oldschool-pc-font-pack
unifont
vistafonts
xlibs.fontadobe100dpi
xlibs.fontadobe75dpi
xlibs.fontadobeutopia100dpi
xlibs.fontadobeutopia75dpi
xlibs.fontadobeutopiatype1
xlibs.fontarabicmisc
xlibs.fontbh100dpi
xlibs.fontbh75dpi
xlibs.fontbhlucidatypewriter100dpi
xlibs.fontbhlucidatypewriter75dpi
xlibs.fontbhttf
xlibs.fontbhtype1
xlibs.fontbitstream100dpi
xlibs.fontbitstream75dpi
xlibs.fontbitstreamtype1
xlibs.fontcronyxcyrillic
xlibs.fontcursormisc
xlibs.fontdaewoomisc
xlibs.fontdecmisc
xlibs.fontibmtype1
xlibs.fontisasmisc
xlibs.fontjismisc
xlibs.fontmicromisc
xlibs.fontmisccyrillic
xlibs.fontmiscethiopic
xlibs.fontmiscmeltho
xlibs.fontmiscmisc
xlibs.fontmuttmisc
xlibs.fontschumachermisc
xlibs.fontscreencyrillic
xlibs.fontsonymisc
xlibs.fontsunmisc
xlibs.fontwinitzkicyrillic
xlibs.fontxfree86type1
];
};
}

View File

@ -2,7 +2,9 @@
{
imports = [
./common-ui.nix
./desktop.nix
./laptop.nix
./server.nix
];
}

View File

@ -1,154 +1,8 @@
{ config, lib, pkgs, ... }:
with lib;
{
config = mkIf (config.fudo.common.profile == "desktop") {
environment.systemPackages = with pkgs; [
cool-retro-term
chrome-gnome-shell
chromium
ffmpeg-full
firefox
gimp
glxinfo
gnome3.gnome-shell
gnome3.gnome-session
google-chrome
gtk2
gtk2-x11
gtk3
gtkimageview
i3lock
libfixposix
minecraft
mplayer
nomacs
openssl_1_1
redshift
rhythmbox
shotwell
spotify
sqlite
steam
system-config-printer
virtmanager
xorg.xev
xzgv
virtmanager-qt
];
# Splash screen
boot.plymouth.enable = true;
services.avahi = {
enable = true;
browseDomains = [config.fudo.domain];
domainName = config.fudo.domain;
};
boot.tmpOnTmpfs = true;
services.xserver = {
enable = true;
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
desktopManager.gnome3.enable = true;
desktopManager.default = "gnome3";
displayManager.gdm.enable = true;
windowManager.session = pkgs.lib.singleton {
name = "stumpwm";
start = ''
${pkgs.lispPackages.stumpwm}/bin/stumpwm &
waidPID=$!
'';
};
};
services.printing = {
enable = true;
};
services.gnome3 = {
evolution-data-server.enable = pkgs.lib.mkForce false;
gnome-user-share.enable = pkgs.lib.mkForce false;
};
services.dbus.socketActivated = true;
services.openssh.forwardX11 = true;
programs.ssh.forwardX11 = true;
sound.enable = true;
hardware.pulseaudio.enable = true;
fonts = {
enableCoreFonts = true;
enableFontDir = true;
enableGhostscriptFonts = false;
fontconfig.ultimate.enable = true;
fonts = with pkgs; [
cantarell_fonts
dejavu_fonts
dina-font
dosemu_fonts
fira-code
fira-code-symbols
freefont_ttf
liberation_ttf
mplus-outline-fonts
nerdfonts
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
proggyfonts
terminus_font
ubuntu_font_family
ucsFonts
unifont
vistafonts
xlibs.fontadobe100dpi
xlibs.fontadobe75dpi
xlibs.fontadobeutopia100dpi
xlibs.fontadobeutopia75dpi
xlibs.fontadobeutopiatype1
xlibs.fontarabicmisc
xlibs.fontbh100dpi
xlibs.fontbh75dpi
xlibs.fontbhlucidatypewriter100dpi
xlibs.fontbhlucidatypewriter75dpi
xlibs.fontbhttf
xlibs.fontbhtype1
xlibs.fontbitstream100dpi
xlibs.fontbitstream75dpi
xlibs.fontbitstreamtype1
xlibs.fontcronyxcyrillic
xlibs.fontcursormisc
xlibs.fontdaewoomisc
xlibs.fontdecmisc
xlibs.fontibmtype1
xlibs.fontisasmisc
xlibs.fontjismisc
xlibs.fontmicromisc
xlibs.fontmisccyrillic
xlibs.fontmiscethiopic
xlibs.fontmiscmeltho
xlibs.fontmiscmisc
xlibs.fontmuttmisc
xlibs.fontschumachermisc
xlibs.fontscreencyrillic
xlibs.fontsonymisc
xlibs.fontsunmisc
xlibs.fontwinitzkicyrillic
xlibs.fontxfree86type1
];
};
mkIf (config.fudo.common.profile == "desktop") {
networking = {
networkmanager.enable = mkForce false;
};
}

41
fudo/profiles/laptop.nix Normal file
View File

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
with lib;
{
options.fudo.laptop = {
use-network-manager = mkOption {
type = types.bool;
description = "Use NetworkManager instead of wpa_supplicant.";
default = false;
};
};
config = mkIf (config.fudo.common.profile == "laptop") {
environment.systemPackages = with pkgs; [
acpi
upower
wpa_supplicant
];
networking = if (config.fudo.laptop.use-network-manager) then {
networkmanager.enable = true;
} else {
networkmanager.enable = false;
wireless = {
enable = true;
userControlled = {
enable = true;
group = "wheel";
};
networks = {
"sea.fudo.org" = {
psk = "DahHaocheiD5";
};
"Pixel_9041" = {
psk = "ea72027e4e6";
};
};
};
};
};
}

View File

@ -51,6 +51,8 @@ in {
config = mkIf (config.fudo.common.profile == "server") {
environment = {
systemPackages = with pkgs; [
ldns
ldns.examples
test-config
reboot-if-necessary
];

View File

@ -2,6 +2,7 @@
{
imports = [
./joes.nix
./portage.nix
./seattle.nix
];

55
fudo/sites/joes.nix Normal file
View File

@ -0,0 +1,55 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.networking.hostName;
gateway = "172.86.179.17";
local-domain = "informis.land";
admin = "admin@${local-domain}";
in {
config = mkIf (config.fudo.common.site == "joes") {
time.timeZone = "America/Winnipeg";
services.cron = {
mailto = admin;
};
networking = {
domain = local-domain;
search = [ local-domain "fudo.org" ];
firewall.enable = false;
defaultGateway = gateway;
# defaultGateway6 = gateway6;
hosts = {
"127.0.0.1" = [
"${config.networking.hostName}.${local-domain}"
config.networking.hostName
];
};
};
krb5.libdefaults.default_realm = "INFORMIS.LAND";
fudo.node-exporter = {
enable = false;
hostname = hostname;
};
security.acme.certs."${hostname}.${local-domain}" = {
email = "admin@${local-domain}";
};
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
};
};
}

View File

@ -6,24 +6,45 @@ let
local-domain = "sea.fudo.org";
gateway = "10.0.0.1";
nameservers = ["10.0.0.1"];
in {
config = mkIf (config.fudo.common.site == "seattle") {
time.timeZone = "America/Los_Angeles";
services.printing = {
enable = true;
};
services.cron = {
mailto = admin;
};
krb5.libdefaults.default_realm = "FUDO.ORG";
networking = {
domain = local-domain;
search = [local-domain "fudo.org"];
firewall.enable = false;
networkmanager.enable = pkgs.lib.mkForce false;
nameservers = nameservers;
# Until Comcast gets it's shit together... :(
enableIPv6 = false;
# Don't set the gateway if we ARE the gateway.
# This is the most generic way I can think of to do that. local-network is really
# about running all the local servers (DNS, DHCP, and providing gateway).
defaultGateway = optionalString (config.fudo.local-network.enable != true) gateway;
enableIPv6 = true;
# Necessary to make sure than Kerberos and Avahi both work (the former
# needs the full reverse-lookup name of the server, the latter wants
# `hostname` to return just the host itself.
hosts = {
"127.0.0.1" = [ "${config.networking.hostName}.${local-domain}" config.networking.hostName];
};
};
users.extraUsers = {
@ -53,6 +74,12 @@ in {
home = "/home/xiaoxuan";
hashedPassword = "$6$C8lYHrK7KvdKm/RE$cHZ2hg5gEOEjTV8Zoayik8sz5h.Vh0.ClCgOlQn8l/2Qx/qdxqZ7xCsAZ1GZ.IEyESfhJeJbjLpykXDwPpfVF0";
};
kevin = {
isNormalUser = true;
createHome = true;
home = "/home/kevin";
hashedPassword = "";
};
};
fileSystems."/mnt/documents" = {
@ -84,118 +111,207 @@ in {
fsType = "nfs4";
};
# Should use this eventually...
# fudo.localNetwork = {
# masterNameServer = {
# ip = "10.0.0.1";
# ipReverseDomain = "0.10.in-addr.arpa";
# };
fudo.common.domain = "sea.fudo.org";
# domain = "${local-domain}";
fudo.local-network = {
# hostAliases = {
# kadmin = "slab";
# kdc = "slab";
# photo = "doraemon";
# music = "doraemon";
# panopticon = "hyperion";
# hole = "dnshole";
# ipfs = "nostromo";
# };
domain = "${local-domain}";
# hosts = {
# slab = {
# ipv4Address = "10.0.0.1";
# };
# volsung = {
# ipv4Address = "10.0.0.106";
# macAddress = "ac:bc:32:7b:75:a5";
# };
# nest = {
# ipv4Address = "10.0.0.176";
# macAddress = "18:b4:30:16:7c:5a";
# };
# monolith = {
# ipv4Address = "10.0.0.100";
# macAddress = "6c:62:6d:c8:b0:d8";
# };
# brother-wireless = {
# ipv4Address = "10.0.0.160";
# macAddress = "c0:38:96:64:49:65";
# };
# doraemon = {
# ipv4Address = "10.0.0.52";
# macAddress = "00:11:32:0a:06:c5";
# };
# lm = {
# ipv4Address = "10.0.0.21";
# macAddress = "52:54:00:D8:34:92";
# };
# ubiquiti-wifi = {
# ipv4Address = "10.0.0.126";
# macAddress = "04:18:d6:20:48:fb";
# };
# front-light = {
# ipv4Address = "10.0.0.221";
# macAddress = "94:10:3e:48:94:ed";
# };
# ipad = {
# ipv4Address = "10.0.0.202";
# macAddress = "9c:35:eb:48:6e:71";
# };
# chromecast-2 = {
# ipv4Address = "10.0.0.215";
# macAddress = "a4:77:33:59:a2:ba";
# };
# taipan = {
# ipv4Address = "10.0.0.107";
# macAddress = "52:54:00:34:c4:78";
# };
# dns-hole = {
# ipv4Address = "10.0.0.185";
# macAddress = "b8:27:eb:b2:95:fd";
# };
# family-tv = {
# ipv4Address = "10.0.0.205";
# macAddress = "84:a4:66:3a:b1:f8";
# };
# spark = {
# ipv4Address = "10.0.0.108";
# macAddress = "78:24:af:04:f7:dd";
# };
# babycam = {
# ipv4Address = "10.0.0.206";
# macAddress = "08:ea:40:59:5f:9e";
# };
# hyperion = {
# ipv4Address = "10.0.0.109";
# macAddress = "52:54:00:33:46:de";
# };
# cargo = {
# ipv4Address = "10.0.0.50";
# macAddress = "00:11:32:75:d8:b7";
# };
# cam-entrance = {
# ipv4Address = "10.0.0.31";
# macAddress = "9c:8e:cd:0e:99:7b";
# };
# cam-driveway = {
# ipv4Address = "10.0.0.32";
# macAddress = "9c:8e:cd:0d:3b:09";
# };
# cam-deck = {
# ipv4Address = "10.0.0.33";
# macAddress = "9c:8e:cd:0e:98:c8";
# };
# nostromo = {
# ipv4Address = "10.0.0.2";
# macAddress = "14:fe:b5:ca:a2:c9";
# };
# zbox = {
# ipv4Address = "10.0.0.110";
# macAddress = "18:60:24:91:CC:27";
# };
# };
# };
aliases = {
kadmin = "slab";
kdc = "slab";
photo = "doraemon";
music = "doraemon";
panopticon = "hyperion";
ipfs = "nostromo";
hole = "nostromo";
pihole = "nostromo";
dns-hole = "nostromo";
};
network = "10.0.0.0/16";
dhcp-dynamic-network = "10.0.1.0/24";
enable-reverse-mappings = true;
srv-records = {
tcp = {
domain = [{
port = 53;
host = "nostromo.sea.fudo.org";
}];
kerberos = [{
port = 88;
host = "france.fudo.org";
}];
kerberos-adm = [{
port = 88;
host = "france.fudo.org";
}];
ssh = [{
port = 22;
host = "nostromo.sea.fudo.org";
}];
ldap = [{
port = 389;
host = "france.fudo.org";
}];
};
udp = {
domain = [{
port = 53;
host = "nostromo.sea.fudo.org";
}];
kerberos = [{
port = 88;
host = "france.fudo.org";
}];
kerboros-master = [{
port = 88;
host = "france.fudo.org";
}];
kpasswd = [{
port = 464;
host = "france.fudo.org";
}];
};
};
hosts = {
nostromo = {
ip-address = "10.0.0.1";
mac-address = "46:54:76:06:f1:10";
};
lm = {
ip-address = "10.0.0.2";
mac-address = "00:23:7d:e6:d9:ea";
};
switch-master = {
ip-address = "10.0.0.5";
mac-address = "00:14:1C:B6:BB:40";
};
# lm = {
# ip-address = "10.0.0.21";
# mac-address = "52:54:00:D8:34:92";
# };
cam-entrance = {
ip-address = "10.0.0.31";
mac-address = "9c:8e:cd:0e:99:7b";
};
cam-driveway = {
ip-address = "10.0.0.32";
mac-address = "9c:8e:cd:0d:3b:09";
};
cam-deck = {
ip-address = "10.0.0.33";
mac-address = "9c:8e:cd:0e:98:c8";
};
cargo = {
ip-address = "10.0.0.50";
mac-address = "00:11:32:75:d8:b7";
};
whitedwarf = {
ip-address = "10.0.0.51";
mac-address = "00:11:32:12:14:1d";
};
doraemon = {
ip-address = "10.0.0.52";
mac-address = "00:11:32:0a:06:c5";
};
android = {
ip-address = "10.0.0.81";
mac-address = "00:16:3e:43:39:fc";
};
retro-wired = {
ip-address = "10.0.0.82";
mac-address = "dc:a6:32:6b:57:43";
};
retro = {
ip-address = "10.0.0.83";
mac-address = "dc:a6:32:6b:57:45";
};
monolith = {
ip-address = "10.0.0.100";
mac-address = "6c:62:6d:c8:b0:d8";
};
taipan = {
ip-address = "10.0.0.107";
mac-address = "52:54:00:34:c4:78";
};
spark = {
ip-address = "10.0.0.108";
mac-address = "78:24:af:04:f7:dd";
};
hyperion = {
ip-address = "10.0.0.109";
mac-address = "52:54:00:33:46:de";
};
zbox = {
ip-address = "10.0.0.110";
mac-address = "02:dd:80:52:83:9b";
};
ubiquiti-wifi = {
ip-address = "10.0.0.126";
mac-address = "04:18:d6:20:48:fb";
};
brother-wireless = {
ip-address = "10.0.0.160";
mac-address = "c0:38:96:64:49:65";
};
nest = {
ip-address = "10.0.0.176";
mac-address = "18:b4:30:16:7c:5a";
};
xixi-phone = {
ip-address = "10.0.0.193";
mac-address = "48:43:7c:75:89:42";
};
ipad = {
ip-address = "10.0.0.202";
mac-address = "9c:35:eb:48:6e:71";
};
cam-front = {
ip-address = "10.0.0.203";
mac-address = "c4:d6:55:3e:b4:c3";
};
family-tv = {
ip-address = "10.0.0.205";
mac-address = "84:a4:66:3a:b1:f8";
};
babycam = {
ip-address = "10.0.0.206";
mac-address = "08:ea:40:59:5f:9e";
};
workphone = {
ip-address = "10.0.0.211";
mac-address = "a8:8e:24:5c:12:67";
};
chromecast-2 = {
ip-address = "10.0.0.215";
mac-address = "a4:77:33:59:a2:ba";
};
front-light = {
ip-address = "10.0.0.221";
mac-address = "94:10:3e:48:94:ed";
};
# Storage network
node-1 = {
ip-address = "10.0.10.101";
mac-address = "00:1e:06:36:81:cf";
};
node-2 = {
ip-address = "10.0.10.102";
mac-address = "00:1e:06:36:ec:3e";
};
node-3 = {
ip-address = "10.0.10.103";
mac-address = "00:1e:06:36:ec:4b";
};
};
};
};
}

View File

@ -1,31 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "usb_storage" "floppy" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/87833c39-299b-4e84-9854-beda4a8e0115";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/bfb464c0-c259-4c29-8e8f-b3011bd30c95";
fsType = "ext4";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/ac0fe2b7-dd7a-4e86-aaa0-942acf3d541d"; }
];
nix.maxJobs = lib.mkDefault 8;
}

46
hosts/atom.nix Normal file
View File

@ -0,0 +1,46 @@
{ config, pkgs, ... }:
let
hostname = "atom";
in {
imports = [
../defaults.nix
../hardware-configuration.nix
];
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
networking.hostName = hostname;
environment.systemPackages = with pkgs; [
glxinfo
hll2380dw-cups
usbutils
];
fudo.common = {
profile = "laptop";
site = "seattle";
};
hardware.cpu.amd.updateMicrocode = true;
programs = {
bash.enableCompletion = true;
};
fudo.laptop.use-network-manager = false;
fudo.common.enable-gui = false;
hardware.opengl.driSupport32Bit = true;
hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ];
hardware.opengl.driSupport = true;
hardware.pulseaudio.support32Bit = true;
hardware.bluetooth.enable = true;
}

View File

@ -239,11 +239,13 @@ in {
state-directory = "${system-mail-directory}/var";
mail-directory = "${system-mail-directory}/mailboxes";
dovecot.ldap-reader-dn = "cn=user_db_reader,dc=fudo,dc=org";
dovecot.ldap-reader-passwd = fileContents /srv/ldap/secure/user_db.passwd;
dovecot.ldap = {
reader-dn = "cn=user_db_reader,dc=fudo,dc=org";
reader-passwd = fileContents /srv/ldap/secure/user_db.passwd;
# FIXME: use SSL once I can figure out Acme SSL cert CA for LDAP.
dovecot.ldap-urls = [ "ldap://france.fudo.org" ];
# FIXME: use SSL once I can figure out Acme SSL cert CA for LDAP.
server-urls = [ "ldap://france.fudo.org" ];
};
clamav.enable = true;
@ -265,7 +267,7 @@ in {
name = "webmail";
hostname = "localhost";
user = "webmail";
password-file = /srv/webmail/secure/db.passwd;
password-file = "/srv/webmail/secure/db.passwd";
};
};
@ -279,7 +281,7 @@ in {
name = "webmail";
hostname = "localhost";
user = "webmail";
password-file = /srv/webmail/secure/db.passwd;
password-file = "/srv/webmail/secure/db.passwd";
};
};
@ -292,7 +294,7 @@ in {
name = "webmail";
hostname = "localhost";
user = "webmail";
password-file = /srv/webmail/secure/db.passwd;
password-file = "/srv/webmail/secure/db.passwd";
};
};
};
@ -305,12 +307,12 @@ in {
site-name = "Fudo Chat";
smtp-server = "france.fudo.org";
smtp-user = "chat";
smtp-password-file = /srv/mattermost/secure/smtp.passwd;
smtp-password-file = "/srv/mattermost/secure/smtp.passwd";
database = {
name = "mattermost";
hostname = "localhost";
user = "mattermost";
password-file = /srv/mattermost/secure/db.passwd;
password-file = "/srv/mattermost/secure/db.passwd";
};
};

View File

@ -1,7 +1,9 @@
{ config, pkgs, ... }:
{ lib, config, pkgs, ... }:
let
hostname = "nostromo.sea.fudo.org";
hostname = "nostromo";
host-internal-ip = "10.0.0.1";
inherit (lib.strings) concatStringsSep;
in {
@ -9,77 +11,214 @@ in {
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sdb";
boot.loader.grub.device = "/dev/sda";
hardware.bluetooth.enable = false;
imports = [
../defaults.nix
../networks/sea.fudo.org.nix
../profiles/server.nix
../hardware-configuration.nix
# ../profiles/services/local_nameserver.nix
];
fudo.postgresql = {
enable = true;
ssl-private-key = "/srv/nostromo.sea.fudo.org/certs/private/privkey.pem";
ssl-certificate = "/srv/nostromo.sea.fudo.org/certs/cert.pem";
keytab = "/srv/nostromo.sea.fudo.org/keytabs/postgres.keytab";
fudo.common = {
profile = "server";
site = "seattle";
};
local-networks = [
"10.0.0.1/24"
];
fudo.local-network = {
enable = true;
# See fudo/sites/seattle.nix for general settings
dns-servers = [ host-internal-ip ];
gateway = host-internal-ip;
dhcp-interfaces = [ "intif0" ];
dns-serve-ips = [ host-internal-ip "127.0.0.1" "127.0.1.1" ];
# Using a pihole running in docker, see below
recursive-resolver = "${host-internal-ip} port 5353";
# recursive-resolver = "1.1.1.1";
server-ip = host-internal-ip;
};
fudo.slynk = {
enable = true;
};
networking = {
hostName = hostname;
defaultGateway = "10.0.0.1";
nameservers = [ "10.0.0.1" ];
# Turn off for hypervisor: dhcp by default everywhere is a fuckin pain.
dhcpcd.enable = false;
nameservers = [ host-internal-ip ];
# Create a bridge for VMs to use
macvlans.intlan0 = {
interface = "eno1";
mode = "bridge";
macvlans = {
intif0 = {
interface = "eno1";
mode = "bridge";
};
};
interfaces = {
intlan0 = {
eno1.useDHCP = false;
eno3.useDHCP = false;
eno4.useDHCP = false;
enp33s0f0.useDHCP = false;
enp33s0f1.useDHCP = false;
enp9s0f0.useDHCP = false;
enp9s0f1.useDHCP = false;
eno2.useDHCP = true;
intif0 = {
useDHCP = false;
macAddress = "46:54:76:06:f1:10";
ipv4.addresses = [
{
address = "10.0.0.2";
prefixLength = 23;
address = host-internal-ip;
prefixLength = 22;
}
{
address = "10.0.10.2";
prefixLength = 24;
}
];
};
};
nat = {
enable = true;
externalInterface = "eno2";
internalInterfaces = ["intif0"];
};
};
hardware.bluetooth.enable = false;
fudo = {
postgresql = {
enable = true;
ssl-private-key = "/srv/nostromo/certs/private/privkey.pem";
ssl-certificate = "/srv/nostromo/certs/cert.pem";
keytab = "/srv/nostromo/keytabs/postgres.keytab";
local-networks = [
"10.0.0.1/24"
"127.0.0.1/8"
];
};
secure-dns-proxy = {
enable = true;
port = 3535;
upstream-dns = [
"https://cloudflare-dns.com/dns-query"
# "https://dns.adguard.com/dns-query"
];
bootstrap-dns = "1.1.1.1";
};
};
environment.systemPackages = with pkgs; [
ipfs
dnsproxy
libguestfs-with-appliance
libvirt
virtmanager
];
virtualisation.libvirtd = {
enable = true;
qemuPackage = pkgs.qemu_kvm;
onShutdown = "shutdown";
virtualisation = {
docker = {
enable = true;
autoPrune.enable = true;
enableOnBoot = true;
};
libvirtd = {
enable = true;
qemuPackage = pkgs.qemu_kvm;
onShutdown = "shutdown";
};
};
services.ipfs = {
enable = true;
enableGC = true;
autoMount = false;
defaultMode = "online";
apiAddress = "/ip4/10.0.0.2/tcp/5001";
gatewayAddress = "/ipv4/10.0.0.2/tcp/8080";
docker-containers = {
pihole = {
image = "pihole/pihole:4.3.2-1";
ports = [
"5353:53/tcp"
"5353:53/udp"
"3080:80/tcp"
];
environment = {
ServerIP = host-internal-ip;
VIRTUAL_HOST = "dns-hole.sea.fudo.org";
DNS1 = "1.1.1.1";
DNS2 = "8.8.8.8";
};
volumes = [
"/srv/pihole/etc-pihole/:/etc/pihole/"
"/srv/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/"
];
# TODO: DNS-over-HTTPS via cloudflared
# extraDockerOptions = [
# "--dns=1.1.1.1"
# ];
};
};
services = {
dhcpd6.enable = false;
# glusterfs = {
# enable = true;
# enableGlustereventsd = true;
# useRpcbind = true;
# };
nginx = {
enable = true;
virtualHosts = {
"pihole.sea.fudo.org" = {
serverAliases = [
"dns-hole.sea.fudo.org"
"hole.sea.fudo.org"
];
locations."/" = {
proxyPass = "http://127.0.0.1:3080";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
};
};
# ceph = {
# enable = true;
# global = {
# clusterName = "sea-data";
# clusterNetwork = "10.0.10.0/24";
# fsid = "d443e192-896d-4102-a60f-f8f0777eb2a3";
# monHost = "10.0.10.2";
# monInitialMembers = "mon-1";
# publicNetwork = "10.0.0.0/22";
# };
# mds = {
# enable = true;
# daemons = ["srv-2"];
# };
# mgr = {
# enable = true;
# daemons = ["srv-2"];
# };
# mon = {
# enable = true;
# daemons = ["srv-2"];
# };
# };
};
}

285
hosts/procul.nix Normal file
View File

@ -0,0 +1,285 @@
{ config, pkgs, lib, ... }:
with lib;
let
hostname = "procul";
domain = "informis.land";
mail-hostname = hostname;
host_ipv4 = "172.86.179.18";
host-fqdn = "${hostname}.${domain}";
all-hostnames = [];
acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem";
acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem";
acme-ca = "/etc/nixos/static/letsencryptauthorityx3.pem";
fudo-ca = "/etc/nixos/static/fudo_ca.pem";
in {
boot.loader.grub = {
enable = true;
version = 2;
device = "/dev/sdb";
};
imports = [
../hardware-configuration.nix
../defaults.nix
../informis/users.nix
];
environment.systemPackages = with pkgs; [
multipath-tools
];
networking = {
hostName = hostname;
# provided by secure-dns-proxy
nameservers = [ "127.0.0.1" ];
dhcpcd.enable = false;
useDHCP = false;
# TODO: fix IPv6
enableIPv6 = true;
# Create a bridge for VMs to use
macvlans = {
extif0 = {
interface = "enp0s25";
mode = "bridge";
};
};
interfaces = {
extif0 = {
# result of:
# echo $FQDN-extif|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
macAddress = "02:e2:b7:db:e8:af";
ipv4.addresses = [
{
address = host_ipv4;
prefixLength = 29;
}
];
};
};
};
hardware.bluetooth.enable = false;
users = {
users = {
gituser = {
isSystemUser = true;
group = "nogroup";
};
};
};
fudo = {
common = {
# Sets some server-common settings. See /etc/nixos/fudo/profiles/...
profile = "server";
# Sets some common site-specific settings: gateway, monitoring, etc. See /etc/nixos/fudo/sites/...
site = "joes";
domain = domain;
admin-email = "admin@${domain}";
local-networks = [
"172.86.179.16/29"
"208.81.1.128/28"
"208.81.3.112/28"
"172.17.0.0/16"
"127.0.0.0/8"
];
};
# Not all users need access to procul; don't allow LDAP-user access.
authentication.enable = false;
auth.kdc = {
enable = true;
database-path = "/var/heimdal/heimdal";
realm = "INFORMIS.LAND";
mkey-file = "/srv/heimdal/secure/m-key";
acl-file = "/etc/heimdal/kdc.acl";
bind-addresses = [
host_ipv4
"127.0.0.1"
"127.0.1.1"
];
};
secure-dns-proxy = {
enable = true;
upstream-dns = [ "https://cloudflare-dns.com/dns-query" ];
bootstrap-dns = "1.1.1.1";
listen-ips = [ "127.0.0.1" ];
port = 53;
};
dns = {
enable = true;
dns-hosts = {
"ns1.informis.land" = "172.86.179.18";
"ns2.informis.land" = "172.86.179.18";
};
listen-ips = [host_ipv4];
domains = {
"informis.land" = import ../informis/informis.land.nix {
inherit host_ipv4 config;
};
};
};
mail-server = {
enable = true;
debug = true;
domain = domain;
hostname = "${host-fqdn}";
monitoring = false;
mail-user = "mailuser";
mail-user-id = 525;
mail-group = "mailgroup";
clamav.enable = true;
dkim.signing = true;
dovecot = {
ssl-certificate = acme-certificate "imap.${domain}";
ssl-private-key = acme-private-key "imap.${domain}";
};
postfix = {
ssl-certificate = acme-certificate "smtp.${domain}";
ssl-private-key = acme-private-key "smtp.${domain}";
};
# This should NOT include the primary domain
local-domains = [
host-fqdn
"smtp.${domain}"
];
mail-directory = "/srv/mailserver/mail";
state-directory = "/srv/mailserver/state";
trusted-networks = [
"172.86.179.16/29"
"127.0.0.0/16"
];
alias-users = {
root = ["niten"];
postmaster = ["niten"];
hostmaster = ["niten"];
webmaster = ["niten"];
system = ["niten"];
admin = ["niten"];
dmarc-report = ["niten"];
};
};
postgresql = {
enable = true;
ssl-certificate = (acme-certificate host-fqdn);
ssl-private-key = (acme-private-key host-fqdn);
keytab = "/srv/postgres/secure/postgres.keytab";
local-networks = [
"172.86.179.16/29"
"127.0.0.0/16"
];
users = {
gituser = {
password = fileContents "/srv/git/secure/db.passwd";
databases = {
git = "ALL PRIVILEGES";
};
};
};
databases = {
git = ["niten"];
};
};
git = {
enable = true;
hostname = "git.informis.land";
site-name = "informis git";
user = "gituser";
repository-dir = /srv/git/repo;
state-dir = /srv/git/state;
database = {
user = "gituser";
password-file = /srv/git/secure/db.passwd;
hostname = "127.0.0.1";
name = "git";
};
};
acme = {
enable = true;
admin-address = "admin@${domain}";
hostnames = [
"informis.land"
"imap.informis.land"
"smtp.informis.land"
"gemini.informis.land"
];
};
};
security.acme.certs.${host-fqdn}.email = "admin@${domain}";
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
virtualHosts = {
"${host-fqdn}" = {
enableACME = true;
forceSSL = true;
};
};
};
informis.cl-gemini = {
enable = true;
server-ip = host_ipv4;
document-root = "/srv/gemini/root";
ssl-private-key = "/srv/gemini/private/key.pem";
ssl-certificate = "/srv/gemini/private/cert.pem";
slynk-port = 4005;
textfiles-archive = "/srv/gemini/textfiles";
feeds = {
viator = {
title = "viator's phlog";
path = "/home/viator/gemini-public/feed/";
url = "gemini://informis.land/user/viator/feed/";
};
};
};
}

View File

@ -4,34 +4,87 @@ let
hostname = "zbox";
in {
imports = [
../defaults.nix
../networks/sea.fudo.org.nix
../profiles/desktop.nix
../hardware-configuration.nix
];
environment.systemPackages = with pkgs; [
glxinfo
];
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
imports = [
../defaults.nix
../hardware-configuration.nix
];
environment.systemPackages = with pkgs; [
androidenv.androidPkgs_9_0.platform-tools
android-studio
dnsproxy
glxinfo
hll2380dw-cups
nodejs
signal-desktop
thunderbird
usbutils
];
fudo.common = {
profile = "desktop";
site = "seattle";
enable-gui = true;
};
fudo.slynk = {
enable = true;
};
hardware.cpu.intel.updateMicrocode = true;
programs.bash.enableCompletion = true;
programs = {
adb.enable = true;
bash.enableCompletion = true;
};
services.xserver = {
videoDrivers = ["nvidia"];
displayManager.gdm.wayland = false;
# displayManager.gdm.wayland = false;
};
hardware.opengl.driSupport32Bit = true;
hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ];
hardware.opengl.driSupport = true;
networking.hostName = hostname;
hardware.pulseaudio.support32Bit = true;
networking = {
hostName = hostname;
macvlans = {
intif0 = {
interface = "eno1";
mode = "bridge";
};
};
interfaces = {
eno1.useDHCP = false;
intif0 = {
macAddress = "02:dd:80:52:83:9b";
useDHCP = false;
ipv4.addresses = [
{
address = "10.0.0.110";
prefixLength = 24;
}
];
};
};
};
hardware.bluetooth.enable = true;
users.users.niten = {
extraGroups = ["adbusers"];
};
virtualisation.lxd.enable = true;
}

View File

@ -0,0 +1,98 @@
{ host_ipv4, config }:
{
dnssec = true;
mx = ["smtp.informis.land"];
hosts = {
procul = {
ip-addresses = [ "172.86.179.18" ];
ssh-fingerprints = [
"4 1 2a8e086d3589ce50b58c55bc35638af8da23988e"
"4 2 55a9f7c0addf08bb24c62ced954574db6e95eff38ee56d6a2cff312d20eb910e"
"1 1 d089902f60751b3d35b5329bf7b906df254d5fa7"
"1 2 8deebf42bbc40881a327f561bffd5d7bd328a4fc94d4e4ce8c502a9c6cbdfb92"
];
};
};
default-host = "172.86.179.18";
srv-records = {
tcp = {
domain = [{
host = "ns1.informis.land";
port = 53;
}];
ssh = [{
host = "procul.informis.land";
port = 22;
}];
submission = [{
host = "procul.informis.land";
port = 587;
}];
kerberos = [{
host = "procul.informis.land";
port = 88;
}];
kerberos-adm = [{
host = "procul.informis.land";
port = 749;
}];
imaps = [{
host = "procul.informis.land";
port = 993;
priority = 0;
}];
pop3s = [{
host = "procul.informis.land";
port = 995;
priority = 10;
}];
http = [{
host = "procul.informis.land";
port = 80;
}];
https = [{
host = "procul.informis.land";
port = 443;
}];
};
udp = {
domain = [{
host = "ns1.informis.land";
port = 53;
}];
kerberos = [{
host = "procul.informis.land";
port = 88;
}];
kerberos-master = [{
host = "procul.informis.land";
port = 88;
}];
kpasswd = [{
host = "procul.informis.land";
port = 464;
}];
};
};
aliases = {
smtp = "procul.informis.land.";
imap = "procul.informis.land.";
gemini = "procul.informis.land.";
git = "procul.informis.land.";
};
extra-dns-records = [
''_kerberos IN TXT "INFORMIS.LAND"''
''@ IN TXT "v=spf1 mx ip4:${host_ipv4}/29 -all"''
''@ IN SPF "v=spf1 mx ip4:${host_ipv4}/29 -all"''
];
dmarc-report-address = "dmarc-report@informis.land";
}

14
informis/users.nix Normal file
View File

@ -0,0 +1,14 @@
{ config, ... }:
{
config = {
users.users = {
viator = {
isNormalUser = true;
description = "Viator";
createHome = true;
hashedPassword = "$6$a1q2Duoe35hd5$IaZGXPfqyGv9uq5DQm7DZq0vIHsUs39sLktBiBBqMiwl/f/Z4jSvNZLJp9DZJYe5u2qGBYh1ca.jsXvQA8FPZ/";
};
};
};
}

56
lib/ip.nix Normal file
View File

@ -0,0 +1,56 @@
{ lib }:
with lib;
let
joinString = lib.concatStringsSep;
pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1));
in rec {
generateNBits = n: let
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;
reverseIpv4 = ip: joinString "." (reverseList (splitString "." ip));
intToBinaryList = int: let
helper = int: cur: let
curExp = pow 2 cur;
in if (curExp > int) then
[]
else
[(if ((bitAnd curExp int) > 0) then 1 else 0)] ++ (helper int (cur + 1));
in reverseList (helper int 0);
leftShift = 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);
getNetworkBase = network: let
ip = elemAt (splitString "/" network) 0;
insignificantBits = 32 - (getNetworkMask network);
in intToIpv4 (leftShift (rightShift (ipv4ToInt ip) insignificantBits) insignificantBits);
networkMinIp = network: intToIpv4 (1 + (ipv4ToInt (getNetworkBase network)));
networkMaxIp = network: intToIpv4 (rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network)));
# To avoid broadcast IP...
networkMaxButOneIp = network: intToIpv4 ((rightPadBits (ipv4ToInt (getNetworkBase network)) (32 - (getNetworkMask network))) - 1);
}

24
packages/cl-gemini.nix Normal file
View File

@ -0,0 +1,24 @@
{ stdenv, fetchgit, pkgs }:
let
url = "https://git.informis.land/viator/cl-gemini.git";
version = "0.1";
in stdenv.mkDerivation {
name = "cl-gemini-${version}";
src = fetchgit {
url = "https://git.informis.land/viator/cl-gemini.git";
rev = "3de4d1945fc91d0c9f8f65a5d4b0d3f39fbdaa80";
sha256 = "1xzfsp5vp36a3yas5pnhj2a1dd72r72fw3l49ah19hvbapaikhsw";
fetchSubmodules = false;
};
phases = ["installPhase"];
installPhase = ''
mkdir -p "$out/lib/common-lisp/cl-gemini"
cp "$src/cl-gemini.asd" "$out/lib/common-lisp/cl-gemini"
cp -R "$src/src" "$out/lib/common-lisp/cl-gemini"
'';
}

View File

@ -0,0 +1,40 @@
{ stdenv, fetchurl, makeWrapper, cups, dpkg, a2ps, ghostscript, gnugrep, gnused, coreutils, file, perl, which }:
stdenv.mkDerivation rec {
pname = "hll2380dw-cups";
version = "3.2.0-1";
platform = "i386";
src = fetchurl {
url = "https://download.brother.com/welcome/dlf101772/hll2380dwcupswrapper-${version}.i386.deb";
sha256 = "08g3kx5lgwzb3f9ypj8knmpkkj0h3kv1i4gd20rzjxrx6vx1wbpl";
};
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ cups ghostscript dpkg a2ps ];
dontUnpack = true;
installPhase = ''
dpkg-deb -x $src $out
wrapProgram $out/opt/brother/Printers/HLL2380DW/cupswrapper/paperconfigml1 \
--prefix PATH : ${stdenv.lib.makeBinPath [
coreutils ghostscript gnugrep gnused
]}
mkdir -p $out/lib/cups/filter/
ln -s $out/opt/brother/Printers/HLL2380DW/cupswrapper/brother_lpdwrapper_HLL2380W \
$out/lib/cups/filter/brother_lpdwrapper_HLL2380DW
ln -s $out/opt/brother/Printers/HLL2380DW/paperconfigml1 \
$out/lib/cups/filter/
mkdir -p $out/share/cups/model
ln -s $out/opt/brother/Printers/HLL2380DW/cupswrapper/brother-HLL2380DW-cups-en.ppd $out/share/cups/model/
'';
meta = with stdenv.lib; {
homepage = http://www.brother.com/;
description = "Brother HL-L2380DW combined print driver";
license = licenses.unfree;
platforms = [ "x86_64-linux" ];
downloadPage = http://support.brother.com/g/b/downloadlist.aspx?c=us_ot&lang=en&prod=hll2380dw_us&os=128;
};
}

View File

@ -15,11 +15,10 @@
};
});
minecraft-server_1_15_2 = pkgs.minecraft-server.overrideAttrs (oldAttrs: rec {
version = "1.15.2";
minecraft-current = pkgs.minecraft.overrideAttrs (oldAttrs: rec {
src = builtins.fetchurl {
url = "https://launcher.mojang.com/v1/objects/bb2b6b1aefcd70dfd1892149ac3a215f6c636b07/server.jar";
sha256 = "12kynrpxgcdg8x12wcvwkxka0fxgm5siqg8qq0nnmv0443f8dkw0";
url = "https://launcher.mojang.com/download/Minecraft.tar.gz";
sha256 = "1k9gf1v1law4kiz8f7i2fxkj5vq2cm37b3ys95zpyf4aiw5nzg33";
};
});
@ -45,5 +44,15 @@
configureFlags = oldAttrs.configureFlags ++ [ "--with-gssapi" ];
buildInputs = oldAttrs.buildInputs ++ [ pkgs.krb5 ];
});
hll2380dw-cups = import ./hll2380dw-cups.nix {
inherit (pkgs) stdenv fetchurl makeWrapper cups dpkg a2ps ghostscript gnugrep gnused coreutils file perl which;
};
cl-gemini = import ./cl-gemini.nix {
pkgs = pkgs;
stdenv = pkgs.stdenv;
fetchgit = pkgs.fetchgit;
};
};
}

View File

@ -0,0 +1,109 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
qCVCWa+H/Pev226LSn2PVKK5AlBbs6uSNx/Phg==
=8Fyj
-----END PGP PUBLIC KEY BLOCK-----