Replace tailscale with wireguard
This commit is contained in:
parent
81f59a8148
commit
d0084adcc9
8 changed files with 278 additions and 77 deletions
92
README.md
92
README.md
|
|
@ -83,7 +83,7 @@ home-manager switch --flake github:Logan-Lin/nix-config#yanlin@vps
|
||||||
│ ├── btop.nix # Modern system monitor (includes package)
|
│ ├── btop.nix # Modern system monitor (includes package)
|
||||||
│ ├── ghostty.nix # GPU-accelerated terminal emulator
|
│ ├── ghostty.nix # GPU-accelerated terminal emulator
|
||||||
│ ├── syncthing.nix # File synchronization service (includes package)
|
│ ├── syncthing.nix # File synchronization service (includes package)
|
||||||
│ ├── tailscale.nix # Secure networking and VPN service
|
│ ├── wireguard.nix # Hub-and-spoke VPN networking
|
||||||
│ ├── borg.nix # Borg backup system with automated scheduling
|
│ ├── borg.nix # Borg backup system with automated scheduling
|
||||||
│ └── homebrew.nix # Homebrew and nix-homebrew configuration
|
│ └── homebrew.nix # Homebrew and nix-homebrew configuration
|
||||||
├── config/ # Configuration files
|
├── config/ # Configuration files
|
||||||
|
|
@ -890,69 +890,65 @@ echo "BORG_PASSPHRASE=your-secure-passphrase" | sudo tee /etc/borg-passphrase
|
||||||
sudo chmod 600 /etc/borg-passphrase
|
sudo chmod 600 /etc/borg-passphrase
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔒 Secure Networking: Tailscale
|
## 🔒 Secure Networking: WireGuard VPN
|
||||||
|
|
||||||
**Configuration**: `modules/tailscale.nix`
|
**Configuration**: `modules/wireguard.nix`
|
||||||
**Purpose**: Secure mesh VPN for private networking across devices
|
**Purpose**: Hub-and-spoke VPN for secure connectivity between VPS and home server
|
||||||
|
|
||||||
|
### Network Architecture:
|
||||||
|
- **VPS (Hub)**: 10.2.2.1/24 - Central WireGuard server with public endpoint
|
||||||
|
- **HS (Spoke)**: 10.2.2.20/24 - Home server connecting through VPS
|
||||||
|
- **LAN Access**: HS remains accessible at 10.1.1.152 on local network
|
||||||
|
- **DNS Setup**: hs.yanlincs.com resolves to 10.1.1.152 (LAN) with 10.2.2.20 (WireGuard) fallback
|
||||||
|
|
||||||
### Key Features:
|
### Key Features:
|
||||||
- **Automatic Startup**: Runs as a system service at boot
|
- **Hub-and-Spoke Topology**: VPS acts as central gateway for all connections
|
||||||
- **MagicDNS**: Access devices by name instead of IP addresses
|
- **Dual Access**: Home server accessible via both LAN (10.1.1.152) and WireGuard (10.2.2.20)
|
||||||
- **Secure Connectivity**: Zero-configuration encrypted connections
|
- **Automatic Key Management**: Private keys generated and managed per host
|
||||||
- **Exit Nodes**: Route traffic through specific devices
|
- **Firewall Integration**: Automatic firewall rules and IP forwarding
|
||||||
|
- **Systemd Integration**: Uses wg-quick for reliable service management
|
||||||
|
|
||||||
### Command Line Usage:
|
### Command Line Usage:
|
||||||
|
|
||||||
#### Basic Operations:
|
#### Service Management:
|
||||||
```bash
|
```bash
|
||||||
# Check connection status and see all devices
|
# Check WireGuard status
|
||||||
tailscale status
|
sudo systemctl status wg-quick-wg0
|
||||||
|
|
||||||
# Connect to your Tailscale network (first-time setup)
|
# Start/stop WireGuard
|
||||||
tailscale up
|
sudo systemctl start wg-quick-wg0
|
||||||
|
sudo systemctl stop wg-quick-wg0
|
||||||
|
|
||||||
# Disconnect temporarily
|
# View WireGuard interface status
|
||||||
tailscale down
|
sudo wg show
|
||||||
|
|
||||||
# View current Tailscale IP address
|
# Check connectivity
|
||||||
tailscale ip -4
|
ping 10.2.2.1 # Ping VPS from HS
|
||||||
|
ping 10.2.2.20 # Ping HS from VPS
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Exit Node Management:
|
#### Key Management:
|
||||||
```bash
|
```bash
|
||||||
# List available exit nodes
|
# View public key (add to peer configurations)
|
||||||
tailscale exit-node list
|
sudo wg pubkey < /etc/wireguard/private.key
|
||||||
|
|
||||||
# Use a specific exit node
|
# Generate new keys if needed
|
||||||
tailscale set --exit-node=<hostname>
|
wg genkey | sudo tee /etc/wireguard/private.key
|
||||||
# or
|
sudo wg pubkey < /etc/wireguard/private.key
|
||||||
tailscale up --exit-node=<hostname>
|
|
||||||
|
|
||||||
# Stop using exit node
|
|
||||||
tailscale set --exit-node=
|
|
||||||
# or
|
|
||||||
tailscale up --exit-node=
|
|
||||||
|
|
||||||
# Allow LAN access while using exit node
|
|
||||||
tailscale set --exit-node=<hostname> --exit-node-allow-lan-access
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Advanced Usage:
|
|
||||||
```bash
|
|
||||||
# Get suggested exit node
|
|
||||||
tailscale exit-node suggest
|
|
||||||
|
|
||||||
# Check detailed network diagnostics
|
|
||||||
tailscale netcheck
|
|
||||||
|
|
||||||
# Show network configuration
|
|
||||||
tailscale debug netmap
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration Details:
|
### Configuration Details:
|
||||||
- **Auto-start**: Enabled via nix-darwin service management
|
- **Server Mode**: Configured on VPS with NAT forwarding and firewall rules
|
||||||
- **DNS Override**: Uses Tailscale's MagicDNS (100.100.100.100) for name resolution
|
- **Client Mode**: Configured on HS with persistent keepalive to VPS
|
||||||
- **System Integration**: Runs as a daemon accessible to all users
|
- **Automatic Startup**: Enabled via systemd wg-quick service
|
||||||
|
- **Key Storage**: Private keys stored in `/etc/wireguard/private.key` with 600 permissions
|
||||||
|
- **Port**: Default UDP 51820 (configurable)
|
||||||
|
|
||||||
|
### Setup Process:
|
||||||
|
1. Deploy configurations to both VPS and HS
|
||||||
|
2. Retrieve public keys from each host after first boot
|
||||||
|
3. Update peer configurations with actual public keys and VPS endpoint IP
|
||||||
|
4. Restart WireGuard services to establish connection
|
||||||
|
|
||||||
## 🏠 Home Server (`hs` Host)
|
## 🏠 Home Server (`hs` Host)
|
||||||
|
|
||||||
|
|
@ -1054,7 +1050,7 @@ Comprehensive suite of self-hosted services managed via Podman with automatic st
|
||||||
|
|
||||||
### 📍 Service Access
|
### 📍 Service Access
|
||||||
|
|
||||||
All services accessible via Tailscale VPN with SSL certificates:
|
All services accessible via DNS with dual-IP resolution (LAN: 10.1.1.152, WireGuard: 10.2.2.20) with SSL certificates:
|
||||||
|
|
||||||
| Service | URL | Purpose |
|
| Service | URL | Purpose |
|
||||||
|---------|-----|---------|
|
|---------|-----|---------|
|
||||||
|
|
|
||||||
|
|
@ -78,10 +78,6 @@
|
||||||
name = "Gotify";
|
name = "Gotify";
|
||||||
url = "https://notify.yanlincs.com/";
|
url = "https://notify.yanlincs.com/";
|
||||||
}
|
}
|
||||||
{
|
|
||||||
name = "Tailscale";
|
|
||||||
url = "https://login.tailscale.com/admin/machines";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
./containers.nix # Host-specific container definitions
|
./containers.nix # Host-specific container definitions
|
||||||
./proxy.nix # Host-specific Traefik dynamic configuration
|
./proxy.nix # Host-specific Traefik dynamic configuration
|
||||||
./disk-health.nix # Host-specific disk health monitoring
|
./disk-health.nix # Host-specific disk health monitoring
|
||||||
../../../modules/tailscale.nix
|
../../../modules/wireguard.nix
|
||||||
../../../modules/podman.nix
|
../../../modules/podman.nix
|
||||||
../../../modules/traefik.nix
|
../../../modules/traefik.nix
|
||||||
../../../modules/samba.nix
|
../../../modules/samba.nix
|
||||||
|
|
@ -290,6 +290,20 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# WireGuard VPN configuration (HS as client/spoke)
|
||||||
|
services.wireguard-custom = {
|
||||||
|
enable = true;
|
||||||
|
mode = "client";
|
||||||
|
clientConfig = {
|
||||||
|
address = "10.2.2.20/24";
|
||||||
|
# Public key will be generated when VPS is configured
|
||||||
|
# Replace with actual public key from VPS after initial setup
|
||||||
|
serverPublicKey = "REPLACE_WITH_VPS_PUBLIC_KEY";
|
||||||
|
serverEndpoint = "YOUR_VPS_IP:51820"; # Replace with actual VPS public IP
|
||||||
|
allowedIPs = [ "10.2.2.0/24" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# This value determines the NixOS release from which the default
|
# This value determines the NixOS release from which the default
|
||||||
# settings for stateful data, like file locations and database versions
|
# settings for stateful data, like file locations and database versions
|
||||||
# on your system were taken. It's perfectly fine and recommended to leave
|
# on your system were taken. It's perfectly fine and recommended to leave
|
||||||
|
|
|
||||||
|
|
@ -56,20 +56,20 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Photo service backend
|
# Photo service backend (via WireGuard)
|
||||||
photo = {
|
photo = {
|
||||||
loadBalancer = {
|
loadBalancer = {
|
||||||
servers = [{
|
servers = [{
|
||||||
url = "http://hs.yanlincs.com:5000";
|
url = "http://10.2.2.20:5000";
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Cloud service backend
|
# Cloud service backend (via WireGuard)
|
||||||
cloud = {
|
cloud = {
|
||||||
loadBalancer = {
|
loadBalancer = {
|
||||||
servers = [{
|
servers = [{
|
||||||
url = "http://hs.yanlincs.com:5001";
|
url = "http://10.2.2.20:5001";
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
./disk-config.nix
|
./disk-config.nix
|
||||||
./containers.nix # Host-specific container definitions
|
./containers.nix # Host-specific container definitions
|
||||||
./proxy.nix # Host-specific Traefik dynamic configuration
|
./proxy.nix # Host-specific Traefik dynamic configuration
|
||||||
../../../modules/tailscale.nix
|
../../../modules/wireguard.nix
|
||||||
../../../modules/podman.nix
|
../../../modules/podman.nix
|
||||||
../../../modules/traefik.nix
|
../../../modules/traefik.nix
|
||||||
../../../modules/borg.nix
|
../../../modules/borg.nix
|
||||||
|
|
@ -135,6 +135,24 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# WireGuard VPN configuration (VPS as hub/server)
|
||||||
|
services.wireguard-custom = {
|
||||||
|
enable = true;
|
||||||
|
mode = "server";
|
||||||
|
serverConfig = {
|
||||||
|
address = "10.2.2.1/24";
|
||||||
|
peers = [
|
||||||
|
{
|
||||||
|
name = "hs";
|
||||||
|
# Public key will be generated when HS is configured
|
||||||
|
# Replace with actual public key from HS after initial setup
|
||||||
|
publicKey = "REPLACE_WITH_HS_PUBLIC_KEY";
|
||||||
|
allowedIPs = [ "10.2.2.20/32" ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# This value determines the NixOS release from which the default
|
# This value determines the NixOS release from which the default
|
||||||
# settings for stateful data, like file locations and database versions
|
# settings for stateful data, like file locations and database versions
|
||||||
# on your system were taken. It's perfectly fine and recommended to leave
|
# on your system were taken. It's perfectly fine and recommended to leave
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
];
|
];
|
||||||
casks = [
|
casks = [
|
||||||
# GUI applications - manually installed apps now managed by Homebrew
|
# GUI applications - manually installed apps now managed by Homebrew
|
||||||
"tailscale-app"
|
|
||||||
"inkscape"
|
"inkscape"
|
||||||
"firefox"
|
"firefox"
|
||||||
"obsidian"
|
"obsidian"
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{ config, pkgs, lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# Enable Tailscale service for NixOS
|
|
||||||
services.tailscale = {
|
|
||||||
enable = true;
|
|
||||||
# Enable MagicDNS for better name resolution on NixOS server
|
|
||||||
useRoutingFeatures = "server";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Allow Tailscale through the firewall if enabled
|
|
||||||
networking.firewall = {
|
|
||||||
# Allow Tailscale UDP port
|
|
||||||
allowedUDPPorts = [ 41641 ];
|
|
||||||
# Allow traffic from Tailscale subnet
|
|
||||||
trustedInterfaces = [ "tailscale0" ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
196
modules/wireguard.nix
Normal file
196
modules/wireguard.nix
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.wireguard-custom;
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
options.services.wireguard-custom = {
|
||||||
|
enable = mkEnableOption "WireGuard VPN";
|
||||||
|
|
||||||
|
mode = mkOption {
|
||||||
|
type = types.enum [ "server" "client" ];
|
||||||
|
description = "Whether to run as server (hub) or client (spoke)";
|
||||||
|
};
|
||||||
|
|
||||||
|
interface = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "wg0";
|
||||||
|
description = "WireGuard interface name";
|
||||||
|
};
|
||||||
|
|
||||||
|
listenPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 51820;
|
||||||
|
description = "UDP port to listen on (server mode only)";
|
||||||
|
};
|
||||||
|
|
||||||
|
privateKeyFile = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/etc/wireguard/private.key";
|
||||||
|
description = "Path to private key file";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverConfig = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
address = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "10.2.2.1/24";
|
||||||
|
description = "Server IP address with CIDR";
|
||||||
|
};
|
||||||
|
|
||||||
|
peers = mkOption {
|
||||||
|
type = types.listOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Peer name for identification";
|
||||||
|
};
|
||||||
|
|
||||||
|
publicKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Peer's public key";
|
||||||
|
};
|
||||||
|
|
||||||
|
allowedIPs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "IP addresses this peer is allowed to use";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = [];
|
||||||
|
description = "List of client peers";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "Server-specific configuration";
|
||||||
|
};
|
||||||
|
|
||||||
|
clientConfig = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
address = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "10.2.2.20/24";
|
||||||
|
description = "Client IP address with CIDR";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverPublicKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Server's public key";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverEndpoint = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "vpn.example.com:51820";
|
||||||
|
description = "Server endpoint (host:port)";
|
||||||
|
};
|
||||||
|
|
||||||
|
allowedIPs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ "10.2.2.0/24" ];
|
||||||
|
description = "IP ranges to route through the tunnel";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "Client-specific configuration";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Install WireGuard tools
|
||||||
|
environment.systemPackages = with pkgs; [ wireguard-tools ];
|
||||||
|
|
||||||
|
# Create private key file if it doesn't exist
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /etc/wireguard 0700 root root - -"
|
||||||
|
"f ${cfg.privateKeyFile} 0600 root root - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Generate private key on first run
|
||||||
|
systemd.services.wireguard-keygen = {
|
||||||
|
description = "Generate WireGuard private key";
|
||||||
|
before = [ "wg-quick-${cfg.interface}.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
if [ ! -s ${cfg.privateKeyFile} ]; then
|
||||||
|
echo "Generating WireGuard private key..."
|
||||||
|
${pkgs.wireguard-tools}/bin/wg genkey > ${cfg.privateKeyFile}
|
||||||
|
chmod 600 ${cfg.privateKeyFile}
|
||||||
|
echo "Private key generated. Public key:"
|
||||||
|
${pkgs.wireguard-tools}/bin/wg pubkey < ${cfg.privateKeyFile}
|
||||||
|
echo "Please add this public key to your peer configurations."
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Server configuration
|
||||||
|
networking.wg-quick.interfaces = mkIf (cfg.mode == "server") {
|
||||||
|
${cfg.interface} = {
|
||||||
|
address = [ cfg.serverConfig.address ];
|
||||||
|
listenPort = cfg.listenPort;
|
||||||
|
privateKeyFile = cfg.privateKeyFile;
|
||||||
|
|
||||||
|
# Enable IP forwarding and NAT for server
|
||||||
|
preUp = ''
|
||||||
|
${pkgs.iptables}/bin/iptables -A FORWARD -i ${cfg.interface} -j ACCEPT
|
||||||
|
${pkgs.iptables}/bin/iptables -A FORWARD -o ${cfg.interface} -j ACCEPT
|
||||||
|
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE
|
||||||
|
'';
|
||||||
|
|
||||||
|
postDown = ''
|
||||||
|
${pkgs.iptables}/bin/iptables -D FORWARD -i ${cfg.interface} -j ACCEPT
|
||||||
|
${pkgs.iptables}/bin/iptables -D FORWARD -o ${cfg.interface} -j ACCEPT
|
||||||
|
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE
|
||||||
|
'';
|
||||||
|
|
||||||
|
peers = map (peer: {
|
||||||
|
publicKey = peer.publicKey;
|
||||||
|
allowedIPs = peer.allowedIPs;
|
||||||
|
}) cfg.serverConfig.peers;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Client configuration
|
||||||
|
networking.wg-quick.interfaces = mkIf (cfg.mode == "client") {
|
||||||
|
${cfg.interface} = {
|
||||||
|
address = [ cfg.clientConfig.address ];
|
||||||
|
privateKeyFile = cfg.privateKeyFile;
|
||||||
|
|
||||||
|
peers = [{
|
||||||
|
publicKey = cfg.clientConfig.serverPublicKey;
|
||||||
|
allowedIPs = cfg.clientConfig.allowedIPs;
|
||||||
|
endpoint = cfg.clientConfig.serverEndpoint;
|
||||||
|
persistentKeepalive = 25;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Firewall configuration
|
||||||
|
networking.firewall = mkMerge [
|
||||||
|
# Server firewall rules
|
||||||
|
(mkIf (cfg.mode == "server") {
|
||||||
|
allowedUDPPorts = [ cfg.listenPort ];
|
||||||
|
trustedInterfaces = [ cfg.interface ];
|
||||||
|
})
|
||||||
|
|
||||||
|
# Client firewall rules
|
||||||
|
(mkIf (cfg.mode == "client") {
|
||||||
|
trustedInterfaces = [ cfg.interface ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
# Enable IP forwarding for server
|
||||||
|
boot.kernel.sysctl = mkIf (cfg.mode == "server") {
|
||||||
|
"net.ipv4.ip_forward" = 1;
|
||||||
|
"net.ipv6.conf.all.forwarding" = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue