Merged upstream
This commit is contained in:
commit
0740a6f0d1
|
@ -1,3 +1,3 @@
|
|||
configuration.nix
|
||||
hardware-configuration.nix
|
||||
*~
|
||||
hardware-configuration.nix
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./dns.nix
|
||||
];
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
{ 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" ];
|
||||
|
||||
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
|
||||
|
||||
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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
{ 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.";
|
||||
};
|
||||
|
||||
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.";
|
||||
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;
|
||||
};
|
||||
|
||||
# FIXME: take the relevant SSH package
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
path = [ pkgs.openssh ];
|
||||
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}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.garbage-collector;
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.garbage-collector = {
|
||||
enable = mkEnableOption "Enable periodic NixOS garbage collection";
|
||||
|
||||
timing = mkOption {
|
||||
type = types.str;
|
||||
default = "weekly";
|
||||
description = "Period (systemd format) at which to run garbage collector.";
|
||||
};
|
||||
|
||||
age = mkOption {
|
||||
type = types.str;
|
||||
default = "30d";
|
||||
description = "Age of garbage to collect (eg. 30d).";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd = {
|
||||
timers.fudo-garbage-collector = {
|
||||
enable = true;
|
||||
description = "Collect NixOS garbage older than ${cfg.age}";
|
||||
partOf = [ "fudo-garbage-collector.service" ];
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = cfg.timing;
|
||||
};
|
||||
};
|
||||
|
||||
services.fudo-garbage-collector = {
|
||||
enable = true;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
StandardOutput = "journal";
|
||||
};
|
||||
script = ''
|
||||
${pkgs.nix}/bin/nix-collect-garbage --delete-older-than ${cfg.age}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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 = {
|
||||
|
@ -19,10 +20,11 @@ let
|
|||
};
|
||||
|
||||
mac-address = mkOption {
|
||||
type = types.str;
|
||||
type = with types; nullOr types.str;
|
||||
description = ''
|
||||
The MAC address of a given host, if desired for IP reservation.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
ssh-fingerprints = mkOption {
|
||||
|
@ -35,33 +37,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 = {
|
||||
|
@ -69,7 +44,7 @@ in {
|
|||
enable = mkEnableOption "Enable local network configuration (DHCP & DNS).";
|
||||
|
||||
hosts = mkOption {
|
||||
type = with types; loaOf (submodule hostOpts);
|
||||
type = with types; attrsOf (submodule hostOpts);
|
||||
default = {};
|
||||
description = "A map of hostname => { host_attributes }.";
|
||||
};
|
||||
|
@ -100,7 +75,7 @@ in {
|
|||
};
|
||||
|
||||
aliases = mkOption {
|
||||
type = with types; loaOf str;
|
||||
type = with types; attrsOf str;
|
||||
default = {};
|
||||
description = "A mapping of host-alias => hostname to use on the local network.";
|
||||
};
|
||||
|
@ -143,7 +118,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 = {
|
||||
|
@ -174,7 +149,7 @@ in {
|
|||
ethernetAddress = hostOpts.mac-address;
|
||||
hostName = hostname;
|
||||
ipAddress = hostOpts.ip-address;
|
||||
}) cfg.hosts;
|
||||
}) (filterAttrs (host: hostOpts: hostOpts.mac-address != null) cfg.hosts);
|
||||
|
||||
interfaces = cfg.dhcp-interfaces;
|
||||
|
||||
|
@ -231,17 +206,18 @@ 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" ];
|
||||
forwarders = [ cfg.recursive-resolver ];
|
||||
listenOn = cfg.dns-serve-ips;
|
||||
extraOptions = concatStringsSep "\n" [
|
||||
"dnssec-enable yes;"
|
||||
"dnssec-validation yes;"
|
||||
"auth-nxdomain no;"
|
||||
"recursion yes;"
|
||||
"allow-recursion { any; };"
|
||||
];
|
||||
zones = [
|
||||
{
|
||||
master = true;
|
||||
|
@ -267,7 +243,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;
|
||||
|
|
|
@ -160,22 +160,6 @@ in rec {
|
|||
};
|
||||
};
|
||||
|
||||
# users = {
|
||||
# users = {
|
||||
# ${container-mail-user} = {
|
||||
# isSystemUser = true;
|
||||
# uid = container-mail-user-id;
|
||||
# group = "mailer";
|
||||
# };
|
||||
# };
|
||||
|
||||
# groups = {
|
||||
# ${container-mail-group} = {
|
||||
# members = ["mailer"];
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
fudo.mail-server = {
|
||||
enable = true;
|
||||
hostname = cfg.hostname;
|
||||
|
|
|
@ -232,6 +232,10 @@ in {
|
|||
user = root
|
||||
}
|
||||
|
||||
service imap {
|
||||
vsz_limit = 1024M
|
||||
}
|
||||
|
||||
namespace inbox {
|
||||
separator = "/"
|
||||
inbox = yes
|
||||
|
|
|
@ -130,7 +130,7 @@ in {
|
|||
virtual = ''
|
||||
${make-user-aliases cfg.user-aliases}
|
||||
|
||||
${make-alias-users cfg.local-domains cfg.alias-users}
|
||||
${make-alias-users ([cfg.domain] ++ cfg.local-domains) cfg.alias-users}
|
||||
'';
|
||||
|
||||
sslCert = cfg.postfix.ssl-certificate;
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.netinfo-email;
|
||||
|
||||
make-script = server: port: target: pkgs.writeText "netinfo-script.rb" ''
|
||||
#!${pkgs.ruby}/bin/ruby
|
||||
|
||||
require 'net/smtp'
|
||||
|
||||
raise RuntimeError.new("NETINFO_SMTP_USERNAME not set!") if not ENV['NETINFO_SMTP_USERNAME']
|
||||
user = ENV['NETINFO_SMTP_USERNAME']
|
||||
|
||||
raise RuntimeError.new("NETINFO_SMTP_PASSWD not set!") if not ENV['NETINFO_SMTP_PASSWD']
|
||||
passwd = ENV['NETINFO_SMTP_PASSWD']
|
||||
|
||||
hostname = `${pkgs.inetutils}/bin/hostname -f`.strip
|
||||
date = `${pkgs.coreutils}/bin/date +%Y-%m-%d`.strip
|
||||
email_date = `${pkgs.coreutils}/bin/date`
|
||||
ipinfo = `${pkgs.iproute}/bin/ip addr`
|
||||
|
||||
message = <<EOM
|
||||
From: #{user}@fudo.org
|
||||
To: ${target}
|
||||
Subject: #{hostname} network info for #{date}
|
||||
Date: #{email_date}
|
||||
|
||||
#{ipinfo}
|
||||
EOM
|
||||
|
||||
smtp = Net::SMTP.new("${server}", ${toString port})
|
||||
smtp.enable_starttls
|
||||
|
||||
smtp.start('localhost', user, passwd) do |server|
|
||||
server.send_message(message, "#{user}@fudo.org", ["${target}"])
|
||||
end
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.netinfo-email = {
|
||||
enable = mkEnableOption "Enable netinfo email (hacky way to keep track of a host's IP";
|
||||
|
||||
smtp-server = mkOption {
|
||||
type = types.str;
|
||||
default = "mail.fudo.org";
|
||||
};
|
||||
|
||||
smtp-port = mkOption {
|
||||
type = types.port;
|
||||
default = 587;
|
||||
};
|
||||
|
||||
env-file = mkOption {
|
||||
type = types.str;
|
||||
description = "Path to file containing NETINFO_SMTP_USERNAME and NETINFO_SMTP_PASSWD";
|
||||
};
|
||||
|
||||
target-email = mkOption {
|
||||
type = types.str;
|
||||
default = "network-info@fudo.link";
|
||||
description = "Email to which to send network info report.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd = {
|
||||
timers.netinfo = {
|
||||
enable = true;
|
||||
description = "Send network info to ${cfg.target-email}";
|
||||
partOf = ["netinfo.service"];
|
||||
wantedBy = [ "timers.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = "daily";
|
||||
};
|
||||
};
|
||||
|
||||
services.netinfo = {
|
||||
enable = true;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
StandardOutput = "journal";
|
||||
EnvironmentFile = cfg.env-file;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.ruby}/bin/ruby ${make-script cfg.smtp-server cfg.smtp-port cfg.target-email}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
{ 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" ''
|
||||
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}"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -f ${file} ]; then
|
||||
echo "Failed to create file ${file}"
|
||||
exit 3
|
||||
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}" = {
|
||||
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 == [])) {
|
||||
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);
|
||||
};
|
||||
}
|
|
@ -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"
|
||||
join-lines (mapAttrsToList
|
||||
(user: opts: join-lines
|
||||
(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);
|
||||
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
|
||||
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*
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -111,6 +111,8 @@ in {
|
|||
|
||||
webExternalUrl = "https://${cfg.hostname}";
|
||||
|
||||
listenAddress = "127.0.0.1:9090";
|
||||
|
||||
scrapeConfigs = [
|
||||
{
|
||||
job_name = "docker";
|
||||
|
|
|
@ -72,7 +72,7 @@ in {
|
|||
|
||||
peers = mkOption {
|
||||
type = loaOf str;
|
||||
description = "A map of peer name to a path pointing to that client's private key.";
|
||||
description = "A map of peers to shared private keys.";
|
||||
default = {};
|
||||
example = {
|
||||
peer0 = "/path/to/priv.key";
|
||||
|
|
|
@ -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"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,9 +5,12 @@ 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/garbage-collector.nix
|
||||
./fudo/git.nix
|
||||
./fudo/grafana.nix
|
||||
./fudo/kdc.nix
|
||||
|
@ -16,7 +19,9 @@ with lib;
|
|||
./fudo/mail.nix
|
||||
./fudo/mail-container.nix
|
||||
./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
|
||||
|
|
20
defaults.nix
20
defaults.nix
|
@ -30,7 +30,6 @@
|
|||
clang
|
||||
curl
|
||||
dpkg
|
||||
emacs
|
||||
enca
|
||||
fail2ban
|
||||
file
|
||||
|
@ -46,7 +45,9 @@
|
|||
ipfs
|
||||
iptables
|
||||
jdk
|
||||
jq
|
||||
kerberos
|
||||
leiningen
|
||||
libisofs
|
||||
libstdcxxHook
|
||||
lispPackages.alexandria
|
||||
|
@ -57,6 +58,7 @@
|
|||
lshw
|
||||
mkpasswd
|
||||
ncurses5
|
||||
nixfmt
|
||||
nix-index
|
||||
nix-prefetch-git
|
||||
nmap
|
||||
|
@ -164,7 +166,6 @@
|
|||
'';
|
||||
|
||||
security.pam = {
|
||||
enableSSHAgentAuth = true;
|
||||
# TODO: add yubico?
|
||||
services = {
|
||||
sshd = {
|
||||
|
@ -221,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
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -15,4 +15,6 @@ in {
|
|||
system = admin-users;
|
||||
|
||||
asdf = ["mswaffer@gmail.com" "bouncetest@fudo.org"];
|
||||
|
||||
network-info = ["niten@fudo.org"];
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
# Fudo email settings
|
||||
{ config }:
|
||||
|
||||
let
|
||||
mail-hostname = "france.fudo.org";
|
||||
|
||||
in {
|
||||
|
||||
{
|
||||
domain = "fudo.org";
|
||||
|
||||
local-domains = [
|
||||
"mail.fudo.org"
|
||||
"${config.networking.hostName}"
|
||||
"selby.ca"
|
||||
"mail.selby.ca"
|
||||
|
|
|
@ -19,6 +19,7 @@ let
|
|||
corefonts
|
||||
chrome-gnome-shell
|
||||
chromium
|
||||
emacs
|
||||
evince
|
||||
firefox
|
||||
gimp
|
||||
|
@ -87,15 +88,17 @@ in mkIf ((profile == "desktop") || (profile == "laptop")) {
|
|||
|
||||
displayManager.gdm.enable = true;
|
||||
|
||||
displayManager.defaultSession = "gnome";
|
||||
# displayManager.defaultSession = "gnome";
|
||||
|
||||
windowManager.session = pkgs.lib.singleton {
|
||||
name = "stumpwm";
|
||||
start = ''
|
||||
${pkgs.lispPackages.stumpwm}/bin/stumpwm &
|
||||
waidPID=$!
|
||||
'';
|
||||
};
|
||||
windowManager.stumpwm.enable = true;
|
||||
|
||||
# windowManager.session = pkgs.lib.singleton {
|
||||
# name = "stumpwm";
|
||||
# start = ''
|
||||
# ${pkgs.lispPackages.stumpwm}/bin/stumpwm &
|
||||
# waidPID=$!
|
||||
# '';
|
||||
# };
|
||||
} else {
|
||||
layout = "us";
|
||||
xkbVariant = "dvp";
|
||||
|
|
|
@ -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
|
||||
|
@ -51,6 +51,7 @@ in {
|
|||
config = mkIf (config.fudo.common.profile == "server") {
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
emacs-nox
|
||||
ldns
|
||||
ldns.examples
|
||||
racket-minimal
|
||||
|
@ -67,8 +68,8 @@ in {
|
|||
|
||||
networking = {
|
||||
networkmanager.enable = mkForce false;
|
||||
}
|
||||
;
|
||||
};
|
||||
|
||||
boot.tmpOnTmpfs = true;
|
||||
|
||||
services.xserver.enable = false;
|
||||
|
|
|
@ -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";
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
imports = [
|
||||
./joes.nix
|
||||
./portage.nix
|
||||
./russell.nix
|
||||
./seattle.nix
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
admin = "admin@fudo.org";
|
||||
|
||||
local-domain = "rus.selby.ca";
|
||||
|
||||
gateway = "10.0.0.1";
|
||||
|
||||
nameservers = [ "10.0.0.1" ];
|
||||
|
||||
hostname = config.networking.hostName;
|
||||
|
||||
in {
|
||||
config = mkIf (config.fudo.common.site == "russell") {
|
||||
time.timeZone = "America/Winnipeg";
|
||||
|
||||
services.cron = {
|
||||
mailto = admin;
|
||||
};
|
||||
|
||||
networking = {
|
||||
domain = "rus.selby.ca";
|
||||
search = [local-domain "fudo.org" "selby.ca"];
|
||||
nameservers = nameservers;
|
||||
|
||||
# Don't set the gateway if we ARE the gateway.
|
||||
# This is the most generic way I can think of to do that. local-network is really
|
||||
# about running all the local servers (DNS, DHCP, and providing gateway).
|
||||
defaultGateway = optionalString (config.fudo.local-network.enable != true) gateway;
|
||||
|
||||
enableIPv6 = true;
|
||||
|
||||
|
||||
# Necessary to make sure than Kerberos and Avahi both work (the former
|
||||
# needs the full reverse-lookup name of the server, the latter wants
|
||||
# `hostname` to return just the host itself.
|
||||
hosts = {
|
||||
"127.0.0.1" = [
|
||||
"${config.networking.hostName}.${local-domain}"
|
||||
config.networking.hostName
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
krb5.libdefaults.default_realm = "FUDO.ORG";
|
||||
|
||||
users.extraUsers = {
|
||||
guest = {
|
||||
isNormalUser = true;
|
||||
uid = 1000;
|
||||
description = "Guest User";
|
||||
extraGroups = ["audio" "video" "disk" "floppy" "lp" "cdrom" "tape" "input"];
|
||||
};
|
||||
ken = {
|
||||
isNormalUser = true;
|
||||
uid = 10035;
|
||||
createHome = true;
|
||||
description = "Ken Selby";
|
||||
extraGroups = ["audio" "video" "disk" "floppy" "lp" "cdrom" "tape" "input"];
|
||||
group = "users";
|
||||
home = "/home/selby/ken";
|
||||
hashedPassword = "$6$EwK9fpbH8$gYVzYY1IYw2/G0wCeUxXrZZqvjWCkCZbBqCOhxowbMuYtC5G0vp.AoYhVKWOJcHJM2c7TdPmAdnhLIe2KYStf.";
|
||||
};
|
||||
xiaoxuan = {
|
||||
isNormalUser = true;
|
||||
uid = 10065;
|
||||
createHome = true;
|
||||
description = "Xiaoxuan Jin";
|
||||
extraGroups = ["audio" "video" "disk" "floppy" "lp" "cdrom" "tape" "input"];
|
||||
group = "users";
|
||||
home = "/home/xiaoxuan";
|
||||
hashedPassword = "$6$C8lYHrK7KvdKm/RE$cHZ2hg5gEOEjTV8Zoayik8sz5h.Vh0.ClCgOlQn8l/2Qx/qdxqZ7xCsAZ1GZ.IEyESfhJeJbjLpykXDwPpfVF0";
|
||||
};
|
||||
};
|
||||
|
||||
fudo.common.domain = "sea.fudo.org";
|
||||
|
||||
fudo.local-network = {
|
||||
domain = "${local-domain}";
|
||||
|
||||
network = "10.0.0.0/16";
|
||||
|
||||
dhcp-dynamic-network = "10.0.1.0/24";
|
||||
|
||||
enable-reverse-mappings = true;
|
||||
|
||||
srv-records = {
|
||||
tcp = {
|
||||
domain = [{
|
||||
port = 53;
|
||||
host = "clunk.${local-domain}";
|
||||
}];
|
||||
kerberos = [{
|
||||
port = 88;
|
||||
host = "france.fudo.org";
|
||||
}];
|
||||
kerberos-adm = [{
|
||||
port = 88;
|
||||
host = "france.fudo.org";
|
||||
}];
|
||||
ssh = [{
|
||||
port = 22;
|
||||
host = "clunk.${local-domain}";
|
||||
}];
|
||||
};
|
||||
|
||||
udp = {
|
||||
domain = [{
|
||||
port = 53;
|
||||
host = "clunk.${local-domain}";
|
||||
}];
|
||||
kerberos = [{
|
||||
port = 88;
|
||||
host = "france.fudo.org";
|
||||
}];
|
||||
kerboros-master = [{
|
||||
port = 88;
|
||||
host = "france.fudo.org";
|
||||
}];
|
||||
kpasswd = [{
|
||||
port = 464;
|
||||
host = "france.fudo.org";
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
aliases = {
|
||||
dns-hole = "clunk";
|
||||
};
|
||||
|
||||
hosts = {
|
||||
clunk = {
|
||||
ip-address = "10.0.0.1";
|
||||
mac-address = "02:44:d1:eb:c3:6b";
|
||||
};
|
||||
|
||||
dns-proxy = {
|
||||
ip-address = "10.0.0.2";
|
||||
# This is just an alias for clunk's primary interface
|
||||
};
|
||||
|
||||
google-wifi = {
|
||||
ip-address = "10.0.0.11";
|
||||
mac-address = "70:3a:cb:c0:3b:09";
|
||||
};
|
||||
|
||||
pselby-work = {
|
||||
ip-address = "10.0.0.151";
|
||||
mac-address = "00:50:b6:aa:bd:b3";
|
||||
};
|
||||
|
||||
downstairs-desktop = {
|
||||
ip-address = "10.0.0.100";
|
||||
mac-address = "90:b1:1c:8e:29:cf";
|
||||
};
|
||||
|
||||
upstairs-desktop = {
|
||||
ip-address = "10.0.0.101";
|
||||
mac-address = "00:21:70:70:25:7d";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -118,8 +118,8 @@ in {
|
|||
domain = "${local-domain}";
|
||||
|
||||
aliases = {
|
||||
kadmin = "slab";
|
||||
kdc = "slab";
|
||||
kadmin = "nostromo";
|
||||
kdc = "nostromo";
|
||||
photo = "doraemon";
|
||||
music = "doraemon";
|
||||
panopticon = "hyperion";
|
||||
|
@ -127,6 +127,7 @@ in {
|
|||
hole = "nostromo";
|
||||
pihole = "nostromo";
|
||||
dns-hole = "nostromo";
|
||||
mon-1 = "srv-1";
|
||||
};
|
||||
|
||||
network = "10.0.0.0/16";
|
||||
|
@ -188,10 +189,18 @@ in {
|
|||
ip-address = "10.0.0.2";
|
||||
mac-address = "00:23:7d:e6:d9:ea";
|
||||
};
|
||||
lambda = {
|
||||
ip-address = "10.0.0.3";
|
||||
mac-address = "02:50:f6:52:9f:9d";
|
||||
};
|
||||
switch-master = {
|
||||
ip-address = "10.0.0.5";
|
||||
mac-address = "00:14:1C:B6:BB:40";
|
||||
};
|
||||
google-wifi = {
|
||||
ip-address = "10.0.0.7";
|
||||
mac-address = "7C:D9:5C:9F:6F:E9";
|
||||
};
|
||||
# lm = {
|
||||
# ip-address = "10.0.0.21";
|
||||
# mac-address = "52:54:00:D8:34:92";
|
||||
|
@ -256,6 +265,10 @@ in {
|
|||
ip-address = "10.0.0.126";
|
||||
mac-address = "04:18:d6:20:48:fb";
|
||||
};
|
||||
generator-wireless = {
|
||||
ip-address = "10.0.0.130";
|
||||
mac-address = "B8:27:EB:A6:32:26";
|
||||
};
|
||||
brother-wireless = {
|
||||
ip-address = "10.0.0.160";
|
||||
mac-address = "c0:38:96:64:49:65";
|
||||
|
@ -298,7 +311,11 @@ in {
|
|||
};
|
||||
|
||||
|
||||
# Storage network
|
||||
# Ceph network
|
||||
srv-1 = {
|
||||
ip-address = "10.0.10.1";
|
||||
mac-address = "02:65:d7:00:7d:1b";
|
||||
};
|
||||
node-1 = {
|
||||
ip-address = "10.0.10.101";
|
||||
mac-address = "00:1e:06:36:81:cf";
|
||||
|
@ -311,6 +328,10 @@ in {
|
|||
ip-address = "10.0.10.103";
|
||||
mac-address = "00:1e:06:36:ec:4b";
|
||||
};
|
||||
node-4 = {
|
||||
ip-address = "10.0.10.104";
|
||||
mac-address = "00:1e:06:36:dd:8c";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,4 +13,9 @@
|
|||
description = "User Database Reader";
|
||||
hashed-password = "{SSHA}IVKhrB+wMOCI/CCzbJW8sNDbH67ZTMBv";
|
||||
};
|
||||
|
||||
jabber = {
|
||||
description = "Jabber Server";
|
||||
hashed-password = "{SSHA}KlQpe0n+NP0WcJUniHTD+JzUugzLo8Ib";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -422,4 +422,11 @@
|
|||
common-name = "Kevin";
|
||||
hashed-password = "{SSHA}1onx6HPMKCJvmLnRf1tiWFJ1D92DEtnl";
|
||||
};
|
||||
|
||||
netinfo = {
|
||||
uid = 10113;
|
||||
group = "fudo";
|
||||
common-name = "Network Info Mailer";
|
||||
hashed-password = "{SSHA}UQHfW0IzjIbRU6VV+DraxvZFWt0to3oc";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ in {
|
|||
};
|
||||
|
||||
fudo.laptop.use-network-manager = false;
|
||||
fudo.common.enable-gui = false;
|
||||
fudo.common.enable-gui = true;
|
||||
|
||||
hardware.opengl.driSupport32Bit = true;
|
||||
hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ];
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
let
|
||||
hostname = "clunk";
|
||||
host-internal-ip = "10.0.0.1";
|
||||
dns-proxy-ip = "10.0.0.2";
|
||||
inherit (lib.strings) concatStringsSep;
|
||||
|
||||
in {
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
boot = {
|
||||
runSize = "50%";
|
||||
};
|
||||
|
||||
hardware.bluetooth.enable = false;
|
||||
|
||||
imports = [
|
||||
../defaults.nix
|
||||
../hardware-configuration.nix
|
||||
];
|
||||
|
||||
fudo.common = {
|
||||
profile = "server";
|
||||
site = "russell";
|
||||
};
|
||||
|
||||
fudo.local-network = {
|
||||
enable = true;
|
||||
dns-servers = [ host-internal-ip ];
|
||||
gateway = host-internal-ip;
|
||||
dhcp-interfaces = [ "intif0" ];
|
||||
dns-serve-ips = [ host-internal-ip "127.0.0.1" "127.0.1.1" "::1" ];
|
||||
# Using a pihole running in docker, see below
|
||||
recursive-resolver = "${host-internal-ip} port 5353";
|
||||
server-ip = host-internal-ip;
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = hostname;
|
||||
|
||||
nameservers = [ host-internal-ip ];
|
||||
|
||||
# Create a bridge for VMs to use
|
||||
macvlans = {
|
||||
intif0 = {
|
||||
interface = "enp2s0";
|
||||
mode = "bridge";
|
||||
};
|
||||
};
|
||||
|
||||
firewall = {
|
||||
enable = true;
|
||||
trustedInterfaces = [ "intif0" "docker0" ];
|
||||
};
|
||||
|
||||
interfaces = {
|
||||
enp2s0.useDHCP = false;
|
||||
enp3s0.useDHCP = false;
|
||||
enp4s0.useDHCP = false;
|
||||
|
||||
enp1s0.useDHCP = true;
|
||||
|
||||
intif0 = {
|
||||
useDHCP = false;
|
||||
# Result of:
|
||||
# echo clunk-intif0|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
|
||||
macAddress = "02:44:d1:eb:c3:6b";
|
||||
ipv4.addresses = [
|
||||
{
|
||||
address = host-internal-ip;
|
||||
prefixLength = 22;
|
||||
}
|
||||
|
||||
{
|
||||
address = dns-proxy-ip;
|
||||
prefixLength = 32;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
nat = {
|
||||
enable = true;
|
||||
externalInterface = "enp1s0";
|
||||
internalInterfaces = ["intif0"];
|
||||
};
|
||||
};
|
||||
|
||||
fudo = {
|
||||
garbage-collector = {
|
||||
enable = true;
|
||||
timing = "hourly";
|
||||
};
|
||||
|
||||
secure-dns-proxy = {
|
||||
enable = true;
|
||||
port = 53;
|
||||
upstream-dns = [
|
||||
"https://1.1.1.1/dns-query"
|
||||
"https://1.0.0.1/dns-query"
|
||||
#"https://9.9.9.9/dns-query"
|
||||
];
|
||||
bootstrap-dns = "1.1.1.1";
|
||||
listen-ips = [dns-proxy-ip];
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
dnsproxy
|
||||
];
|
||||
|
||||
virtualisation = {
|
||||
docker = {
|
||||
enable = true;
|
||||
autoPrune.enable = true;
|
||||
enableOnBoot = true;
|
||||
};
|
||||
};
|
||||
|
||||
docker-containers = {
|
||||
pihole = {
|
||||
image = "pihole/pihole:v5.1.2";
|
||||
ports = [
|
||||
"5353:53/tcp"
|
||||
"5353:53/udp"
|
||||
"3080:80/tcp"
|
||||
];
|
||||
environment = {
|
||||
ServerIP = host-internal-ip;
|
||||
VIRTUAL_HOST = "dns-hole.rus.selby.ca";
|
||||
# Not working?
|
||||
DNS1 = dns-proxy-ip;
|
||||
#DNS1 = "1.1.1.1";
|
||||
};
|
||||
volumes = [
|
||||
"/srv/pihole/etc-pihole/:/etc/pihole/"
|
||||
"/srv/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
recommendedOptimisation = true;
|
||||
recommendedGzipSettings = true;
|
||||
|
||||
virtualHosts = {
|
||||
"dns-hole.rus.selby.ca" = {
|
||||
serverAliases = [
|
||||
"pihole.rus.selby.ca"
|
||||
"hole.rus.selby.ca"
|
||||
"pihole"
|
||||
"dns-hole"
|
||||
"hole"
|
||||
];
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3080";
|
||||
|
||||
extraConfig = ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
162
hosts/france.nix
162
hosts/france.nix
|
@ -4,7 +4,7 @@ with lib;
|
|||
let
|
||||
domain = "fudo.org";
|
||||
hostname = "france.${domain}";
|
||||
mail-hostname = hostname;
|
||||
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";
|
||||
|
@ -30,18 +30,18 @@ in {
|
|||
|
||||
imports = [
|
||||
../hardware-configuration.nix
|
||||
|
||||
../defaults.nix
|
||||
./france/jabber.nix
|
||||
./france/backplane.nix
|
||||
];
|
||||
|
||||
# services.openssh = {
|
||||
# listenAddresses = [
|
||||
# {
|
||||
# addr = host_ipv4;
|
||||
# port = 22;
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
environment.systemPackages = with pkgs; [
|
||||
docker
|
||||
lxd
|
||||
multipath-tools
|
||||
nix-prefetch-docker
|
||||
tshark
|
||||
];
|
||||
|
||||
fudo.common = {
|
||||
# Sets some server-common settings. See /etc/nixos/fudo/profiles/...
|
||||
|
@ -62,14 +62,6 @@ in {
|
|||
];
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
docker
|
||||
lxd
|
||||
multipath-tools
|
||||
nix-prefetch-docker
|
||||
tshark
|
||||
];
|
||||
|
||||
fudo.prometheus = {
|
||||
enable = true;
|
||||
hostname = "metrics.fudo.org";
|
||||
|
@ -122,43 +114,93 @@ 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;
|
||||
|
@ -186,16 +228,9 @@ in {
|
|||
# sslKey = (acme-private-key hostname);
|
||||
# sslCACert = acme-ca;
|
||||
|
||||
# TODO: loop over v4 and v6 IPs.
|
||||
listen-uris = [
|
||||
"ldap:///"
|
||||
"ldaps:///"
|
||||
# "ldap://${host_ipv4}/"
|
||||
# "ldaps://${host_ipv4}/"
|
||||
# "ldap://localhost/"
|
||||
# "ldaps://localhost/"
|
||||
# "ldap://127.0.1.1/"
|
||||
# "ldaps://127.0.1.1/"
|
||||
"ldapi:///"
|
||||
];
|
||||
|
||||
|
@ -274,7 +309,21 @@ in {
|
|||
"webmail.test.fudo.org" = {
|
||||
title = "Fudo Webmail";
|
||||
favicon = "/etc/nixos/static/fudo.org/favicon.ico";
|
||||
mail-server = "mail.fudo.org";
|
||||
mail-server = mail-hostname;
|
||||
domain = "fudo.org";
|
||||
edit-mode = "Plain";
|
||||
database = {
|
||||
name = "webmail";
|
||||
hostname = "localhost";
|
||||
user = "webmail";
|
||||
password-file = "/srv/webmail/secure/db.passwd";
|
||||
};
|
||||
};
|
||||
|
||||
"webmail.fudo.org" = {
|
||||
title = "Fudo Webmail";
|
||||
favicon = "/etc/nixos/static/fudo.org/favicon.ico";
|
||||
mail-server = mail-hostname;
|
||||
domain = "fudo.org";
|
||||
edit-mode = "Plain";
|
||||
database = {
|
||||
|
@ -288,7 +337,20 @@ in {
|
|||
"webmail.test.selby.ca" = {
|
||||
title = "Selby Webmail";
|
||||
favicon = "/etc/nixos/static/selby.ca/favicon.ico";
|
||||
mail-server = "mail.selby.ca";
|
||||
mail-server = mail-hostname;
|
||||
domain = "selby.ca";
|
||||
database = {
|
||||
name = "webmail";
|
||||
hostname = "localhost";
|
||||
user = "webmail";
|
||||
password-file = "/srv/webmail/secure/db.passwd";
|
||||
};
|
||||
};
|
||||
|
||||
"webmail.selby.ca" = {
|
||||
title = "Selby Webmail";
|
||||
favicon = "/etc/nixos/static/selby.ca/favicon.ico";
|
||||
mail-server = mail-hostname;
|
||||
domain = "selby.ca";
|
||||
database = {
|
||||
name = "webmail";
|
||||
|
@ -305,7 +367,7 @@ in {
|
|||
|
||||
hostname = "chat.fudo.org";
|
||||
site-name = "Fudo Chat";
|
||||
smtp-server = "france.fudo.org";
|
||||
smtp-server = "mail.fudo.org";
|
||||
smtp-user = "chat";
|
||||
smtp-password-file = "/srv/mattermost/secure/smtp.passwd";
|
||||
database = {
|
||||
|
@ -342,7 +404,7 @@ in {
|
|||
useDHCP = false;
|
||||
|
||||
# TODO: fix IPv6
|
||||
enableIPv6 = false;
|
||||
enableIPv6 = true;
|
||||
|
||||
# Create a bridge for VMs to use
|
||||
macvlans = {
|
||||
|
@ -460,14 +522,10 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
fudo.system = {
|
||||
disableTransparentHugePages = true;
|
||||
postHugePageServices = ["redis.service"];
|
||||
};
|
||||
|
||||
security.acme.certs = {
|
||||
"archiva.fudo.org".email = config.fudo.common.admin-email;
|
||||
"git.fudo.org".email = config.fudo.common.admin-email;
|
||||
"mail.fudo.org".email = config.fudo.common.admin-email;
|
||||
};
|
||||
|
||||
services = {
|
||||
|
@ -494,6 +552,15 @@ in {
|
|||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# Needed to grab a cert for the mail server.
|
||||
"mail.fudo.org" = {
|
||||
enableACME = true;
|
||||
# Stopped relocating all because we need /metrics/... paths to remain unforwarded
|
||||
locations."/" = {
|
||||
return = "301 https://webmail.fudo.org$request_uri";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -510,6 +577,7 @@ in {
|
|||
environment = {
|
||||
# Not directly connected to the world anyway
|
||||
SSL_ENABLED = "false";
|
||||
PROXY_BASE_URL = "https://archiva.fudo.org/";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -520,7 +588,7 @@ in {
|
|||
|
||||
fudo.minecraft-server = {
|
||||
enable = true;
|
||||
package = pkgs.minecraft-server_1_16_1;
|
||||
package = pkgs.minecraft-server_1_16_2;
|
||||
data-dir = minecraft-data-dir;
|
||||
world-name = "selbyland";
|
||||
motd = "Welcome to the Selby Minecraft server.";
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
{ pkgs, lib, config, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
backplane-auth = "/etc/nixos/static/backplane-auth.scm";
|
||||
|
||||
cert-basedir = "/var/lib/ejabberd/certs";
|
||||
|
||||
target-certs = ["key" "cert" "chain" "fullchain"];
|
||||
|
||||
cert-origin = hostname: filename: "/var/lib/acme/${hostname}/${filename}.pem";
|
||||
cert-target = hostname: filename: "${cert-basedir}/${hostname}-${filename}.pem";
|
||||
|
||||
move-server-certs = hostnames:
|
||||
let
|
||||
move-server-cert = hostname:
|
||||
map (filename: ''
|
||||
ensure_exists ${cert-origin hostname filename}
|
||||
cp -L ${cert-origin hostname filename} ${cert-target hostname filename}
|
||||
'')
|
||||
target-certs;
|
||||
in pkgs.writeShellScript "move-server-certs" ''
|
||||
function ensure_exists() {
|
||||
FILENAME=$1
|
||||
if [ ! -e $FILENAME ]; then
|
||||
echo "file does not exist: $FILENAME"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -d ${cert-basedir} ]; then
|
||||
mkdir ${cert-basedir}
|
||||
fi
|
||||
|
||||
${concatStringsSep "\n" (concatMap move-server-cert hostnames)}
|
||||
|
||||
chown -R ${config.services.ejabberd.user}:${config.services.ejabberd.group} ${cert-basedir}
|
||||
|
||||
exit 0
|
||||
'';
|
||||
|
||||
remove-server-certs = pkgs.writeShellScript "ejabberd-rm-combined-certs" ''
|
||||
rm ${cert-basedir}/*.pem
|
||||
'';
|
||||
|
||||
|
||||
in {
|
||||
config = {
|
||||
|
||||
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"
|
||||
];
|
||||
|
||||
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 = "/srv/jabber/secret/hosts-passwd.scm";
|
||||
FUDO_SERVICE_PASSWD_FILE = "/srv/jabber/secret/services-passwd.scm";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
nginx = {
|
||||
virtualHosts = {
|
||||
"backplane.fudo.org" = {
|
||||
enableACME = true;
|
||||
};
|
||||
|
||||
"fudo.im" = {
|
||||
enableACME = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ejabberd = {
|
||||
enable = true;
|
||||
|
||||
configFile = pkgs.writeText "ejabberd-config.yml" (builtins.toJSON {
|
||||
loglevel = 4;
|
||||
|
||||
access_rules = {
|
||||
c2s = { allow = "all"; };
|
||||
announce = { allow = "admin"; };
|
||||
configure = { allow = "admin"; };
|
||||
pubsub_createnode = { allow = "local"; };
|
||||
};
|
||||
|
||||
acl = {
|
||||
admin = {
|
||||
user = [
|
||||
"niten@fudo.org"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
hosts = [
|
||||
"fudo.im"
|
||||
"backplane.fudo.org"
|
||||
];
|
||||
|
||||
listen = [
|
||||
{
|
||||
port = 5222;
|
||||
module = "ejabberd_c2s";
|
||||
ip = "0.0.0.0";
|
||||
starttls = true;
|
||||
starttls_required = true;
|
||||
}
|
||||
];
|
||||
|
||||
certfiles =
|
||||
concatMap (hostname: map (filename: cert-target hostname filename) target-certs)
|
||||
["fudo.im" "backplane.fudo.org"];
|
||||
|
||||
host_config = {
|
||||
"fudo.im" = {
|
||||
auth_method = "ldap";
|
||||
ldap_servers = ["auth.fudo.org"];
|
||||
ldap_port = 389;
|
||||
ldap_rootdn = "cn=jabber,dc=fudo,dc=org";
|
||||
ldap_password = fileContents /srv/jabber/secret/ldap.passwd;
|
||||
ldap_base = "ou=members,dc=fudo,dc=org";
|
||||
ldap_filter = "(objectClass=posixAccount)";
|
||||
ldap_uids = { uid = "%u"; };
|
||||
|
||||
modules = {
|
||||
mod_adhoc = {};
|
||||
mod_announce = {};
|
||||
mod_avatar = {};
|
||||
mod_blocking = {};
|
||||
mod_caps = {};
|
||||
mod_carboncopy = {};
|
||||
mod_client_state = {};
|
||||
mod_configure = {};
|
||||
mod_disco = {};
|
||||
mod_fail2ban = {};
|
||||
mod_last = {};
|
||||
mod_offline = {
|
||||
access_max_user_messages = 5000;
|
||||
};
|
||||
mod_ping = {};
|
||||
mod_privacy = {};
|
||||
mod_private = {};
|
||||
mod_pubsub = {
|
||||
access_createnode = "pubsub_createnode";
|
||||
ignore_pep_from_offline = true;
|
||||
last_item_cache = false;
|
||||
plugins = [
|
||||
"flat"
|
||||
"pep"
|
||||
];
|
||||
};
|
||||
mod_roster = {};
|
||||
mod_stream_mgmt = {};
|
||||
mod_time = {};
|
||||
mod_vcard = {
|
||||
search = false;
|
||||
};
|
||||
mod_vcard_xupdate = {};
|
||||
mod_version = {};
|
||||
};
|
||||
};
|
||||
|
||||
"backplane.fudo.org" = {
|
||||
auth_method = "external";
|
||||
extauth_program = "${pkgs.guile}/bin/guile -s ${backplane-auth}";
|
||||
extauth_pool_size = 3;
|
||||
auth_use_cache = true;
|
||||
|
||||
modules = {
|
||||
mod_adhoc = {};
|
||||
mod_caps = {};
|
||||
mod_carboncopy = {};
|
||||
mod_client_state = {};
|
||||
mod_configure = {};
|
||||
mod_disco = {};
|
||||
mod_fail2ban = {};
|
||||
mod_last = {};
|
||||
mod_offline = {
|
||||
access_max_user_messages = 5000;
|
||||
};
|
||||
mod_ping = {};
|
||||
mod_pubsub = {
|
||||
access_createnode = "pubsub_createnode";
|
||||
ignore_pep_from_offline = true;
|
||||
last_item_cache = false;
|
||||
plugins = [
|
||||
"flat"
|
||||
"pep"
|
||||
];
|
||||
};
|
||||
mod_roster = {};
|
||||
mod_stream_mgmt = {};
|
||||
mod_time = {};
|
||||
mod_version = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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,11 +43,11 @@ 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;
|
||||
};
|
||||
|
||||
fudo.slynk = {
|
||||
# NOTE: for now, had to do: XDG_RUNTIME_DIR=/run/user/$UID systemctl start --user slynk
|
||||
enable = true;
|
||||
};
|
||||
|
||||
|
@ -89,17 +98,22 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
fudo = {
|
||||
postgresql = {
|
||||
enable = true;
|
||||
ssl-private-key = "/srv/nostromo/certs/private/privkey.pem";
|
||||
ssl-certificate = "/srv/nostromo/certs/cert.pem";
|
||||
keytab = "/srv/nostromo/keytabs/postgres.keytab";
|
||||
users = {
|
||||
users = {
|
||||
fudo-client = {
|
||||
isSystemUser = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
local-networks = [
|
||||
"10.0.0.1/24"
|
||||
"127.0.0.1/8"
|
||||
];
|
||||
fudo = {
|
||||
client.dns = {
|
||||
enable = true;
|
||||
ipv4 = true;
|
||||
ipv6 = true;
|
||||
user = "fudo-client";
|
||||
external-interface = "eno2";
|
||||
password-file = "/srv/client/secure/client.passwd";
|
||||
};
|
||||
|
||||
secure-dns-proxy = {
|
||||
|
@ -111,14 +125,12 @@ in {
|
|||
];
|
||||
bootstrap-dns = "1.1.1.1";
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
dnsproxy
|
||||
libguestfs-with-appliance
|
||||
libvirt
|
||||
virtmanager
|
||||
];
|
||||
netinfo-email = {
|
||||
enable = true;
|
||||
env-file = "/srv/netinfo/secure/env";
|
||||
};
|
||||
};
|
||||
|
||||
virtualisation = {
|
||||
docker = {
|
||||
|
@ -160,13 +172,7 @@ in {
|
|||
};
|
||||
|
||||
services = {
|
||||
dhcpd6.enable = false;
|
||||
|
||||
# glusterfs = {
|
||||
# enable = true;
|
||||
# enableGlustereventsd = true;
|
||||
# useRpcbind = true;
|
||||
# };
|
||||
# dhcpd6.enable = true;
|
||||
|
||||
nginx = {
|
||||
enable = true;
|
||||
|
@ -192,33 +198,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"];
|
||||
# };
|
||||
# };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,20 +6,42 @@ let
|
|||
in {
|
||||
imports = [
|
||||
../defaults.nix
|
||||
../networks/sea.fudo.org.nix
|
||||
../profiles/desktop.nix
|
||||
../hardware-configuration.nix
|
||||
];
|
||||
|
||||
nixpkgs.config.permittedInsecurePackages = [
|
||||
"google-chrome-81.0.4044.138"
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
# androidStudioPackages.canary
|
||||
androidenv.androidPkgs_9_0.platform-tools
|
||||
cmake
|
||||
glxinfo
|
||||
opencv4
|
||||
qemu_kvm
|
||||
signal-cli
|
||||
signal-desktop
|
||||
teamviewer
|
||||
thunderbird
|
||||
wireshark
|
||||
];
|
||||
|
||||
fudo.common = {
|
||||
profile = "desktop";
|
||||
site = "seattle";
|
||||
enable-gui = true;
|
||||
};
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi = {
|
||||
canTouchEfiVariables = true;
|
||||
efibootmgr = {
|
||||
efiDisk = "/dev/sda1";
|
||||
};
|
||||
|
||||
# efiSysMountPoint = "/boot/efi";
|
||||
programs = {
|
||||
adb.enable = true;
|
||||
bash.enableCompletion = true;
|
||||
};
|
||||
|
||||
networking.hostName = hostname;
|
||||
|
@ -29,4 +51,13 @@ in {
|
|||
hardware.opengl.driSupport32Bit = true;
|
||||
hardware.opengl.driSupport = true;
|
||||
|
||||
services = {
|
||||
trezord.enable = true;
|
||||
};
|
||||
|
||||
virtualisation.libvirtd = {
|
||||
enable = true;
|
||||
qemuPackage = pkgs.qemu_kvm;
|
||||
onShutdown = "shutdown";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{ pkgs, fetchurl, ... }:
|
||||
|
||||
let
|
||||
version = "2.2.5";
|
||||
url = "https://mirrors.sonic.net/apache/archiva/${version}/binaries/apache-archiva-${version}-bin.tar.gz";
|
||||
sha256 = "01119af2d9950eacbcce0b7f8db5067b166ad26c1e1701bef829105441bb6e29";
|
||||
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = "archiva-${version}";
|
||||
|
||||
src = builtins.fetchurl {
|
||||
url = url;
|
||||
sha256 = sha256;
|
||||
};
|
||||
|
||||
phases = ["installPhase"];
|
||||
|
||||
buildInputs = with pkgs; [ stdenv procps makeWrapper ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
tar -xzf $src
|
||||
cd apache-archiva-${version}
|
||||
mv {LICENSE,NOTICE,apps,bin,conf,contexts,lib,logs,temp} $out
|
||||
makeWrapper $out/bin/archiva $out/bin/archivaWrapped --set PATH ${pkgs.stdenv.lib.makeBinPath [ pkgs.procps ]}
|
||||
'';
|
||||
}
|
|
@ -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 <<EOF
|
||||
#!${pkgs.bash}/bin/bash -e
|
||||
exec ${gems}/bin/bundle exec ${pkgs.ruby}/bin/ruby $out/lib/dns-client.rb "\$@"
|
||||
EOF
|
||||
chmod +x $BIN
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{ stdenv, fetchgit, pkgs }:
|
||||
|
||||
let
|
||||
url = "https://git.fudo.org/fudo-public/backplane-dns.git";
|
||||
version = "0.1";
|
||||
|
||||
in stdenv.mkDerivation {
|
||||
name = "backplane-dns-${version}";
|
||||
|
||||
src = fetchgit {
|
||||
url = url;
|
||||
rev = "543df72f3962cf91b0e0508d15cdc083a3cd7ed4";
|
||||
sha256 = "0hda1wjf9wd4rvxchdlxw0af3i2cvl5plg37ric3ckma6gfzkmm0";
|
||||
fetchSubmodules = false;
|
||||
};
|
||||
|
||||
phases = ["installPhase"];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p "$out/lib/common-lisp/backplane-dns"
|
||||
cp "$src/backplane-dns.asd" "$out/lib/common-lisp/backplane-dns"
|
||||
cp -R $src/*.lisp "$out/lib/common-lisp/backplane-dns"
|
||||
'';
|
||||
}
|
|
@ -35,6 +35,18 @@
|
|||
};
|
||||
}));
|
||||
|
||||
minecraft-server_1_16_2 = let
|
||||
version = "1.16.2";
|
||||
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;
|
||||
};
|
||||
}));
|
||||
|
||||
postgresql_11_gssapi = pkgs.postgresql_11.overrideAttrs (oldAttrs: rec {
|
||||
configureFlags = oldAttrs.configureFlags ++ [ "--with-gssapi" ];
|
||||
buildInputs = oldAttrs.buildInputs ++ [ pkgs.krb5 ];
|
||||
|
@ -49,10 +61,51 @@
|
|||
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;
|
||||
fetchgit = pkgs.fetchgit;
|
||||
bundlerEnv = pkgs.bundlerEnv;
|
||||
};
|
||||
|
||||
cl-gemini = import ./cl-gemini.nix {
|
||||
pkgs = pkgs;
|
||||
stdenv = pkgs.stdenv;
|
||||
fetchgit = pkgs.fetchgit;
|
||||
};
|
||||
|
||||
fudo-service = import ./fudo-service.nix {
|
||||
fetchgit = pkgs.fetchgit;
|
||||
pkgs = pkgs;
|
||||
};
|
||||
|
||||
google-photos-uploader = pkgs.buildGoModule rec {
|
||||
pname = "google-photos-uploader";
|
||||
version = "1.6.1";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "int128";
|
||||
repo = "gpup";
|
||||
rev = "${version}";
|
||||
sha256 = "0zdkd5iwkp270p0810dijg25djkzrsdyqiqaqv6rzzgzj5d5pwhm";
|
||||
};
|
||||
|
||||
modSha256 = "15ndc6jq51f9mz1v089416x2lxrifp3wglbxpff8b055jj07hbkw";
|
||||
|
||||
subPackages = [ "." ];
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "Google photos uploader, written in Go.";
|
||||
homepage = https://github.com/int128/gpup;
|
||||
license = licenses.asl20;
|
||||
platforms = platforms.linux ++ platforms.darwin;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
(use-modules (srfi srfi-1)
|
||||
(srfi srfi-13)
|
||||
(ice-9 binary-ports)
|
||||
(ice-9 textual-ports)
|
||||
(ice-9 format)
|
||||
(ice-9 regex)
|
||||
(rnrs bytevectors))
|
||||
|
||||
(define *host-passwd-file* (getenv "FUDO_HOST_PASSWD_FILE"))
|
||||
(when (not *host-passwd-file*)
|
||||
(format (current-error-port "FUDO_HOST_PASSWD_FILE not set~%"))
|
||||
(exit 1))
|
||||
|
||||
(define *service-passwd-file* (getenv "FUDO_SERVICE_PASSWD_FILE"))
|
||||
(when (not *service-passwd-file*)
|
||||
(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 (make-verifier passwd-file)
|
||||
(let ((passwds (load passwd-file)))
|
||||
(lambda (username passwd)
|
||||
(and (> (string-length passwd) 6)
|
||||
(equal? (assoc-ref passwds username) passwd)))))
|
||||
|
||||
(define (make-authenticator host-verifier service-verifier)
|
||||
(lambda (username hostname password)
|
||||
(cond ((string-match host-regex username)
|
||||
(host-verifier (match:substring (string-match host-regex username) 1)
|
||||
password))
|
||||
|
||||
((string-match service-regex username)
|
||||
(service-verifier (match:substring (string-match service-regex username) 1)
|
||||
password))
|
||||
|
||||
(else #f))))
|
||||
|
||||
(define (make-handler handlers)
|
||||
(lambda (request)
|
||||
(let ((op (assoc-ref handlers (first request))))
|
||||
(if op
|
||||
(apply op (cdr request))
|
||||
#f))))
|
||||
|
||||
(define (auth-listener handler)
|
||||
(let ((in (current-input-port))
|
||||
(out (current-output-port)))
|
||||
(while #t
|
||||
(let ((size (bytevector-u16-ref (get-bytevector-n in 2) 0 (endianness big)))
|
||||
(response (make-bytevector 4 0)))
|
||||
(bytevector-u8-set! response 1 #x02)
|
||||
(if (handler (string-split (get-string-n in size) #\:))
|
||||
(begin (bytevector-u8-set! response 3 #x01)
|
||||
(put-bytevector out response 0 4)
|
||||
(force-output out))
|
||||
(begin (bytevector-u8-set! response 3 #x00)
|
||||
(put-bytevector out response 0 4)
|
||||
(force-output out)))))))
|
||||
|
||||
(auth-listener
|
||||
(make-handler
|
||||
(list (cons "auth"
|
||||
(make-authenticator (make-verifier *host-passwd-file*)
|
||||
(make-verifier *service-passwd-file*))))))
|
|
@ -0,0 +1,3 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem "xmpp4r"
|
|
@ -0,0 +1,13 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
xmpp4r (0.5.6)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
xmpp4r
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
|
@ -0,0 +1,295 @@
|
|||
|
||||
require "ipaddr"
|
||||
require "socket"
|
||||
require "optparse"
|
||||
require "json"
|
||||
require "securerandom"
|
||||
|
||||
require "xmpp4r"
|
||||
|
||||
puts ARGV
|
||||
|
||||
options = {}
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "usage: ${$0} [opts]"
|
||||
|
||||
opts.on("-i", "--interface=INTERFACE",
|
||||
"Publicly-accessible interface") do |interface|
|
||||
options[:interface] = interface
|
||||
end
|
||||
|
||||
opts.on("-d", "--domain=DOMAIN",
|
||||
"Domain on which we wish to set the new ip") do |domain|
|
||||
options[:domain] = domain
|
||||
end
|
||||
|
||||
opts.on("-s", "--server=SERVER",
|
||||
"Backplane DNS XMPP server") do |server|
|
||||
options[:server] = server
|
||||
end
|
||||
|
||||
opts.on("-p", "--password-file=/path/to/file",
|
||||
"File containing password for XMPP server") do |pw_file|
|
||||
options[:pw_file] = pw_file
|
||||
end
|
||||
|
||||
opts.on("-4", "--ipv4",
|
||||
"Check for a public IPv4 and register with the backplane.") do
|
||||
options[:ipv4] = true
|
||||
end
|
||||
|
||||
opts.on("-6", "--ipv6",
|
||||
"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)
|
||||
puts msg
|
||||
throw msg
|
||||
end
|
||||
|
||||
error("domain is required") if not options[:domain]
|
||||
error("server is required") if not options[:server]
|
||||
error("password file is required") if not options[:pw_file]
|
||||
error("at least one of -4 or -6 required") if not (options[:ipv4] or options[:ipv6])
|
||||
|
||||
if not File::readable?(options[:pw_file])
|
||||
error("file does not exist or is not readable")
|
||||
end
|
||||
password = File::open(options[:pw_file]) { |f| f.gets.strip }
|
||||
|
||||
class XMPPClient
|
||||
def initialize(domain, hostname, server, password)
|
||||
@jid = "host-#{hostname}@#{server}"
|
||||
@service_jid = "service-dns@#{server}"
|
||||
@server = server
|
||||
@domain = domain
|
||||
@password = password
|
||||
@responses = Queue.new
|
||||
@responses_lock = Mutex.new
|
||||
end
|
||||
|
||||
def connect
|
||||
disconnect if connected?
|
||||
@client = Jabber::Client::new(@jid)
|
||||
@client.connect # will use SRV records
|
||||
error("failed to initialize TLS connection") if not @client.is_tls?
|
||||
@client.auth(@password)
|
||||
register_response_callback
|
||||
end
|
||||
|
||||
def connected?
|
||||
@client ||= nil
|
||||
@client.respond_to?(:is_connected?) and @client.is_connected?
|
||||
end
|
||||
|
||||
def disconnect
|
||||
if @client.respond_to?(:is_connected?) && @client.is_connected?
|
||||
begin
|
||||
@client.close
|
||||
rescue Errno::EPIPE, IOError => e
|
||||
nil
|
||||
end
|
||||
end
|
||||
@client = nil
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def send_ip(ip)
|
||||
send(ip_payload(ip))
|
||||
end
|
||||
|
||||
def send_sshfp(fps)
|
||||
send(sshfp_payload(fps))
|
||||
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 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))
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def hostname
|
||||
Socket.gethostname.split(".").first
|
||||
end
|
||||
|
||||
client = XMPPClient::new(options[:domain],
|
||||
hostname,
|
||||
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]}: #{hostname}.#{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]}: #{hostname}.#{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
|
||||
|
||||
if options[:sshfp]
|
||||
fps = host_sshfp
|
||||
if not fps.empty?
|
||||
puts "#{options[:server]}: #{hostname}.#{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
|
||||
|
||||
exit success ? 0 : 1
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
xmpp4r = {
|
||||
groups = ["default"];
|
||||
platforms = [];
|
||||
source = {
|
||||
remotes = ["https://rubygems.org"];
|
||||
sha256 = "15ls2yqjvflxrc8chv5pcdh2p1p9fjsky74yc8y7wvw90wz0izrb";
|
||||
type = "gem";
|
||||
};
|
||||
version = "0.5.6";
|
||||
};
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env ruby
|
||||
# coding: utf-8
|
||||
|
||||
require 'net/smtp'
|
||||
|
||||
error "usage: #{$0} <smtp-server> <smtp-user>" if not (ARGV[0] and ARGV[1])
|
||||
server = ARGV[0]
|
||||
user = ARGV[1]
|
||||
|
||||
error "NETINFO_SMTP_PASSWD not set!" if not ENV['NETINFO_SMTP_PASSWD']
|
||||
passwd = ENV['NETINFO_SMTP_PASSWD']
|
||||
|
||||
hostname = `hostname -f`.strip
|
||||
date = `date +%Y-%m-%d`.strip
|
||||
email_date = `date`
|
||||
ipinfo = `ip addr`
|
||||
|
||||
message = <<EOM
|
||||
From: #{user}@fudo.org
|
||||
To: network-info@fudo.link
|
||||
Subject: #{hostname} network info for #{date}
|
||||
Date: #{email_date}
|
||||
|
||||
#{ipinfo}
|
||||
EOM
|
||||
|
||||
smtp = Net::SMTP.new(server, 587)
|
||||
smtp.enable_starttls
|
||||
|
||||
smtp.start('localhost', user, passwd) do |server|
|
||||
server.send_message(message, "#{user}@fudo.org", ["network-info@fudo.link"])
|
||||
end
|
Loading…
Reference in New Issue