From 2ea01ff3e9e2995eaa381088995308ab89e338bb Mon Sep 17 00:00:00 2001 From: Giulio De Pasquale Date: Sat, 7 Aug 2021 13:38:18 +0200 Subject: [PATCH] a lot of things...? --- hosts/architect/backup.nix | 17 +++-- hosts/architect/default.nix | 10 ++- hosts/architect/fail2ban.nix | 26 ++++++++ hosts/architect/firewall.nix | 10 +++ hosts/architect/hardware.nix | 3 +- hosts/architect/matrix.nix | 114 ++++++++++++++++++++++++++++++++++ hosts/architect/network.nix | 6 +- hosts/architect/nginx.nix | 2 + hosts/architect/plex.nix | 84 +++++++++++++++++++++++++ hosts/architect/wireguard.nix | 6 ++ 10 files changed, 264 insertions(+), 14 deletions(-) create mode 100644 hosts/architect/fail2ban.nix create mode 100644 hosts/architect/matrix.nix create mode 100644 hosts/architect/plex.nix diff --git a/hosts/architect/backup.nix b/hosts/architect/backup.nix index 93b9764..43b00a1 100644 --- a/hosts/architect/backup.nix +++ b/hosts/architect/backup.nix @@ -2,15 +2,12 @@ { services.restic.backups = { - local = { + backblaze = { initialize = true; - passwordFile = "/etc/restic/local.key"; - repository = "/backup"; - paths = [ "/" ]; - extraBackupArgs = [ - "-x" # same filesystem - "-e /nix" - ]; + passwordFile = "/secrets/restic/data.key"; + s3CredentialsFile = "/secrets/restic/credentials.txt"; + repository = "b2:architect:/"; + paths = [ "/var/lib" "/secrets" ]; pruneOpts = [ "--keep-daily 45" "--keep-weekly 12" @@ -18,8 +15,8 @@ "--keep-yearly 3" ]; timerConfig = { - OnCalendar = "00:05"; - RandomizedDelaySec = "5h"; + OnCalendar = "monday 00:05"; + RandomizedDelaySec = "2h"; }; }; }; diff --git a/hosts/architect/default.nix b/hosts/architect/default.nix index e9b8d41..0c5cb95 100644 --- a/hosts/architect/default.nix +++ b/hosts/architect/default.nix @@ -8,6 +8,7 @@ in { imports = [ # Include the results of the hardware scan. + ./backup.nix ./hardware.nix ../../common.nix ../../users.nix @@ -18,16 +19,19 @@ in ./radarr.nix ./bazarr.nix ./nzbget.nix - ./jellyfin.nix +# ./jellyfin.nix ./nextcloud.nix ./wireguard.nix ./minio.nix + ./matrix.nix + ./fail2ban.nix + ./plex.nix ]; time.timeZone = "Europe/Rome"; system.stateVersion = "21.05"; # Did you read the comment? users.users.giulio.openssh.authorizedKeys.keys = pubkeys; - + fileSystems."/tmp" = { device = "tmpfs"; fsType = "tmpfs"; @@ -65,6 +69,7 @@ in }; supportedFilesystems = ["zfs"]; + zfs.enableUnstable = true; zfs.requestEncryptionCredentials = true; }; @@ -87,6 +92,7 @@ in 10.0.0.1 router.devs.giugl.io ${dvr-lan} dvr.devs.giugl.io ${nas-lan} nas.devs.giugl.io + ${giupi-lan} giupi.devs.giugl.io # Wireguard hosts ${architect-wg} ${hostname}.devs.giugl.io giugl.io diff --git a/hosts/architect/fail2ban.nix b/hosts/architect/fail2ban.nix new file mode 100644 index 0000000..72305e4 --- /dev/null +++ b/hosts/architect/fail2ban.nix @@ -0,0 +1,26 @@ +{ config, pkgs, ... }: +{ + services.fail2ban = { + enable = true; + package = pkgs.fail2ban; + packageFirewall = pkgs.nftables; + banaction = "nftables-multiport"; + banaction-allports = "nftables-allport"; + bantime-increment.enable = true; +# ignoreIP = [ "10.0.0.0/24" "10.3.0.0/24" ]; + daemonConfig = '' + [Definition] + loglevel = INFO + logtarget = SYSLOG + socket = /run/fail2ban/fail2ban.sock + pidfile = /run/fail2ban/fail2ban.pid + dbfile = /var/lib/fail2ban/fail2ban.sqlite3 + ''; + jails = { + sshd = '' + maxretry = 3 + mode = aggressive + ''; + }; + }; +} diff --git a/hosts/architect/firewall.nix b/hosts/architect/firewall.nix index 6251ef7..f6f0a63 100644 --- a/hosts/architect/firewall.nix +++ b/hosts/architect/firewall.nix @@ -7,10 +7,20 @@ let 22 # ssh 80 # http 443 # https + 3478 # turn 10022 # gitea + 40000 + 40001 + 40002 + 40003 ]; open_udp_ports = lib.concatMapStringsSep "," (x: toString x) [ 1194 # wireguard + 3478 # turn + 40000 + 40001 + 40002 + 40003 ]; in { networking = { diff --git a/hosts/architect/hardware.nix b/hosts/architect/hardware.nix index aa2aa5b..f0c939f 100644 --- a/hosts/architect/hardware.nix +++ b/hosts/architect/hardware.nix @@ -43,6 +43,7 @@ fsType = "vfat"; }; - swapDevices = [ ]; + + swapDevices = [ { device = "/dev/zpool/data/swap"; size = 40000; } ]; } diff --git a/hosts/architect/matrix.nix b/hosts/architect/matrix.nix new file mode 100644 index 0000000..f9fc309 --- /dev/null +++ b/hosts/architect/matrix.nix @@ -0,0 +1,114 @@ +{ pkgs, config, ... }: + +with import ./network.nix; +{ + services = { + matrix-synapse = { + enable = true; + server_name = "${matrixdomain}"; + database_type = "sqlite3"; + public_baseurl = "https://${matrixdomain}"; + registration_shared_secret = "runas!"; + dynamic_thumbnails = true; + enable_registration = true; + app_service_config_files = [ "/var/lib/matrix-synapse/discord-registration.yaml" ]; + extraConfig = '' + auto_join_rooms: + - "#infrastruttura:matrix.giugl.io" + - "#general:matrix.giugl.io" + ''; + listeners = [ + { + port = 8008; + bind_address = "::1"; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = false; + } + ]; + } + ]; + }; + + nginx.virtualHosts = { + # server + ${matrixdomain} = { + enableACME = true; + forceSSL = true; + locations."= /.well-known/matrix/server".extraConfig = + let + server = { "m.server" = "${matrixdomain}:443"; }; + in '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server}'; + ''; + + locations."= /.well-known/matrix/client".extraConfig = + let + client = { + "m.homeserver" = { "base_url" = "https://${matrixdomain}:443"; }; + "m.identity_server" = { "base_url" = "https://vector.im"; }; + }; + # ACAO required to allow element-web on any URL to request this json file + in '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client}'; + ''; + + + locations."/".extraConfig = '' + return 404; + ''; + + # forward all Matrix API calls to the synapse Matrix homeserver + locations."/_matrix" = { + proxyPass = "http://[::1]:8008"; # without a trailing / + }; + }; + + # web client + + "${matrixwebdomain}" = { + enableACME = true; + forceSSL = true; + + root = pkgs.element-web.override { + conf = { + default_server_config."m.homeserver" = { + "base_url" = "https://${matrixdomain}"; + "server_name" = "${matrixdomain}"; + }; + }; + }; + }; + }; + + matrix-appservice-discord = { + enable = true; + environmentFile = /secrets/matrix-appservice-discord/tokens.env; + # The appservice is pre-configured to use SQLite by default. + # It's also possible to use PostgreSQL. + settings = { + bridge = { + domain = matrixdomain; + homeserverUrl = "https://${matrixdomain}"; + disablePresence = true; + }; + }; + }; + }; + + networking.extraHosts = '' + 127.0.0.1 ${matrixdomain} + ${architect-lan} ${matrixdomain} + ${architect-wg} ${matrixdomain} + ''; + + users.groups.acme.members = [ "turnserver" ]; +} + diff --git a/hosts/architect/network.nix b/hosts/architect/network.nix index caf0e5c..a480fb5 100644 --- a/hosts/architect/network.nix +++ b/hosts/architect/network.nix @@ -9,9 +9,10 @@ rec { external_lan-net = "192.168.1.0/24"; # ips - architect-lan = "10.0.0.250"; dvr-lan = "10.0.0.2"; nas-lan = "10.0.0.3"; + architect-lan = "10.0.0.250"; + giupi-lan = "10.0.0.251"; architect-wg = "10.3.0.1"; galuminum-wg = "10.3.0.2"; @@ -32,6 +33,7 @@ rec { papa-wg = "10.3.0.17"; defy-wg = "10.3.0.18"; germano-wg = "10.3.0.19"; + flavio-wg = "10.3.0.20"; eleonora-wg = "10.3.0.100"; broccolino-wg = "10.3.0.200"; hotpottino-wg = "10.3.0.201"; @@ -52,4 +54,6 @@ rec { nextclouddomain = "cloud.giugl.io"; miniodomain = "s3.giugl.io"; clouddomain = "cloud.giugl.io"; + matrixdomain = "matrix.giugl.io"; + matrixwebdomain = "chat.giugl.io"; } diff --git a/hosts/architect/nginx.nix b/hosts/architect/nginx.nix index c9fd448..a1e083f 100644 --- a/hosts/architect/nginx.nix +++ b/hosts/architect/nginx.nix @@ -11,4 +11,6 @@ recommendedProxySettings = true; recommendedTlsSettings = true; }; + + users.groups.acme.members = [ "nginx" ]; } diff --git a/hosts/architect/plex.nix b/hosts/architect/plex.nix new file mode 100644 index 0000000..2594616 --- /dev/null +++ b/hosts/architect/plex.nix @@ -0,0 +1,84 @@ +with import ./network.nix; +{ + services.plex.enable = true; + + services.nginx = { + enable = true; + # give a name to the virtual host. It also becomes the server name. + virtualHosts.${mediadomain} = { + forceSSL = true; + enableACME = true; + http2 = true; + + extraConfig = '' + + #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause + send_timeout 100m; + + # Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/ + ssl_stapling on; + ssl_stapling_verify on; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + #Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384. + ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; + + # Forward real ip and host to Plex + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $server_addr; + proxy_set_header Referer $server_addr; + proxy_set_header Origin $server_addr; + + # Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off. + gzip on; + gzip_vary on; + gzip_min_length 1000; + gzip_proxied any; + gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml; + gzip_disable "MSIE [1-6]\."; + + # Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones. + # Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more + client_max_body_size 100M; + + # Plex headers + proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier; + proxy_set_header X-Plex-Device $http_x_plex_device; + proxy_set_header X-Plex-Device-Name $http_x_plex_device_name; + proxy_set_header X-Plex-Platform $http_x_plex_platform; + proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version; + proxy_set_header X-Plex-Product $http_x_plex_product; + proxy_set_header X-Plex-Token $http_x_plex_token; + proxy_set_header X-Plex-Version $http_x_plex_version; + proxy_set_header X-Plex-Nocache $http_x_plex_nocache; + proxy_set_header X-Plex-Provides $http_x_plex_provides; + proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor; + proxy_set_header X-Plex-Model $http_x_plex_model; + + # Websockets + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Buffering off send to the client as soon as the data is received from Plex. + proxy_redirect off; + proxy_buffering off; + ''; + locations."/" = { + proxyPass = "http://localhost:32400/"; + }; + }; + }; + + networking.extraHosts = '' + 127.0.0.1 ${mediadomain} + ${architect-lan} ${mediadomain} + ${architect-wg} ${mediadomain} + ''; + + users.groups.media.members = ["plex"]; + +} diff --git a/hosts/architect/wireguard.nix b/hosts/architect/wireguard.nix index 4a0d8a3..b515dc7 100644 --- a/hosts/architect/wireguard.nix +++ b/hosts/architect/wireguard.nix @@ -147,6 +147,12 @@ with import ./network.nix; allowedIPs = [germano-wg]; publicKey = "gi4o+pZWKItzVs7vY8fvXh98jX6CNeCwc1YDzhc3mA4="; } + + { + # flavio + allowedIPs = [flavio-wg]; + publicKey = "Yg0P+yHi/9SZHyoel8jT9fmmu+irLYmT8yMp/CZoaSg="; + } ]; }; };