- Multi-host flake.nix (3x N97 x86_64 + 2x RPi4 aarch64) - Shared modules: k3s-cluster, tailscale, meos-gateway - Node configs: n97-1 (control plane), n97-2/n97-3 (Ollama workers), rpi4-1/rpi4-2 (ARM workers) - K8s manifests: Forgejo, Postgres (CNPG), Umami, OpenBrain, MeOS Dashboard, MCP servers - Bootstrap and DB migration scripts - Migration plan doc (7-day phased rollout)
94 lines
3 KiB
Nix
94 lines
3 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.meos-gateway;
|
|
certDir = config.security.acme.certs."os.scoble.me".directory;
|
|
in {
|
|
options.services.meos-gateway = {
|
|
enable = mkEnableOption "MeOS Gateway — nginx reverse proxy for os.scoble.me";
|
|
tailscaleIP = mkOption {
|
|
type = types.str;
|
|
description = "n97-1 Tailscale IP for nginx to bind to.";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
# ── Nginx — SSL-terminating reverse proxy ──
|
|
services.nginx = {
|
|
enable = true;
|
|
virtualHosts."os.scoble.me" = {
|
|
addSSL = true;
|
|
sslCertificate = "${certDir}/fullchain.pem";
|
|
sslCertificateKey = "${certDir}/key.pem";
|
|
listen = [
|
|
{ addr = cfg.tailscaleIP; port = 443; ssl = true; }
|
|
];
|
|
|
|
locations = {
|
|
"/forgejo/" = {
|
|
proxyPass = "http://k3s-forgejo.kube-system.svc.cluster.local:3000/";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_set_header Host $host;
|
|
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 X-Forwarded-Prefix /forgejo;
|
|
'';
|
|
};
|
|
"/umami" = {
|
|
proxyPass = "http://k3s-umami.kube-system.svc.cluster.local:8069";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_set_header Host $host;
|
|
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;
|
|
'';
|
|
};
|
|
"/umami/" = {
|
|
proxyPass = "http://k3s-umami.kube-system.svc.cluster.local:8069";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_set_header Host $host;
|
|
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;
|
|
'';
|
|
};
|
|
"/dashboard/" = {
|
|
proxyPass = "http://k3s-dashboard.kube-system.svc.cluster.local:3000/";
|
|
proxyWebsockets = true;
|
|
};
|
|
"/" = {
|
|
proxyPass = "http://127.0.0.1:8082";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
# HTTP -> HTTPS redirect
|
|
virtualHosts."os.scoble.me-http" = {
|
|
listen = [ { addr = cfg.tailscaleIP; port = 80; } ];
|
|
serverName = "os.scoble.me";
|
|
extraConfig = "return 301 https://$server_name$request_uri;";
|
|
};
|
|
};
|
|
|
|
# ACME with Cloudflare DNS challenge
|
|
security.acme = {
|
|
acceptTerms = true;
|
|
defaults.email = "scott@scoble.me";
|
|
certs."os.scoble.me" = {
|
|
dnsProvider = "cloudflare";
|
|
environmentFile = "/var/lib/acme/cloudflare.env";
|
|
group = "nginx";
|
|
};
|
|
};
|
|
|
|
# Firewall
|
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
|
};
|
|
}
|