Merge branch 'main' into fix/openclaw-network-isolation
This commit is contained in:
commit
b7ccb0a07e
3 changed files with 170 additions and 0 deletions
|
|
@ -85,6 +85,7 @@
|
||||||
kitty.terminfo
|
kitty.terminfo
|
||||||
htop
|
htop
|
||||||
tmux
|
tmux
|
||||||
|
headscale
|
||||||
];
|
];
|
||||||
|
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
|
@ -179,6 +180,22 @@
|
||||||
# === CrowdSec ===
|
# === CrowdSec ===
|
||||||
myModules.crowdsec.enable = true;
|
myModules.crowdsec.enable = true;
|
||||||
|
|
||||||
|
# === Headscale (Self-hosted Tailscale) ===
|
||||||
|
myModules.headscale = {
|
||||||
|
enable = true;
|
||||||
|
domain = "vpn.ashisgreat.xyz";
|
||||||
|
# OIDC not enabled by default — use pre-shared auth keys:
|
||||||
|
# headscale apikeys create
|
||||||
|
# tailscale up --login-server=https://vpn.ashisgreat.xyz --authkey=<key>
|
||||||
|
#
|
||||||
|
# To enable OIDC login (Google, GitHub, etc.):
|
||||||
|
# oidc.enable = true;
|
||||||
|
# oidc.issuer = "https://accounts.google.com";
|
||||||
|
# oidc.clientId = "your-client-id";
|
||||||
|
# oidc.clientSecret = config.sops.placeholder.headscale_oidc_secret;
|
||||||
|
# And add headscale_oidc_secret to your secrets.yaml
|
||||||
|
};
|
||||||
|
|
||||||
# === Backups (Restic + B2) ===
|
# === Backups (Restic + B2) ===
|
||||||
myModules.backup = {
|
myModules.backup = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@
|
||||||
./backup.nix
|
./backup.nix
|
||||||
./adguard.nix
|
./adguard.nix
|
||||||
./forgejo.nix
|
./forgejo.nix
|
||||||
|
./headscale.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
152
modules/headscale.nix
Normal file
152
modules/headscale.nix
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
# Headscale Module
|
||||||
|
# Provides: Self-hosted Tailscale control server (mesh VPN)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# myModules.headscale = {
|
||||||
|
# enable = true;
|
||||||
|
# domain = "vpn.example.com";
|
||||||
|
# };
|
||||||
|
#
|
||||||
|
# After deploying, generate an auth key to register devices:
|
||||||
|
# headscale apikeys create
|
||||||
|
#
|
||||||
|
# Then on each device:
|
||||||
|
# tailscale up --login-server=https://vpn.example.com --authkey=<key>
|
||||||
|
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.myModules.headscale;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.myModules.headscale = {
|
||||||
|
enable = lib.mkEnableOption "Headscale (self-hosted Tailscale control server)";
|
||||||
|
|
||||||
|
domain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "vpn.example.com";
|
||||||
|
description = "Public domain name for the Headscale control server";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 8085;
|
||||||
|
description = "Internal port for Headscale to listen on";
|
||||||
|
};
|
||||||
|
|
||||||
|
oidc = {
|
||||||
|
enable = lib.mkEnableOption "OIDC authentication (e.g. Google, GitHub)";
|
||||||
|
|
||||||
|
issuer = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "https://accounts.google.com";
|
||||||
|
description = "OIDC issuer URL";
|
||||||
|
};
|
||||||
|
|
||||||
|
clientId = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "OIDC client ID (store in SOPS)";
|
||||||
|
};
|
||||||
|
|
||||||
|
clientSecret = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "OIDC client secret SOPS placeholder";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
services.headscale = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
server_url = "https://${cfg.domain}";
|
||||||
|
listen_addr = "127.0.0.1:${toString cfg.port}";
|
||||||
|
grpc_listen_addr = "127.0.0.1:50443";
|
||||||
|
metrics_listen_addr = "127.0.0.1:9090";
|
||||||
|
|
||||||
|
# Use SQLite for simplicity (Postgres available for larger deployments)
|
||||||
|
database.sqlite.path = "/var/lib/headscale/db.sqlite";
|
||||||
|
|
||||||
|
# Private key for WireGuard — auto-generated on first run
|
||||||
|
private_key_path = "/var/lib/headscale/private.key";
|
||||||
|
|
||||||
|
# DERP relay servers (use Tailscale's public ones)
|
||||||
|
derp = {
|
||||||
|
urls = [
|
||||||
|
"https://controlplane.tailscale.com/derpmap/default"
|
||||||
|
];
|
||||||
|
auto_update = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Disable built-in DERP server (use public relays only)
|
||||||
|
derp.server.enabled = false;
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
policy.mode = lib.mkIf cfg.oidc.enable "oidc";
|
||||||
|
|
||||||
|
oidc = lib.mkIf cfg.oidc.enable {
|
||||||
|
issuer = cfg.oidc.issuer;
|
||||||
|
client_id = cfg.oidc.clientId;
|
||||||
|
client_secret_path = config.sops.secrets.headscale_oidc_secret.path;
|
||||||
|
allowed_domains = [ ];
|
||||||
|
allowed_users = [ ];
|
||||||
|
scopes = [ "openid" "profile" "email" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# DNS configuration
|
||||||
|
dns = {
|
||||||
|
magic_dns = true;
|
||||||
|
base_domain = "headscale.net";
|
||||||
|
domains = [ ];
|
||||||
|
nameservers = {
|
||||||
|
global = [
|
||||||
|
"https://dns.mullvad.net/dns-query"
|
||||||
|
"https://dns.quad9.net/dns-query"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
override_local_dns = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log.level = "info";
|
||||||
|
|
||||||
|
# Unix socket for headscale CLI
|
||||||
|
unix_socket = "/run/headscale/headscale.sock";
|
||||||
|
unix_socket_permission = "0770";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# OIDC secret from SOPS
|
||||||
|
sops.secrets.headscale_oidc_secret = lib.mkIf cfg.oidc.enable { };
|
||||||
|
|
||||||
|
# Nginx reverse proxy
|
||||||
|
myModules.nginx.domains."${cfg.domain}" = {
|
||||||
|
port = cfg.port;
|
||||||
|
extraConfig = ''
|
||||||
|
# Headscale needs WebSocket upgrade for client connections
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# Larger buffers for Headscale
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
'';
|
||||||
|
# Headscale doesn't serve HTML — skip CSP
|
||||||
|
contentSecurityPolicy = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Add Headscale data to backups
|
||||||
|
myModules.backup.paths = [
|
||||||
|
config.services.headscale.settings.database.sqlite.path
|
||||||
|
];
|
||||||
|
|
||||||
|
# Headscale needs its own group for socket permissions
|
||||||
|
users.groups.headscale = { };
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue