diff --git a/modules/nixos/services/arr.nix b/modules/nixos/services/arr.nix index 293dfd6..2b832ce 100644 --- a/modules/nixos/services/arr.nix +++ b/modules/nixos/services/arr.nix @@ -2,6 +2,7 @@ let + # This config specifies ports for Prometheus to scrape information arrConfig = { radarr = { exportarrPort = "9707"; @@ -41,6 +42,8 @@ in { sabnzbd = { enable = true; group = "media"; + # The config file must be editable within the application + # It contains server configs and credentials configFile = "/data/downloads/sabnzbd/sabnzbd.ini"; }; sonarr = { @@ -53,16 +56,23 @@ in { }; }; + # Create a media group to be shared between services users.groups.media = { }; + + # Give the human user access to the media group users.users.${config.user}.extraGroups = [ "media" ]; + + # Allows media group to read/write the sabnzbd directory users.users.sabnzbd.homeMode = "0770"; - unfreePackages = [ "unrar" ]; # Required for sabnzbd + unfreePackages = [ "unrar" ]; # Required as a dependency for sabnzbd # Requires updating the base_url config value in each service # If you try to rewrite the URL, the service won't redirect properly caddy.routes = [ { + # Group means that routes with the same name are mutually exclusive, + # so they are split between the appropriate services. group = "download"; match = [{ host = [ config.hostnames.download ]; @@ -70,6 +80,7 @@ in { }]; handle = [{ handler = "reverse_proxy"; + # We're able to reference the url and port of the service dynamically upstreams = [{ dial = arrConfig.sonarr.url; }]; }]; } @@ -92,6 +103,7 @@ in { }]; handle = [{ handler = "reverse_proxy"; + # Prowlarr doesn't offer a dynamic config, so we have to hardcode it upstreams = [{ dial = "localhost:9696"; }]; }]; } @@ -104,6 +116,7 @@ in { handle = [{ handler = "reverse_proxy"; upstreams = [{ + # Bazarr only dynamically sets the port, not the host dial = "localhost:${ builtins.toString config.services.bazarr.listenPort }"; @@ -145,10 +158,12 @@ in { Type = "simple"; DynamicUser = true; ExecStart = let + # Sabnzbd doesn't accept the URI path, unlike the others url = if name != "sabnzbd" then "http://${attrs.url}/${name}" else "http://${attrs.url}"; + # Exportarr is trained to pull from the arr services in '' ${pkgs.exportarr}/bin/exportarr ${name} \ --url ${url} \ @@ -197,7 +212,7 @@ in { prefix = "API_KEY="; }; - # Prometheus scrape targets + # Prometheus scrape targets (expose Exportarr to Prometheus) prometheus.scrapeTargets = map (key: "127.0.0.1:${ lib.attrsets.getAttrFromPath [ key "exportarrPort" ] arrConfig diff --git a/modules/nixos/services/backups.nix b/modules/nixos/services/backups.nix index 06496e4..939783e 100644 --- a/modules/nixos/services/backups.nix +++ b/modules/nixos/services/backups.nix @@ -1,3 +1,6 @@ +# This is my setup for backing up SQlite databases and other systems to S3 or +# S3-equivalent services (like Backblaze B2). + { config, lib, ... }: { options = { diff --git a/modules/nixos/services/bind.nix b/modules/nixos/services/bind.nix index 39ad2d9..c971daa 100644 --- a/modules/nixos/services/bind.nix +++ b/modules/nixos/services/bind.nix @@ -1,3 +1,10 @@ +# Bind is a DNS service. This allows me to resolve public domains locally so +# when I'm at home, I don't have to travel over the Internet to reach my +# server. + +# To set this on all home machines, I point my router's DNS resolver to the +# local IP address of the machine running this service (swan). + { config, pkgs, lib, ... }: let @@ -16,11 +23,19 @@ in { config = lib.mkIf config.services.bind.enable { + # Normally I block all requests not coming from Cloudflare, so I have to also + # allow my local network. caddy.cidrAllowlist = [ "192.168.0.0/16" ]; services.bind = { + + # Allow requests coming from these IPs. This way I don't somehow get + # spammed with DNS requests coming from the Internet. cacheNetworks = [ "127.0.0.0/24" "192.168.0.0/16" ]; + + # When making normal DNS requests, forward them to Cloudflare to resolve. forwarders = [ "1.1.1.1" "1.0.0.1" ]; + ipv4Only = true; # Use rpz zone as an override @@ -47,6 +62,7 @@ in { }; + # We must allow DNS traffic to hit our machine as well networking.firewall.allowedTCPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [ 53 ]; diff --git a/modules/nixos/services/caddy.nix b/modules/nixos/services/caddy.nix index 13284d3..75850e2 100644 --- a/modules/nixos/services/caddy.nix +++ b/modules/nixos/services/caddy.nix @@ -1,3 +1,14 @@ +# Caddy is a reverse proxy, like Nginx or Traefik. This creates an ingress +# point from my local network or the public (via Cloudflare). Instead of a +# Caddyfile, I'm using the more expressive JSON config file format. This means +# I can source routes from other areas in my config and build the JSON file +# using the result of the expression. + +# Caddy helpfully provides automatic ACME cert generation and management, but +# it requires a form of validation. We are using a custom build of Caddy +# (compiled with an overlay) to insert a plugin for managing DNS validation +# with Cloudflare's DNS API. + { config, pkgs, lib, ... }: { options = { @@ -42,12 +53,17 @@ configFile = pkgs.writeText "Caddyfile" (builtins.toJSON { apps.http.servers.main = { listen = [ ":443" ]; + + # These routes are pulled from the rest of this repo routes = config.caddy.routes; errors.routes = config.caddy.blocks; - logs = { }; # Uncomment to collect access logs + + logs = { }; # Uncommenting collects access logs }; apps.http.servers.metrics = { }; # Enables Prometheus metrics apps.tls.automation.policies = config.caddy.tlsPolicies; + + # Setup logging to file logging.logs.main = { encoder = { format = "console"; }; writer = { @@ -58,13 +74,23 @@ }; level = "INFO"; }; + }); }; + # Allows Caddy to serve lower ports (443, 80) + systemd.services.caddy.serviceConfig.AmbientCapabilities = + "CAP_NET_BIND_SERVICE"; + + # Required for web traffic to reach this machine networking.firewall.allowedTCPPorts = [ 80 443 ]; + + # HTTP/3 QUIC uses UDP (not sure if being used) networking.firewall.allowedUDPPorts = [ 443 ]; + # Caddy exposes Prometheus metrics with the admin API + # https://caddyserver.com/docs/api prometheus.scrapeTargets = [ "127.0.0.1:2019" ]; }; diff --git a/modules/nixos/services/calibre.nix b/modules/nixos/services/calibre.nix index 814a110..981ac46 100644 --- a/modules/nixos/services/calibre.nix +++ b/modules/nixos/services/calibre.nix @@ -1,3 +1,9 @@ +# Calibre-web is an E-Book library and management tool. + +# - Exposed to the public via Caddy. +# - Hostname defined with config.hostnames.books +# - File directory backed up to S3 on a cron schedule. + { config, pkgs, lib, ... }: let @@ -26,6 +32,7 @@ in { }; }; + # Allow web traffic to Caddy caddy.routes = [{ match = [{ host = [ config.hostnames.books ]; }]; handle = [{ @@ -35,6 +42,8 @@ in { builtins.toString config.services.calibre-web.listen.port }"; }]; + # This is required when calibre-web is behind a reverse proxy + # https://github.com/janeczku/calibre-web/issues/19 headers.request.add."X-Script-Name" = [ "/calibre-web" ]; }]; }]; diff --git a/modules/nixos/services/cloudflare-tunnel.nix b/modules/nixos/services/cloudflare-tunnel.nix index f98d5e2..2446519 100644 --- a/modules/nixos/services/cloudflare-tunnel.nix +++ b/modules/nixos/services/cloudflare-tunnel.nix @@ -1,3 +1,12 @@ +# Cloudflare Tunnel is a service for accessing the network even behind a +# firewall, through outbound-only requests. It works by installing an agent on +# our machines that exposes services through Cloudflare Access (Zero Trust), +# similar to something like Tailscale. + +# In this case, we're using Cloudflare Tunnel to enable SSH access over a web +# browser even when outside of my network. This is probably not the safest +# choice but I feel comfortable enough with it anyway. + { config, lib, ... }: # First time setup: @@ -40,23 +49,28 @@ tunnels = { "${config.cloudflareTunnel.id}" = { credentialsFile = config.secrets.cloudflared.dest; + # Catch-all if no match (should never happen anyway) default = "http_status:404"; + # Match from ingress of any valid server name to SSH access ingress = { "*.masu.rs" = "ssh://localhost:22"; }; }; }; }; + # Grant Cloudflare access to SSH into this server environment.etc = { "ssh/ca.pub".text = '' ${config.cloudflareTunnel.ca} ''; - # Must match the username of the email address in Cloudflare Access + # Must match the username portion of the email address in Cloudflare + # Access "ssh/authorized_principals".text = '' ${config.user} ''; }; + # Adjust SSH config to allow access from Cloudflare's certificate services.openssh.extraConfig = '' PubkeyAuthentication yes TrustedUserCAKeys /etc/ssh/ca.pub diff --git a/modules/nixos/services/cloudflare.nix b/modules/nixos/services/cloudflare.nix index 5fc7838..8e513c5 100644 --- a/modules/nixos/services/cloudflare.nix +++ b/modules/nixos/services/cloudflare.nix @@ -1,5 +1,13 @@ # This module is necessary for hosts that are serving through Cloudflare. +# Cloudflare is a CDN service that is used to serve the domain names and +# caching for my websites and services. Since Cloudflare acts as our proxy, we +# must allow access over the Internet from Cloudflare's IP ranges. + +# We also want to validate our HTTPS certificates from Caddy. We'll use Caddy's +# DNS validation plugin to connect to Cloudflare and automatically create +# validation DNS records for our generated certificates. + { config, pkgs, lib, ... }: let @@ -59,10 +67,9 @@ in { }; }]; }]; + # Allow Caddy to read Cloudflare API key for DNS validation systemd.services.caddy.serviceConfig.EnvironmentFile = config.secrets.cloudflareApi.dest; - systemd.services.caddy.serviceConfig.AmbientCapabilities = - "CAP_NET_BIND_SERVICE"; # API key must have access to modify Cloudflare DNS records secrets.cloudflareApi = { diff --git a/modules/nixos/services/default.nix b/modules/nixos/services/default.nix index 7dee817..02543c4 100644 --- a/modules/nixos/services/default.nix +++ b/modules/nixos/services/default.nix @@ -1,3 +1,6 @@ +# This file imports all the other files in this directory for use as modules in +# my config. + { ... }: { imports = [ diff --git a/modules/nixos/services/gitea-runner.nix b/modules/nixos/services/gitea-runner.nix index ffee800..a7b79c2 100644 --- a/modules/nixos/services/gitea-runner.nix +++ b/modules/nixos/services/gitea-runner.nix @@ -1,3 +1,9 @@ +# Gitea Actions is a CI/CD service for the Gitea source code server, meaning it +# allows us to run code operations (such as testing or deploys) when our git +# repositories are updated. Any machine can act as a Gitea Action Runner, so +# the Runners don't necessarily need to be running Gitea. All we need is an API +# key for Gitea to connect to it and register ourselves as a Runner. + { config, pkgs, lib, ... }: { diff --git a/modules/nixos/services/gitea.nix b/modules/nixos/services/gitea.nix index da0f795..b906708 100644 --- a/modules/nixos/services/gitea.nix +++ b/modules/nixos/services/gitea.nix @@ -11,11 +11,21 @@ in { actions.ENABLED = true; metrics.ENABLED = true; repository = { + # Pushing to a repo that doesn't exist automatically creates one as + # private. DEFAULT_PUSH_CREATE_PRIVATE = true; + + # Allow git over HTTP. DISABLE_HTTP_GIT = false; + + # Allow requests hitting the specified hostname. ACCESS_CONTROL_ALLOW_ORIGIN = config.hostnames.git; + + # Automatically create viable users/orgs on push. ENABLE_PUSH_CREATE_USER = true; ENABLE_PUSH_CREATE_ORG = true; + + # Default when creating new repos. DEFAULT_BRANCH = "main"; }; server = { @@ -25,11 +35,15 @@ in { SSH_PORT = 22; START_SSH_SERVER = false; # Use sshd instead DISABLE_SSH = false; - # SSH_LISTEN_HOST = "0.0.0.0"; - # SSH_LISTEN_PORT = 122; }; + + # Don't allow public users to register accounts. service.DISABLE_REGISTRATION = true; + + # Force using HTTPS for all session access. session.COOKIE_SECURE = true; + + # Hide users' emails. ui.SHOW_USER_EMAIL = false; }; extraConfig = null; @@ -39,6 +53,7 @@ in { users.users.${config.user}.extraGroups = [ "gitea" ]; caddy.routes = [ + # Prevent public access to Prometheus metrics. { match = [{ host = [ config.hostnames.git ]; @@ -49,6 +64,7 @@ in { status_code = "403"; }]; } + # Allow access to primary server. { match = [{ host = [ config.hostnames.git ]; }]; handle = [{ @@ -63,6 +79,7 @@ in { } ]; + # Scrape the metrics endpoint for Prometheus. prometheus.scrapeTargets = [ "127.0.0.1:${ builtins.toString config.services.gitea.settings.server.HTTP_PORT diff --git a/modules/nixos/services/gnupg.nix b/modules/nixos/services/gnupg.nix index f4f3ca0..db02f57 100644 --- a/modules/nixos/services/gnupg.nix +++ b/modules/nixos/services/gnupg.nix @@ -1,3 +1,5 @@ +# GPG is an encryption tool. This isn't really in use for me at the moment. + { config, pkgs, lib, ... }: { options.gpg.enable = lib.mkEnableOption "GnuPG encryption."; diff --git a/modules/nixos/services/grafana.nix b/modules/nixos/services/grafana.nix index b602762..fc4bfc7 100644 --- a/modules/nixos/services/grafana.nix +++ b/modules/nixos/services/grafana.nix @@ -7,6 +7,7 @@ in { config = lib.mkIf config.services.grafana.enable { + # Allow Grafana to connect to email service secrets.mailpass-grafana = { source = ../../../private/mailpass-grafana.age; dest = "${config.secretsDirectory}/mailpass-grafana"; diff --git a/modules/nixos/services/honeypot.nix b/modules/nixos/services/honeypot.nix index 8c109a4..d0f6bbf 100644 --- a/modules/nixos/services/honeypot.nix +++ b/modules/nixos/services/honeypot.nix @@ -1,7 +1,10 @@ -{ config, lib, pkgs, ... }: +# This is a tool for blocking IPs of anyone who attempts to scan all of my +# ports. # Currently has some issues that don't make this viable. +{ config, lib, pkgs, ... }: + # Taken from: # https://dataswamp.org/~solene/2022-09-29-iblock-implemented-in-nixos.html diff --git a/modules/nixos/services/influxdb2.nix b/modules/nixos/services/influxdb2.nix index 4d5598f..a6c2aab 100644 --- a/modules/nixos/services/influxdb2.nix +++ b/modules/nixos/services/influxdb2.nix @@ -1,3 +1,8 @@ +# InfluxDB is a timeseries database similar to Prometheus. While +# VictoriaMetrics can also act as an InfluxDB, this version of it allows for +# infinite retention separate from our other metrics, which can be nice for +# recording health information, for example. + { config, lib, ... }: { config = { diff --git a/modules/nixos/services/jellyfin.nix b/modules/nixos/services/jellyfin.nix index 2eab282..0d80cc2 100644 --- a/modules/nixos/services/jellyfin.nix +++ b/modules/nixos/services/jellyfin.nix @@ -1,3 +1,6 @@ +# Jellyfin is a self-hosted video streaming service. This means I can play my +# server's videos from a webpage, mobile app, or TV client. + { config, pkgs, lib, ... }: { config = lib.mkIf config.services.jellyfin.enable { @@ -6,6 +9,7 @@ users.users.jellyfin = { isSystemUser = true; }; caddy.routes = [ + # Prevent public access to Prometheus metrics. { match = [{ host = [ config.hostnames.stream ]; @@ -16,6 +20,7 @@ status_code = "403"; }]; } + # Allow access to normal route. { match = [{ host = [ config.hostnames.stream ]; }]; handle = [{ diff --git a/modules/nixos/services/keybase.nix b/modules/nixos/services/keybase.nix index 63efc00..96cb4e0 100644 --- a/modules/nixos/services/keybase.nix +++ b/modules/nixos/services/keybase.nix @@ -1,3 +1,6 @@ +# Keybase is an encrypted communications tool with a synchronized encrypted +# filestore that can be mounted onto a machine's filesystem. + { config, pkgs, lib, ... }: { options.keybase.enable = lib.mkEnableOption "Keybase."; diff --git a/modules/nixos/services/mullvad.nix b/modules/nixos/services/mullvad.nix index ca97d0e..a51a8d2 100644 --- a/modules/nixos/services/mullvad.nix +++ b/modules/nixos/services/mullvad.nix @@ -1,3 +1,5 @@ +# Mullvad is a VPN service. This isn't currently in use for me at the moment. + { config, pkgs, lib, ... }: { options.mullvad.enable = lib.mkEnableOption "Mullvad VPN."; diff --git a/modules/nixos/services/n8n.nix b/modules/nixos/services/n8n.nix index 1b1c3d4..a8213c0 100644 --- a/modules/nixos/services/n8n.nix +++ b/modules/nixos/services/n8n.nix @@ -1,3 +1,6 @@ +# n8n is an automation integration tool for connecting data from services +# together with triggers. + { config, lib, ... }: { options = { diff --git a/modules/nixos/services/netdata.nix b/modules/nixos/services/netdata.nix index 5911c89..72056a2 100644 --- a/modules/nixos/services/netdata.nix +++ b/modules/nixos/services/netdata.nix @@ -1,3 +1,6 @@ +# Netdata is an out-of-the-box monitoring tool that exposes many different +# metrics. Not currently in use, in favor of VictoriaMetrics and Grafana. + { config, lib, ... }: { options.netdata.enable = lib.mkEnableOption "Netdata metrics."; diff --git a/modules/nixos/services/nextcloud.nix b/modules/nixos/services/nextcloud.nix index 020275f..e1ca553 100644 --- a/modules/nixos/services/nextcloud.nix +++ b/modules/nixos/services/nextcloud.nix @@ -16,12 +16,14 @@ }; extraOptions = { default_phone_region = "US"; + # Allow access when hitting either of these hosts or IPs trusted_domains = [ config.hostnames.content ]; trusted_proxies = [ "127.0.0.1" ]; }; extraAppsEnable = true; extraApps = with config.services.nextcloud.package.packages.apps; { inherit calendar contacts; + # These apps are defined and pinned by overlay in flake. news = pkgs.nextcloudApps.news; external = pkgs.nextcloudApps.external; cookbook = pkgs.nextcloudApps.cookbook; diff --git a/modules/nixos/services/paperless.nix b/modules/nixos/services/paperless.nix index 17b5243..1607468 100644 --- a/modules/nixos/services/paperless.nix +++ b/modules/nixos/services/paperless.nix @@ -1,3 +1,5 @@ +# Paperless-ngx is a document scanning and management solution. + { config, lib, ... }: { config = lib.mkIf config.services.paperless.enable { diff --git a/modules/nixos/services/prometheus.nix b/modules/nixos/services/prometheus.nix index ff9e150..3f4944e 100644 --- a/modules/nixos/services/prometheus.nix +++ b/modules/nixos/services/prometheus.nix @@ -1,3 +1,9 @@ +# Prometheus is a timeseries database that exposes system and service metrics +# for use in visualizing, monitoring, and alerting (with Grafana). + +# Instead of running traditional Prometheus, I generally run VictoriaMetrics as +# a more efficient drop-in replacement. + { config, pkgs, lib, ... }: { options.prometheus = { diff --git a/modules/nixos/services/samba.nix b/modules/nixos/services/samba.nix index ea4e972..fba1faa 100644 --- a/modules/nixos/services/samba.nix +++ b/modules/nixos/services/samba.nix @@ -1,3 +1,5 @@ +# Samba is a Windows-compatible file-sharing service. + { config, lib, ... }: { config = { diff --git a/modules/nixos/services/sshd.nix b/modules/nixos/services/sshd.nix index 4c2eed5..bcbb866 100644 --- a/modules/nixos/services/sshd.nix +++ b/modules/nixos/services/sshd.nix @@ -1,3 +1,5 @@ +# SSHD service for allowing SSH access to my machines. + { config, pkgs, lib, ... }: { options = { diff --git a/modules/nixos/services/transmission.nix b/modules/nixos/services/transmission.nix index 38f3a09..7508ba9 100644 --- a/modules/nixos/services/transmission.nix +++ b/modules/nixos/services/transmission.nix @@ -1,3 +1,6 @@ +# Transmission is a bittorrent client, which can run in the background for +# automated downloads with a web GUI. + { config, pkgs, lib, ... }: { options = { diff --git a/modules/nixos/services/vaultwarden.nix b/modules/nixos/services/vaultwarden.nix index 2338e90..2ab1283 100644 --- a/modules/nixos/services/vaultwarden.nix +++ b/modules/nixos/services/vaultwarden.nix @@ -1,3 +1,7 @@ +# Vaultwarden is an implementation of the Bitwarden password manager backend +# service, which allows for self-hosting the synchronization of a Bitwarden +# password manager client. + { config, pkgs, lib, ... }: let vaultwardenPath = "/var/lib/bitwarden_rs"; # Default service directory diff --git a/modules/nixos/services/victoriametrics.nix b/modules/nixos/services/victoriametrics.nix index 6e0975e..191c889 100644 --- a/modules/nixos/services/victoriametrics.nix +++ b/modules/nixos/services/victoriametrics.nix @@ -1,3 +1,6 @@ +# VictoriaMetrics is a more efficient drop-in replacement for Prometheus and +# InfluxDB (timeseries databases built for monitoring system metrics). + { config, pkgs, lib, ... }: let diff --git a/modules/nixos/services/wireguard.nix b/modules/nixos/services/wireguard.nix index 718cd57..440ff2f 100644 --- a/modules/nixos/services/wireguard.nix +++ b/modules/nixos/services/wireguard.nix @@ -1,3 +1,6 @@ +# Wireguard is a VPN protocol that can be setup to create a mesh network +# between machines on different LANs. This is currently not in use in my setup. + { config, pkgs, lib, ... }: { options.wireguard.enable = lib.mkEnableOption "Wireguard VPN setup.";