From 4d6e8cb2648e31777d0cba28129f66835f033193 Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Mon, 16 Nov 2020 12:39:37 -0800 Subject: [PATCH 01/16] Working backplane dyndns client/server --- config/fudo/backplane/default.nix | 7 + config/fudo/backplane/dns.nix | 271 ++++++++++++++++++++++ config/fudo/client/dns.nix | 94 ++++++++ config/fudo/local-network.nix | 40 +--- config/fudo/password.nix | 114 +++++++++ config/fudo/postgres.nix | 218 ++++++++++++----- config/local.nix | 3 + defaults.nix | 1 + fudo/profiles/server.nix | 2 +- hosts/nostromo.nix | 180 ++++++++++---- lib/dns.nix | 58 +++++ lib/utils.nix | 14 ++ packages/backplane-dns-client.nix | 35 +++ packages/backplane-dns.nix | 24 ++ packages/local.nix | 36 +++ static/backplane-dns-client/Gemfile | 3 + static/backplane-dns-client/Gemfile.lock | 13 ++ static/backplane-dns-client/dns-client.rb | 250 ++++++++++++++++++++ static/backplane-dns-client/gemset.nix | 12 + 19 files changed, 1242 insertions(+), 133 deletions(-) create mode 100644 config/fudo/backplane/default.nix create mode 100644 config/fudo/backplane/dns.nix create mode 100644 config/fudo/client/dns.nix create mode 100644 config/fudo/password.nix create mode 100644 lib/dns.nix create mode 100644 lib/utils.nix create mode 100644 packages/backplane-dns-client.nix create mode 100644 packages/backplane-dns.nix create mode 100644 static/backplane-dns-client/Gemfile create mode 100644 static/backplane-dns-client/Gemfile.lock create mode 100644 static/backplane-dns-client/dns-client.rb create mode 100644 static/backplane-dns-client/gemset.nix diff --git a/config/fudo/backplane/default.nix b/config/fudo/backplane/default.nix new file mode 100644 index 0000000..028c784 --- /dev/null +++ b/config/fudo/backplane/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + imports = [ + ./dns.nix + ]; +} diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix new file mode 100644 index 0000000..beeeaa3 --- /dev/null +++ b/config/fudo/backplane/dns.nix @@ -0,0 +1,271 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.fudo.backplane.dns; + + dns = import ../../../lib/dns.nix { inherit lib; }; + + backup-directory = "/var/lib/fudo/backplane/dns"; + + powerdns-home = "/var/lib/powerdns"; + + powerdns-conf-dir = "${powerdns-home}/conf.d"; + + backplaneOpts = { ... }: { + options = { + host = mkOption { + type = types.str; + description = "Hostname of the backplane jabber server."; + }; + + role = mkOption { + type = types.str; + description = "Backplane XMPP role name for the DNS server."; + default = "service-dns"; + }; + + password-file = mkOption { + type = types.str; + description = "File containing XMPP password for backplane role."; + }; + + database = mkOption { + type = with types; submodule databaseOpts; + description = "Database settings for backplane server."; + }; + }; + }; + + databaseOpts = { ... }: { + options = { + host = mkOption { + type = types.str; + description = "Hostname or IP of the PostgreSQL server."; + }; + + database = mkOption { + type = types.str; + description = "Database to use for DNS backplane."; + default = "backplane_dns"; + }; + + username = mkOption { + type = types.str; + description = "Database user for DNS backplane."; + default = "backplane_dns"; + }; + + password-file = mkOption { + type = types.str; + description = "File containing password for database user."; + }; + }; + }; + + lisp-libs = []; + + launchScript = pkgs.writeText "launch-backplane-dns.lisp" '' + (load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))) + (ql:quickload :backplane-dns) + (backplane-dns:start-listener-with-env) + (loop (sleep 600)) + ''; + +in { + options.fudo.backplane.dns = { + enable = mkEnableOption "Enable backplane dynamic DNS server."; + + port = mkOption { + type = types.port; + description = "Port on which to serve authoritative DNS requests."; + default = 53; + }; + + listen-addresses = mkOption { + type = with types; listOf str; + description = "IP addresses on which to listen for dns requests."; + default = [ "0.0.0.0" ]; + }; + + required-services = mkOption { + type = with types; listOf str; + description = "A list of services required before the DNS server can start."; + }; + + user = mkOption { + type = types.str; + description = "User as which to run DNS backplane listener service."; + default = "backplane-dns"; + }; + + group = mkOption { + type = types.str; + description = "Group as which to run DNS backplane listener service."; + default = "backplane-dns"; + }; + + database = mkOption { + type = with types; submodule databaseOpts; + description = "Database settings for the DNS server."; + }; + + backplane = mkOption { + type = with types; submodule backplaneOpts; + description = "Backplane Jabber settings for the DNS server."; + }; + }; + + config = mkIf cfg.enable { + users = { + users = { + "${cfg.user}" = { + isSystemUser = true; + group = cfg.group; + createHome = true; + home = "/var/home/${cfg.user}"; + }; + backplane-powerdns = { + isSystemUser = true; + }; + }; + + groups = { + "${cfg.group}" = { + members = [cfg.user]; + }; + backplane-powerdns = { + members = [ "backplane-powerdns" ]; + }; + }; + }; + + systemd = { + targets = { + backplane-dns = { + description = "Fudo DNS backplane services."; + wantedBy = [ "multi-user.target" ]; + }; + }; + + services = { + + backplane-powerdns = let + configDir = pkgs.writeTextDir "pdns.conf" '' + local-address=${lib.concatStringsSep ", " cfg.listen-addresses} + local-port=${toString cfg.port} + launch= + include-dir=${powerdns-conf-dir}/ + ''; + + psql-user = config.services.postgresql.superUser; + + in { + unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)"; + description = "Backplane PowerDNS name server"; + requires = [ + "postgresql.service" + "backplane-dns-config-generator.service" + "backplane-dns.target" + ]; + after = [ + "network.target" + "postgresql.service" + ]; + wantedBy = [ "multi-user.target" ]; + + path = with pkgs; [ postgresql ]; + + serviceConfig = { + Restart="on-failure"; + RestartSec="10"; + StartLimitInterval="0"; + PrivateDevices=true; + # CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT"; + # NoNewPrivileges=true; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${powerdns-home}"; + ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}"; + ProtectSystem="full"; + # ProtectHome=true; + RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6"; + }; + }; + + backplane-dns-config-generator = { + description = "Generate postgres configuration for backplane DNS server."; + requiredBy = [ "backplane-powerdns.service" ]; + requires = cfg.required-services; + serviceConfig.Type = "oneshot"; + restartIfChanged = true; + partOf = [ "backplane-dns.target" ]; + + # This builds the config in a bash script, to avoid storing the password + # in the nix store at any point + script = '' + if [ ! -d ${powerdns-conf-dir} ]; then + mkdir ${powerdns-conf-dir} + fi + + TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX) + TMPCONF=$TMPDIR/pdns.local.gpgsql.conf + + if [ ! -f ${cfg.database.password-file} ]; then + echo "${cfg.database.password-file} does not exist!" + exit 1 + fi + + touch $TMPCONF + chown backplane-powerdns:backplane-powerdns $TMPCONF + chmod go-rwx $TMPCONF + PASSWORD=$(cat ${cfg.database.password-file}) + echo "launch+=gpgsql" >> $TMPCONF + echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF + echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF + echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF + echo "gpgsql-password=$PASSWORD" >> $TMPCONF + echo "gpgsql-dnssec=yes" >> $TMPCONF + + mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf + + rm -rf $TMPDIR + + exit 0 + ''; + }; + + backplane-dns = { + description = "Fudo DNS Backplane Server"; + restartIfChanged = true; + + serviceConfig = { + ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init"; + ExecStart = "${pkgs.sbcl}/bin/sbcl --load ${launchScript}"; + Restart = "on-failure"; + PIDFile = "/run/backplane-dns.$USERNAME.pid"; + User = cfg.user; + Group = cfg.group; + }; + + environment = { + LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; + + FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; + FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; + FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; + FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host; + FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database; + FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.backplane.database.username; + FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = cfg.backplane.database.password-file; + + CL_SOURCE_REGISTRY = lib.concatStringsSep ":" (map (pkg: "${pkg}//") + (lisp-libs ++ [pkgs.backplane-dns])); + }; + + requires = cfg.required-services; + partOf = [ "backplane-dns.target" ]; + wantedBy = [ "multi-user.target" ]; + }; + }; + }; + }; +} diff --git a/config/fudo/client/dns.nix b/config/fudo/client/dns.nix new file mode 100644 index 0000000..c2bcced --- /dev/null +++ b/config/fudo/client/dns.nix @@ -0,0 +1,94 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.fudo.client.dns; + +in { + options.fudo.client.dns = { + enable = mkEnableOption "Enable Fudo DynDNS Client."; + + ipv4 = mkOption { + type = types.bool; + default = true; + description = "Report host external IPv4 address to Fudo DynDNS server."; + }; + + ipv6 = mkOption { + type = types.bool; + default = true; + description = "Report host external IPv6 address to Fudo DynDNS server."; + }; + + domain = mkOption { + type = types.str; + description = "Domain under which this host is registered."; + default = "fudo.link"; + }; + + server = mkOption { + type = types.str; + description = "Backplane DNS server to which changes will be reported."; + default = "backplane.fudo.org"; + }; + + password-file = mkOption { + type = types.str; + description = "File containing host password for backplane."; + example = "/path/to/secret.passwd"; + }; + + frequency = mkOption { + type = types.str; + description = "Frequency at which to report the local IP(s) to backplane."; + default = "*:0/15"; + }; + + user = mkOption { + type = types.str; + description = "User as which to run the client script (must have access to password file)."; + default = "backplane-dns-client"; + }; + + external-interface = mkOption { + type = with types; nullOr str; + description = "Interface with which this host communicates with the larger internet."; + default = null; + }; + }; + + config = mkIf cfg.enable { + users.users = { + "${cfg.user}" = { + isSystemUser = true; + createHome = true; + home = "/var/home/${cfg.user}"; + }; + }; + + systemd = { + timers.backplane-dns-client = { + enable = true; + description = "Report local IP addresses to Fudo backplane."; + partOf = [ "backplane-dns-client.service" ]; + wantedBy = [ "timers.target" ]; + requires = [ "network-online.target" ]; + timerConfig = { + OnCalendar = cfg.frequency; + }; + }; + + services.backplane-dns-client = { + enable = true; + serviceConfig = { + Type = "oneshot"; + StandardOutput = "journal"; + User = cfg.user; + }; + script = '' + ${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file} + ''; + }; + }; + }; +} diff --git a/config/fudo/local-network.nix b/config/fudo/local-network.nix index 4cc653a..1b036f2 100644 --- a/config/fudo/local-network.nix +++ b/config/fudo/local-network.nix @@ -7,7 +7,8 @@ let join-lines = concatStringsSep "\n"; - ip = import ../../lib/ip.nix { lib = lib; }; + ip = import ../../lib/ip.nix { inherit lib; }; + dns = import ../../lib/dns.nix { inherit lib; }; hostOpts = { hostname, ... }: { options = { @@ -35,33 +36,6 @@ let traceout = out: builtins.trace out out; - 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 when connecting."; - }; - - host = mkOption { - type = str; - description = "Host to contact for this service."; - example = "my-host.my-domain.com."; - }; - }; - }; - in { options.fudo.local-network = { @@ -143,7 +117,7 @@ in { }; srv-records = mkOption { - type = with types; attrsOf (attrsOf (listOf (submodule srvRecordOpts))); + type = dns.srvRecords; description = "Map of traffic type to srv records."; default = {}; example = { @@ -231,12 +205,6 @@ in { 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" ]; @@ -267,7 +235,7 @@ in { ${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)} + ${dns.srvRecordsToBindZone cfg.srv-records} ''; } ] ++ blockZones; diff --git a/config/fudo/password.nix b/config/fudo/password.nix new file mode 100644 index 0000000..8381df1 --- /dev/null +++ b/config/fudo/password.nix @@ -0,0 +1,114 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.fudo.password; + + genOpts = { + options = { + file = mkOption { + type = types.str; + description = "Password file in which to store a generated password."; + }; + + user = mkOption { + type = types.str; + description = "User to which the file should belong."; + }; + + group = mkOption { + type = with types; nullOr str; + description = "Group to which the file should belong."; + default = "nogroup"; + }; + + restart-services = mkOption { + type = with types; listOf str; + description = "List of services to restart when the password file is generated."; + default = []; + }; + }; + }; + + generate-passwd-file = file: user: group: pkgs.writeShellScriptBin "generate-passwd-file.sh" '' + if touch ${file}; then + ${pkgs.pwgen}/bin/pwgen 30 1 > ${file} + else + echo "cannot write to ${file}" + exit 2 + fi + + if [ ! -f ${file} ]; then + echo "Failed to create file ${file}" + exit 3 + fi + + chown ${user}${optionalString (group != null) ":${group}"} ${file} + + if [ $? -ne 0 ]; then + rm ${file} + echo "failed to set permissions on ${file}" + exit 4 + fi + + ${if (group != null) then + "chmod 640 ${file}" + else + "chmod 600 ${file}"} + + echo "created password file ${file}" + exit 0 + ''; + + restart-script = service-name: '' + SYSCTL=${pkgs.systemd}/bin/systemctl + JOBTYPE=$(${pkgs.systemd}/bin/systemctl show ${service-name} -p Type) + if $SYSCTL is-active --quiet ${service-name} || + [ $JOBTYPE == "Type=simple" ] || + [ $JOBTYPE == "Type=oneshot" ] ; then + echo "restarting service ${service-name} because password has changed." + $SYSCTL restart ${service-name} + fi + ''; + + filterForRestarts = filterAttrs (name: opts: opts.restart-services != []); + +in { + options.fudo.password = { + file-generator = mkOption { + type = with types; loaOf (submodule genOpts); + description = "List of password files to generate."; + default = {}; + }; + }; + + config = { + systemd.targets.fudo-passwords = { + description = "Target indicating that all Fudo passwords have been generated."; + wantedBy = [ "default.target" ]; + }; + + systemd.services = fold (a: b: a // b) {} (mapAttrsToList (name: opts: { + "file-generator-${name}" = { + partOf = [ "fudo-passwords.target" ]; + serviceConfig.Type = "oneshot"; + description = "Generate password file for ${name}."; + script = "${generate-passwd-file opts.file opts.user opts.group}/bin/generate-passwd-file.sh"; + }; + + "file-generator-watcher-${name}" = mkIf (! (opts.restart-services == [])) { + description = "Restart services upon regenerating password for ${name}"; + after = [ "file-generator-${name}.service" ]; + partOf = [ "fudo-passwords.target" ]; + serviceConfig.Type = "oneshot"; + script = concatStringsSep "\n" (map restart-script opts.restart-services); + }; + }) cfg.file-generator); + + systemd.paths = mapAttrs' (name: opts: + nameValuePair "file-generator-watcher-${name}" { + partOf = [ "fudo-passwords.target"]; + pathConfig.PathChanged = opts.file; + }) (filterForRestarts cfg.file-generator); + }; +} diff --git a/config/fudo/postgres.nix b/config/fudo/postgres.nix index 4486a11..198db56 100644 --- a/config/fudo/postgres.nix +++ b/config/fudo/postgres.nix @@ -2,23 +2,51 @@ with lib; let - cfg = config.fudo.postgresql; + utils = import ../../lib/utils.nix { inherit lib; }; + + join-lines = lib.concatStringsSep "\n"; + + userDatabaseOpts = { database, ... }: { + options = { + access = mkOption { + type = types.str; + description = "Privileges for user on this database."; + default = "CONNECT"; + }; + + entity-access = mkOption { + type = with types; attrsOf str; + description = "A list of entities mapped to the access this user should have."; + default = {}; + example = { + "TABLE users" = "SELECT,DELETE"; + "ALL SEQUENCES IN public" = "SELECT"; + }; + }; + }; + }; + userOpts = { username, ... }: { options = { - password = mkOption { + password-file = mkOption { type = with types; nullOr str; - description = "The user's (plaintext) password."; + description = "A file containing the user's (plaintext) password."; default = null; }; databases = mkOption { - type = with types; loaOf str; - description = "Map of databases to which this user has access, to the required perms."; + type = with types; attrsOf (submodule userDatabaseOpts); + description = "Map of databases to required database/table perms."; default = {}; example = { - my_database = "ALL PRIVILEGES"; + my_database = { + access = "ALL PRIVILEGES"; + entity-access = { + "ALL TABLES" = "SELECT"; + }; + }; }; }; }; @@ -34,43 +62,68 @@ let }; }; - userDatabaseAccess = user: databases: - mapAttrs' (database: perms: - nameValuePair "DATABASE ${database}" perms) - databases; + filterPasswordedUsers = filterAttrs (user: opts: opts.password-file != null); - stringJoin = joiner: els: - if (length els) == 0 then - "" - else - foldr(lel: rel: "${lel}${joiner}${rel}") (last els) (init els); + password-setter-script = user: password-file: sql-file: '' + unset PASSWORD + if [ ! -f ${password-file} ]; then + echo "file does not exist: ${password-file}" + exit 1 + fi + PASSWORD=$(cat ${password-file}) + echo "setting password for user ${user}" + echo "ALTER USER ${user} ENCRYPTED PASSWORD '$PASSWORD';" >> ${sql-file} + ''; + + passwords-setter-script = users: + pkgs.writeScriptBin "postgres-set-passwords.sh" '' + #!${pkgs.bash}/bin/bash + + if [ $# -ne 1 ]; then + echo "usage: $0 output-file.sql" + exit 1 + fi + + OUTPUT_FILE=$1 + + if [ ! -f $OUTPUT_FILE ]; then + echo "file doesn't exist: $OUTPUT_FILE" + exit 2 + fi + + ${join-lines + (mapAttrsToList + (user: opts: password-setter-script user opts.password-file "$OUTPUT_FILE") + (filterPasswordedUsers users))} + ''; + + userDatabaseAccess = user: databases: + mapAttrs' (database: databaseOpts: + nameValuePair "DATABASE ${database}" databaseOpts.access) + databases; makeEntry = nw: "host all all ${nw} gss include_realm=0 krb_realm=FUDO.ORG"; - makeNetworksEntry = networks: - stringJoin "\n" (map makeEntry networks); - - setPasswordSql = username: attrs: - "ALTER USER ${username} ENCRYPTED PASSWORD '${attrs.password}';"; - - setPasswordsSql = users: - (stringJoin "\n" - (mapAttrsToList (username: attrs: setPasswordSql username attrs) - (filterAttrs (user: attrs: attrs.password != null) users))) + "\n"; + makeNetworksEntry = networks: join-lines (map makeEntry networks); makeLocalUserPasswordEntries = users: - stringJoin "\n" - (mapAttrsToList - (username: attrs: - stringJoin "\n" - (map (db: '' - local ${db} ${username} md5 - host ${db} ${username} 127.0.0.1/16 md5 - host ${db} ${username} ::1/128 md5 - '') (attrNames attrs.databases))) - users); + join-lines (mapAttrsToList + (user: opts: join-lines + (map (db: '' + local ${db} ${user} md5 + host ${db} ${user} 127.0.0.1/16 md5 + host ${db} ${user} ::1/128 md5 + '') (attrNames opts.databases))) + (filterPasswordedUsers users)); + userTableAccessSql = user: entity: access: "GRANT ${access} ON ${entity} TO ${user};"; + userDatabaseAccessSql = user: database: dbOpts: '' + \c ${database} + ${join-lines (mapAttrsToList (userTableAccessSql user) dbOpts.entity-access)} + ''; + userAccessSql = user: userOpts: join-lines (mapAttrsToList (userDatabaseAccessSql user) userOpts.databases); + usersAccessSql = users: join-lines (mapAttrsToList userAccessSql users); in { @@ -106,8 +159,15 @@ in { description = "A map of users to user attributes."; example = { sampleUser = { - password = "some-password"; - databases = [ "sample_user_db" ]; + password-file = "/path/to/password/file"; + databases = { + some_database = { + access = "CONNECT"; + entity-access = { + "TABLE some_table" = "SELECT,UPDATE"; + }; + }; + }; }; }; default = {}; @@ -136,6 +196,13 @@ in { description = "Users able to access the server via local socket."; default = []; }; + + required-services = mkOption { + type = with types; listOf str; + description = "List of services that should run before postgresql."; + default = []; + example = [ "password-generator.service" ]; + }; }; config = mkIf cfg.enable { @@ -166,13 +233,6 @@ in { group = "postgres"; source = cfg.keytab; }; - - "postgresql/private/user-script.sql" = { - mode = "0400"; - user = "postgres"; - group = "postgres"; - text = setPasswordsSql cfg.users; - }; }; }; @@ -187,15 +247,20 @@ in { package = pkgs.postgresql_11_gssapi; enableTCPIP = true; ensureDatabases = mapAttrsToList (name: value: name) cfg.databases; - ensureUsers = mapAttrsToList + ensureUsers = ((mapAttrsToList (username: attrs: { name = username; - ensurePermissions = - #{ "DATABASE ${username}" = "ALL PRIVILEGES"; }; - (userDatabaseAccess username attrs.databases); + ensurePermissions = userDatabaseAccess username attrs.databases; }) - cfg.users; + cfg.users) ++ (flatten (mapAttrsToList + (database: opts: + (map (username: { + name = username; + ensurePermissions = { + "DATABASE ${database}" = "ALL PRIVILEGES"; + }; + }) opts.users)) cfg.databases))); extraConfig = '' krb_server_keyfile = '/etc/postgresql/private/postgres.keytab' @@ -221,15 +286,54 @@ in { # local networks ${makeNetworksEntry cfg.local-networks} ''; - - # initialScript = pkgs.writeText "database-init.sql" '' - # ${setPasswordsSql cfg.users} - # ''; }; - systemd.services.postgresql.postStart = '' - ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${pkgs.postgresql}/bin/psql --port ${toString config.services.postgresql.port} -f /etc/postgresql/private/user-script.sql -d postgres - ${pkgs.coreutils}/bin/chgrp ${cfg.socket-group} ${cfg.socket-directory}/.s.PGSQL* - ''; + systemd = { + + services = { + + postgresql-password-setter = let + passwords-script = passwords-setter-script cfg.users; + password-wrapper-script = pkgs.writeScriptBin "password-script-wrapper.sh" '' + #!${pkgs.bash}/bin/bash + TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX) + echo "using temp dir $TMPDIR" + PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql + echo "password file $PASSWORD_SQL_FILE" + touch $PASSWORD_SQL_FILE + chown ${config.services.postgresql.superUser} $PASSWORD_SQL_FILE + chmod go-rwx $PASSWORD_SQL_FILE + ${passwords-script}/bin/postgres-set-passwords.sh $PASSWORD_SQL_FILE + echo "executing $PASSWORD_SQL_FILE" + ${pkgs.postgresql}/bin/psql --port ${toString config.services.postgresql.port} -d postgres -f $PASSWORD_SQL_FILE + echo rm $PASSWORD_SQL_FILE + echo "Postgresql user passwords set."; + exit 0 + ''; + + in { + description = "A service to set postgresql user passwords after the server has started."; + after = [ "postgresql.service" ] ++ cfg.required-services; + requires = [ "postgresql.service" ] ++ cfg.required-services; + serviceConfig = { + Type = "oneshot"; + User = config.services.postgresql.superUser; + }; + script = "${password-wrapper-script}/bin/password-script-wrapper.sh"; + }; + + postgresql.postStart = let + allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;"; + + extra-settings-sql = pkgs.writeText "settings.sql" '' + ${concatStringsSep "\n" (map allow-user-login (mapAttrsToList (key: val: key) cfg.users))} + ${usersAccessSql cfg.users} + ''; + in '' + ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${pkgs.postgresql}/bin/psql --port ${toString config.services.postgresql.port} -d postgres -f ${extra-settings-sql} + ${pkgs.coreutils}/bin/chgrp ${cfg.socket-group} ${cfg.socket-directory}/.s.PGSQL* + ''; + }; + }; }; } diff --git a/config/local.nix b/config/local.nix index e2fdf34..681da1e 100644 --- a/config/local.nix +++ b/config/local.nix @@ -5,7 +5,9 @@ with lib; imports = [ ./fudo/acme-for-hostname.nix ./fudo/authentication.nix + ./fudo/backplane ./fudo/chat.nix + ./fudo/client/dns.nix ./fudo/common.nix ./fudo/dns.nix ./fudo/git.nix @@ -18,6 +20,7 @@ with lib; ./fudo/minecraft-server.nix ./fudo/netinfo-email.nix ./fudo/node-exporter.nix + ./fudo/password.nix ./fudo/postgres.nix ./fudo/prometheus.nix ./fudo/secure-dns-proxy.nix diff --git a/defaults.nix b/defaults.nix index d6b19cd..4178d9c 100644 --- a/defaults.nix +++ b/defaults.nix @@ -59,6 +59,7 @@ lshw mkpasswd ncurses5 + nixfmt nix-index nix-prefetch-git nmap diff --git a/fudo/profiles/server.nix b/fudo/profiles/server.nix index b99c451..c9d9dd3 100644 --- a/fudo/profiles/server.nix +++ b/fudo/profiles/server.nix @@ -32,7 +32,7 @@ let if [ $# -gt 1 ]; then echo "usage: $0 [timeout]" exit 1 - elif [ $# -eq 1 ]; then + elif [ $# -eq 1 ]; the TIMEOUT=$1 else TIMEOUT=15m diff --git a/hosts/nostromo.nix b/hosts/nostromo.nix index 4779a37..d912e2c 100644 --- a/hosts/nostromo.nix +++ b/hosts/nostromo.nix @@ -1,12 +1,21 @@ { lib, config, pkgs, ... }: +with lib; let hostname = "nostromo"; host-internal-ip = "10.0.0.1"; - inherit (lib.strings) concatStringsSep; in { + environment.systemPackages = with pkgs; [ + dnsproxy + google-photos-uploader + libguestfs-with-appliance + libvirt + powerdns + virtmanager + ]; + boot.kernelModules = [ "kvm-amd" ]; boot.loader.grub.enable = true; @@ -34,7 +43,6 @@ in { 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; }; @@ -86,21 +94,150 @@ in { nat = { enable = true; externalInterface = "eno2"; - internalInterfaces = ["intif0"]; + internalInterfaces = [ "intif0" ]; + }; + }; + + users = { + users = { + backplane-powerdns = { + isSystemUser = true; + }; + backplane-dns = { + isSystemUser = true; + }; + fudo-client = { + isSystemUser = true; + }; + }; + + groups = { + backplane-powerdns = { + members = [ "backplane-powerdns" ]; + }; + backplane-dns = { + members = [ "backplane-dns" ]; + }; }; }; fudo = { + password.file-generator = { + dns_backplane_powerdns = { + file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + user = config.services.postgresql.superUser; + group = "backplane-powerdns"; + restart-services = [ + "backplane-dns-config-generator.service" + "postgresql-password-setter.service" + "backplane-powerdns.service" + ]; + }; + dns_backplane_database = { + file = "/srv/backplane/dns/secure/db_backplane.passwd"; + user = config.services.postgresql.superUser; + group = "backplane-dns"; + restart-services = [ + "backplane-dns.service" + "postgresql-password-setter.service" + ]; + }; + }; + + backplane.dns = { + enable = true; + port = 353; + listen-addresses = [ "10.0.0.1" ]; + required-services = [ "fudo-passwords.target" ]; + user = "backplane-dns"; + group = "backplane-dns"; + database = { + username = "backplane_powerdns"; + database = "backplane_dns"; + # Uses an IP to avoid cyclical dependency...not really relevant, but + # whatever + host = "127.0.0.1"; + password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + }; + backplane = { + host = "backplane.fudo.org"; + role = "service-dns"; + password-file = "/srv/backplane/dns/secure/backplane.passwd"; + database = { + username = "backplane_dns"; + database = "backplane_dns"; + host = "127.0.0.1"; + password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; + }; + }; + }; + + client.dns = { + enable = true; + ipv4 = true; + ipv6 = true; + domain = "dyn.fudo.org"; + user = "fudo-client"; + external-interface = "eno2"; + password-file = "/srv/client/secure/client.passwd"; + }; + 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"; + required-services = [ "fudo-passwords.target" ]; local-networks = [ "10.0.0.1/24" "127.0.0.1/8" ]; + + users = { + backplane_powerdns = { + password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT"; + }; + }; + }; + }; + backplane_dns = { + password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; + }; + }; + niten = { + databases = { + backplane_dns = { + access = "ALL PRIVILEGES"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; + "ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES"; + }; + }; + }; + }; + }; + + local-users = ["niten"]; + + databases = { + backplane_dns = { + users = ["niten"]; + }; + }; }; secure-dns-proxy = { @@ -119,13 +256,6 @@ in { }; }; - environment.systemPackages = with pkgs; [ - dnsproxy - libguestfs-with-appliance - libvirt - virtmanager - ]; - virtualisation = { docker = { enable = true; @@ -166,7 +296,7 @@ in { }; services = { - dhcpd6.enable = false; + # dhcpd6.enable = true; nginx = { enable = true; @@ -192,33 +322,5 @@ in { }; }; }; - - # 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"]; - # }; - # }; }; } diff --git a/lib/dns.nix b/lib/dns.nix new file mode 100644 index 0000000..7efaf8c --- /dev/null +++ b/lib/dns.nix @@ -0,0 +1,58 @@ +{ lib }: + +with lib; +let + join-lines = concatStringsSep "\n"; + + 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); + + 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 when connecting."; + }; + + host = mkOption { + type = str; + description = "Host to contact for this service."; + example = "my-host.my-domain.com."; + }; + }; + }; + + srvRecordPair = domain: protocol: type: record: { + "_${type}._${protocol}.${domain}" = "${toString record.priority} ${toString record.weight} ${toString record.port} ${record.host}."; + }; + +in rec { + + srvRecords = with types; attrsOf (attrsOf (listOf (submodule srvRecordOpts))); + + srvRecordsToBindZone = srvRecords: join-lines (mapAttrsToList makeSrvProtocolRecords srvRecords); + + concatMapAttrs = f: attrs: concatMap (x: x) (mapAttrsToList (key: val: f key val) attrs); + + srvRecordsToPairs = domain: srvRecords: + listToAttrs + (concatMapAttrs (protocol: types: + concatMapAttrs (type: records: map (srvRecordPair domain protocol type) records) types) + srvRecords); +} diff --git a/lib/utils.nix b/lib/utils.nix new file mode 100644 index 0000000..6c6a581 --- /dev/null +++ b/lib/utils.nix @@ -0,0 +1,14 @@ +{ lib }: + +with lib; +{ + recursiveMergeAttrs = a: b: let + commonAttrs = intersectLists (attrNames a) (attrNames b); + aAttrs = subtractLists (attrNames a) commonAttrs; + bAttrs = subtractLists (attrNames b) commonAttrs; + aSide = (filterAttrs (k: v: elem k aAttrs) a); + bSide = (filterAttrs (k: v: elem k bAttrs) b); + common = (foldr (a: b: a // b) {} + (map (k: { ${k} = a.${k} // b.${k}; }) commonAttrs)); + in aSide // bSide // common; +} diff --git a/packages/backplane-dns-client.nix b/packages/backplane-dns-client.nix new file mode 100644 index 0000000..ab9dfdc --- /dev/null +++ b/packages/backplane-dns-client.nix @@ -0,0 +1,35 @@ +{ stdenv, fetchgit, pkgs, bundlerEnv }: + +let + url = "https://git.fudo.org/fudo-public/backplane-dns-client.git"; + version = "0.1"; + srcdir = ../static/backplane-dns-client; + gems = bundlerEnv { + name = "backplane-dns-client-env"; + ruby = pkgs.ruby; + gemdir = srcdir; + }; + +in stdenv.mkDerivation { + name = "backplane-dns-client-${version}"; + + src = srcdir; + + buildInputs = [gems pkgs.ruby]; + + phases = ["installPhase"]; + + installPhase = '' + mkdir -p "$out/bin" "$out/lib" + + cp "$src/dns-client.rb" "$out/lib" + + BIN="$out/bin/backplane-dns-client" + + cat > $BIN < e + nil + end + end + @client = nil + end + + def send(msg_content) + msg_id = SecureRandom::uuid + encoded_payload = payload(msg_content, msg_id).to_json + msg = Jabber::Message.new(@service_jid, encoded_payload) + msg.type = :chat + @client.send(msg) + response = receive_response(msg_id) + response and response["status"] == "OK" + end + + def send_ip(ip) + send(ip_payload(ip)) + end + + def payload(req, msg_id) + { + version: 1, + service: :dns, + msgid: msg_id, + payload: req + } + end + + def ip_payload(ip) + { + request: ip.ipv4? ? :change_ipv4 : :change_ipv6, + domain: @domain, + ip: ip.to_s + } + end + + def register_response_callback + @client.add_message_callback do |msg| + enqueue_message(JSON.parse(msg.body)) + end + end + + def enqueue_message(msg) + @responses << msg + end + + def receive_response(msg_id) + msg = @responses.pop + return msg if (msg and (msg["msgid"] == msg_id.to_s)) + raise "failed to receive message: #{msg}" + end +end + +RESERVED_V4_NETWORKS = [ + "0.0.0.0/8", + "10.0.0.0/8", + "100.64.0.0/10", + "127.0.0.0/8", + "169.254.0.0/16", + "172.16.0.0/12", + "192.0.0.0/24", + "192.0.2.0/24", + "192.88.99.0/24", + "192.168.0.0/16", + "198.18.0.0/15", + "198.51.100.0/24", + "203.0.113.0/24", + "224.0.0.0/4", + "240.0.0.0/4", + "255.255.255.255/32" +].map { |ip| IPAddr.new(ip) } + +def public_ip?(ip) + if (ip.ipv4?) + not RESERVED_V4_NETWORKS.any? { |network| network.include? ip } + elsif (ip.ipv6?) + not (ip.link_local? or ip.loopback? or ip.private?) + else + false + end +end + +def to_ipaddr(addrinfo) + if addrinfo.ipv4? + IPAddr.new addrinfo.ip_address + else + IPAddr.new(addrinfo.ip_address.split("%")[0]) + end +end + +def local_addresses + Socket::ip_address_list.map do |addrinfo| + to_ipaddr(addrinfo) + end.select { |ip| public_ip?(ip) } +end + +def interface_addresses(interface) + Socket::getifaddrs.select do |ifaddr| + ifaddr.name == interface + end.select do |ifaddr| + ifaddr.addr.ip? and (ifaddr.flags & Socket::IFF_MULTICAST != 0) + end.map do |ifaddr| + to_ipaddr(ifaddr.addr) + end.filter do |ip| + public_ip? ip + end +end + +client = XMPPClient::new(options[:domain], + Socket::gethostname, + options[:server], + password) + +success = true + +begin + client.connect + + addrs = if options[:interface] + interface_addresses(options[:interface]) + else + local_addresses + end + + if options[:ipv4] + ipv4 = addrs.find { |ip| ip.ipv4? } + if ipv4 + puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN A => #{ipv4.to_s}" + if client.send_ip(ipv4) + puts "OK" + else + puts "ERROR" + success = false + end + else + puts "#{options[:server]}: no valid public IPv4 found on the local host" + end + end + + if options[:ipv6] + ipv6 = addrs.find { |ip| ip.ipv6? } + if ipv6 + puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN AAAA => #{ipv6.to_s}" + if client.send_ip(ipv6) + puts "OK" + else + puts "ERROR" + success = false + end + else + puts "#{options[:server]}: no valid public IPv6 found on the local host" + end + end +ensure + client.disconnect +end + +exit success ? 0 : 1 diff --git a/static/backplane-dns-client/gemset.nix b/static/backplane-dns-client/gemset.nix new file mode 100644 index 0000000..fc227aa --- /dev/null +++ b/static/backplane-dns-client/gemset.nix @@ -0,0 +1,12 @@ +{ + xmpp4r = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "15ls2yqjvflxrc8chv5pcdh2p1p9fjsky74yc8y7wvw90wz0izrb"; + type = "gem"; + }; + version = "0.5.6"; + }; +} \ No newline at end of file From 80be04edd5abe6fe6ba00b22dc8e829198e59ece Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Tue, 17 Nov 2020 15:23:07 -0800 Subject: [PATCH 02/16] Working backplane dns client/server --- config/fudo/client/dns.nix | 11 +- hosts/nostromo.nix | 124 ---------------------- static/backplane-dns-client/dns-client.rb | 41 +++++++ 3 files changed, 51 insertions(+), 125 deletions(-) diff --git a/config/fudo/client/dns.nix b/config/fudo/client/dns.nix index c2bcced..7a9d348 100644 --- a/config/fudo/client/dns.nix +++ b/config/fudo/client/dns.nix @@ -20,6 +20,12 @@ in { description = "Report host external IPv6 address to Fudo DynDNS server."; }; + sshfp = mkOption { + type = types.bool; + default = true; + description = "Report host SSH fingerprints to the Fudo DynDNS server."; + }; + domain = mkOption { type = types.str; description = "Domain under which this host is registered."; @@ -55,6 +61,8 @@ in { description = "Interface with which this host communicates with the larger internet."; default = null; }; + + # FIXME: take the relevant SSH package }; config = mkIf cfg.enable { @@ -85,8 +93,9 @@ in { StandardOutput = "journal"; User = cfg.user; }; + path = [ pkgs.openssh ]; script = '' - ${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file} + ${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString cfg.sshfp "-f"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file} ''; }; }; diff --git a/hosts/nostromo.nix b/hosts/nostromo.nix index d912e2c..22a1675 100644 --- a/hosts/nostromo.nix +++ b/hosts/nostromo.nix @@ -100,146 +100,22 @@ in { users = { users = { - backplane-powerdns = { - isSystemUser = true; - }; - backplane-dns = { - isSystemUser = true; - }; fudo-client = { isSystemUser = true; }; }; - - groups = { - backplane-powerdns = { - members = [ "backplane-powerdns" ]; - }; - backplane-dns = { - members = [ "backplane-dns" ]; - }; - }; }; fudo = { - password.file-generator = { - dns_backplane_powerdns = { - file = "/srv/backplane/dns/secure/db_powerdns.passwd"; - user = config.services.postgresql.superUser; - group = "backplane-powerdns"; - restart-services = [ - "backplane-dns-config-generator.service" - "postgresql-password-setter.service" - "backplane-powerdns.service" - ]; - }; - dns_backplane_database = { - file = "/srv/backplane/dns/secure/db_backplane.passwd"; - user = config.services.postgresql.superUser; - group = "backplane-dns"; - restart-services = [ - "backplane-dns.service" - "postgresql-password-setter.service" - ]; - }; - }; - - backplane.dns = { - enable = true; - port = 353; - listen-addresses = [ "10.0.0.1" ]; - required-services = [ "fudo-passwords.target" ]; - user = "backplane-dns"; - group = "backplane-dns"; - database = { - username = "backplane_powerdns"; - database = "backplane_dns"; - # Uses an IP to avoid cyclical dependency...not really relevant, but - # whatever - host = "127.0.0.1"; - password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; - }; - backplane = { - host = "backplane.fudo.org"; - role = "service-dns"; - password-file = "/srv/backplane/dns/secure/backplane.passwd"; - database = { - username = "backplane_dns"; - database = "backplane_dns"; - host = "127.0.0.1"; - password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; - }; - }; - }; - client.dns = { enable = true; ipv4 = true; ipv6 = true; - domain = "dyn.fudo.org"; user = "fudo-client"; external-interface = "eno2"; password-file = "/srv/client/secure/client.passwd"; }; - 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"; - required-services = [ "fudo-passwords.target" ]; - - local-networks = [ - "10.0.0.1/24" - "127.0.0.1/8" - ]; - - users = { - backplane_powerdns = { - password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; - databases = { - backplane_dns = { - access = "CONNECT"; - entity-access = { - "ALL TABLES IN SCHEMA public" = "SELECT"; - }; - }; - }; - }; - backplane_dns = { - password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; - databases = { - backplane_dns = { - access = "CONNECT"; - entity-access = { - "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE"; - "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; - }; - }; - }; - }; - niten = { - databases = { - backplane_dns = { - access = "ALL PRIVILEGES"; - entity-access = { - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; - "ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES"; - }; - }; - }; - }; - }; - - local-users = ["niten"]; - - databases = { - backplane_dns = { - users = ["niten"]; - }; - }; - }; - secure-dns-proxy = { enable = true; port = 3535; diff --git a/static/backplane-dns-client/dns-client.rb b/static/backplane-dns-client/dns-client.rb index 34570a1..d2fca16 100644 --- a/static/backplane-dns-client/dns-client.rb +++ b/static/backplane-dns-client/dns-client.rb @@ -42,6 +42,11 @@ OptionParser.new do |opts| "Check for a public IPv6 and register with the backplane.") do options[:ipv6] = true end + + opts.on("-f", "--sshfp", + "Register host SSH key fingerprints with the backplane.") do + options[:sshfp] = true + end end.parse! def error(msg) @@ -98,10 +103,12 @@ class XMPPClient def send(msg_content) msg_id = SecureRandom::uuid encoded_payload = payload(msg_content, msg_id).to_json + puts "payload: #{encoded_payload}" msg = Jabber::Message.new(@service_jid, encoded_payload) msg.type = :chat @client.send(msg) response = receive_response(msg_id) + puts "response: #{response}" response and response["status"] == "OK" end @@ -109,6 +116,10 @@ class XMPPClient send(ip_payload(ip)) end + def send_sshfp(fps) + send(sshfp_payload(fps)) + end + def payload(req, msg_id) { version: 1, @@ -126,6 +137,14 @@ class XMPPClient } end + def sshfp_payload(fp) + { + request: :change_sshfp, + domain: @domain, + sshfp: fp + } + end + def register_response_callback @client.add_message_callback do |msg| enqueue_message(JSON.parse(msg.body)) @@ -198,6 +217,13 @@ def interface_addresses(interface) end end +def host_sshfp + keys = `ssh-keygen -r hostname`.split("\n").map do |k| + k.match(/[0-9] [0-9] [a-fA-F0-9]{32,64}$/)[0] + end + keys.compact +end + client = XMPPClient::new(options[:domain], Socket::gethostname, options[:server], @@ -243,6 +269,21 @@ begin puts "#{options[:server]}: no valid public IPv6 found on the local host" end end + + if options[:sshfp] + fps = host_sshfp + if not fps.empty? + puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN SSHFP => #{fps}" + if client.send_sshfp(fps) + puts "OK" + else + puts "ERROR" + success = false + end + else + puts "#{options[:server]}: no valid sshfps found" + end + end ensure client.disconnect end From 9c92bb7e80334c5dd57197373eb9ee584c2ae17b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 17 Nov 2020 17:29:44 -0600 Subject: [PATCH 03/16] france as a dns backplane server --- config/fudo/backplane/dns.nix | 83 ++++++++++++------------ config/fudo/password.nix | 18 +++--- config/fudo/webmail.nix | 7 +- defaults.nix | 15 +++++ fudo/selby.ca.nix | 81 +++++++++++++++++++++++ hosts/france.nix | 84 ++++++++++++++++++------ hosts/france/backplane.nix | 117 ++++++++++++++++++++++++++++++++++ packages/backplane-dns.nix | 4 +- static/backplane-auth.scm | 4 +- 9 files changed, 339 insertions(+), 74 deletions(-) create mode 100644 fudo/selby.ca.nix create mode 100644 hosts/france/backplane.nix diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix index beeeaa3..e6dee50 100644 --- a/config/fudo/backplane/dns.nix +++ b/config/fudo/backplane/dns.nix @@ -191,17 +191,22 @@ in { }; }; - backplane-dns-config-generator = { - description = "Generate postgres configuration for backplane DNS server."; - requiredBy = [ "backplane-powerdns.service" ]; - requires = cfg.required-services; - serviceConfig.Type = "oneshot"; - restartIfChanged = true; - partOf = [ "backplane-dns.target" ]; + backplane-dns-config-generator = { + description = "Generate postgres configuration for backplane DNS server."; + requiredBy = [ "backplane-powerdns.service" ]; + requires = cfg.required-services; + serviceConfig.Type = "oneshot"; + restartIfChanged = true; + partOf = [ "backplane-dns.target" ]; - # This builds the config in a bash script, to avoid storing the password - # in the nix store at any point - script = '' + preStart = '' + mkdir -p ${powerdns-conf-dir} + chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir} + ''; + + # This builds the config in a bash script, to avoid storing the password + # in the nix store at any point + script = '' if [ ! -d ${powerdns-conf-dir} ]; then mkdir ${powerdns-conf-dir} fi @@ -231,40 +236,40 @@ in { exit 0 ''; + }; + + backplane-dns = { + description = "Fudo DNS Backplane Server"; + restartIfChanged = true; + + serviceConfig = { + ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init"; + ExecStart = "${pkgs.sbcl}/bin/sbcl --load ${launchScript}"; + Restart = "on-failure"; + PIDFile = "/run/backplane-dns.$USERNAME.pid"; + User = cfg.user; + Group = cfg.group; }; - backplane-dns = { - description = "Fudo DNS Backplane Server"; - restartIfChanged = true; + environment = { + LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; - serviceConfig = { - ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init"; - ExecStart = "${pkgs.sbcl}/bin/sbcl --load ${launchScript}"; - Restart = "on-failure"; - PIDFile = "/run/backplane-dns.$USERNAME.pid"; - User = cfg.user; - Group = cfg.group; - }; + FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; + FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; + FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; + FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host; + FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database; + FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.backplane.database.username; + FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = cfg.backplane.database.password-file; - environment = { - LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; - - FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; - FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; - FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; - FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host; - FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database; - FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.backplane.database.username; - FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = cfg.backplane.database.password-file; - - CL_SOURCE_REGISTRY = lib.concatStringsSep ":" (map (pkg: "${pkg}//") - (lisp-libs ++ [pkgs.backplane-dns])); - }; - - requires = cfg.required-services; - partOf = [ "backplane-dns.target" ]; - wantedBy = [ "multi-user.target" ]; + CL_SOURCE_REGISTRY = lib.concatStringsSep ":" (map (pkg: "${pkg}//") + (lisp-libs ++ [pkgs.backplane-dns])); }; + + requires = cfg.required-services; + partOf = [ "backplane-dns.target" ]; + wantedBy = [ "multi-user.target" ]; + }; }; }; }; diff --git a/config/fudo/password.nix b/config/fudo/password.nix index 8381df1..2322d9e 100644 --- a/config/fudo/password.nix +++ b/config/fudo/password.nix @@ -31,7 +31,15 @@ let }; generate-passwd-file = file: user: group: pkgs.writeShellScriptBin "generate-passwd-file.sh" '' + mkdir -p $(dirname ${file}) + if touch ${file}; then + chown ${user}${optionalString (group != null) ":${group}"} ${file} + if [ $? -ne 0 ]; then + rm ${file} + echo "failed to set permissions on ${file}" + exit 4 + fi ${pkgs.pwgen}/bin/pwgen 30 1 > ${file} else echo "cannot write to ${file}" @@ -43,14 +51,6 @@ let exit 3 fi - chown ${user}${optionalString (group != null) ":${group}"} ${file} - - if [ $? -ne 0 ]; then - rm ${file} - echo "failed to set permissions on ${file}" - exit 4 - fi - ${if (group != null) then "chmod 640 ${file}" else @@ -90,10 +90,12 @@ in { systemd.services = fold (a: b: a // b) {} (mapAttrsToList (name: opts: { "file-generator-${name}" = { + enable = true; partOf = [ "fudo-passwords.target" ]; serviceConfig.Type = "oneshot"; description = "Generate password file for ${name}."; script = "${generate-passwd-file opts.file opts.user opts.group}/bin/generate-passwd-file.sh"; + reloadIfChanged = true; }; "file-generator-watcher-${name}" = mkIf (! (opts.restart-services == [])) { diff --git a/config/fudo/webmail.nix b/config/fudo/webmail.nix index cf93502..9c8a5cd 100644 --- a/config/fudo/webmail.nix +++ b/config/fudo/webmail.nix @@ -329,6 +329,7 @@ in { wantedBy = [ "multi-user.target" ]; description = "Change ownership of the phpfpm socket for webmail once it's started."; requires = [ "phpfpm-webmail.service" ]; + after = [ "phpfpm.target" ]; serviceConfig = { ExecStart = '' ${pkgs.coreutils}/bin/chown ${webmail-user}:${webmail-group} ${config.services.phpfpm.pools.webmail.socket} @@ -337,8 +338,10 @@ in { }; nginx = { - requires = [ "webmail-init.service" ]; - wantedBy = [ "phpfpm-webmail-socket-perm.service" ]; + requires = [ + "webmail-init.service" + "phpfpm-webmail-socket-perm.service" + ]; }; }; }; diff --git a/defaults.nix b/defaults.nix index 89e36e1..daa666a 100644 --- a/defaults.nix +++ b/defaults.nix @@ -222,4 +222,19 @@ }; }; + systemd.services.fudo-environment-init = { + enable = true; + description = "Fudo common settings."; + wantedBy = [ "default.target" ]; + + # Careful, this WILL run many times + script = '' + # Create a directory for system user homedirs if it doesn't already exist + if [ ! -d /var/home ]; then + mkdir -p /var/home + chmod +x /var/home + fi + ''; + }; + } diff --git a/fudo/selby.ca.nix b/fudo/selby.ca.nix new file mode 100644 index 0000000..8d62144 --- /dev/null +++ b/fudo/selby.ca.nix @@ -0,0 +1,81 @@ +{ host_ipv4, config }: + +{ + dnssec = true; + + mx = ["mail.fudo.org"]; + + hosts = { + forum = { + ip-addresses = [ "208.81.3.117" ]; + }; + }; + + default-host = "208.81.3.117"; + + srv-records = { + tcp = { + domain = [{ + host = "ns1.fudo.org"; + port = "53"; + }]; + ssh = [{ + host = "france.fudo.org"; + port = 22; + }]; + submission = [{ + host = "mail.fudo.org"; + port = 587; + }]; + kerberos = [{ + host = "auth.fudo.org"; + port = 88; + }]; + imaps = [{ + host = "mail.fudo.org"; + port = 993; + }]; + pop3s = [{ + host = "mail.fudo.org"; + port = 995; + }]; + http = [{ + host = "forum.selby.ca"; + port = 80; + }]; + https = [{ + host = "forum.selby.ca"; + port = 80; + }]; + }; + udp = { + domain = [{ + host = "auth.fudo.org"; + port = 53; + }]; + kerberos = [{ + host = "auth.fudo.org"; + port = 88; + }]; + }; + }; + + aliases = { + pop = "mail.fudo.org."; + smtp = "mail.fudo.org."; + imap = "mail.fudo.org."; + mail = "mail.fudo.org."; + ns1 = "ns1.fudo.org."; + ns2 = "ns2.fudo.org."; + webmail = "france.fudo.org."; + forum = "frankfurt.fudo.org."; + }; + + extra-dns-records = [ + ''_kerberos IN TXT "FUDO.ORG"'' + ''@ 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@selby.ca"; +} diff --git a/hosts/france.nix b/hosts/france.nix index 72f8aec..73c6e46 100644 --- a/hosts/france.nix +++ b/hosts/france.nix @@ -32,6 +32,7 @@ in { ../hardware-configuration.nix ../defaults.nix ./france/jabber.nix + ./france/backplane.nix ]; environment.systemPackages = with pkgs; [ @@ -42,15 +43,6 @@ in { tshark ]; - # services.openssh = { - # listenAddresses = [ - # { - # addr = host_ipv4; - # port = 22; - # } - # ]; - # }; - fudo.common = { # Sets some server-common settings. See /etc/nixos/fudo/profiles/... profile = "server"; @@ -122,44 +114,94 @@ in { users = { fudo_git = { - password = fileContents "/srv/git/secure/db.passwd"; + password-file = "/srv/git/secure/db.passwd"; databases = { - fudo_git = "ALL PRIVILEGES"; + fudo_git = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; }; }; grafana = { - password = fileContents "/srv/grafana/secure/db.passwd"; + password-file = "/srv/grafana/secure/db.passwd"; databases = { - grafana = "ALL PRIVILEGES"; + grafana = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; }; }; mattermost = { - password = fileContents "/srv/mattermost/secure/db.passwd"; + password-file = "/srv/mattermost/secure/db.passwd"; databases = { - mattermost = "ALL PRIVILEGES"; + mattermost = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; }; }; webmail = { - password = fileContents "/srv/webmail/secure/db.passwd"; + password-file = "/srv/webmail/secure/db.passwd"; databases = { - webmail = "ALL PRIVILEGES"; + webmail = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; }; }; niten = {}; }; local-users = [ + "niten" "fudo_git" ]; databases = { - fudo_git = ["niten"]; - grafana = ["niten"]; - mattermost = ["niten"]; - webmail = ["niten"]; + fudo_git = { + users = ["niten"]; + }; + grafana = { + users = ["niten"]; + }; + mattermost = { + users = ["niten"]; + }; + webmail = { + users = ["niten"]; + }; }; }; + # fudo.dns = { + # enable = true; + + # dns-hosts = { + # "ns1.fudo.org" = host_ipv4; + # "ns2.fudo.org" = ""; + # }; + + # listen-ips = [host_ipv4]; + + # domains = { + # "selby.ca" = import ../fudo.org/selby.ca.nix { + # inherit host_ipv4 config; + # }; + # }; + # }; + # Not all users need access to france; don't allow LDAP-user access. fudo.authentication.enable = false; diff --git a/hosts/france/backplane.nix b/hosts/france/backplane.nix new file mode 100644 index 0000000..8ce0ca8 --- /dev/null +++ b/hosts/france/backplane.nix @@ -0,0 +1,117 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + +in { + config = { + users = { + users = { + backplane-powerdns = { + isSystemUser = true; + }; + backplane-dns = { + isSystemUser = true; + }; + }; + + groups = { + backplane-powerdns = { + members = [ "backplane-powerdns" ]; + }; + backplane-dns = { + members = [ "backplane-dns" ]; + }; + }; + }; + + fudo = { + password.file-generator = { + dns_backplane_powerdns = { + file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + user = config.services.postgresql.superUser; + group = "backplane-powerdns"; + restart-services = [ + "backplane-dns-config-generator.service" + "postgresql-password-setter.service" + "backplane-powerdns.service" + ]; + }; + dns_backplane_database = { + file = "/srv/backplane/dns/secure/db_backplane.passwd"; + user = config.services.postgresql.superUser; + group = "backplane-dns"; + restart-services = [ + "backplane-dns.service" + "postgresql-password-setter.service" + ]; + }; + }; + + postgresql = { + enable = true; + required-services = [ "fudo-passwords.target" ]; + + users = { + backplane_powerdns = { + password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT"; + }; + }; + }; + }; + backplane_dns = { + password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; + databases = { + backplane_dns = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; + }; + }; + }; + }; + }; + + databases = { + backplane_dns = { + users = ["niten"]; + }; + }; + }; + + backplane.dns = { + enable = true; + port = 353; + listen-addresses = [ "208.81.3.117" ]; + required-services = [ "fudo-passwords.target" ]; + user = "backplane-dns"; + group = "backplane-dns"; + database = { + username = "backplane_powerdns"; + database = "backplane_dns"; + # Uses an IP to avoid cyclical dependency...not really relevant, but + # whatever + host = "127.0.0.1"; + password-file = "/srv/backplane/dns/secure/db_powerdns.passwd"; + }; + backplane = { + host = "backplane.fudo.org"; + role = "service-dns"; + password-file = "/srv/backplane/dns/secure/backplane.passwd"; + database = { + username = "backplane_dns"; + database = "backplane_dns"; + host = "127.0.0.1"; + password-file = "/srv/backplane/dns/secure/db_backplane.passwd"; + }; + }; + }; + }; + }; +} diff --git a/packages/backplane-dns.nix b/packages/backplane-dns.nix index 0f437e6..85287bb 100644 --- a/packages/backplane-dns.nix +++ b/packages/backplane-dns.nix @@ -9,8 +9,8 @@ in stdenv.mkDerivation { src = fetchgit { url = url; - rev = "bfad36c9d223c7c8949fab50424c32a11164cd3a"; - sha256 = "0s8g5cm9mdjr9wb8w6a8lc1dv5cg85hxp8bdcgr1xd6hs4fnr745"; + rev = "543df72f3962cf91b0e0508d15cdc083a3cd7ed4"; + sha256 = "0hda1wjf9wd4rvxchdlxw0af3i2cvl5plg37ric3ckma6gfzkmm0"; fetchSubmodules = false; }; diff --git a/static/backplane-auth.scm b/static/backplane-auth.scm index f23ee80..e017da5 100644 --- a/static/backplane-auth.scm +++ b/static/backplane-auth.scm @@ -16,8 +16,8 @@ (format (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%")) (exit 1)) -(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)$") -(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)$") +(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)") +(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)") (define (make-verifier passwd-file) (let ((passwds (load passwd-file))) From 74784a28d74d703c9783c9ca5aa0772c7e3d10c9 Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Wed, 18 Nov 2020 10:48:40 -0800 Subject: [PATCH 04/16] switch to minimal hostname --- static/backplane-dns-client/dns-client.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/static/backplane-dns-client/dns-client.rb b/static/backplane-dns-client/dns-client.rb index d2fca16..0688cdf 100644 --- a/static/backplane-dns-client/dns-client.rb +++ b/static/backplane-dns-client/dns-client.rb @@ -224,8 +224,12 @@ def host_sshfp keys.compact end +def hostname + Socket.gethostname.split(".").first +end + client = XMPPClient::new(options[:domain], - Socket::gethostname, + hostname, options[:server], password) @@ -243,7 +247,7 @@ begin if options[:ipv4] ipv4 = addrs.find { |ip| ip.ipv4? } if ipv4 - puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN A => #{ipv4.to_s}" + puts "#{options[:server]}: #{hostname}.#{options[:domain]} IN A => #{ipv4.to_s}" if client.send_ip(ipv4) puts "OK" else @@ -258,7 +262,7 @@ begin if options[:ipv6] ipv6 = addrs.find { |ip| ip.ipv6? } if ipv6 - puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN AAAA => #{ipv6.to_s}" + puts "#{options[:server]}: #{hostname}.#{options[:domain]} IN AAAA => #{ipv6.to_s}" if client.send_ip(ipv6) puts "OK" else @@ -273,7 +277,7 @@ begin if options[:sshfp] fps = host_sshfp if not fps.empty? - puts "#{options[:server]}: #{Socket::gethostname}.#{options[:domain]} IN SSHFP => #{fps}" + puts "#{options[:server]}: #{hostname}.#{options[:domain]} IN SSHFP => #{fps}" if client.send_sshfp(fps) puts "OK" else From 8d88c8682efcd14373f5960019199c59647e781f Mon Sep 17 00:00:00 2001 From: "root@procul" Date: Thu, 19 Nov 2020 13:05:24 -0600 Subject: [PATCH 05/16] Changes on informis, for VPN --- config/fudo/vpn.nix | 107 ++++++++++++++++++++++++++++++++------------ hosts/procul.nix | 25 +++-------- 2 files changed, 85 insertions(+), 47 deletions(-) diff --git a/config/fudo/vpn.nix b/config/fudo/vpn.nix index bd0908b..5558153 100644 --- a/config/fudo/vpn.nix +++ b/config/fudo/vpn.nix @@ -4,29 +4,46 @@ with lib; let cfg = config.fudo.vpn; - peerOpts = { peer-name, ... }: { - options = with types; { - public-key = mkOption { - type = str; - description = "Peer public key."; - }; + ip-util = import ../../lib/ip.nix { lib = lib; }; - allowed-ips = mkOption { - type = listOf str; - description = "List of allowed IP ranges from which this peer can connect."; - example = [ "10.100.0.0/16" ]; - default = []; - }; - }; + generate-pubkey-pkg = name: privkey: + pkgs.runCommand "wireguard-${name}-pubkey" { + WIREGUARD_PRIVATE_KEY = privkey; + } '' + mkdir $out + PUBKEY=$(echo $WIREGUARD_PRIVATE_KEY | ${pkgs.wireguard-tools}/bin/wg pubkey) + echo $PUBKEY > $out/pubkey.key + ''; + + generate-client-config = privkey-file: server-pubkey: network: server-ip: listen-port: dns-servers: '' + [Interface] + Address = ${ip-util.networkMinIp network} + PrivateKey = ${fileContents privkey-file} + ListenPort = ${toString listen-port} + DNS = ${concatStringsSep ", " dns-servers} + + [Peer] + PublicKey = ${server-pubkey} + Endpoint = ${server-ip}:${toString listen-port} + AllowedIps = 0.0.0.0/0, ::/0 + PersistentKeepalive = 25 + ''; + + generate-peer-entry = peer-name: peer-privkey-path: peer-allowed-ips: let + peer-pkg = generate-pubkey-pkg "client-${peer-name}" (fileContents peer-privkey-path); + pubkey-path = "${peer-pkg}/pubkey.key"; + in { + publicKey = fileContents pubkey-path; + allowedIPs = peer-allowed-ips; }; in { options.fudo.vpn = with types; { enable = mkEnableOption "Enable Fudo VPN"; - ips = mkOption { + network = mkOption { type = str; - description = "IP range to assign this interface."; + description = "Network range to assign this interface."; default = "10.100.0.0/16"; }; @@ -42,36 +59,68 @@ in { default = 51820; }; - peers = mkOption { + dns-servers = mkOption { type = listOf str; - description = "A list of peers for which to generate config files."; - default = []; + description = "A list of dns servers to pass to clients."; + default = ["1.1.1.1" "8.8.8.8"]; + }; + + server-ip = mkOption { + type = str; + description = "IP of this WireGuard server."; }; peers = mkOption { - type = loaOf (submodule peerOpts); - description = "A list of peers allowed to connect."; + type = loaOf str; + description = "A map of peer name to a path pointing to that client's private key."; default = {}; example = { - peer0 = { - public-key = "xyz"; - allowed-ips = ["10.100.1.0/24"]; - }; + peer0 = "/path/to/priv.key"; }; }; }; config = mkIf cfg.enable { + environment.etc = let + peer-data = imap1 (i: peer:{ + name = peer.name; + privkey-path = peer.privkey-path; + network-range = let + base = ip-util.intToIpv4 + ((ip-util.ipv4ToInt (ip-util.getNetworkBase cfg.network)) + (i * 256)); + in "${base}/24"; + }) (mapAttrsToList (name: privkey-path: { + name = name; + privkey-path = privkey-path; + }) cfg.peers); + + server-pubkey-pkg = generate-pubkey-pkg "server-pubkey" (fileContents cfg.private-key-file); + + server-pubkey = fileContents "${server-pubkey-pkg}/pubkey.key"; + + in listToAttrs + (map (peer: nameValuePair "wireguard/clients/${peer.name}.conf" { + mode = "0400"; + user = "root"; + group = "root"; + text = generate-client-config + peer.privkey-path + server-pubkey + peer.network-range + cfg.server-ip + cfg.listen-port + cfg.dns-servers; + }) peer-data); + networking.wireguard = { enable = true; interfaces.wgtun0 = { generatePrivateKeyFile = false; - ips = [ cfg.ips ]; + ips = [ cfg.network ]; listenPort = cfg.listen-port; - peers = mapAttrsToList (peer-name: peer-config: { - publicKey = peer-config.public-key; - allowedIPs = peer-config.allowed-ips; - }) cfg.peers; + peers = mapAttrsToList + (name: private-key: generate-peer-entry name private-key ["0.0.0.0/0" "::/0"]) + cfg.peers; privateKeyFile = cfg.private-key-file; }; }; diff --git a/hosts/procul.nix b/hosts/procul.nix index ead5d09..3829b15 100644 --- a/hosts/procul.nix +++ b/hosts/procul.nix @@ -31,10 +31,6 @@ in { ../informis/users.nix ]; - environment.systemPackages = with pkgs; [ - multipath-tools - ]; - networking = { hostName = hostname; @@ -273,22 +269,15 @@ in { }; fudo.vpn = { - enable = true; - ips = "10.100.0.0/16"; + # fer some fuckin reason this sets the default gw to the vpn interface + enable = false; + network = "10.100.0.0/16"; + server-ip = host_ipv4; private-key-file = "/srv/wireguard/secure/secret.key"; peers = { - peter = { - allowed-ips = [ "10.100.1.0/24" ]; - public-key = "d1NfRFWRkcKq2gxvqfMy7Oe+JFYf5DjomnsTyisvgB4="; - }; - ken = { - allowed-ips = [ "10.100.2.0/24" ]; - public-key = "y294rTCK0iSRhA6EIOErPzEuqzJMuYAG4XbHasySMVU="; - }; - helen = { - allowed-ips = [ "10.100.3.0/24" ]; - public-key = "7Hdko6RibhIYdoPLWXGwmElY5vKvZ+rURmqFTDUfC2w="; - }; + peter = "/srv/wireguard/clients/peter.key"; + ken = "/srv/wireguard/clients/ken.key"; + helen = "/srv/wireguard/clients/helen.key"; }; }; From b5fcbc1a7037b425b71c791c37237647e75a2fca Mon Sep 17 00:00:00 2001 From: "root@procul" Date: Thu, 19 Nov 2020 16:16:04 -0600 Subject: [PATCH 06/16] Local changes to procul --- config/fudo/dns.nix | 2 +- hosts/procul.nix | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/config/fudo/dns.nix b/config/fudo/dns.nix index 9de4805..e61920c 100644 --- a/config/fudo/dns.nix +++ b/config/fudo/dns.nix @@ -149,7 +149,7 @@ let nsRecords = ns-hosts: join-lines ((mapAttrsToList (host: _: "@ IN NS ${host}.") ns-hosts) ++ - (mapAttrsToList (host: ip: "${host} IN A ${ip}") ns-hosts)); + (mapAttrsToList (host: ip: "${host}. IN A ${ip}") ns-hosts)); in { diff --git a/hosts/procul.nix b/hosts/procul.nix index 3829b15..5272d2a 100644 --- a/hosts/procul.nix +++ b/hosts/procul.nix @@ -106,6 +106,15 @@ in { ]; }; + client.dns = { + enable = true; + ipv4 = true; + ipv6 = true; + user = "fudo-client"; + external-interface = "extif0"; + password-file = "/srv/client/secure/client.passwd"; + }; + # Not all users need access to procul; don't allow LDAP-user access. authentication.enable = false; @@ -210,15 +219,23 @@ in { users = { gituser = { - password = fileContents "/srv/git/secure/db.passwd"; + password-file = "/srv/git/secure/db.passwd"; databases = { - git = "ALL PRIVILEGES"; + git = { + access = "CONNECT"; + entity-access = { + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT, UPDATE"; + }; + }; }; }; }; databases = { - git = ["niten"]; + git = { + users = ["niten"]; + }; }; }; From e6795d6d2edb04b4e468e237ee443501b173b951 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 19 Nov 2020 16:21:18 -0600 Subject: [PATCH 07/16] Modifications to DNS config --- config/fudo/backplane/dns.nix | 16 +++++-- config/fudo/client/dns.nix | 14 ++++++ config/fudo/dns.nix | 24 ++++++++--- fudo/users.nix | 3 +- hosts/france.nix | 18 ++++++-- hosts/france/backplane.nix | 12 ++++-- hosts/france/jabber.nix | 80 ++++++++++++++++++++++++++--------- packages/backplane-dns.nix | 4 +- static/backplane-auth.scm | 4 +- 9 files changed, 133 insertions(+), 42 deletions(-) diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix index e6dee50..068cc2f 100644 --- a/config/fudo/backplane/dns.nix +++ b/config/fudo/backplane/dns.nix @@ -82,12 +82,21 @@ in { default = 53; }; - listen-addresses = mkOption { + listen-v4-addresses = mkOption { type = with types; listOf str; - description = "IP addresses on which to listen for dns requests."; + description = "IPv4 addresses on which to listen for dns requests."; default = [ "0.0.0.0" ]; }; + listen-v6-addresses = mkOption { + type = with types; listOf str; + description = "IPv6 addresses on which to listen for dns requests."; + example = [ + "[abcd::1]" + ]; + default = []; + }; + required-services = mkOption { type = with types; listOf str; description = "A list of services required before the DNS server can start."; @@ -152,7 +161,8 @@ in { backplane-powerdns = let configDir = pkgs.writeTextDir "pdns.conf" '' - local-address=${lib.concatStringsSep ", " cfg.listen-addresses} + local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses} + local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses} local-port=${toString cfg.port} launch= include-dir=${powerdns-conf-dir}/ diff --git a/config/fudo/client/dns.nix b/config/fudo/client/dns.nix index 7a9d348..a1db07e 100644 --- a/config/fudo/client/dns.nix +++ b/config/fudo/client/dns.nix @@ -86,6 +86,19 @@ in { }; }; + services.backplane-dns-client-pw-file = { + enable = true; + requiredBy = [ "backplane-dns-client.services" ]; + reloadIfChanged = true; + serviceConfig = { + Type = "oneshot"; + }; + script = '' + chmod 600 ${cfg.password-file} + chown ${cfg.user} ${cfg.password-file} + ''; + }; + services.backplane-dns-client = { enable = true; serviceConfig = { @@ -94,6 +107,7 @@ in { User = cfg.user; }; path = [ pkgs.openssh ]; + reloadIfChanged = true; script = '' ${pkgs.backplane-dns-client}/bin/backplane-dns-client ${optionalString cfg.ipv4 "-4"} ${optionalString cfg.ipv6 "-6"} ${optionalString cfg.sshfp "-f"} ${optionalString (cfg.external-interface != null) "--interface=${cfg.external-interface}"} --domain=${cfg.domain} --server=${cfg.server} --password-file=${cfg.password-file} ''; diff --git a/config/fudo/dns.nix b/config/fudo/dns.nix index 9de4805..4d04546 100644 --- a/config/fudo/dns.nix +++ b/config/fudo/dns.nix @@ -33,6 +33,18 @@ let A list of DNS SSHFP records for this host. ''; }; + + description = mkOption { + type = with types; nullOr str; + description = "Description of this host for a TXT record."; + default = null; + }; + + rp = mkOption { + type = with types; nullOr str; + description = "Responsible person."; + default = null; + }; }; }; @@ -125,9 +137,12 @@ let }; }; - hostARecords = host: data: + hostRecords = host: data: join-lines ((map (ip: "${host} IN A ${ip}") data.ip-addresses) ++ - (map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses)); + (map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses) ++ + (map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints) ++ + (optional (data.rp != null) "${host} IN RP ${data.rp}") ++ + (optional (data.description != null) "${host} IN TXT ${data.description}")); makeSrvRecords = protocol: type: records: join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${toString record.host}.") @@ -137,8 +152,6 @@ let 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); @@ -207,8 +220,7 @@ in { ${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 hostRecords dom-cfg.hosts)} ${join-lines (mapAttrsToList cnameRecord dom-cfg.aliases)} ${join-lines dom-cfg.extra-dns-records} ''; diff --git a/fudo/users.nix b/fudo/users.nix index b7c943e..07e7c63 100644 --- a/fudo/users.nix +++ b/fudo/users.nix @@ -75,7 +75,8 @@ uid = 10035; group = "selby"; common-name = "Ken Selby"; - hashed-password = "{SSHA}X8DxUcwH2Fzel5UKbGVNhC5B2vg0Prsc"; + hashed-password = "{SSHA}wUGV/9dr8inz/HyqSF/OWKxy0DCy5AI3"; + # hashed-password = "{SSHA}X8DxUcwH2Fzel5UKbGVNhC5B2vg0Prsc"; }; reaper = { diff --git a/hosts/france.nix b/hosts/france.nix index 73c6e46..911f0d1 100644 --- a/hosts/france.nix +++ b/hosts/france.nix @@ -7,7 +7,7 @@ let mail-hostname = "mail.${domain}"; host_ipv4 = "208.81.3.117"; # Use a special IP for git.fudo.org, since it needs to be SSH-able - git_ipv4 = "208.81.3.126"; + link_ipv4 = "208.81.3.126"; all-hostnames = []; acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem"; @@ -40,6 +40,7 @@ in { lxd multipath-tools nix-prefetch-docker + powerdns tshark ]; @@ -259,9 +260,18 @@ in { # TODO: not used yet fudo.acme.hostnames = all-hostnames; + fudo.client.dns = { + enable = true; + ipv4 = true; + ipv6 = true; + user = "fudo-client"; + external-interface = "extif0"; + password-file = "/srv/client/secure/client.passwd"; + }; + fudo.mail-server = import ../fudo/email.nix { inherit config; } // { enableContainer = true; - debug = true; + # debug = true; monitoring = true; hostname = mail-hostname; @@ -392,7 +402,7 @@ in { repository-dir = /srv/git/repo; state-dir = /srv/git/state; ssh = { - listen-ip = git_ipv4; + listen-ip = link_ipv4; listen-port = 2222; }; }; @@ -438,7 +448,7 @@ in { macAddress = "02:6d:e2:e1:ad:ca"; ipv4.addresses = [ { - address = git_ipv4; + address = link_ipv4; prefixLength = 28; } ]; diff --git a/hosts/france/backplane.nix b/hosts/france/backplane.nix index 8ce0ca8..0d98b60 100644 --- a/hosts/france/backplane.nix +++ b/hosts/france/backplane.nix @@ -58,8 +58,12 @@ in { databases = { backplane_dns = { access = "CONNECT"; + # entity-access = { + # "ALL TABLES IN SCHEMA public" = "SELECT"; + # }; entity-access = { - "ALL TABLES IN SCHEMA public" = "SELECT"; + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; + "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; }; }; }; @@ -70,7 +74,7 @@ in { backplane_dns = { access = "CONNECT"; entity-access = { - "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE"; + "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; }; }; @@ -87,8 +91,8 @@ in { backplane.dns = { enable = true; - port = 353; - listen-addresses = [ "208.81.3.117" ]; + listen-v4-addresses = [ "208.81.3.126" ]; + listen-v6-addresses = [ "[2605:e200:d200:1:6d:e2ff:fee1:adca]" ]; required-services = [ "fudo-passwords.target" ]; user = "backplane-dns"; group = "backplane-dns"; diff --git a/hosts/france/jabber.nix b/hosts/france/jabber.nix index df4c16b..b900bad 100644 --- a/hosts/france/jabber.nix +++ b/hosts/france/jabber.nix @@ -4,6 +4,9 @@ with lib; let backplane-auth = "/etc/nixos/static/backplane-auth.scm"; + host-passwd-file = "/srv/jabber/secret/hosts-passwd.scm"; + service-passwd-file = "/srv/jabber/secret/services-passwd.scm"; + cert-basedir = "/var/lib/ejabberd/certs"; target-certs = ["key" "cert" "chain" "fullchain"]; @@ -50,30 +53,67 @@ in { security.acme.certs."fudo.im".email = "admin@fudo.org"; security.acme.certs."backplane.fudo.org".email = "admin@fudo.org"; - systemd.services = { - ejabberd-generate-certs = { - enable = true; - description = "Generate required SSL certs for ejabberd."; - wantedBy = [ "ejabberd.service" ]; - after = [ - "acme-backplane.fudo.org.service" - "acme-fudo.im.service" - ]; + systemd = { + services = { + ejabberd-generate-certs = { + enable = true; + description = "Generate required SSL certs for ejabberd."; + wantedBy = [ "ejabberd.service" ]; + after = [ + "acme-backplane.fudo.org.service" + "acme-fudo.im.service" + ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${move-server-certs ["fudo.im" "backplane.fudo.org"]}"; + RemainAfterExit = true; + ExecStop = remove-server-certs; + StandardOutput = "journal"; + }; + }; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${move-server-certs ["fudo.im" "backplane.fudo.org"]}"; - RemainAfterExit = true; - ExecStop = remove-server-certs; - StandardOutput = "journal"; + ejabberd = { + requires = [ "ejabberd-generate-certs.service" ]; + environment = { + FUDO_HOST_PASSWD_FILE = host-passwd-file; + FUDO_SERVICE_PASSWD_FILE = service-passwd-file; + }; + }; + + ejabberd-hostfile-watcher = { + description = "Watch the ejabberd host file and restart if changes occur."; + serviceConfig.Type = "oneshot"; + after = [ "ejabberd.service" ]; + script = '' + SYSCTL=${pkgs.systemd}/bin/systemctl + if $SYSCTL is-active --quiet ejabberd.service; then + echo "restarting ejabberd.service because hostfile has changed." + $SYSCTL restart ejabberd.service + fi + ''; + }; + + ejabberd-servicefile-watcher = { + description = "Watch the ejabberd service file and restart if changes occur."; + serviceConfig.Type = "oneshot"; + after = [ "ejabberd.service" ]; + script = '' + SYSCTL=${pkgs.systemd}/bin/systemctl + if $SYSCTL is-active --quiet ejabberd.service; then + echo "restarting ejabberd.service because servicefile has changed." + $SYSCTL restart ejabberd.service + fi + ''; }; }; - ejabberd = { - requires = [ "ejabberd-generate-certs.service" ]; - environment = { - FUDO_HOST_PASSWD_FILE = "/srv/jabber/secret/hosts-passwd.scm"; - FUDO_SERVICE_PASSWD_FILE = "/srv/jabber/secret/services-passwd.scm"; + paths = { + ejabberd-hostfile-watcher = { + pathConfig.PathChanged = host-passwd-file; + }; + + ejabberd-servicefile-watcher = { + pathConfig.PathChanged = service-passwd-file; }; }; }; diff --git a/packages/backplane-dns.nix b/packages/backplane-dns.nix index 85287bb..10d08c2 100644 --- a/packages/backplane-dns.nix +++ b/packages/backplane-dns.nix @@ -9,8 +9,8 @@ in stdenv.mkDerivation { src = fetchgit { url = url; - rev = "543df72f3962cf91b0e0508d15cdc083a3cd7ed4"; - sha256 = "0hda1wjf9wd4rvxchdlxw0af3i2cvl5plg37ric3ckma6gfzkmm0"; + rev = "c552394e55816541a9426974c5f8e6f1f83bf195"; + sha256 = "0r61bwj5a2dvzl41cwdf2pdnhdsmp3kzfyxa5x5hsg67al6s7vi8"; fetchSubmodules = false; }; diff --git a/static/backplane-auth.scm b/static/backplane-auth.scm index e017da5..f23ee80 100644 --- a/static/backplane-auth.scm +++ b/static/backplane-auth.scm @@ -16,8 +16,8 @@ (format (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%")) (exit 1)) -(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)") -(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)") +(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)$") +(define service-regex "^service-([a-zA-Z][a-zA-Z0-9_-]+)$") (define (make-verifier passwd-file) (let ((passwds (load passwd-file))) From 9f52086eb77f71d0da48111045ae7f2991aa1608 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 23 Nov 2020 16:22:28 -0600 Subject: [PATCH 08/16] Added fudo.org DNS config (unused) and better lib imports --- config/fudo/dns.nix | 35 +++-- config/fudo/local-network.nix | 3 - config/fudo/vpn.nix | 8 +- defaults.nix | 8 +- fudo/fudo.org.nix | 268 ++++++++++++++++++++++++++++++++++ fudo/users.nix | 3 +- hosts/france.nix | 50 +++++-- lib/buildLisp.nix | 259 ++++++++++++++++++++++++++++++++ lib/dns.nix | 4 +- lib/ip.nix | 49 ++++--- 10 files changed, 629 insertions(+), 58 deletions(-) create mode 100644 fudo/fudo.org.nix create mode 100644 lib/buildLisp.nix diff --git a/config/fudo/dns.nix b/config/fudo/dns.nix index c3fb911..6a345ef 100644 --- a/config/fudo/dns.nix +++ b/config/fudo/dns.nix @@ -5,8 +5,6 @@ with lib; let cfg = config.fudo.dns; - ip = import ../../lib/ip.nix { lib = lib; }; - join-lines = concatStringsSep "\n"; hostOpts = { host, ...}: { @@ -32,6 +30,7 @@ let description = '' A list of DNS SSHFP records for this host. ''; + default = []; }; description = mkOption { @@ -83,7 +82,11 @@ let description = "A map of hostname to { host_attributes }."; }; - dnssec = mkEnableOption "Enable DNSSEC security for this zone."; + dnssec = mkOption { + type = bool; + description = "Enable DNSSEC security for this zone."; + default = true; + }; mx = mkOption { type = listOf str; @@ -160,9 +163,7 @@ let 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)); + nsRecords = dom: ns-hosts: join-lines (mapAttrsToList (host: _: "@ IN NS ${host}.${dom}.") ns-hosts); in { @@ -170,10 +171,21 @@ in { enable = mkEnableOption "Enable master DNS services."; # FIXME: This should allow for AAAA addresses too... - dns-hosts = mkOption { - type = loaOf str; + nameservers = mkOption { + type = loaOf (submodule hostOpts); description = "Map of domain nameserver FQDNs to IP."; - example = { "ns1.domain.com" = "1.1.1.1"; }; + example = { + "ns1.domain.com" = { + ip-addresses = [ "1.1.1.1" ]; + ipv6-addresses = []; + description = "my fancy dns server"; + }; + }; + }; + + identity = mkOption { + type = str; + description = "The identity (CH TXT ID.SERVER) of this host."; }; domains = mkOption { @@ -192,7 +204,7 @@ in { config = mkIf cfg.enable { services.nsd = { enable = true; - identity = "procul.informis.land"; + identity = cfg.identity; interfaces = cfg.listen-ips; zones = mapAttrs' (dom: dom-cfg: nameValuePair "${dom}." { @@ -215,7 +227,8 @@ in { $TTL 6h - ${nsRecords cfg.dns-hosts} + ${nsRecords dom cfg.nameservers} + ${join-lines (mapAttrsToList hostRecords cfg.nameservers)} ${dmarcRecord dom-cfg.dmarc-report-address} diff --git a/config/fudo/local-network.nix b/config/fudo/local-network.nix index 023ad5d..5435dac 100644 --- a/config/fudo/local-network.nix +++ b/config/fudo/local-network.nix @@ -7,9 +7,6 @@ let join-lines = concatStringsSep "\n"; - ip = import ../../lib/ip.nix { inherit lib; }; - dns = import ../../lib/dns.nix { inherit lib; }; - hostOpts = { hostname, ... }: { options = { ip-address = mkOption { diff --git a/config/fudo/vpn.nix b/config/fudo/vpn.nix index 79f55df..7f6eeeb 100644 --- a/config/fudo/vpn.nix +++ b/config/fudo/vpn.nix @@ -4,8 +4,6 @@ with lib; let cfg = config.fudo.vpn; - ip-util = import ../../lib/ip.nix { lib = lib; }; - generate-pubkey-pkg = name: privkey: pkgs.runCommand "wireguard-${name}-pubkey" { WIREGUARD_PRIVATE_KEY = privkey; @@ -17,7 +15,7 @@ let generate-client-config = privkey-file: server-pubkey: network: server-ip: listen-port: dns-servers: '' [Interface] - Address = ${ip-util.networkMinIp network} + Address = ${ip.networkMinIp network} PrivateKey = ${fileContents privkey-file} ListenPort = ${toString listen-port} DNS = ${concatStringsSep ", " dns-servers} @@ -86,8 +84,8 @@ in { name = peer.name; privkey-path = peer.privkey-path; network-range = let - base = ip-util.intToIpv4 - ((ip-util.ipv4ToInt (ip-util.getNetworkBase cfg.network)) + (i * 256)); + base = ip.intToIpv4 + ((ip.ipv4ToInt (ip.getNetworkBase cfg.network)) + (i * 256)); in "${base}/24"; }) (mapAttrsToList (name: privkey-path: { name = name; diff --git a/defaults.nix b/defaults.nix index daa666a..361acad 100644 --- a/defaults.nix +++ b/defaults.nix @@ -1,6 +1,6 @@ # Ref: https://learnxinyminutes.com/docs/nix/ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { imports = [ @@ -9,6 +9,12 @@ ./config/local.nix ]; + lib = { + buildLisp = import ./lib/buildLisp.nix; + ip = import ./lib/ip.nix; + dns = import ./lib/dns.nix; + }; + nixpkgs.config.allowUnfree = true; security.acme.acceptTerms = true; diff --git a/fudo/fudo.org.nix b/fudo/fudo.org.nix new file mode 100644 index 0000000..c977504 --- /dev/null +++ b/fudo/fudo.org.nix @@ -0,0 +1,268 @@ +{ config, ... }: + +{ + dnssec = true; + + mx = ["mail.fudo.org"]; + + hosts = { + cisco = { + ip-addresses = [ "198.163.150.211" ]; + description = "\"allbran\" \"converge\""; + }; + cisco-int = { + ip-addresses = [ "10.73.77.10" ]; + description = "\"fruitloops\" \"aironet\""; + }; + cupid = { + ip-addresses = [ "208.38.36.100" ]; + }; + docker = { + ip-addresses = [ "208.81.3.126" ]; + }; + france = { + ip-addresses = [ "208.81.3.117" ]; + ssh-fingerprints = [ + "4 1 c95a198f504a589fc62893a95424b12f0b24732d" + "4 2 3e7dad879d6cab7f7fb6769e156d7988d0c01281618d03b793834eea2f09bc96" + "1 1 1b6d62dafae9ebc59169dfb4ef828582a5450d94" + "1 2 079e7a57873542541095bf3d2f97b7350bb457d027b423a6fb56f7f6aa84ac80" + ]; + }; + frankfurt = { + ip-addresses = [ "208.81.3.120" ]; + ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe8c:9738" ]; + ssh-fingerprints = [ + "2 1 4b9e4ed16a6b3fe6d41ed0f5cdeed853cc101e12" + "2 2 286ce32326874fe8aa15e3fd60b176b906ebd87306109f7c250d077db4ba85c5" + "3 1 3531dfd2f240ce0cd548b748462f78451df3f081" + "3 2 338809345ed38eb6808fd468067a74b2a8000fd8cc3bc016b9f977050bf1bba8" + "1 1 fb9ba707daa78243f8a8801f024fe790516b99a7" + "1 2 407f9692fedbd83449f0daf1cf795258b561a7e9c7e8072577cc84ffc0c84130" + ]; + }; + germany = { + ip-addresses = [ "208.81.3.116" ]; + ipv6-addresses = [ "2605:e200:d200:1:78d9:d8ff:fe0f:dd88" ]; + ssh-fingerprints = [ + "2 1 5609a728a91d7e52a6060ea7f3a7790005ba5e81" + "2 2 520a8eb3b9013837ac3ab4b28254f96b7718f9613e751a20dc488bf7d967b485" + "3 1 ee5b49888a36a34e7d4ee0d18626c82a16c2fcdf" + "3 2 d5e44cf2d85032638d49c030a9ccbff6638198c354efcb11bf173017d1257f49" + "1 1 9915d2515d7acdb38924d8829925113d5ce80b88" + "1 2 a7c866306e9661b8b568b2de282367c84065301d6228e58e57e6c4d3d33e3051" + ]; + }; + hanover = { + ip-addresses = [ "208.81.1.130" ]; + ipv6-addresses = [ "2605:e200:d100:1:5054:ff:fe61:ac8b" ]; + }; + localhost = { + ip-addresses = [ "127.0.0.1" ]; + }; + lsbb-gba = { + ip-addresses = [ "199.101.56.34" ]; + }; + lsbb-abg = { + ip-addresses = [ "199.101.56.38" ]; + }; + lsbb-hwd = { + ip-addresses = [ "199.101.56.106" ]; + }; + lsbb-hcl = { + ip-addresses = [ "199.101.56.110" ]; + }; + procul = { + ip-addresses = [ "172.86.179.18" ]; + }; + prunel = { + ip-addresses = [ "208.81.3.123" ]; + }; + mbix = { + ip-addresses = [ "208.81.7.146" ]; + }; + ns3-fudo = { + ip-addresses = [ "208.75.74.205" ]; + }; + ns3-dair = { + ip-addresses = [ "208.75.74.205" ]; + }; + ns4-fudo = { + ip-addresses = [ "208.75.75.157" ]; + }; + ns4-dair = { + ip-addresses = [ "208.75.75.157" ]; + }; + paris = { + ip-addresses = [ "208.81.3.125" ]; + ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe67:d0c1" ]; + ssh-fingerprints = [ + "2 1 9fe9e689a36316831ffafffc22c85913748670a6" + "2 2 f2ce57bf470c907604b79b6ef031c928a64a81031e78892fd475bbcf65ae728b" + "3 1 5c56e93a20868886ffe76e1fab012989ce8e995f" + "3 2 af4f383cb349fc3b2496a0bf0911da3a09f98a6d4d2a3c81bb0fb23a45bde950" + "4 1 71a1d35c32b4445b98ce339696f155e1d4c39bd5" + "4 2 a9e4810a24bd52082c9bb2b1019a9de7d7983246fecb454dd8d918ac5a11af81" + "1 1 18e8dd7cac48f1ac6103ec21c279e339d8690be1" + "1 2 72e4aa05c733441da57c464e6540486f5306b6768d784dd97c666e16629d77a0" + ]; + }; + probe = { + ip-addresses = [ "208.81.3.119" ]; + }; + tours = { + ip-addresses = [ "208.81.3.121" ]; + ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe95:34e5" ]; + ssh-fingerprints = [ + "2 2 41cddf1457880c7e86fa3838eabdbbe7cf803f98998ed406319ba3e43036964c" + "3 1 89b72a740ef6ef7ad9aaf5fe2178d356cdc7ee5b" + "3 2 c39346def56817aaf4c64db5667ccc6aeb400ff1166125fe630b63b5eab0ef29" + "4 1 049b1e6ef1d338d35e97baf312d8a371a266b7d1" + "4 2 1a889e43148ea1ded9f8bc60799ccf1bc32cb084946c8815abed6cc31f212594" + "1 1 bae37560759ec8dba35755473fbb346f9dc4e333" + "1 2 3d0d5efe2da329ea19b191f227c3aaad45271c651717ec3315cda131e992bbcf" + ]; + }; + }; + + default-host = "208.81.3.117"; + + srv-records = { + tcp = { + domain = [ + { + host = "ns1.fudo.org"; + port = 53; + } + { + host = "ns2.fudo.org"; + port = 53; + } + { + host = "ns3.fudo.org"; + port = 53; + } + { + host = "ns4.fudo.org"; + port = 53; + } + ]; + ssh = [{ + host = "france.fudo.org"; + port = 22; + }]; + smtp = [{ + host = "mail.fudo.org"; + port = 25; + }]; + submission = [{ + host = "mail.fudo.org"; + port = 587; + }]; + kerberos = [{ + host = "france.fudo.org"; + port = 88; + }]; + imaps = [{ + host = "mail.fudo.org"; + port = 993; + }]; + ldap = [{ + host = "france.fudo.org"; + port = 389; + }]; + ldaps = [{ + host = "france.fudo.org"; + port = 636; + }]; + pop3s = [{ + host = "mail.fudo.org"; + port = 995; + }]; + http = [{ + host = "wiki.fudo.org"; + port = 80; + }]; + https = [{ + host = "wiki.fudo.org"; + port = 80; + }]; + xmpp-server = [{ + host = "fudo.im"; + port = 5269; + }]; + xmpp-client = [{ + host = "fudo.im"; + port = 5222; + }]; + }; + udp = { + domain = [ + { + host = "ns1.fudo.org"; + port = 53; + } + { + host = "ns2.fudo.org"; + port = 53; + } + { + host = "ns3.fudo.org"; + port = 53; + } + { + host = "ns4.fudo.org"; + port = 53; + } + ]; + kerberos = [{ + host = "france.fudo.org"; + port = 88; + }]; + kerberos-master = [{ + host = "france.fudo.org"; + port = 88; + }]; + kpasswd = [{ + host = "france.fudo.org"; + port = 464; + }]; + xmpp-server = [{ + host = "fudo.im"; + port = 5269; + }]; + }; + }; + + aliases = { + pop = "mail.fudo.org."; + smtp = "mail.fudo.org."; + imap = "mail.fudo.org."; + webmail = "france.fudo.org."; + + archiva = "france.fudo.org."; + auth = "france.fudo.org."; + backplane = "france.fudo.org."; + chat = "france.fudo.org."; + de = "germany.fudo.org."; + fr = "france.fudo.org."; + git = "france.fudo.org."; + metrics = "france.fudo.org."; + minecraft = "france.fudo.org."; + monitor = "france.fudo.org."; + user = "paris.fudo.org."; + u = "user.fudo.org."; + w = "www.fudo.org."; + ww = "www.fudo.org."; + www = "hanover.fudo.org."; + wiki = "hanover.fudo.org."; + }; + + extra-dns-records = [ + ''_kerberos IN TXT "FUDO.ORG"'' + ''@ IN TXT "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"'' + ''@ IN SPF "v=spf1 mx ip4:208.81.3.112/28 ip6:2605:e200:d200::1/48 -all"'' + ]; + + dmarc-report-address = "dmarc-report@fudo.org"; +} diff --git a/fudo/users.nix b/fudo/users.nix index 07e7c63..3f53c62 100644 --- a/fudo/users.nix +++ b/fudo/users.nix @@ -391,7 +391,8 @@ uid = 10108; group = "selby"; common-name = "Lauren Hotel"; - hashed-password = "{SSHA}DKnhrycmXSu4HKWFPeBXA9xvZ0ytgXIpZA10tg=="; + hashed-password = "{SSHA}1q/MC5LKROlIT1nDrKrMvcFAXFtcQXIR"; + # hashed-password = "{SSHA}DKnhrycmXSu4HKWFPeBXA9xvZ0ytgXIpZA10tg=="; }; # Used to send alerts from grafana diff --git a/hosts/france.nix b/hosts/france.nix index 911f0d1..596e2cc 100644 --- a/hosts/france.nix +++ b/hosts/france.nix @@ -186,22 +186,44 @@ in { }; }; - # fudo.dns = { - # enable = true; + fudo.dns = { + enable = true; - # dns-hosts = { - # "ns1.fudo.org" = host_ipv4; - # "ns2.fudo.org" = ""; - # }; + identity = "france.fudo.org"; - # listen-ips = [host_ipv4]; + nameservers = { + ns1 = { + ip-addresses = [ "208.81.3.117" ]; + ipv6-addresses = [ "2605:e200:d200:1:5054:ff:fe8c:9738" ]; + description = "Nameserver 1, france, in Winnipeg, MB, CA"; + rp = "reaper reaper.rp"; + }; + ns2 = { + ip-addresses = [ "209.117.102.102" ]; + ipv6-addresses = [ "2001:470:1f16:40::2" ]; + description = "Nameserver 2, musashi, in Winnipeg, MB, CA"; + rp = "reaper reaper.rp"; + }; + ns3 = { + ip-addresses = [ "104.131.53.95" ]; + ipv6-addresses = [ "2604:a880:800:10::8:7001" ]; + description = "Nameserver 3, ns2.henchmman21.net, in New York City, NY, US"; + rp = "reaper reaper.rp"; + }; + ns4 = { + ip-addresses = [ "204.42.254.5" ]; + ipv6-addresses = [ "2001:418:3f4::5" ]; + description = "Nameserver 4, puck.nether.net, in Chicago, IL, US"; + rp = "reaper reaper.rp"; + }; + }; - # domains = { - # "selby.ca" = import ../fudo.org/selby.ca.nix { - # inherit host_ipv4 config; - # }; - # }; - # }; + listen-ips = [host_ipv4]; + + domains = { + "fudo.org" = import ../fudo/fudo.org.nix { inherit config; }; + }; + }; # Not all users need access to france; don't allow LDAP-user access. fudo.authentication.enable = false; @@ -271,7 +293,7 @@ in { fudo.mail-server = import ../fudo/email.nix { inherit config; } // { enableContainer = true; - # debug = true; + debug = true; monitoring = true; hostname = mail-hostname; diff --git a/lib/buildLisp.nix b/lib/buildLisp.nix new file mode 100644 index 0000000..51411b9 --- /dev/null +++ b/lib/buildLisp.nix @@ -0,0 +1,259 @@ +# buildLisp provides Nix functions to build Common Lisp packages, +# targeting SBCL. +# +# buildLisp is designed to enforce conventions and do away with the +# free-for-all of existing Lisp build systems. + +{ pkgs ? import {}, ... }: + +let + inherit (builtins) map elemAt match filter; + inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl; + + # + # Internal helper definitions + # + + # 'genLoadLisp' generates Lisp code that instructs SBCL to load all + # the provided Lisp libraries. + genLoadLisp = deps: lib.concatStringsSep "\n" + (map (lib: "(load \"${lib}/${lib.lispName}.fasl\")") (allDeps deps)); + + # 'genCompileLisp' generates a Lisp file that instructs SBCL to + # compile the provided list of Lisp source files to $out. + genCompileLisp = srcs: deps: writeText "compile.lisp" '' + ;; This file compiles the specified sources into the Nix build + ;; directory, creating one FASL file for each source. + (require 'sb-posix) + + ${genLoadLisp deps} + + (defun nix-compile-lisp (file srcfile) + (let ((outfile (make-pathname :type "fasl" + :directory (or (sb-posix:getenv "NIX_BUILD_TOP") + (error "not running in a Nix build")) + :name (substitute #\- #\/ srcfile)))) + (multiple-value-bind (_outfile _warnings-p failure-p) + (compile-file srcfile :output-file outfile) + (if failure-p (sb-posix:exit 1) + (progn + ;; For the case of multiple files belonging to the same + ;; library being compiled, load them in order: + (load outfile) + + ;; Write them to the FASL list in the same order: + (format file "cat ~a~%" (namestring outfile))))))) + + (let ((*compile-verbose* t) + ;; FASL files are compiled into the working directory of the + ;; build and *then* moved to the correct out location. + (pwd (sb-posix:getcwd))) + + (with-open-file (file "cat_fasls" + :direction :output + :if-does-not-exist :create) + + ;; These forms were inserted by the Nix build: + ${ + lib.concatStringsSep "\n" (map (src: "(nix-compile-lisp file \"${src}\")") srcs) + } + )) + ''; + + # 'genTestLisp' generates a Lisp file that loads all sources and deps and + # executes expression + genTestLisp = name: srcs: deps: expression: writeText "${name}.lisp" '' + ;; Dependencies + ${genLoadLisp deps} + + ;; Sources + ${lib.concatStringsSep "\n" (map (src: "(load \"${src}\")") srcs)} + + ;; Test expression + (unless ${expression} + (exit :code 1)) + ''; + + # 'dependsOn' determines whether Lisp library 'b' depends on 'a'. + dependsOn = a: b: builtins.elem a b.lispDeps; + + # 'allDeps' flattens the list of dependencies (and their + # dependencies) into one ordered list of unique deps. + allDeps = deps: (lib.toposort dependsOn (lib.unique ( + lib.flatten (deps ++ (map (d: d.lispDeps) deps)) + ))).result; + + # 'allNative' extracts all native dependencies of a dependency list + # to ensure that library load paths are set correctly during all + # compilations and program assembly. + allNative = native: deps: lib.unique ( + lib.flatten (native ++ (map (d: d.lispNativeDeps) deps)) + ); + + # 'genDumpLisp' generates a Lisp file that instructs SBCL to dump + # the currently loaded image as an executable to $out/bin/$name. + # + # TODO(tazjin): Compression is currently unsupported because the + # SBCL in nixpkgs is, by default, not compiled with zlib support. + genDumpLisp = name: main: deps: writeText "dump.lisp" '' + (require 'sb-posix) + + ${genLoadLisp deps} + + (let* ((bindir (concatenate 'string (sb-posix:getenv "out") "/bin")) + (outpath (make-pathname :name "${name}" + :directory bindir))) + (save-lisp-and-die outpath + :executable t + :toplevel (function ${main}) + :purify t)) + ;; + ''; + + # Add an `overrideLisp` attribute to a function result that works + # similar to `overrideAttrs`, but is used specifically for the + # arguments passed to Lisp builders. + makeOverridable = f: orig: (f orig) // { + overrideLisp = new: makeOverridable f (orig // (new orig)); + }; + + # 'testSuite' builds a Common Lisp test suite that loads all of srcs and deps, + # and then executes expression to check its result + testSuite = { name, expression, srcs, deps ? [], native ? [] }: + let + lispNativeDeps = allNative native deps; + lispDeps = allDeps deps; + in runCommandNoCC name { + LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps; + LANG = "C.UTF-8"; + } '' + echo "Running test suite ${name}" + + ${sbcl}/bin/sbcl --script ${genTestLisp name srcs deps expression} \ + | tee $out + + echo "Test suite ${name} succeeded" + ''; + + # + # Public API functions + # + + # 'library' builds a list of Common Lisp files into a single FASL + # which can then be loaded into SBCL. + library = + { name + , srcs + , deps ? [] + , native ? [] + , tests ? null + }: + let + lispNativeDeps = (allNative native deps); + lispDeps = allDeps deps; + testDrv = if ! isNull tests + then testSuite { + name = tests.name or "${name}-test"; + srcs = srcs ++ (tests.srcs or []); + deps = deps ++ (tests.deps or []); + expression = tests.expression; + } + else null; + in lib.fix (self: runCommandNoCC "${name}-cllib" { + LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps; + LANG = "C.UTF-8"; + } '' + ${if ! isNull testDrv + then "echo 'Test ${testDrv} succeeded'" + else "echo 'No tests run'"} + ${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps} + + echo "Compilation finished, assembling FASL files" + + # FASL files can be combined by simply concatenating them + # together, but it needs to be in the compilation order. + mkdir $out + + chmod +x cat_fasls + ./cat_fasls > $out/${name}.fasl + '' // { + inherit lispNativeDeps lispDeps; + lispName = name; + lispBinary = false; + tests = testDrv; + sbcl = sbclWith [ self ]; + }); + + # 'program' creates an executable containing a dumped image of the + # specified sources and dependencies. + program = + { name + , main ? "${name}:main" + , srcs + , deps ? [] + , native ? [] + , tests ? null + }: + let + lispDeps = allDeps deps; + libPath = lib.makeLibraryPath (allNative native lispDeps); + selfLib = library { + inherit name srcs native; + deps = lispDeps; + }; + testDrv = if ! isNull tests + then testSuite { + name = tests.name or "${name}-test"; + srcs = + ( + srcs ++ (tests.srcs or [])); + deps = deps ++ (tests.deps or []); + expression = tests.expression; + } + else null; + in lib.fix (self: runCommandNoCC "${name}" { + nativeBuildInputs = [ makeWrapper ]; + LD_LIBRARY_PATH = libPath; + LANG = "C.UTF-8"; + } '' + ${if ! isNull testDrv + then "echo 'Test ${testDrv} succeeded'" + else ""} + mkdir -p $out/bin + + ${sbcl}/bin/sbcl --script ${ + genDumpLisp name main ([ selfLib ] ++ lispDeps) + } + + wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}" + '' // { + lispName = name; + lispDeps = [ selfLib ] ++ (tests.deps or []); + lispNativeDeps = native; + lispBinary = true; + tests = testDrv; + sbcl = sbclWith [ self ]; + }); + + # 'bundled' creates a "library" that calls 'require' on a built-in + # package, such as any of SBCL's sb-* packages. + bundled = name: (makeOverridable library) { + inherit name; + srcs = lib.singleton (builtins.toFile "${name}.lisp" "(require '${name})"); + }; + + # 'sbclWith' creates an image with the specified libraries / + # programs loaded. + sbclWith = deps: + let lispDeps = filter (d: !d.lispBinary) (allDeps deps); + in writeShellScriptBin "sbcl" '' + export LD_LIBRARY_PATH="${lib.makeLibraryPath (allNative [] lispDeps)}" + export LANG="C.UTF-8" + exec ${sbcl}/bin/sbcl ${lib.optionalString (deps != []) "--load ${writeText "load.lisp" (genLoadLisp lispDeps)}"} $@ + ''; +in { + library = makeOverridable library; + program = makeOverridable program; + sbclWith = makeOverridable sbclWith; + bundled = makeOverridable bundled; +} diff --git a/lib/dns.nix b/lib/dns.nix index 7efaf8c..876b2a5 100644 --- a/lib/dns.nix +++ b/lib/dns.nix @@ -1,6 +1,6 @@ -{ lib }: +{ pkgs ? import {}, ... }: -with lib; +with pkgs.lib; let join-lines = concatStringsSep "\n"; diff --git a/lib/ip.nix b/lib/ip.nix index 7f55bf0..d38997b 100644 --- a/lib/ip.nix +++ b/lib/ip.nix @@ -1,45 +1,32 @@ -{ lib }: +{ pkgs ? import {}, ... }: -with lib; +with pkgs.lib; let - joinString = lib.concatStringsSep; + joinString = 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; + rightPadBits = int: bits: bitOr int (generateNBits bits); + 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)); + [] + 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 @@ -47,10 +34,30 @@ in rec { insignificantBits = 32 - (getNetworkMask network); in intToIpv4 (leftShift (rightShift (ipv4ToInt ip) insignificantBits) insignificantBits); +in rec { + + ipv4ToInt = ip: let + els = map toInt (reverseList (splitString "." ip)); + in foldr (a: b: a + b) 0 (imap0 (i: el: (leftShift el (i * 8))) els); + + intToIpv4 = int: joinString "." (map (i: toString (bitAnd (rightShift int (i * 8)) 255)) [ 3 2 1 0 ]); + + maskFromV32Network = network: let + fullMask = ipv4ToInt "255.255.255.255"; + insignificantBits = 32 - (getNetworkMask network); + in intToIpv4 (leftShift (rightShift fullMask insignificantBits) insignificantBits); + networkMinIp = network: intToIpv4 (1 + (ipv4ToInt (getNetworkBase network))); 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); + + ipv4OnNetwork = ip: network: let + ip-int = ipv4ToInt ip; + net-min = networkMinIp network; + net-max = networkMaxIp network; + in + (ip-int >= networkMinIp) && (ip-int <= networkMaxIp); } From f85d8c3ef7e564ad6e58add410d43170d5f48ebc Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Mon, 23 Nov 2020 14:22:51 -0800 Subject: [PATCH 09/16] Trying to add lisp libraries --- hosts/nostromo.nix | 36 +++++++++++++++++++++++++++++++++--- packages/local.nix | 15 +++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/hosts/nostromo.nix b/hosts/nostromo.nix index 22a1675..d2baf76 100644 --- a/hosts/nostromo.nix +++ b/hosts/nostromo.nix @@ -9,6 +9,7 @@ in { environment.systemPackages = with pkgs; [ dnsproxy + fudoLispPackages.cl-sasl google-photos-uploader libguestfs-with-appliance libvirt @@ -51,11 +52,31 @@ in { enable = true; }; + # systemd.network.networks.eno2 = { + # dhcpV6Config = { + # IPv6PrefixDelegation = "dhcpv6"; + # PrefixDelegationHint = 60; + # }; + # }; + + systemd.network.networks.eno2 = { + extraConfig = { + IPv6AcceptRA = true; + IPv6PrefixDelegation = "dhcpv6"; + }; + }; + networking = { hostName = hostname; nameservers = [ host-internal-ip ]; + dhcpcd.extraConfig = '' + interface eno2 + ia_na 1 + ia_pd 2 eno2/0 + ''; + # Create a bridge for VMs to use macvlans = { intif0 = { @@ -73,7 +94,9 @@ in { enp9s0f0.useDHCP = false; enp9s0f1.useDHCP = false; - eno2.useDHCP = true; + eno2 = { + useDHCP = true; + }; intif0 = { useDHCP = false; @@ -104,6 +127,15 @@ in { isSystemUser = true; }; }; + + groups = { + backplane-powerdns = { + members = [ "backplane-powerdns" ]; + }; + backplane-dns = { + members = [ "backplane-dns" ]; + }; + }; }; fudo = { @@ -172,8 +204,6 @@ in { }; services = { - # dhcpd6.enable = true; - nginx = { enable = true; diff --git a/packages/local.nix b/packages/local.nix index 3e4024b..c38a16c 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -1,7 +1,18 @@ -{ pkgs, ... }: +{ pkgs, lib, ... }: -{ +let + lispPackageRepo = builtins.fetchGit { + url = "https://git.fudo.org/fudo-public/lisp-packages.git"; + }; + +in { nixpkgs.config.packageOverrides = pkgs: rec { + + fudoLispPackages = import "${lispPackageRepo}" { + unstablePkgs = import {}; + inherit lib; + }; + letsencrypt-ca = import ./letsencrypt-ca.nix { stdenv = pkgs.stdenv; fetchurl = builtins.fetchurl; From c715720ea566c80ba64ad33d23f83b179b6005e6 Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Mon, 23 Nov 2020 16:54:50 -0800 Subject: [PATCH 10/16] Fuckit, I'm not gonna keep a whole separate lisp infrastructure --- config/fudo/backplane/dns.nix | 2 - config/fudo/local-network.nix | 3 + defaults.nix | 10 +- hosts/nostromo.nix | 1 - lib/buildLisp.nix | 259 ---------------------------------- lib/ip.nix | 16 +-- packages/local.nix | 8 -- 7 files changed, 15 insertions(+), 284 deletions(-) delete mode 100644 lib/buildLisp.nix diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix index 068cc2f..eacc5d7 100644 --- a/config/fudo/backplane/dns.nix +++ b/config/fudo/backplane/dns.nix @@ -4,8 +4,6 @@ with lib; let cfg = config.fudo.backplane.dns; - dns = import ../../../lib/dns.nix { inherit lib; }; - backup-directory = "/var/lib/fudo/backplane/dns"; powerdns-home = "/var/lib/powerdns"; diff --git a/config/fudo/local-network.nix b/config/fudo/local-network.nix index 5435dac..cb52e2a 100644 --- a/config/fudo/local-network.nix +++ b/config/fudo/local-network.nix @@ -5,6 +5,9 @@ with lib; let cfg = config.fudo.local-network; + ip = import ../../lib/ip.nix {}; + dns = import ../../lib/dns.nix {}; + join-lines = concatStringsSep "\n"; hostOpts = { hostname, ... }: { diff --git a/defaults.nix b/defaults.nix index 361acad..9d34495 100644 --- a/defaults.nix +++ b/defaults.nix @@ -3,18 +3,16 @@ { config, pkgs, lib, ... }: { + lib = { + buildLisp = import ./lib/buildLisp.nix {}; + }; + imports = [ ./hardware-configuration.nix ./packages/local.nix ./config/local.nix ]; - lib = { - buildLisp = import ./lib/buildLisp.nix; - ip = import ./lib/ip.nix; - dns = import ./lib/dns.nix; - }; - nixpkgs.config.allowUnfree = true; security.acme.acceptTerms = true; diff --git a/hosts/nostromo.nix b/hosts/nostromo.nix index d2baf76..657730f 100644 --- a/hosts/nostromo.nix +++ b/hosts/nostromo.nix @@ -9,7 +9,6 @@ in { environment.systemPackages = with pkgs; [ dnsproxy - fudoLispPackages.cl-sasl google-photos-uploader libguestfs-with-appliance libvirt diff --git a/lib/buildLisp.nix b/lib/buildLisp.nix deleted file mode 100644 index 51411b9..0000000 --- a/lib/buildLisp.nix +++ /dev/null @@ -1,259 +0,0 @@ -# buildLisp provides Nix functions to build Common Lisp packages, -# targeting SBCL. -# -# buildLisp is designed to enforce conventions and do away with the -# free-for-all of existing Lisp build systems. - -{ pkgs ? import {}, ... }: - -let - inherit (builtins) map elemAt match filter; - inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl; - - # - # Internal helper definitions - # - - # 'genLoadLisp' generates Lisp code that instructs SBCL to load all - # the provided Lisp libraries. - genLoadLisp = deps: lib.concatStringsSep "\n" - (map (lib: "(load \"${lib}/${lib.lispName}.fasl\")") (allDeps deps)); - - # 'genCompileLisp' generates a Lisp file that instructs SBCL to - # compile the provided list of Lisp source files to $out. - genCompileLisp = srcs: deps: writeText "compile.lisp" '' - ;; This file compiles the specified sources into the Nix build - ;; directory, creating one FASL file for each source. - (require 'sb-posix) - - ${genLoadLisp deps} - - (defun nix-compile-lisp (file srcfile) - (let ((outfile (make-pathname :type "fasl" - :directory (or (sb-posix:getenv "NIX_BUILD_TOP") - (error "not running in a Nix build")) - :name (substitute #\- #\/ srcfile)))) - (multiple-value-bind (_outfile _warnings-p failure-p) - (compile-file srcfile :output-file outfile) - (if failure-p (sb-posix:exit 1) - (progn - ;; For the case of multiple files belonging to the same - ;; library being compiled, load them in order: - (load outfile) - - ;; Write them to the FASL list in the same order: - (format file "cat ~a~%" (namestring outfile))))))) - - (let ((*compile-verbose* t) - ;; FASL files are compiled into the working directory of the - ;; build and *then* moved to the correct out location. - (pwd (sb-posix:getcwd))) - - (with-open-file (file "cat_fasls" - :direction :output - :if-does-not-exist :create) - - ;; These forms were inserted by the Nix build: - ${ - lib.concatStringsSep "\n" (map (src: "(nix-compile-lisp file \"${src}\")") srcs) - } - )) - ''; - - # 'genTestLisp' generates a Lisp file that loads all sources and deps and - # executes expression - genTestLisp = name: srcs: deps: expression: writeText "${name}.lisp" '' - ;; Dependencies - ${genLoadLisp deps} - - ;; Sources - ${lib.concatStringsSep "\n" (map (src: "(load \"${src}\")") srcs)} - - ;; Test expression - (unless ${expression} - (exit :code 1)) - ''; - - # 'dependsOn' determines whether Lisp library 'b' depends on 'a'. - dependsOn = a: b: builtins.elem a b.lispDeps; - - # 'allDeps' flattens the list of dependencies (and their - # dependencies) into one ordered list of unique deps. - allDeps = deps: (lib.toposort dependsOn (lib.unique ( - lib.flatten (deps ++ (map (d: d.lispDeps) deps)) - ))).result; - - # 'allNative' extracts all native dependencies of a dependency list - # to ensure that library load paths are set correctly during all - # compilations and program assembly. - allNative = native: deps: lib.unique ( - lib.flatten (native ++ (map (d: d.lispNativeDeps) deps)) - ); - - # 'genDumpLisp' generates a Lisp file that instructs SBCL to dump - # the currently loaded image as an executable to $out/bin/$name. - # - # TODO(tazjin): Compression is currently unsupported because the - # SBCL in nixpkgs is, by default, not compiled with zlib support. - genDumpLisp = name: main: deps: writeText "dump.lisp" '' - (require 'sb-posix) - - ${genLoadLisp deps} - - (let* ((bindir (concatenate 'string (sb-posix:getenv "out") "/bin")) - (outpath (make-pathname :name "${name}" - :directory bindir))) - (save-lisp-and-die outpath - :executable t - :toplevel (function ${main}) - :purify t)) - ;; - ''; - - # Add an `overrideLisp` attribute to a function result that works - # similar to `overrideAttrs`, but is used specifically for the - # arguments passed to Lisp builders. - makeOverridable = f: orig: (f orig) // { - overrideLisp = new: makeOverridable f (orig // (new orig)); - }; - - # 'testSuite' builds a Common Lisp test suite that loads all of srcs and deps, - # and then executes expression to check its result - testSuite = { name, expression, srcs, deps ? [], native ? [] }: - let - lispNativeDeps = allNative native deps; - lispDeps = allDeps deps; - in runCommandNoCC name { - LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps; - LANG = "C.UTF-8"; - } '' - echo "Running test suite ${name}" - - ${sbcl}/bin/sbcl --script ${genTestLisp name srcs deps expression} \ - | tee $out - - echo "Test suite ${name} succeeded" - ''; - - # - # Public API functions - # - - # 'library' builds a list of Common Lisp files into a single FASL - # which can then be loaded into SBCL. - library = - { name - , srcs - , deps ? [] - , native ? [] - , tests ? null - }: - let - lispNativeDeps = (allNative native deps); - lispDeps = allDeps deps; - testDrv = if ! isNull tests - then testSuite { - name = tests.name or "${name}-test"; - srcs = srcs ++ (tests.srcs or []); - deps = deps ++ (tests.deps or []); - expression = tests.expression; - } - else null; - in lib.fix (self: runCommandNoCC "${name}-cllib" { - LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps; - LANG = "C.UTF-8"; - } '' - ${if ! isNull testDrv - then "echo 'Test ${testDrv} succeeded'" - else "echo 'No tests run'"} - ${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps} - - echo "Compilation finished, assembling FASL files" - - # FASL files can be combined by simply concatenating them - # together, but it needs to be in the compilation order. - mkdir $out - - chmod +x cat_fasls - ./cat_fasls > $out/${name}.fasl - '' // { - inherit lispNativeDeps lispDeps; - lispName = name; - lispBinary = false; - tests = testDrv; - sbcl = sbclWith [ self ]; - }); - - # 'program' creates an executable containing a dumped image of the - # specified sources and dependencies. - program = - { name - , main ? "${name}:main" - , srcs - , deps ? [] - , native ? [] - , tests ? null - }: - let - lispDeps = allDeps deps; - libPath = lib.makeLibraryPath (allNative native lispDeps); - selfLib = library { - inherit name srcs native; - deps = lispDeps; - }; - testDrv = if ! isNull tests - then testSuite { - name = tests.name or "${name}-test"; - srcs = - ( - srcs ++ (tests.srcs or [])); - deps = deps ++ (tests.deps or []); - expression = tests.expression; - } - else null; - in lib.fix (self: runCommandNoCC "${name}" { - nativeBuildInputs = [ makeWrapper ]; - LD_LIBRARY_PATH = libPath; - LANG = "C.UTF-8"; - } '' - ${if ! isNull testDrv - then "echo 'Test ${testDrv} succeeded'" - else ""} - mkdir -p $out/bin - - ${sbcl}/bin/sbcl --script ${ - genDumpLisp name main ([ selfLib ] ++ lispDeps) - } - - wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}" - '' // { - lispName = name; - lispDeps = [ selfLib ] ++ (tests.deps or []); - lispNativeDeps = native; - lispBinary = true; - tests = testDrv; - sbcl = sbclWith [ self ]; - }); - - # 'bundled' creates a "library" that calls 'require' on a built-in - # package, such as any of SBCL's sb-* packages. - bundled = name: (makeOverridable library) { - inherit name; - srcs = lib.singleton (builtins.toFile "${name}.lisp" "(require '${name})"); - }; - - # 'sbclWith' creates an image with the specified libraries / - # programs loaded. - sbclWith = deps: - let lispDeps = filter (d: !d.lispBinary) (allDeps deps); - in writeShellScriptBin "sbcl" '' - export LD_LIBRARY_PATH="${lib.makeLibraryPath (allNative [] lispDeps)}" - export LANG="C.UTF-8" - exec ${sbcl}/bin/sbcl ${lib.optionalString (deps != []) "--load ${writeText "load.lisp" (genLoadLisp lispDeps)}"} $@ - ''; -in { - library = makeOverridable library; - program = makeOverridable program; - sbclWith = makeOverridable sbclWith; - bundled = makeOverridable bundled; -} diff --git a/lib/ip.nix b/lib/ip.nix index d38997b..f5c8920 100644 --- a/lib/ip.nix +++ b/lib/ip.nix @@ -27,13 +27,6 @@ let rightShift = int: n: int / (pow 2 n); - 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); - in rec { ipv4ToInt = ip: let @@ -59,5 +52,12 @@ in rec { net-min = networkMinIp network; net-max = networkMaxIp network; in - (ip-int >= networkMinIp) && (ip-int <= networkMaxIp); + (ip-int >= networkMinIp) && (ip-int <= networkMaxIp); + + 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); } diff --git a/packages/local.nix b/packages/local.nix index c38a16c..08d6226 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -1,18 +1,10 @@ { pkgs, lib, ... }: let - lispPackageRepo = builtins.fetchGit { - url = "https://git.fudo.org/fudo-public/lisp-packages.git"; - }; in { nixpkgs.config.packageOverrides = pkgs: rec { - fudoLispPackages = import "${lispPackageRepo}" { - unstablePkgs = import {}; - inherit lib; - }; - letsencrypt-ca = import ./letsencrypt-ca.nix { stdenv = pkgs.stdenv; fetchurl = builtins.fetchurl; From 96da822c5a199e272e9dd60485e48fe6b3353194 Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Tue, 24 Nov 2020 12:01:03 -0800 Subject: [PATCH 11/16] Added Lisp stuff --- packages/lisp/arrows.nix | 20 +++++++++++++++++ packages/lisp/backplane-dns.nix | 28 ++++++++++++++++++++++++ packages/lisp/backplane-server.nix | 25 +++++++++++++++++++++ packages/lisp/cl-sasl.nix | 22 +++++++++++++++++++ packages/lisp/cl-xmpp.nix | 35 ++++++++++++++++++++++++++++++ packages/lisp/default.nix | 14 ++++++++++++ packages/lisp/ip-utils.nix | 24 ++++++++++++++++++++ packages/local.nix | 6 +++++ 8 files changed, 174 insertions(+) create mode 100644 packages/lisp/arrows.nix create mode 100644 packages/lisp/backplane-dns.nix create mode 100644 packages/lisp/backplane-server.nix create mode 100644 packages/lisp/cl-sasl.nix create mode 100644 packages/lisp/cl-xmpp.nix create mode 100644 packages/lisp/default.nix create mode 100644 packages/lisp/ip-utils.nix diff --git a/packages/lisp/arrows.nix b/packages/lisp/arrows.nix new file mode 100644 index 0000000..71df65a --- /dev/null +++ b/packages/lisp/arrows.nix @@ -0,0 +1,20 @@ +{ pkgs, ... }: + +pkgs.lispPackages.buildLispPackage { + baseName = "arrows"; + packageName = "arrows"; + description = "Clojure-style arrows for Common Lisp"; + + buildSystems = [ "arrows" ]; + + deps = with pkgs.lispPackages; []; + + src = pkgs.fetchgit { + url = "https://gitlab.com/Harleqin/arrows.git"; + rev = "df7cf0067e0132d9697ac8b1a4f1b9c88d4f5382"; + sha256 = "042k9vkssrqx9nhp14wdzm942zgdxvp35mba0p2syz98i75im2yy"; + fetchSubmodules = false; + }; + + asdFilesToKeep = [ "arrows.asd" ]; +} diff --git a/packages/lisp/backplane-dns.nix b/packages/lisp/backplane-dns.nix new file mode 100644 index 0000000..2f3345e --- /dev/null +++ b/packages/lisp/backplane-dns.nix @@ -0,0 +1,28 @@ +{ pkgs, backplane-server, arrows, ip-utils, ... }: + +pkgs.lispPackages.buildLispPackage { + baseName = "backplane-dns"; + packageName = "backplane-dns"; + description = "XMPP Backplane DNS Server"; + + buildSystems = [ "backplane-dns" ]; + + deps = with pkgs.lispPackages; [ + arrows + alexandria + backplane-server + cl-ppcre + ip-utils + postmodern + trivia + ]; + + src = pkgs.fetchgit { + url = "https://git.fudo.org/fudo-public/backplane-dns.git"; + rev = "d9e13bae165b08976fd025053bb2dde44bb4278d"; + sha256 = "0b4y75hq5753v8pk47c4pwpyc95igpjl7md7f29jjvqaviys66xh"; + fetchSubmodules = false; + }; + + asdFilesToKeep = [ "backplane-dns.asd" ]; +} diff --git a/packages/lisp/backplane-server.nix b/packages/lisp/backplane-server.nix new file mode 100644 index 0000000..f23f621 --- /dev/null +++ b/packages/lisp/backplane-server.nix @@ -0,0 +1,25 @@ +{ pkgs, lib, arrows, cl-xmpp, ... }: + +pkgs.lispPackages.buildLispPackage { + baseName = "backplane-server"; + packageName = "backplane-server"; + description = "XMPP Backplane Server"; + + buildSystems = [ "backplane-server" ]; + + deps = with pkgs.lispPackages; [ + alexandria + arrows + cl-json + cl-xmpp + ]; + + src = pkgs.fetchgit { + url = "https://git.fudo.org/fudo-public/backplane-server.git"; + rev = "665f362ce1a0a22bc10d3bbe95d5a8adec2df653"; + sha256 = "0lzq0vlqjymcwxbc80x6wp5mij80am12w9fi7abs5wnqcs68lwnf"; + fetchSubmodules = false; + }; + + asdFilesToKeep = [ "backplane-server.asd" ]; +} diff --git a/packages/lisp/cl-sasl.nix b/packages/lisp/cl-sasl.nix new file mode 100644 index 0000000..be4af4f --- /dev/null +++ b/packages/lisp/cl-sasl.nix @@ -0,0 +1,22 @@ +{ pkgs, ... }: + +pkgs.lispPackages.buildLispPackage { + description = "SASL package for common lisp"; + baseName = "cl-sasl"; + packageName = "cl-sasl"; + + buildSystems = [ "cl-sasl" ]; + + deps = with pkgs.lispPackages; [ + ironclad + ]; + + src = pkgs.fetchFromGitHub { + owner = "legoscia"; + repo = "cl-sasl"; + rev = "64f195c0756cb80fa5961c072b62907be20a7380"; + sha256 = "0a05q8rls2hn46rbbk6w5km9kqvhsj365zlw6hp32724xy2nd98w"; + }; + + asdFilesToKeep = [ "cl-sasl.asd" ]; +} diff --git a/packages/lisp/cl-xmpp.nix b/packages/lisp/cl-xmpp.nix new file mode 100644 index 0000000..54fb5e5 --- /dev/null +++ b/packages/lisp/cl-xmpp.nix @@ -0,0 +1,35 @@ +{ pkgs, cl-sasl, ... }: + +pkgs.lispPackages.buildLispPackage { + baseName = "cl-xmpp"; + packageName = "cl-xmpp"; + description = "XMPP library for Common Lisp"; + + buildSystems = [ + "cl-xmpp" + "cl-xmpp-sasl" + "cl-xmpp-tls" + ]; + + deps = with pkgs.lispPackages; [ + cl-base64 + cl_plus_ssl + cl-sasl + cxml + ironclad + usocket + ]; + + src = pkgs.fetchFromGitHub { + owner = "j4yk"; + repo = "cl-xmpp"; + rev = "a0f206e583c72d80523bdf108e7d507597555c6d"; + sha256 = "16qwm7yvwi73q07rsg0i5wrxbv44wm75m3710ph0vf1lzdkrsizk"; + }; + + asdFilesToKeep = [ + "cl-xmpp.asd" + "cl-xmpp-sasl.asd" + "cl-xmpp-tls.asd" + ]; +} diff --git a/packages/lisp/default.nix b/packages/lisp/default.nix new file mode 100644 index 0000000..2397ce9 --- /dev/null +++ b/packages/lisp/default.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, ... }: + +rec { + arrows = import ./arrows.nix { inherit pkgs; }; + cl-sasl = import ./cl-sasl.nix { inherit pkgs; }; + cl-xmpp = import ./cl-xmpp.nix { inherit pkgs cl-sasl; }; + backplane-dns = import ./backplane-dns.nix { + inherit pkgs arrows backplane-server ip-utils; + }; + backplane-server = import ./backplane-server.nix { + inherit pkgs lib arrows cl-xmpp; + }; + ip-utils = import ./ip-utils.nix { inherit pkgs; }; +} diff --git a/packages/lisp/ip-utils.nix b/packages/lisp/ip-utils.nix new file mode 100644 index 0000000..bac706c --- /dev/null +++ b/packages/lisp/ip-utils.nix @@ -0,0 +1,24 @@ +{ pkgs, ... }: + +pkgs.lispPackages.buildLispPackage { + baseName = "ip-utils"; + packageName = "ip-utils"; + description = "Simple Common Lisp utility functions for working with IPs"; + + buildSystems = [ "ip-utils" ]; + + deps = with pkgs.lispPackages; [ + cl-ppcre + split-sequence + trivia + ]; + + src = pkgs.fetchgit { + url = "https://git.fudo.org/fudo-public/ip-utils.git"; + rev = "bf590d0eeab9496bc47db43c997dfe9f0151163a"; + sha256 = "19n17pdzyl8j0fw82dr8lrjy6hkcagszm8kbyv8qbv2jl80176hp"; + fetchSubmodules = false; + }; + + asdFilesToKeep = [ "ip-utils.asd" ]; +} diff --git a/packages/local.nix b/packages/local.nix index 08d6226..b392b06 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -1,6 +1,7 @@ { pkgs, lib, ... }: let + unstablePkgs = import {}; in { nixpkgs.config.packageOverrides = pkgs: rec { @@ -110,5 +111,10 @@ in { platforms = platforms.linux ++ platforms.darwin; }; }; + + localLispPackages = import ./lisp { + inherit lib; + pkgs = unstablePkgs; + }; }; } From f442ea729cc63b95f6fb9b1ca09516914957875c Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Nov 2020 11:36:10 -0600 Subject: [PATCH 12/16] Closer to 'correct' backplane-dns-server --- config/fudo/backplane/dns.nix | 72 +++++++++++++++++++++++++------ packages/backplane-dns-server.nix | 33 ++++++++++++++ packages/local.nix | 17 +++++--- 3 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 packages/backplane-dns-server.nix diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix index eacc5d7..e36b775 100644 --- a/config/fudo/backplane/dns.nix +++ b/config/fudo/backplane/dns.nix @@ -4,6 +4,54 @@ with lib; let cfg = config.fudo.backplane.dns; + lisp-pkgs = with pkgs.localLispPackages; [ + arrows + backplane-dns + backplane-server + cl-sasl + cl-xmpp + ip-utils + + alexandria + babel + bordeaux-threads + cffi + cl-base64 + cl-json + cl-postgres + cl-ppcre + cl-unicode + cl_plus_ssl + closer-mop + closure-common + cxml + flexi-streams + global-vars + introspect-environment + ironclad + iterate + lisp-namespace + md5 + nibbles + postmodern + puri + s-sql + split-sequence + trivia + trivia_dot_balland2006 + trivia_dot_level0 + trivia_dot_level1 + trivia_dot_level2 + trivia_dot_trivial + trivial-cltl2 + trivial-features + trivial-garbage + trivial-gray-streams + type-i + uax-15 + usocket + ]; + backup-directory = "/var/lib/fudo/backplane/dns"; powerdns-home = "/var/lib/powerdns"; @@ -32,6 +80,12 @@ let type = with types; submodule databaseOpts; description = "Database settings for backplane server."; }; + + cl-wrapper-package = mkOption { + type = types.package; + description = "Common Lisp wrapper package to use."; + default = pkgs.lispPackages.clwrapper; + }; }; }; @@ -61,15 +115,6 @@ let }; }; - lisp-libs = []; - - launchScript = pkgs.writeText "launch-backplane-dns.lisp" '' - (load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))) - (ql:quickload :backplane-dns) - (backplane-dns:start-listener-with-env) - (loop (sleep 600)) - ''; - in { options.fudo.backplane.dns = { enable = mkEnableOption "Enable backplane dynamic DNS server."; @@ -251,8 +296,7 @@ in { restartIfChanged = true; serviceConfig = { - ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init"; - ExecStart = "${pkgs.sbcl}/bin/sbcl --load ${launchScript}"; + ExecStart = "${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh"; Restart = "on-failure"; PIDFile = "/run/backplane-dns.$USERNAME.pid"; User = cfg.user; @@ -260,7 +304,7 @@ in { }; environment = { - LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; + # LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib"; FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; @@ -270,8 +314,10 @@ in { FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.backplane.database.username; FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = cfg.backplane.database.password-file; + # CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//"; + CL_SOURCE_REGISTRY = lib.concatStringsSep ":" (map (pkg: "${pkg}//") - (lisp-libs ++ [pkgs.backplane-dns])); + lisp-pkgs); }; requires = cfg.required-services; diff --git a/packages/backplane-dns-server.nix b/packages/backplane-dns-server.nix new file mode 100644 index 0000000..a040d5f --- /dev/null +++ b/packages/backplane-dns-server.nix @@ -0,0 +1,33 @@ +{ pkgs, localLispPackages, ... }: + +with pkgs.lib; +let + launcher = pkgs.writeText "launch-backplane-dns.lisp" '' + (require :asdf) + (asdf:load-system :backplane-dns) + (backplane-dns:start-listener-with-env) + (loop (sleep 600)) + ''; + + launcherScript = pkgs.writeShellScriptBin "launch-backplane-dns.sh" '' + ${pkgs.lispPackages.clwrapper}/bin/common-lisp.sh --load ${launcher} + ''; + +in pkgs.stdenv.mkDerivation { + pname = "backplane-dns-server"; + version = "0.1.0"; + + propagatedBuildInputs = with pkgs; [ + asdf + sbcl + lispPackages.clwrapper + localLispPackages.backplane-dns + ]; + + phases = [ "installPhase" ]; + + installPhase = '' + mkdir -p "$out/bin" + cp ${launcherScript}/bin/launch-backplane-dns.sh "$out/bin" + ''; +} diff --git a/packages/local.nix b/packages/local.nix index b392b06..fe7339a 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -65,11 +65,11 @@ in { inherit (pkgs) stdenv fetchurl makeWrapper cups dpkg a2ps ghostscript gnugrep gnused coreutils file perl which; }; - backplane-dns = import ./backplane-dns.nix { - pkgs = pkgs; - stdenv = pkgs.stdenv; - fetchgit = pkgs.fetchgit; - }; + # backplane-dns = import ./backplane-dns.nix { + # pkgs = pkgs; + # stdenv = pkgs.stdenv; + # fetchgit = pkgs.fetchgit; + # }; backplane-dns-client = import ./backplane-dns-client.nix { pkgs = pkgs; @@ -112,9 +112,14 @@ in { }; }; - localLispPackages = import ./lisp { + localLispPackages = (import ./lisp { inherit lib; pkgs = unstablePkgs; + }) // unstablePkgs.lispPackages; + + backplane-dns-server = import ./backplane-dns-server.nix { + inherit localLispPackages; + pkgs = unstablePkgs; }; }; } From df575f9680a0f64bb1e8c334e0899b6cf0c4003d Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Fri, 27 Nov 2020 09:36:55 -0800 Subject: [PATCH 13/16] Minor cleanup --- packages/lisp/arrows.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lisp/arrows.nix b/packages/lisp/arrows.nix index 71df65a..c29d2b3 100644 --- a/packages/lisp/arrows.nix +++ b/packages/lisp/arrows.nix @@ -7,7 +7,7 @@ pkgs.lispPackages.buildLispPackage { buildSystems = [ "arrows" ]; - deps = with pkgs.lispPackages; []; + deps = []; src = pkgs.fetchgit { url = "https://gitlab.com/Harleqin/arrows.git"; From eef3234edbc5bbe73bef1825d131bb23da5182ce Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Wed, 2 Dec 2020 08:44:56 -0800 Subject: [PATCH 14/16] Switch to doom emacs --- defaults.nix | 22 ++++++++--- fudo/profiles/server.nix | 14 ++----- packages/local.nix | 80 +++++++++++++++++++++++++++------------- users/niten.nix | 41 ++++++++++++++++++++ users/root.nix | 23 ++++++++++++ 5 files changed, 139 insertions(+), 41 deletions(-) create mode 100644 users/niten.nix create mode 100644 users/root.nix diff --git a/defaults.nix b/defaults.nix index 9d34495..c2e1d9c 100644 --- a/defaults.nix +++ b/defaults.nix @@ -2,15 +2,15 @@ { config, pkgs, lib, ... }: -{ - lib = { - buildLisp = import ./lib/buildLisp.nix {}; - }; +let + state-version = "20.03"; +in { imports = [ ./hardware-configuration.nix ./packages/local.nix ./config/local.nix + ]; nixpkgs.config.allowUnfree = true; @@ -25,6 +25,7 @@ boot bind binutils + # bpytop btrfs-progs bundix byobu @@ -33,6 +34,7 @@ certbot clang curl + doomEmacsInit dpkg enca fail2ban @@ -45,6 +47,7 @@ google-cloud-sdk guile heimdalFull + home-manager imagemagick ipfs iptables @@ -91,7 +94,7 @@ yubikey-personalization ]; - system.stateVersion = "20.03"; + system.stateVersion = state-version; system.autoUpgrade.enable = true; @@ -134,6 +137,9 @@ enableSSHSupport = true; }; + fish = { + enable = true; + }; }; services = { @@ -200,6 +206,7 @@ uid = 10000; createHome = true; description = "Niten"; + shell = pkgs.fish; extraGroups = ["wheel" "audio" "video" "disk" "floppy" "lp" "cdrom" "tape" "dialout" "adm" "input" "systemd-journal" "fudosys" "libvirtd"]; group = "users"; home = "/home/niten"; @@ -226,6 +233,11 @@ }; }; + home-manager.users = { + niten = import ./users/niten.nix { inherit config pkgs lib; }; + root = import ./users/root.nix { inherit config pkgs lib; }; + }; + systemd.services.fudo-environment-init = { enable = true; description = "Fudo common settings."; diff --git a/fudo/profiles/server.nix b/fudo/profiles/server.nix index a095170..8a45fdc 100644 --- a/fudo/profiles/server.nix +++ b/fudo/profiles/server.nix @@ -2,11 +2,7 @@ with lib; let - reboot-if-necessary = pkgs.writeScriptBin "reboot-if-necessary" '' - #!${pkgs.stdenv.shell} - - set -ne - + reboot-if-necessary = pkgs.writeShellScriptBin "reboot-if-necessary" '' if [ $# -ne 1 ]; then echo "FAILED: no sync file provided." exit 1 @@ -24,15 +20,11 @@ let exit 0 ''; - test-config = pkgs.writeScriptBin "fudo-test-config" '' - #!${pkgs.stdenv.shell} - - set -ne - + test-config = pkgs.writeShellScriptBin "fudo-test-config" '' if [ $# -gt 1 ]; then echo "usage: $0 [timeout]" exit 1 - elif [ $# -eq 1 ]; the + elif [ $# -eq 1 ]; then TIMEOUT=$1 else TIMEOUT=15m diff --git a/packages/local.nix b/packages/local.nix index fe7339a..6e82605 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -1,7 +1,6 @@ { pkgs, lib, ... }: -let - unstablePkgs = import {}; +let unstablePkgs = import { }; in { nixpkgs.config.packageOverrides = pkgs: rec { @@ -11,13 +10,16 @@ in { fetchurl = builtins.fetchurl; }; - minecraft-server_1_15_1 = pkgs.minecraft-server.overrideAttrs (oldAttrs: rec { - version = "1.15.1"; - src = builtins.fetchurl { - url = "https://launcher.mojang.com/v1/objects/4d1826eebac84847c71a77f9349cc22afd0cf0a1/server.jar"; - sha256 = "a0c062686bee5a92d60802ca74d198548481802193a70dda6d5fe7ecb7207993"; - }; - }); + minecraft-server_1_15_1 = pkgs.minecraft-server.overrideAttrs + (oldAttrs: rec { + version = "1.15.1"; + src = builtins.fetchurl { + url = + "https://launcher.mojang.com/v1/objects/4d1826eebac84847c71a77f9349cc22afd0cf0a1/server.jar"; + sha256 = + "a0c062686bee5a92d60802ca74d198548481802193a70dda6d5fe7ecb7207993"; + }; + }); minecraft-current = pkgs.minecraft.overrideAttrs (oldAttrs: rec { src = builtins.fetchurl { @@ -29,26 +31,24 @@ in { # DON'T LEAVE THE HASH--Nix will think the package hasn't changed minecraft-server_1_16_1 = let version = "1.16.1"; - url = "https://launcher.mojang.com/v1/objects/a412fd69db1f81db3f511c1463fd304675244077/server.jar"; + url = + "https://launcher.mojang.com/v1/objects/a412fd69db1f81db3f511c1463fd304675244077/server.jar"; sha256 = "0nwkdig6yw4cnm2ld78z4j4xzhbm1rwv55vfxz0gzhsbf93xb0i7"; in (pkgs.minecraft-server.overrideAttrs (oldAttrs: rec { name = "minecraft-server-${version}"; inherit version; - src = pkgs.fetchurl { - inherit url sha256; - }; + src = pkgs.fetchurl { inherit url sha256; }; })); minecraft-server_1_16_2 = let version = "1.16.2"; - url = "https://launcher.mojang.com/v1/objects/c5f6fb23c3876461d46ec380421e42b289789530/server.jar"; + url = + "https://launcher.mojang.com/v1/objects/c5f6fb23c3876461d46ec380421e42b289789530/server.jar"; sha256 = "0fbghwrj9b2y9lkn2b17id4ghglwvyvcc8065h582ksfz0zys0i9"; in (pkgs.minecraft-server.overrideAttrs (oldAttrs: rec { name = "minecraft-server-${version}"; inherit version; - src = pkgs.fetchurl { - inherit url sha256; - }; + src = pkgs.fetchurl { inherit url sha256; }; })); postgresql_11_gssapi = pkgs.postgresql_11.overrideAttrs (oldAttrs: rec { @@ -62,15 +62,11 @@ in { }); hll2380dw-cups = import ./hll2380dw-cups.nix { - inherit (pkgs) stdenv fetchurl makeWrapper cups dpkg a2ps ghostscript gnugrep gnused coreutils file perl which; + inherit (pkgs) + stdenv fetchurl makeWrapper cups dpkg a2ps ghostscript gnugrep gnused + coreutils file perl which; }; - # backplane-dns = import ./backplane-dns.nix { - # pkgs = pkgs; - # stdenv = pkgs.stdenv; - # fetchgit = pkgs.fetchgit; - # }; - backplane-dns-client = import ./backplane-dns-client.nix { pkgs = pkgs; stdenv = pkgs.stdenv; @@ -106,12 +102,40 @@ in { meta = with pkgs.lib; { description = "Google photos uploader, written in Go."; - homepage = https://github.com/int128/gpup; + homepage = "https://github.com/int128/gpup"; license = licenses.asl20; platforms = platforms.linux ++ platforms.darwin; }; }; + doomEmacsInit = pkgs.writeShellScriptBin "doom-emacs-init.sh" '' + DOOMDIR=$HOME/.emacs.d + + function clone_into() { + ${pkgs.git}/bin/git clone https://github.com/hlissner/doom-emacs.git $1 + } + + if [ ! -d "$DOOMDIR" ]; then + clone_into $DOOMDIR + $DOOMDIR/bin/doom -y install + fi + + if [ ! -f $DOOMDIR/bin/doom ]; then + # legacy...move to a backup + mv $HOME/.emacs.d $HOME/.emacs.d.bak + mv $HOME/.emacs $HOME/.emacs + clone_into $DOOMDIR + $DOOMDIR/bin/doom -y install + fi + + $DOOMDIR/bin/doom sync + + #if ${pkgs.emacs}/bin/emacsclient -ca false -e '(delete-frame)'; then + # # emacs is running + # ${pkgs.emacs}/bin/emacsclient -e '(doom/reload)' + #fi + ''; + localLispPackages = (import ./lisp { inherit lib; pkgs = unstablePkgs; @@ -121,5 +145,11 @@ in { inherit localLispPackages; pkgs = unstablePkgs; }; + + doom-emacs-config = pkgs.fetchgit { + url = "https://git.fudo.org/niten/doom-emacs.git"; + rev = "0613fcec820851e5b92f0ab493fb2078c6754b51"; + sha256 = "00zzghdxpyb6r22znxyknydcl5v7zfai1fvjfy4gwfw8211n4z2m"; + }; }; } diff --git a/users/niten.nix b/users/niten.nix new file mode 100644 index 0000000..ad6e070 --- /dev/null +++ b/users/niten.nix @@ -0,0 +1,41 @@ +{ config, pkgs, lib, ... }: + +let + name = "Niten"; + email = "niten@fudo.org"; + +in { + programs = { + git = { + enable = true; + userName = name; + userEmail = email; + }; + }; + + xresources.properties = { + "Xft.antialias" = 1; + "Xft.autohint" = 0; + "Xft.dpi" = 192; + "Xft.hinting" = 1; + "Xft.hintstyle" = "hintfull"; + "Xft.lcdfilter" = "lcddefault"; + }; + + services.gpg-agent.enable = true; + + # services.redshift = { + # enable = true; + # latitude = "51.0"; + # longitude = "47.0"; + # brightness.day = "1"; + # brightness.night = "0.5"; + # tray = true; + # }; + + home.file.".doom.d" = { + source = pkgs.doom-emacs-config; + recursive = true; + onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + }; +} diff --git a/users/root.nix b/users/root.nix new file mode 100644 index 0000000..ab8b8a1 --- /dev/null +++ b/users/root.nix @@ -0,0 +1,23 @@ +{ config, pkgs, lib, ... }: + +let + name = "Root"; + email = "root@fudo.org"; + +in { + programs = { + git = { + enable = true; + userName = name; + userEmail = email; + }; + }; + + services.gpg-agent.enable = true; + + home.file.".doom.d" = { + source = pkgs.doom-emacs-config; + recursive = true; + onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + }; +} From 4f3575f5a685dc13d3c50abe9961dda74eea4bfd Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Wed, 2 Dec 2020 09:24:58 -0800 Subject: [PATCH 15/16] Add .k5login files --- users/niten.nix | 19 +++++++++++++++---- users/root.nix | 19 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/users/niten.nix b/users/niten.nix index ad6e070..4c37375 100644 --- a/users/niten.nix +++ b/users/niten.nix @@ -33,9 +33,20 @@ in { # tray = true; # }; - home.file.".doom.d" = { - source = pkgs.doom-emacs-config; - recursive = true; - onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + home.file = { + ".doom.d" = { + source = pkgs.doom-emacs-config; + recursive = true; + onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + }; + + ".k5login" = { + source = pkgs.writeText "niten-k5login" '' + niten@FUDO.ORG + niten/root@FUDO.ORG + niten@INFORMIS.LAND + niten/root@INFORMIS.LAND + ''; + }; }; } diff --git a/users/root.nix b/users/root.nix index ab8b8a1..4a7c463 100644 --- a/users/root.nix +++ b/users/root.nix @@ -15,9 +15,20 @@ in { services.gpg-agent.enable = true; - home.file.".doom.d" = { - source = pkgs.doom-emacs-config; - recursive = true; - onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + home.file = { + ".doom.d" = { + source = pkgs.doom-emacs-config; + recursive = true; + onChange = "${pkgs.doomEmacsInit}/bin/doom-emacs-init.sh"; + }; + + ".k5login" = { + source = pkgs.writeText "niten-k5login" '' + niten@FUDO.ORG + niten/root@FUDO.ORG + niten@INFORMIS.LAND + niten/root@INFORMIS.LAND + ''; + }; }; } From c0460e6d12189a68883e129e49a699e8903476f6 Mon Sep 17 00:00:00 2001 From: nostoromo root Date: Fri, 4 Dec 2020 09:03:37 -0800 Subject: [PATCH 16/16] Switch to newest doom config --- packages/local.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/local.nix b/packages/local.nix index 6e82605..4a34ecf 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -148,8 +148,8 @@ in { doom-emacs-config = pkgs.fetchgit { url = "https://git.fudo.org/niten/doom-emacs.git"; - rev = "0613fcec820851e5b92f0ab493fb2078c6754b51"; - sha256 = "00zzghdxpyb6r22znxyknydcl5v7zfai1fvjfy4gwfw8211n4z2m"; + rev = "ed65ca4881d0cc3cfe308f5fc66ccbf15f560043"; + sha256 = "17z4scl4p84hin6yzx69a707s5ibjk3jrxszy07cg8zijrg1744a"; }; }; }