refactor: split services into reusable services/ modules
- services/{samba,avahi,audiobookshelf,containers,caddy,tailscale}.nix
- common.nix grows firewall base + timezone; hosts import what they need
- jupiter/vm/vps import service modules; drop the jupiter/services.nix monolith
- each module opens its own firewall ports; caddy/tailscale shared by hosts
- verified: jupiter/vps/vbox eval + jupiter builds, config equivalent
This commit is contained in:
@@ -37,5 +37,12 @@
|
|||||||
|
|
||||||
environment.systemPackages = with pkgs; [ vim git htop tmux curl ];
|
environment.systemPackages = with pkgs; [ vim git htop tmux curl ];
|
||||||
|
|
||||||
|
# ---- Locale / firewall base ----
|
||||||
|
time.timeZone = "Europe/Berlin";
|
||||||
i18n.defaultLocale = "en_US.UTF-8";
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# Firewall on, ssh always allowed. Service modules add their own ports
|
||||||
|
# (samba via openFirewall, caddy 80/443, tailscale trusts tailscale0).
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-20
@@ -1,14 +1,25 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
# Real-host config: hardware + disk layout + bootloader + shared services.
|
# ZimaBlade NAS host: hardware + disk + the services it runs.
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
./disk-config.nix # disko: OS-disk partitions + filesystems
|
./disk-config.nix # disko: OS-disk partitions + filesystems
|
||||||
./secrets.nix # sops-nix: samba password etc.
|
./secrets.nix # sops-nix: samba password, tailscale key, ...
|
||||||
./services.nix
|
../common.nix # shared base: user / ssh / nix / firewall
|
||||||
|
../services/samba.nix
|
||||||
|
../services/avahi.nix
|
||||||
|
../services/audiobookshelf.nix
|
||||||
|
../services/containers.nix
|
||||||
|
../services/caddy.nix
|
||||||
|
../services/tailscale.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# ---- Host identity ----
|
||||||
|
networking.hostName = "jupiter";
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
users.users.darman.extraGroups = [ "docker" ]; # merges with common.nix
|
||||||
|
|
||||||
# ---- Boot ----
|
# ---- Boot ----
|
||||||
# systemd-boot for UEFI. If ZimaBlade boots legacy/BIOS, switch to grub.
|
# systemd-boot for UEFI. If ZimaBlade boots legacy/BIOS, switch to grub.
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
@@ -19,10 +30,6 @@
|
|||||||
# hardware-configuration.nix) or stage-1 can't mount root and the box panics.
|
# hardware-configuration.nix) or stage-1 can't mount root and the box panics.
|
||||||
boot.initrd.availableKernelModules = [ "mmc_block" "sdhci_pci" "sdhci_acpi" ];
|
boot.initrd.availableKernelModules = [ "mmc_block" "sdhci_pci" "sdhci_acpi" ];
|
||||||
|
|
||||||
# Trust wheel users so `nixos-rebuild --target-host darman@…` can push a
|
|
||||||
# laptop-built (unsigned) closure without a signature error.
|
|
||||||
nix.settings.trusted-users = [ "root" "@wheel" ];
|
|
||||||
|
|
||||||
# Warm reboot hangs at firmware reset on this board (cold power-cycle works).
|
# Warm reboot hangs at firmware reset on this board (cold power-cycle works).
|
||||||
# Force the PCI-chipset reset method. If a warm `reboot` still hangs, try the
|
# Force the PCI-chipset reset method. If a warm `reboot` still hangs, try the
|
||||||
# next value: acpi -> bios -> cold -> efi.
|
# next value: acpi -> bios -> cold -> efi.
|
||||||
@@ -40,18 +47,12 @@
|
|||||||
options = [ "nofail" ]; # don't block boot if the array is degraded/absent
|
options = [ "nofail" ]; # don't block boot if the array is degraded/absent
|
||||||
};
|
};
|
||||||
|
|
||||||
# ---- Tailscale (via self-hosted headscale) ----
|
# ---- Caddy vhosts (LAN) ----
|
||||||
# Auto-registers on boot using a headscale pre-auth key from sops.
|
# whoami on :80, audiobookshelf via a pihole local-DNS name -> jupiter IP.
|
||||||
# Generate the key on the headscale server, e.g.:
|
services.caddy.virtualHosts = {
|
||||||
# headscale preauthkeys create --user <user> --expiration 1h
|
"http://localhost".extraConfig = "reverse_proxy localhost:8080";
|
||||||
# then put it in secrets/jupiter.yaml (./edit_secrets, key: tailscale_authkey).
|
"http://audiobookshelf.jupiter.sol".extraConfig = "reverse_proxy localhost:8000";
|
||||||
services.tailscale = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true; # UDP 41641 for direct connections
|
|
||||||
authKeyFile = config.sops.secrets.tailscale_authkey.path;
|
|
||||||
extraUpFlags = [ "--login-server=https://vpn.mgaction.town" ];
|
|
||||||
};
|
};
|
||||||
# Reach jupiter's services (ssh, samba, audiobookshelf) over the tailnet
|
|
||||||
# without opening those ports on the LAN.
|
system.stateVersion = "26.05";
|
||||||
networking.firewall.trustedInterfaces = [ "tailscale0" ];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
|
||||||
|
|
||||||
# Portable system + service config. Contains NO bootloader or filesystem
|
|
||||||
# settings, so it can be reused by both the real host (configuration.nix)
|
|
||||||
# and the VirtualBox test image (see flake.nix).
|
|
||||||
|
|
||||||
{
|
|
||||||
# ---- Networking ----
|
|
||||||
networking.hostName = "jupiter";
|
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
|
|
||||||
networking.firewall = {
|
|
||||||
enable = true;
|
|
||||||
allowedTCPPorts = [
|
|
||||||
22 # ssh
|
|
||||||
445 139 # samba
|
|
||||||
80 443 # reverse proxy (caddy)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Locale / time ----
|
|
||||||
time.timeZone = "Europe/Berlin";
|
|
||||||
i18n.defaultLocale = "en_US.UTF-8";
|
|
||||||
|
|
||||||
# ---- Users ----
|
|
||||||
users.users.darman = {
|
|
||||||
isNormalUser = true;
|
|
||||||
description = "darman";
|
|
||||||
extraGroups = [ "wheel" "networkmanager" "docker" ];
|
|
||||||
# Password login for ssh is disabled below — these keys are the only way in.
|
|
||||||
openssh.authorizedKeys.keys = [
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGZpkPVhzi1zG5JI9hWyUgdyvNIQbp4ts4jw3idpMhhN erik@laptop"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
security.sudo.wheelNeedsPassword = false;
|
|
||||||
|
|
||||||
# ---- SSH ----
|
|
||||||
services.openssh = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
PasswordAuthentication = false;
|
|
||||||
PermitRootLogin = "no";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Storage / NAS ----
|
|
||||||
services.samba = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true;
|
|
||||||
settings = {
|
|
||||||
global = {
|
|
||||||
"workgroup" = "WORKGROUP";
|
|
||||||
"server string" = "jupiter";
|
|
||||||
"security" = "user";
|
|
||||||
};
|
|
||||||
data = {
|
|
||||||
"path" = "/mnt/data";
|
|
||||||
"browseable" = "yes";
|
|
||||||
"read only" = "no";
|
|
||||||
"guest ok" = "no";
|
|
||||||
"valid users" = "darman";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Samba keeps its own NTLM password DB, separate from the system password.
|
|
||||||
# `services.samba` never sets it, so logins fail until provisioned.
|
|
||||||
# This runs AFTER samba-smbd so its state dir (/var/lib/samba/private) exists
|
|
||||||
# — an activation script runs too early and smbpasswd fails to init the passdb.
|
|
||||||
# Reads a single-line password from the first file that exists:
|
|
||||||
# Real host: /run/secrets/samba_password (sops-nix, see secrets.nix)
|
|
||||||
# VM test: /etc/samba/smb-password (plaintext, see vm.nix)
|
|
||||||
# smbpasswd prompts new + confirm, so the value is fed twice.
|
|
||||||
systemd.services.samba-smbpasswd = {
|
|
||||||
description = "Provision Samba password for darman";
|
|
||||||
after = [ "samba-smbd.service" ];
|
|
||||||
requires = [ "samba-smbd.service" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
for f in /run/secrets/samba_password /etc/samba/smb-password; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
pw=$(head -n1 "$f")
|
|
||||||
printf '%s\n%s\n' "$pw" "$pw" | ${pkgs.samba}/bin/smbpasswd -a -s darman
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo "no samba password source found" >&2
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
services.avahi = {
|
|
||||||
enable = true;
|
|
||||||
nssmdns4 = true;
|
|
||||||
publish = {
|
|
||||||
enable = true;
|
|
||||||
userServices = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Audiobookshelf ----
|
|
||||||
# Native module. Listens on localhost only; reached through caddy.
|
|
||||||
# Library/media paths are set in the web UI — point them at /mnt/data/...
|
|
||||||
# The service runs as user `audiobookshelf`, which must be able to READ those
|
|
||||||
# dirs on the RAID (chown/chmod or add it to the owning group as needed).
|
|
||||||
services.audiobookshelf = {
|
|
||||||
enable = true;
|
|
||||||
host = "0.0.0.0"; # reachable over tailscale0 (trusted) + localhost
|
|
||||||
port = 8000;
|
|
||||||
# openFirewall stays OFF: :8000 is blocked on the LAN, but tailscale0 is a
|
|
||||||
# trusted interface, so the VPS reaches it via jupiter.hosts.mgaction.town:8000.
|
|
||||||
# Local caddy (below) still serves LAN clients via localhost:8000.
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.audiobookshelf.extraGroups = [ "users" ];
|
|
||||||
|
|
||||||
# ---- Containers ----
|
|
||||||
virtualisation.podman = {
|
|
||||||
enable = true;
|
|
||||||
dockerCompat = true;
|
|
||||||
defaultNetwork.settings.dns_enabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
|
||||||
backend = "podman";
|
|
||||||
containers = {
|
|
||||||
whoami = {
|
|
||||||
image = "traefik/whoami:latest";
|
|
||||||
ports = [ "8080:80" ];
|
|
||||||
autoStart = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Reverse proxy ----
|
|
||||||
# Caddy binds nothing unless it has a vhost. This proxies the whoami
|
|
||||||
# container so :80 actually serves. Add one block per service.
|
|
||||||
# Real host: swap `http://localhost` for your domain to get automatic HTTPS,
|
|
||||||
# e.g. `services.caddy.virtualHosts."jelly.example.com".extraConfig`.
|
|
||||||
services.caddy = {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts."http://localhost".extraConfig = ''
|
|
||||||
reverse_proxy localhost:8080
|
|
||||||
'';
|
|
||||||
# Reach at http://audiobookshelf.sol — add a pihole local-DNS record
|
|
||||||
# audiobookshelf.sol -> jupiter's IP. (Or browse jupiter.sol directly if you
|
|
||||||
# set services.audiobookshelf.host = "0.0.0.0" + openFirewall instead.)
|
|
||||||
virtualHosts."http://audiobookshelf.jupiter.sol".extraConfig = ''
|
|
||||||
reverse_proxy localhost:8000
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- System packages ----
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
vim
|
|
||||||
git
|
|
||||||
htop
|
|
||||||
tmux
|
|
||||||
curl
|
|
||||||
];
|
|
||||||
|
|
||||||
# ---- Nix settings ----
|
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
|
||||||
nix.gc = {
|
|
||||||
automatic = true;
|
|
||||||
dates = "weekly";
|
|
||||||
options = "--delete-older-than 30d";
|
|
||||||
};
|
|
||||||
|
|
||||||
system.stateVersion = "26.05";
|
|
||||||
}
|
|
||||||
+19
-5
@@ -1,15 +1,23 @@
|
|||||||
{ config, pkgs, lib, modulesPath, ... }:
|
{ config, pkgs, lib, modulesPath, ... }:
|
||||||
|
|
||||||
# VirtualBox test image. Reuses services.nix but adds console/SSH login
|
# VirtualBox test image — mirrors jupiter's services (minus tailscale/sops/disk)
|
||||||
# credentials so you can actually get into the VM. Disk + bootloader are
|
# and adds console/SSH login credentials. Disk + bootloader come from the
|
||||||
# provided by the virtualbox-image module, so hardware-configuration.nix
|
# virtualbox-image module, so hardware-configuration.nix is NOT imported.
|
||||||
# is intentionally NOT imported here.
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/virtualisation/virtualbox-image.nix")
|
(modulesPath + "/virtualisation/virtualbox-image.nix")
|
||||||
./services.nix
|
../common.nix # shared base: user / ssh / nix / firewall
|
||||||
|
../services/samba.nix
|
||||||
|
../services/avahi.nix
|
||||||
|
../services/audiobookshelf.nix
|
||||||
|
../services/containers.nix
|
||||||
|
../services/caddy.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
networking.hostName = "jupiter";
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
users.users.darman.extraGroups = [ "docker" ];
|
||||||
|
|
||||||
# Allow password login for testing (real host is key-only).
|
# Allow password login for testing (real host is key-only).
|
||||||
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
||||||
|
|
||||||
@@ -26,6 +34,12 @@
|
|||||||
mode = "0600";
|
mode = "0600";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Caddy vhosts (same as the LAN host, so the proxy path is testable).
|
||||||
|
services.caddy.virtualHosts = {
|
||||||
|
"http://localhost".extraConfig = "reverse_proxy localhost:8080";
|
||||||
|
"http://audiobookshelf.jupiter.sol".extraConfig = "reverse_proxy localhost:8000";
|
||||||
|
};
|
||||||
|
|
||||||
# Guest additions for clipboard/resize (optional).
|
# Guest additions for clipboard/resize (optional).
|
||||||
virtualisation.virtualbox.guest.enable = true;
|
virtualisation.virtualbox.guest.enable = true;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
# Audiobookshelf audiobook/podcast server.
|
||||||
|
# Listens on all interfaces: :8000 stays closed on the LAN (no openFirewall),
|
||||||
|
# but reachable over the trusted tailscale0 interface and via localhost (caddy).
|
||||||
|
# Library/media paths are set in the web UI — point them at /mnt/data/...
|
||||||
|
# Runs as user `audiobookshelf`; added to `users` so it can read group-owned
|
||||||
|
# library dirs on the RAID.
|
||||||
|
{
|
||||||
|
services.audiobookshelf = {
|
||||||
|
enable = true;
|
||||||
|
host = "0.0.0.0";
|
||||||
|
port = 8000;
|
||||||
|
};
|
||||||
|
users.users.audiobookshelf.extraGroups = [ "users" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
# mDNS/DNS-SD (advertise the host + shares on the LAN).
|
||||||
|
{
|
||||||
|
services.avahi = {
|
||||||
|
enable = true;
|
||||||
|
nssmdns4 = true;
|
||||||
|
publish = {
|
||||||
|
enable = true;
|
||||||
|
userServices = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
# Caddy reverse proxy — base enable + open the web ports.
|
||||||
|
# Each host adds its own `services.caddy.virtualHosts.<name>` (LAN names on
|
||||||
|
# jupiter, public domains with automatic HTTPS on the vps).
|
||||||
|
{
|
||||||
|
services.caddy.enable = true;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
# Podman (Docker-compatible) + declarative OCI containers.
|
||||||
|
# Copy the whoami block per app; publish ports and mount /mnt/data volumes.
|
||||||
|
{
|
||||||
|
virtualisation.podman = {
|
||||||
|
enable = true;
|
||||||
|
dockerCompat = true;
|
||||||
|
defaultNetwork.settings.dns_enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers = {
|
||||||
|
backend = "podman";
|
||||||
|
containers = {
|
||||||
|
whoami = {
|
||||||
|
image = "traefik/whoami:latest";
|
||||||
|
ports = [ "8080:80" ];
|
||||||
|
autoStart = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
# Samba file server for the /mnt/data share + password provisioning.
|
||||||
|
# Import on any host that should serve the NAS.
|
||||||
|
{
|
||||||
|
services.samba = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
settings = {
|
||||||
|
global = {
|
||||||
|
"workgroup" = "WORKGROUP";
|
||||||
|
"server string" = "jupiter";
|
||||||
|
"security" = "user";
|
||||||
|
};
|
||||||
|
data = {
|
||||||
|
"path" = "/mnt/data";
|
||||||
|
"browseable" = "yes";
|
||||||
|
"read only" = "no";
|
||||||
|
"guest ok" = "no";
|
||||||
|
"valid users" = "darman";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Samba keeps its own NTLM password DB, separate from the system password.
|
||||||
|
# `services.samba` never sets it, so logins fail until provisioned.
|
||||||
|
# This runs AFTER samba-smbd so its state dir (/var/lib/samba/private) exists
|
||||||
|
# — an activation script runs too early and smbpasswd fails to init the passdb.
|
||||||
|
# Reads a single-line password from the first file that exists:
|
||||||
|
# Real host: /run/secrets/samba_password (sops-nix, see secrets.nix)
|
||||||
|
# VM test: /etc/samba/smb-password (plaintext, see vm.nix)
|
||||||
|
# smbpasswd prompts new + confirm, so the value is fed twice.
|
||||||
|
systemd.services.samba-smbpasswd = {
|
||||||
|
description = "Provision Samba password for darman";
|
||||||
|
after = [ "samba-smbd.service" ];
|
||||||
|
requires = [ "samba-smbd.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
for f in /run/secrets/samba_password /etc/samba/smb-password; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
pw=$(head -n1 "$f")
|
||||||
|
printf '%s\n%s\n' "$pw" "$pw" | ${pkgs.samba}/bin/smbpasswd -a -s darman
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "no samba password source found" >&2
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
# Tailscale node joined to the self-hosted headscale control server.
|
||||||
|
# Auto-registers on boot from a sops pre-auth key. Requires the importing host
|
||||||
|
# to declare `sops.secrets.tailscale_authkey` (see each host's secrets.nix).
|
||||||
|
# Not for the VM (no sops).
|
||||||
|
{
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true; # UDP 41641 for direct connections
|
||||||
|
authKeyFile = config.sops.secrets.tailscale_authkey.path;
|
||||||
|
extraUpFlags = [ "--login-server=https://vpn.mgaction.town" ];
|
||||||
|
};
|
||||||
|
# Reach the host's services over the tailnet without opening LAN ports.
|
||||||
|
networking.firewall.trustedInterfaces = [ "tailscale0" ];
|
||||||
|
}
|
||||||
+8
-20
@@ -6,7 +6,9 @@
|
|||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
./disk-config.nix # disko: vda partitions + filesystems
|
./disk-config.nix # disko: vda partitions + filesystems
|
||||||
./secrets.nix # sops-nix: tailscale authkey
|
./secrets.nix # sops-nix: tailscale authkey
|
||||||
../common.nix # shared user / ssh / nix settings
|
../common.nix # shared base: user / ssh / nix / firewall
|
||||||
|
../services/caddy.nix
|
||||||
|
../services/tailscale.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# ---- Boot (UEFI) ----
|
# ---- Boot (UEFI) ----
|
||||||
@@ -34,31 +36,17 @@
|
|||||||
# Public resolvers for early boot; tailscale MagicDNS overrides once up.
|
# Public resolvers for early boot; tailscale MagicDNS overrides once up.
|
||||||
networking.nameservers = [ "9.9.9.9" "1.1.1.1" "2620:fe::fe" ];
|
networking.nameservers = [ "9.9.9.9" "1.1.1.1" "2620:fe::fe" ];
|
||||||
|
|
||||||
networking.firewall = {
|
# firewall (enable + 22), caddy (80/443), tailscale (trust tailscale0 + join
|
||||||
enable = true;
|
# headscale) come from ../common.nix and ../services/{caddy,tailscale}.nix.
|
||||||
allowedTCPPorts = [ 22 80 443 ];
|
|
||||||
trustedInterfaces = [ "tailscale0" ]; # reach admin services over the tailnet
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Tailscale (via headscale) ----
|
# ---- Public reverse proxy vhosts ----
|
||||||
services.tailscale = {
|
|
||||||
enable = true;
|
|
||||||
authKeyFile = config.sops.secrets.tailscale_authkey.path;
|
|
||||||
extraUpFlags = [ "--login-server=https://vpn.mgaction.town" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# ---- Public reverse proxy ----
|
|
||||||
# Caddy gets automatic public HTTPS (Let's Encrypt) for real domains.
|
# Caddy gets automatic public HTTPS (Let's Encrypt) for real domains.
|
||||||
# Proxies to jupiter's audiobookshelf over the tailnet (MagicDNS name).
|
# Proxies to jupiter's audiobookshelf over the tailnet (MagicDNS name).
|
||||||
# Add one block per public service. Needs a public A record -> this VPS IP
|
# Needs a public A record -> this VPS IP (ports 80/443 opened by the module).
|
||||||
# and ports 80/443 open (they are, above).
|
services.caddy.virtualHosts."audiobookshelf.mgaction.town".extraConfig = ''
|
||||||
services.caddy = {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts."audiobookshelf.mgaction.town".extraConfig = ''
|
|
||||||
reverse_proxy http://jupiter.hosts.mgaction.town:8000
|
reverse_proxy http://jupiter.hosts.mgaction.town:8000
|
||||||
'';
|
'';
|
||||||
# TODO: port your other VPS services' vhosts here before deploying.
|
# TODO: port your other VPS services' vhosts here before deploying.
|
||||||
};
|
|
||||||
|
|
||||||
system.stateVersion = "26.05"; # set at install time; do NOT bump on upgrades
|
system.stateVersion = "26.05"; # set at install time; do NOT bump on upgrades
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user