diff options
Diffstat (limited to 'os/kay')
-rw-r--r-- | os/kay/configuration.nix | 18 | ||||
-rw-r--r-- | os/kay/hardware-configuration.nix | 39 | ||||
-rw-r--r-- | os/kay/modules/acme.nix | 23 | ||||
-rw-r--r-- | os/kay/modules/cgit.nix | 33 | ||||
-rw-r--r-- | os/kay/modules/dendrite.nix | 108 | ||||
-rw-r--r-- | os/kay/modules/dns/5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa.zone | 14 | ||||
-rw-r--r-- | os/kay/modules/dns/ddns.nix | 44 | ||||
-rw-r--r-- | os/kay/modules/dns/default.nix | 137 | ||||
-rw-r--r-- | os/kay/modules/dns/sinanmohd.com.zone | 46 | ||||
-rw-r--r-- | os/kay/modules/hurricane.nix | 115 | ||||
-rw-r--r-- | os/kay/modules/iperf3.nix | 10 | ||||
-rw-r--r-- | os/kay/modules/mail.nix | 110 | ||||
-rw-r--r-- | os/kay/modules/matrix-sliding-sync.nix | 18 | ||||
-rw-r--r-- | os/kay/modules/network.nix | 82 | ||||
-rw-r--r-- | os/kay/modules/router.nix | 43 | ||||
-rw-r--r-- | os/kay/modules/sftp.nix | 44 | ||||
-rw-r--r-- | os/kay/modules/sshfwd.nix | 29 | ||||
-rw-r--r-- | os/kay/modules/wireguard.nix | 57 | ||||
-rw-r--r-- | os/kay/modules/www.nix | 134 | ||||
-rw-r--r-- | os/kay/secrets.yaml | 47 |
20 files changed, 1151 insertions, 0 deletions
diff --git a/os/kay/configuration.nix b/os/kay/configuration.nix new file mode 100644 index 0000000..24cc246 --- /dev/null +++ b/os/kay/configuration.nix @@ -0,0 +1,18 @@ +{ ... }: + +{ + imports = [ + ../common/configuration.nix + ./hardware-configuration.nix + + ./modules/network.nix + ./modules/www.nix + ./modules/sftp.nix + ./modules/acme.nix + ./modules/mail.nix + ./modules/dns + ./modules/sshfwd.nix + ]; + + boot.consoleLogLevel = 3; +} diff --git a/os/kay/hardware-configuration.nix b/os/kay/hardware-configuration.nix new file mode 100644 index 0000000..5e2efdf --- /dev/null +++ b/os/kay/hardware-configuration.nix @@ -0,0 +1,39 @@ +{ modulesPath, ... }: + +{ + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; + + boot = { + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + + kernelModules = [ "kvm-intel" ]; + blacklistedKernelModules = [ "nouveau" ]; + initrd.availableKernelModules = [ + "xhci_pci" + "ehci_pci" + "ahci" + "usb_storage" + "usbhid" + "sd_mod" + ]; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/2eeacf49-c51e-4229-bd4a-ae437014725f"; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/disk/by-uuid/A902-90BB"; + fsType = "vfat"; + }; + "/hdd" = { + device = "/dev/disk/by-uuid/c941edb4-e393-4254-bbef-d1b3728290e9"; + fsType = "ext4"; + options = [ "nofail" ]; + }; + }; +} diff --git a/os/kay/modules/acme.nix b/os/kay/modules/acme.nix new file mode 100644 index 0000000..f4ded0a --- /dev/null +++ b/os/kay/modules/acme.nix @@ -0,0 +1,23 @@ +{ config, pkgs, ... }: let + email = config.userdata.email; + domain = config.userdata.domain; + + environmentFile = + pkgs.writeText "acme-dns" "RFC2136_NAMESERVER='[2001:470:ee65::1]:53'"; +in { + security.acme = { + acceptTerms = true; + defaults.email = email; + + certs.${domain} = { + inherit domain; + extraDomainNames = [ "*.${domain}" ]; + + dnsProvider = "rfc2136"; + dnsPropagationCheck = false; # local DNS server + + inherit environmentFile; + group = config.services.nginx.group; + }; + }; +} diff --git a/os/kay/modules/cgit.nix b/os/kay/modules/cgit.nix new file mode 100644 index 0000000..f8fb25c --- /dev/null +++ b/os/kay/modules/cgit.nix @@ -0,0 +1,33 @@ +{ config, pkgs, ... }: + +let + domain = config.userdata.domain; + user = config.userdata.name; +in +{ + environment.systemPackages = with pkgs; [ + luajitPackages.luaossl + lua52Packages.luaossl + ]; + + services.cgit."git.${domain}" = { + enable = true; + nginx.virtualHost = "git.${domain}"; + scanPath = "/var/lib/git"; + settings = { + project-list = "/var/lib/git/project.list"; + remove-suffix = 1; + enable-commit-graph = 1; + root-title = "${user}'s git repository"; + root-desc = "how do i learn github anon"; + source-filter = "${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py"; + about-filter = "${pkgs.cgit}/lib/cgit/filters/about-formatting.sh"; + readme = ":README.md"; + footer = ""; + enable-blame = 1; + clone-url = "https://git.${domain}/$CGIT_REPO_URL"; + enable-log-filecount = 1; + enable-log-linecount = 1; + }; + }; +} diff --git a/os/kay/modules/dendrite.nix b/os/kay/modules/dendrite.nix new file mode 100644 index 0000000..8277e21 --- /dev/null +++ b/os/kay/modules/dendrite.nix @@ -0,0 +1,108 @@ +{ config, lib, pkgs, ... }: + +let + domain = config.userdata.domain; + database = { + connection_string = "postgres:///dendrite?host=/run/postgresql"; + max_open_conns = 90; + max_idle_conns = 5; + conn_max_lifetime = -1; + }; +in +{ + sops.secrets."matrix-${domain}/key" = {}; + + services = { + postgresql = { + enable = true; + package = with pkgs; postgresql_15; + settings = { + log_timezone = config.time.timeZone; + listen_addresses = lib.mkForce ""; + }; + ensureDatabases = [ "dendrite" ]; + ensureUsers = [{ + name = "dendrite"; + ensureDBOwnership = true; + }]; + authentication = lib.mkForce "local all all trust"; + }; + + dendrite = { + enable = true; + loadCredential = [ + "private_key:${config.sops.secrets."matrix-${domain}/key".path}" + ]; + + settings = { + sync_api.search = { + enable = true; + index_path = "/var/lib/dendrite/searchindex"; + }; + global = { + server_name = domain; + private_key = "$CREDENTIALS_DIRECTORY/private_key"; + trusted_third_party_id_servers = [ + "matrix.org" + "vector.im" + ]; + inherit database; + }; + logging = [{ + type = "std"; + level = "warn"; + }]; + mscs = { + inherit database; + mscs = [ "msc2836" ]; + }; + sync_api = { + inherit database; + real_ip_header = "X-Real-IP"; + }; + media_api = { + inherit database; + dynamic_thumbnails = true; + max_file_size_bytes = 12800000000; + }; + federation_api = { + inherit database; + send_max_retries = 8; + key_perspectives = [{ + server_name = "matrix.org"; + keys = [ + { + key_id = "ed25519:auto"; + public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + } + { + key_id = "ed25519:a_RXGa"; + public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ"; + } + ]; + }]; + }; + + app_service_api = { + inherit database; + }; + room_server = { + inherit database; + }; + push_server = { + inherit database; + }; + relay_api = { + inherit database; + }; + key_server = { + inherit database; + }; + user_api = { + account_database = database; + device_database = database; + }; + }; + }; + }; +} diff --git a/os/kay/modules/dns/5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa.zone b/os/kay/modules/dns/5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa.zone new file mode 100644 index 0000000..69b3524 --- /dev/null +++ b/os/kay/modules/dns/5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa.zone @@ -0,0 +1,14 @@ +$ORIGIN 5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa. +$TTL 2d + +@ IN SOA ns1.sinanmohd.com. sinan.sinanmohd.com. ( + 2024020400 ; serial + 2h ; refresh + 5m ; retry + 1d ; expire + 5m ) ; nx ttl + + IN NS ns1.sinanmohd.com. + +1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR ns1.sinanmohd.com. +7.3.3.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR mail.sinanmohd.com. diff --git a/os/kay/modules/dns/ddns.nix b/os/kay/modules/dns/ddns.nix new file mode 100644 index 0000000..e6e417a --- /dev/null +++ b/os/kay/modules/dns/ddns.nix @@ -0,0 +1,44 @@ +{ pkgs, ... }: { + services.pppd.script = { + "02-ddns-ipv4" = { + runtimeInputs = with pkgs; [ coreutils knot-dns ]; + type = "ip-up"; + + text = '' + cat <<- EOF | knsupdate + server 2001:470:ee65::1 + zone sinanmohd.com. + + update delete sinanmohd.com. A + update add sinanmohd.com. 180 A $4 + + update delete mail.sinanmohd.com. A + update add mail.sinanmohd.com. 180 A $4 + + send + EOF + ''; + }; + + "02-ddns-ipv6" = { + runtimeInputs = with pkgs; [ coreutils knot-dns iproute2 gnugrep ]; + type = "ipv6-up"; + + text = '' + while ! ipv6="$(ip -6 addr show dev "$1" scope global | grep -o '[0-9a-f:]*::1')"; do + sleep 0.2 + done + + cat <<- EOF | knsupdate + server 2001:470:ee65::1 + zone sinanmohd.com. + + update delete sinanmohd.com. AAAA + update add sinanmohd.com. 180 AAAA $ipv6 + + send + EOF + ''; + }; + }; +} diff --git a/os/kay/modules/dns/default.nix b/os/kay/modules/dns/default.nix new file mode 100644 index 0000000..1146cc3 --- /dev/null +++ b/os/kay/modules/dns/default.nix @@ -0,0 +1,137 @@ +{ config, pkgs, ... }: let + listen_addr = "2001:470:ee65::1"; + + acmeSOA = pkgs.writeText "acmeSOA" '' + $TTL 2d + + @ IN SOA ns1.sinanmohd.com. sinan.sinanmohd.com. ( + 2024020505 ; serial + 2h ; refresh + 5m ; retry + 1d ; expire + 5m ) ; nx ttl + + IN NS ns1.sinanmohd.com. + ''; +in { + imports = [ ./ddns.nix ]; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; + + sops.secrets.dns = { + owner = config.systemd.services.knot.serviceConfig.User; + group = config.systemd.services.knot.serviceConfig.Group; + }; + + services.knot = { + enable = true; + keyFiles = [ config.sops.secrets.dns.path ]; + + settings = { + server.listen = listen_addr; + + remote = [ + { + id = "ns1.he.net"; + address = [ "2001:470:100::2" "216.218.130.2" ]; + via = "2001:470:ee65::1"; + } + { + id = "m.gtld-servers.net"; + address = [ "2001:501:b1f9::30" "192.55.83.30" ]; + } + ]; + + submission = [{ + id = "gtld-servers.net"; + parent = "m.gtld-servers.net"; + }]; + + policy = [{ + id = "gtld-servers.net"; + algorithm = "ecdsap384sha384"; + ksk-lifetime = "365d"; + ksk-submission = "gtld-servers.net"; + }]; + + # generate TSIG key with keymgr -t name + acl = [ + { + id = "ns1.he.net"; + key = "ns1.he.net"; + address = [ "2001:470:600::2" "216.218.133.2" ]; + action = "transfer"; + } + { + id = "localhost"; + address = [ listen_addr ]; + update-type = [ "A" "AAAA" ]; + action = "update"; + } + { + id = "acme"; + address = [ listen_addr ]; + update-type = [ "TXT" ]; + action = "update"; + } + ]; + + mod-rrl = [{ + id = "default"; + rate-limit = 200; + slip = 2; + }]; + + template = [ + { + id = "default"; + semantic-checks = "on"; + global-module = "mod-rrl/default"; + } + { + id = "master"; + semantic-checks = "on"; + + dnssec-signing = "on"; + dnssec-policy = "gtld-servers.net"; + + notify = [ "ns1.he.net" ]; + acl = [ "ns1.he.net" "localhost" ]; + + zonefile-sync = "-1"; + zonefile-load = "difference"; + } + { + id = "acme"; + semantic-checks = "on"; + acl = [ "acme" ]; + + zonefile-sync = "-1"; + zonefile-load = "difference"; + journal-content = "changes"; + } + ]; + + zone = [ + { + domain = "sinanmohd.com"; + file = ./sinanmohd.com.zone; + template = "master"; + } + { + domain = "_acme-challenge.sinanmohd.com"; + file = acmeSOA; + template = "acme"; + } + { + domain = "5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa"; + file = ./5.6.e.e.0.7.4.0.1.0.0.2.ip6.arpa.zone; + } + ]; + }; + }; + +} diff --git a/os/kay/modules/dns/sinanmohd.com.zone b/os/kay/modules/dns/sinanmohd.com.zone new file mode 100644 index 0000000..0409efc --- /dev/null +++ b/os/kay/modules/dns/sinanmohd.com.zone @@ -0,0 +1,46 @@ +$ORIGIN sinanmohd.com. +$TTL 2d + +@ IN SOA ns1 hostmaster ( + 2024022700 ; serial + 2h ; refresh + 5m ; retry + 1d ; expire + 5m ) ; nx ttl + + IN NS ns1 + IN NS ns2.he.net. + IN NS ns3.he.net. + IN NS ns4.he.net. + IN NS ns5.he.net. + + 30 IN A 127.0.0.1 + 30 IN AAAA ::1 + + IN MX 10 mail + + IN TXT "v=spf1 mx -all" +_dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:reports@sinanmohd.com; ruf=mailto:reports@sinanmohd.com; adkim=s; aspf=s" + +ed25519._domainkey IN TXT "v=DKIM1; k=ed25519; p=EHk924AruF9Y0Xaf009rpRl+yGusjmjT1Zeho67BnDU=" +rsa._domainkey IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4HEqO63fSC0cUnJt9vAQBssTkPfT4QefmAK/1BtAIRIOdGakf7PI7p3A1ETgwfYxuHj7BUSzUtESsHMThbhB1Wko79+AR+5ZBDBmD8CE0dOnZfzeG8xIaGfYkaL4gana6YZWiBT2oi/CimJfc22wacF01SufOs4R8cDpy4BZIgDD/zfF4bFTORQ0vMSJQJkp1zdQelERDU5CEezgxgVYgoSmdEpgkhc23PJSyj4Z7hA69N0amsb3cVVrfVXcYvSqTK3S2vLLA89ws4CUjCCpUW40gVIP8QP6CqTL76936Oo7OVWgmV3Sn3wa8FMN6IATY+fbMlrdOMsPY5PauJyEoQIDAQAB" + +ns1 IN AAAA 2001:470:ee65::1 + +mail 30 IN A 127.0.0.1 +mail IN AAAA 2001:470:ee65::1337 +smtp IN CNAME @ +imap IN CNAME @ +mta-sts IN CNAME @ + +_mta-sts IN TXT "v=STSv1; id=2024022500" +_smtp._tls IN TXT "v=TLSRPTv1; rua=mailto:reports@sinanmohd.com" + +www IN CNAME @ +git IN CNAME @ +bin IN CNAME @ +static IN CNAME @ + +lia IN A 65.0.3.127 + +_acme-challenge IN NS ns1 diff --git a/os/kay/modules/hurricane.nix b/os/kay/modules/hurricane.nix new file mode 100644 index 0000000..9e22bf5 --- /dev/null +++ b/os/kay/modules/hurricane.nix @@ -0,0 +1,115 @@ +{ config, pkgs, lib, ... }: + +let + iface = "hurricane"; + remote = "216.218.221.42"; + + clinet = "2001:470:35:72a::2"; + server = "2001:470:35:72a::1"; + + prefix64 = "2001:470:36:72a::/64"; + prefix48 = "2001:470:ee65::/48"; + + makeAddr = prefix: host: let + split = lib.strings.splitString "/" prefix; + in { + address = "${lib.head split}${host}"; + prefixLength = lib.toInt (lib.last split); + }; +in +{ + networking = { + sits.${iface} = { + inherit remote; + ttl = 225; + }; + interfaces.${iface} = { + mtu = 1440; # 1460(ppp0) - 20 + ipv6.addresses = [ + { + address = clinet; + prefixLength = 64; + } + + (makeAddr prefix64 "1") + (makeAddr prefix48 "1") + (makeAddr prefix48 "1337") + ]; + }; + + iproute2 = { + enable = true; + rttablesExtraConfig = "200 hurricane"; + }; + + firewall.extraCommands = + "iptables -A INPUT --proto 41 --source ${remote} --jump ACCEPT"; + }; + + sops.secrets = { + "hurricane/username" = {}; + "hurricane/update_key" = {}; + "hurricane/tunnel_id" = {}; + }; + + systemd.services."network-route-${iface}" = { + description = "Routing configuration of ${iface}"; + wantedBy = [ + "network-setup.service" + "network.target" + ]; + before = [ "network-setup.service" ]; + bindsTo = [ "network-addresses-hurricane.service" ]; + after = [ "network-pre.target" "network-addresses-hurricane.service" ]; + # restart rather than stop+start this unit to prevent the + # network from dying during switch-to-configuration. + stopIfChanged = false; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + path = [ pkgs.iproute2 ]; + script = '' + echo -n "adding route" + + ip -6 rule add from ${clinet}/64 table hurricane || exit 1 + ip -6 rule add from ${prefix64} table hurricane || exit 1 + ip -6 rule add from ${prefix48} table hurricane || exit 1 + + ip -6 route add default via ${server} dev hurricane table hurricane || exit 1 + ''; + preStop = '' + echo -n "deleting route" + + ip -6 route del default via ${server} dev hurricane table hurricane || exit 1 + + ip -6 rule del from ${prefix48} table hurricane || exit 1 + ip -6 rule del from ${prefix64} table hurricane || exit 1 + ip -6 rule del from ${clinet}/64 table hurricane || exit 1 + ''; + }; + + + services.pppd.script."01-${iface}" = { + runtimeInputs = with pkgs; [ curl coreutils iproute2 iputils ]; + text = '' + wan_ip="$4" + username="$(cat ${config.sops.secrets."hurricane/username".path})" + update_key="$(cat ${config.sops.secrets."hurricane/update_key".path})" + tunnel_id="$(cat ${config.sops.secrets."hurricane/tunnel_id".path})" + + auth_url="https://$username:$update_key@ipv4.tunnelbroker.net/nic/update?hostname=$tunnel_id" + until curl --silent "$auth_url"; do + sleep 1 + done + + while [ ! -e /sys/class/net/${iface} ]; do + sleep 1 # make sure ${iface} is up + done + + ip tunnel change ${iface} local "$wan_ip" mode sit + ''; + }; +} diff --git a/os/kay/modules/iperf3.nix b/os/kay/modules/iperf3.nix new file mode 100644 index 0000000..901a93d --- /dev/null +++ b/os/kay/modules/iperf3.nix @@ -0,0 +1,10 @@ +{ ... }: + +{ + services.iperf3 = { + enable = true; + + bind = "10.0.0.1"; + openFirewall = true; + }; +} diff --git a/os/kay/modules/mail.nix b/os/kay/modules/mail.nix new file mode 100644 index 0000000..79e4019 --- /dev/null +++ b/os/kay/modules/mail.nix @@ -0,0 +1,110 @@ +{ config, ... }: let + ipv6 = "2001:470:ee65::1337"; + domain = config.userdata.domain; + + username = config.userdata.name; + secret = "$argon2i$v=19$m=4096,t=3,p=1$SWV5aWU3YWUgZWFTNm9oc28gTGFvdDdlRG8ga2FTaWVjaDYgYWV0aDFHb28$O/sDv7oy9wUxFjvKoxB5o8ZnPvjYJo9DjX0C/AZQFF0"; + email = [ + "${username}@${domain}" + "official@${domain}" + + "postmaster@${domain}" + "hostmaster@${domain}" + ]; + + credentials_directory = "/run/credentials/stalwart-mail.service"; +in { + networking.firewall.allowedTCPPorts = [ + 25 # smto + 465 # submission + 587 # submissions + 993 # imap ssl + 4190 # managesieve + ]; + + security.acme.certs.${domain}.postRun = "systemctl restart stalwart-mail.service"; + sops.secrets = { + "mail.${domain}/dkim_rsa" = {}; + "mail.${domain}/dkim_ed25519" = {}; + }; + + services.stalwart-mail = { + enable = true; + loadCredential = [ + "dkim_rsa:${config.sops.secrets."mail.${domain}/dkim_rsa".path}" + "dkim_ed25519:${config.sops.secrets."mail.${domain}/dkim_ed25519".path}" + + "cert:${config.security.acme.certs.${domain}.directory}/fullchain.pem" + "key:${config.security.acme.certs.${domain}.directory}/key.pem" + ]; + + settings = { + macros = { + host = "mail.${domain}"; + default_domain = domain; + default_directory = "in-memory"; + default_store = "sqlite"; + }; + + queue.outbound = { + ip-strategy = "ipv6_then_ipv4"; + source-ip.v6 = "['${ipv6}']"; + tls.starttls = "optional"; + }; + server.listener = { + smtp.bind = [ "[${ipv6}]:25" "0.0.0.0:25" ]; + jmap.bind = [ "[::]:8034" ]; + }; + + signature = { + rsa = { + private-key = "file://${credentials_directory}/dkim_rsa"; + selector = "rsa"; + set-body-length = true; + }; + ed25519 = { + public-key = "EHk924AruF9Y0Xaf009rpRl+yGusjmjT1Zeho67BnDU="; + private-key = "file://${credentials_directory}/dkim_ed25519"; + domain = "%{DEFAULT_DOMAIN}%"; + selector = "ed25519"; + headers = [ "From" "To" "Date" "Subject" "Message-ID" ]; + algorithm = "ed25519-sha256"; + canonicalization = "relaxed/relaxed"; + set-body-length = true; + report = true; + }; + }; + + certificate."default" = { + cert = "file://${credentials_directory}/cert"; + private-key = "file://${credentials_directory}/key"; + }; + + storage.blob = "fs"; + store = { + fs.disable = false; + sqlite.disable = false; + }; + + directory."in-memory" = { + type = "memory"; + options.subaddressing = true; + + principals = [ + { + inherit email; + inherit secret; + name = username; + type = "admin"; + } + { # for mta-sts & dmarc reports + email = "reports${domain}"; + inherit secret; + name = "reports"; + type = "individual"; + } + ]; + }; + }; + }; +} diff --git a/os/kay/modules/matrix-sliding-sync.nix b/os/kay/modules/matrix-sliding-sync.nix new file mode 100644 index 0000000..ebdc34d --- /dev/null +++ b/os/kay/modules/matrix-sliding-sync.nix @@ -0,0 +1,18 @@ +{ config, ... }: + +let + domain = config.userdata.domain; +in +{ + sops.secrets."matrix-${domain}/sliding_sync" = {}; + + services.matrix-sliding-sync = { + enable = true; + environmentFile = config.sops.secrets."matrix-${domain}/sliding_sync".path; + + settings = { + SYNCV3_LOG_LEVEL = "warn"; + SYNCV3_SERVER = "http://127.0.0.1:${toString config.services.dendrite.httpPort}"; + }; + }; +} diff --git a/os/kay/modules/network.nix b/os/kay/modules/network.nix new file mode 100644 index 0000000..929fb1b --- /dev/null +++ b/os/kay/modules/network.nix @@ -0,0 +1,82 @@ +{ config, ... }: + +let + inetVlan = 722; + voipVlan = 1849; + wanInterface = "enp4s0"; + nameServer = "1.0.0.1"; +in +{ + imports = [ + ./router.nix + ./hurricane.nix + ]; + + sops.secrets = { + "ppp/chap-secrets" = {}; + "ppp/pap-secrets" = {}; + "ppp/username" = {}; + }; + + networking = let + voipVlanIface = "voip"; + in { + vlans = { + wan = { + id = inetVlan; + interface = wanInterface; + }; + ${voipVlanIface} = { + id = voipVlan; + interface = wanInterface; + }; + }; + + interfaces.${voipVlanIface}.useDHCP = true; + dhcpcd.extraConfig = '' + interface ${voipVlanIface} + ipv4only + nogateway + ''; + }; + + services = { + dnsmasq = { + enable = true; + settings = { + server = [ nameServer ]; + bind-interfaces = true; + }; + }; + + pppd = { + enable = true; + + config = '' + plugin pppoe.so + debug + + nic-wan + defaultroute + ipv6 ::1, + noauth + + persist + lcp-echo-adaptive + lcp-echo-interval 1 + lcp-echo-failure 5 + ''; + + peers.bsnl = { + enable = true; + autostart = true; + configFile = config.sops.secrets."ppp/username".path; + }; + + secret = { + chap = config.sops.secrets."ppp/chap-secrets".path; + pap = config.sops.secrets."ppp/pap-secrets".path; + }; + }; + }; +} diff --git a/os/kay/modules/router.nix b/os/kay/modules/router.nix new file mode 100644 index 0000000..2254c3b --- /dev/null +++ b/os/kay/modules/router.nix @@ -0,0 +1,43 @@ +{ ... }: + +let + lanInterface = "enp0s20u1"; + wanInterface = "ppp0"; + subnet = "10.0.0.0"; + prefix = 24; + host = "10.0.0.1"; + leaseRangeStart = "10.0.0.100"; + leaseRangeEnd = "10.0.0.254"; +in +{ + imports = [ + ./wireguard.nix + ./iperf3.nix + ]; + + networking = { + nat = { + enable = true; + externalInterface = wanInterface; + internalInterfaces = [ lanInterface ]; + }; + interfaces."${lanInterface}" = { + ipv4.addresses = [{ + address = host; + prefixLength = prefix; + }]; + }; + firewall = { + allowedUDPPorts = [ 53 67 ]; + allowedTCPPorts = [ 53 ]; + extraCommands = '' + iptables -t nat -I POSTROUTING 1 -s ${subnet}/${toString prefix} -o ${wanInterface} -j MASQUERADE + ''; + }; + }; + + services.dnsmasq.settings = { + dhcp-range = [ "${leaseRangeStart},${leaseRangeEnd}" ]; + interface = [ lanInterface ]; + }; +} diff --git a/os/kay/modules/sftp.nix b/os/kay/modules/sftp.nix new file mode 100644 index 0000000..bbe055e --- /dev/null +++ b/os/kay/modules/sftp.nix @@ -0,0 +1,44 @@ +{ config, ... }: + +let + storage = "/hdd/users"; + user = config.userdata.name; + pubKeys = config.users.users.${user}.openssh.authorizedKeys.keys; +in +{ + users = { + groups."sftp".members = []; + + users."sftp" = { + group = "sftp"; + shell = "/run/current-system/sw/bin/nologin"; + isNormalUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmA1dyV+o9gfoxlbVG0Y+dn3lVqdFs5fMqfxyNc5/Lr sftp@cez" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDCbgjAfyDNtLNyOS+sfLirYtfEAkGqV54LOwabpWkvf sftp@veu" + ] ++ pubKeys; + }; + + users."nazer" = { + group = "sftp"; + shell = "/run/current-system/sw/bin/nologin"; + isNormalUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV09w9Ovk9wk4Bhn/06iOn+Ss8lK3AmQAl8+lXHRycu nazu@pc" + ]; + }; + }; + + services.openssh.extraConfig = '' + Match Group sftp + # chroot dir should be owned by root + # and sub dirs by %u + ChrootDirectory ${storage}/%u + ForceCommand internal-sftp + + PermitTunnel no + AllowAgentForwarding no + AllowTcpForwarding no + X11Forwarding no + ''; +} diff --git a/os/kay/modules/sshfwd.nix b/os/kay/modules/sshfwd.nix new file mode 100644 index 0000000..d70b893 --- /dev/null +++ b/os/kay/modules/sshfwd.nix @@ -0,0 +1,29 @@ +{ ... }: let + group = "sshfwd"; +in { + networking.firewall.allowedTCPPorts = [ 2222 ]; + + users = { + groups.${group}.members = []; + + users."lia" = { + inherit group; + isSystemUser = true; + + openssh.authorizedKeys.keys + = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAe7fJlh9L+9JSq0+hK7jNZjszmZqNXwzqcZ+zx0yJyU lia" ]; + }; + }; + + services.openssh.extraConfig = '' + Match Group ${group} + ForceCommand echo 'this account is only usable for remote forwarding' + PermitTunnel no + AllowAgentForwarding no + X11Forwarding no + + AllowTcpForwarding remote + GatewayPorts clientspecified + PermitListen *:2222 + ''; +} diff --git a/os/kay/modules/wireguard.nix b/os/kay/modules/wireguard.nix new file mode 100644 index 0000000..578a86a --- /dev/null +++ b/os/kay/modules/wireguard.nix @@ -0,0 +1,57 @@ +{ config, ... }: + +let + wgInterface = "wg"; + wanInterface = "ppp0"; + subnet = "10.0.1.0"; + prefix = 24; + port = 51820; +in +{ + sops.secrets."misc/wireguard" = {}; + + networking = { + nat = { + enable = true; + externalInterface = wanInterface; + internalInterfaces = [ wgInterface ]; + }; + firewall = { + allowedUDPPorts = [ port ]; + extraCommands = '' + iptables -t nat -I POSTROUTING 1 -s ${subnet}/${toString prefix} -o ${wanInterface} -j MASQUERADE + ''; + }; + + wireguard.interfaces.${wgInterface} = { + ips = [ "10.0.1.1/${toString prefix}" ]; + listenPort = port; + mtu = 1380; # 1460 (ppp0) - 80 + privateKeyFile = config.sops.secrets."misc/wireguard".path; + + peers = [ + { # cez + publicKey = "IcMpAs/D0u8O/AcDBPC7pFUYSeFQXQpTqHpGOeVpjS8="; + allowedIPs = [ "10.0.1.2/32" ]; + } + { # veu + publicKey = "bJ9aqGYD2Jh4MtWIL7q3XxVHFuUdwGJwO8p7H3nNPj8="; + allowedIPs = [ "10.0.1.3/32" ]; + } + { # dad + publicKey = "q70IyOS2IpubIRWqo5sL3SeEjtUy2V/PT8yqVExiHTQ="; + allowedIPs = [ "10.0.1.4/32" ]; + } + { # shambai + publicKey = "YYDlp/bNKkqFHAhdgaZ2SSEMnIjKTqPTK7Ju6O9/1gY="; + allowedIPs = [ "10.0.1.5/32" ]; + } + ]; + }; + }; + + services.dnsmasq.settings = { + no-dhcp-interface = wgInterface; + interface = [ wgInterface ]; + }; +} diff --git a/os/kay/modules/www.nix b/os/kay/modules/www.nix new file mode 100644 index 0000000..3891bf6 --- /dev/null +++ b/os/kay/modules/www.nix @@ -0,0 +1,134 @@ +{ config, pkgs, lib, ... }: + +let + domain = config.userdata.domain; + fscusat = "fscusat.org"; + mark = "themark.ing"; + storage = "/hdd/users/sftp/shr"; +in +{ + imports = [ + ./dendrite.nix + ./matrix-sliding-sync.nix + ./cgit.nix + ]; + + security.acme.certs.${domain}.postRun = "systemctl reload nginx.service"; + networking.firewall = { + allowedTCPPorts = [ 80 443 ]; + allowedUDPPorts = [ 443 ]; + }; + + services.nginx = { + enable = true; + package = pkgs.nginxQuic; + enableQuicBPF = true; + + recommendedTlsSettings = true; + recommendedZstdSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + recommendedBrotliSettings = true; + eventsConfig = "worker_connections 1024;"; + + virtualHosts = let + defaultOpts = { + quic = true; + http3 = true; + forceSSL = true; + useACMEHost = domain; + }; + in { + "${domain}" = defaultOpts // { + default = true; + globalRedirect = "www.${domain}"; + + extraConfig = '' + client_max_body_size ${toString + config.services.dendrite.settings.media_api.max_file_size_bytes + }; + ''; + + locations = { + "/.well-known/matrix/server".return = '' + 200 '{ "m.server": "${domain}:443" }' + ''; + + "/.well-known/matrix/client".return = '' + 200 '${builtins.toJSON { + "m.homeserver".base_url = "https://${domain}"; + "org.matrix.msc3575.proxy".url = "https://${domain}"; + }}' + ''; + + "/_matrix".proxyPass = "http://127.0.0.1:${toString + config.services.dendrite.httpPort + }"; + + "/_matrix/client/unstable/org.matrix.msc3575/sync".proxyPass = + "http://${config.services.matrix-sliding-sync.settings.SYNCV3_BINDADDR}"; + }; + }; + + "www.${domain}" = defaultOpts // { + root = "/var/www/${domain}"; + }; + + "git.${domain}" = defaultOpts; + + "bin.${domain}" = defaultOpts // { + root = "${storage}/bin"; + locations."= /".return = "307 https://www.${domain}"; + }; + + "static.${domain}" = defaultOpts // { + root = "${storage}/static"; + locations."= /".return = "301 https://www.${domain}"; + }; + + "${fscusat}" = defaultOpts // { + useACMEHost = null; + enableACME = true; + + globalRedirect = "www.${fscusat}"; + }; + "www.${fscusat}" = defaultOpts // { + useACMEHost = null; + enableACME = true; + + locations."/" = { + return = "200 '<h1>under construction</h1>'"; + extraConfig = "add_header Content-Type text/html;"; + }; + }; + + "${mark}" = defaultOpts // { + useACMEHost = null; + enableACME = true; + + globalRedirect = "www.${mark}"; + }; + "www.${mark}" = defaultOpts // { + useACMEHost = null; + enableACME = true; + + locations."/" = { + return = "200 '<h1>under construction, see you soon</h1>'"; + extraConfig = "add_header Content-Type text/html;"; + }; + }; + + "mta-sts.${domain}" = defaultOpts // { + locations."= /.well-known/mta-sts.txt".return = ''200 "${ + lib.strings.concatStringsSep "\\n" [ + "version: STSv1" + "mode: enforce" + "mx: mail.${domain}" + "max_age: 86400" + ] + }"''; + }; + }; + }; +} diff --git a/os/kay/secrets.yaml b/os/kay/secrets.yaml new file mode 100644 index 0000000..47be11b --- /dev/null +++ b/os/kay/secrets.yaml @@ -0,0 +1,47 @@ +ppp: + chap-secrets: ENC[AES256_GCM,data:oTwucN94iWIzrCCAQySpkG+uEBERmEjXfoPm6piook8bS/q3kCd/DQ==,iv:IC1Ii+rnTvFa0F2bi0fnEAEO7XWV7Wues9T+28bhDnc=,tag:239vrwVzeTIVCIw8U30jtg==,type:str] + pap-secrets: ENC[AES256_GCM,data:S72mx8AP8MDWrYZ3TIOnwoKcVWiUzms1ZpckghHjjFcWhW5orOjPOA==,iv:4kNHSZ3+FMA9ROLEgrU38IWd+MBt+vf8CV3WGHkRCCc=,tag:rBtSZH8i7fE7fJhRRda7eA==,type:str] + username: ENC[AES256_GCM,data:GzRdyvnRKSS8iH+RuFU9g6zxXhxl0DeWWkAyF3sefZc0QQ==,iv:yTRruKpMda4N2J3Z8MEesrFxqV4g1usbYoxTeKlWf4M=,tag:8h9cpYn2Zy/32+2HJ76dFw==,type:str] +hurricane: + username: ENC[AES256_GCM,data:pe3igN9AIbc1,iv:stBkppjkDC9nvV/fHaEtfs6KskoiqqEKxCp/KC+Xxeo=,tag:pH5CJXOOp/is7dQmt6wlog==,type:str] + update_key: ENC[AES256_GCM,data:wwd+QWTgKEqstY5d2eWBnWJYq2EisTTaa/Ow4WwBNkyh5FYP+7PEyg==,iv:b93JvsfWppqlJtZxGAa3xbXgLEFs0A5Seq5pNjTnRW4=,tag:+W1t1M+Mm4LopVbcI1x+eg==,type:str] + tunnel_id: ENC[AES256_GCM,data:WUDOxjmA,iv:W8k0pyrAQz+UWtm76uvmzodJ2lZG4ioxrVMWjX1kIVM=,tag:2Q25MXzlptg/rc0HQ1k6rg==,type:str] +dns: ENC[AES256_GCM,data:Pa6Oo7UFDqo5ZN+eyz9MKy0p4KU1ePTpWQ+R8PuSFO9JjFt/I86ru/qSKyymIzhJcjj5hXMT2LPjk4MH8BWaO39ACsPDSD09xA6e1GO0rvsvtB9cffuz/GnfveyHmev+7xzdriD4IHqINPE=,iv:zuSfHnmxrjFCX3DJSRxLDs/3IVBRnkn3crar1pCW1EU=,tag:rZ0TlMMsOCF3Shunx8PnfA==,type:str] +matrix-sinanmohd.com: + key: ENC[AES256_GCM,data:xsSYua3g+ySUVBtfVZ2uZR4761MC5LeJGxmcgf+dWb5+tBSmgzAQL9FFcl7GLzhTmvlq13lARUr599wShS/C9IyMVGOOT9A8hxLFF9Kak64hmM7ERGrwbmzBY1mdTtvibJqzHaeybUVIMbDagczF54zpjDGLmdC5V84wduPFCndSA5FW+4Hhqw==,iv:KJtqrGNPgMDR6Sg/fOUzVAiwnPZwve9wpVfDQPc4g/c=,tag:E2jlbt5WbRA9wu16Lr69Bg==,type:str] + sliding_sync: ENC[AES256_GCM,data:ubFeb/OgYYHaIHVky6KS3icORbpqf7PO3p8bONA8mwG8vU1LB0TDqVm6vQTa8G9pe96JzJ8+IAgSZafG9PaEJc/Bpj53aWRFO3HEV0Pj,iv:P8VD8utVEwNoeQEZUdS2R9GuDe20nKiXYCfKJl0Id3E=,tag:VksV/4IaKN0C2g/alw6r4Q==,type:str] +mail.sinanmohd.com: + dkim_rsa: ENC[AES256_GCM,data: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,iv:dPnpNUPSDiq5C14YzDM2K4mFHNRFgc6p+X3Zu33OH60=,tag:MhgfV3z1wcbAfpwZmVWczw==,type:str] + dkim_ed25519: ENC[AES256_GCM,data:bberg3vGG9M3iPH1aLA+wIU6KNnxHRZxpGU5zT5Gqo9lohQa1wBDXCwsP0JaSfg56dhh9ZxF5HFd4V0nUzL6QMIeiExGkZmtdluaqki3fwFCssILch9pWOuM71Q1d7vi1eIN5PrAuX+6m8bmQBd1JIR+Kbz8dQ==,iv:C7wEFU7/xCh8LzyKXHSzgTX/L9OkmGWTnl5A94GLogw=,tag:j+sYtzzGN9guWa6T+ZUzbw==,type:str] + sinan: ENC[AES256_GCM,data:F3lhwjf6dZpDSmU=,iv:TCIzQeUBqgjqc+/z3Hh1tYpm3OeLGLpVUDeo6ufP7/4=,tag:TXUI8noaK5jyLpo8D+94jQ==,type:str] +misc: + wireguard: ENC[AES256_GCM,data:kbUtxJv3xSmikJWgtu87TSo5N8tUb2BiH3dH3oOV36waYyXI3bp2aBeAl1k=,iv:yB4UIyMDNRS+JmSnt9XuBhNRTLz+k0FqkK4ofjosRto=,tag:BDSD9SfQuQppKT4+6Cu65w==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1q5sfy74d53n6jxlgsc2zrsz4wcl9d830nxuagc3wfmdkrrp55ckq9ev6nv + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2T3NSZ09xUDg5Q2VKM3FB + RXNwNTJrVkxScHR2eksrVlZQMFduOGRFT1RFClhQK2xTWXBUMzdlektSWFhHNDBN + bEMxelVjK1owZHczMVV3MWI2WlU2TncKLS0tIEovSk1uMnlvWFBya1YxNjArQTdh + Unk0a0tvR3VZQmtIU3RZSWNnazZJZTgKe0mjQHEkagnftc2zEbza863dSlnPOM6Q + 0Me0paRmqzsYBizp12SHjaXYiXFpvEeGmOVOMoGvD8UzTa+V5klS0w== + -----END AGE ENCRYPTED FILE----- + - recipient: age15989j5lkkf2kn5wa2p6qc8wlxjjksc63k5ync8rz8t4e87394pzqm7h4rm + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5NDVlc2crekF2b1lVZnZM + YU95N3lRWFhUUzczV1h4eUU0dHdSbWdpWVhZCmREdmFDSzRzY3pZUHpERkhCK1FS + cmxRam1vZ2U0dHBYc3hJWG9CRW13bzgKLS0tIFBpMFFXYTZDT09mTTJkWDhoYWVr + OXgwSml4bkc1dnloNUFsRGFFcXFHc2cK26l2eiKbZUkogmAXoha6HTUs3YFKixYz + bTkpKKyOAIIin3YM975wwvkCuWNG4tbnHBHQFh5JGK2OEyLDXuV7Pg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-02-25T04:23:28Z" + mac: ENC[AES256_GCM,data:SUFBHKTM2tQHX1Xtta3spl/GaaNrIAcNrLFzKzqb2ki3FhXnLLYu0wD+IBxuj1nxICn9TDprHFdcDenfFPV1mYWtmXLmWMeDcIGKXedYex2nakdlIYngGiLkEseuehft46YtoEqLJVksBFoLKmywRi+/ZGux/heSIyD14Toxb3Q=,iv:dqYGObF1SV3VBxSZtrggRdD1ROqvlp7tn8xLdNuDxx4=,tag:N/4L6NgIqYKQ8IbpFGru2g==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.8.1 |