homelab framework module init (everything is a mess)
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled

This commit is contained in:
plasmagoat 2025-07-28 02:05:13 +02:00
parent 0347f4d325
commit bcbcc8b17b
94 changed files with 7289 additions and 436 deletions

View file

@ -1,17 +0,0 @@
🥇 Phase 1: Git + Secrets
✅ Set up Forgejo VM (NixOS declarative)
✅ Set up sops-nix + age keys (can live in the Git repo)
✅ Push flake + ansible + secrets to Forgejo
✅ Write a basic README with how to rebuild infra
🥈 Phase 2: GitOps
🔁 Add CI runner VM
🔁 Configure runner to deploy (nixos-rebuild or ansible-playbook) on commit
🔁 Optional: add webhooks to auto-trigger via Forgejo

View file

@ -18,7 +18,7 @@ in {
stateDir = "/srv/forgejo";
secrets = {
mailer = {
PASSWD = ;
PASSWD = config.sops.secrets.forgejo-mailer-password.path;
};
};
settings = {
@ -76,12 +76,12 @@ in {
ALLOW_DEACTIVATE_ALL = false;
};
oauth2 = {
};
oauth2_client = {
ENABLE_AUTO_REGISTRATION = true;
UPDATE_AVATAR = true;
};
# oauth2 = {
# };
# oauth2_client = {
# ENABLE_AUTO_REGISTRATION = true;
# UPDATE_AVATAR = true;
# };
# log = {
# ROOT_PATH = "/var/log/forgejo";
# MODE = "file";

View file

@ -1,7 +1,6 @@
let
forgejoSops = ../../secrets/forgejo/secrets.yml;
in
{
in {
sops.secrets = {
"forgejo-admin-password" = {
sopsFile = forgejoSops;
@ -15,5 +14,9 @@ in
sopsFile = forgejoSops;
owner = "forgejo";
};
"forgejo-mailer-password" = {
sopsFile = forgejoSops;
owner = "forgejo";
};
};
}

View file

@ -15,6 +15,13 @@
middlewares = [];
};
roundcube = {
rule = "Host(`roundcube.procopius.dk`)";
service = "roundcube";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
forgejo = {
rule = "Host(`git.procopius.dk`)";
service = "forgejo";
@ -34,10 +41,4 @@
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
catchAll = {
rule = "HostRegexp(`.+`)";
service = "nginx";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
}

View file

@ -2,12 +2,11 @@
traefik.loadBalancer.servers = [{url = "http://localhost:8080";}];
mail-acme.loadBalancer.servers = [{url = "http://mail.lab:80";}];
roundcube.loadBalancer.servers = [{url = "http://mail.lab:80";}];
forgejo.loadBalancer.servers = [{url = "http://forgejo.lab:3000";}];
proxmox.loadBalancer.servers = [{url = "https://192.168.1.205:8006";}];
proxmox.loadBalancer.serversTransport = "insecureTransport";
nas.loadBalancer.servers = [{url = "https://192.168.1.226:5001";}];
nas.loadBalancer.serversTransport = "insecureTransport";
nginx.loadBalancer.servers = [{url = "https://192.168.1.226:4433";}];
nginx.loadBalancer.serversTransport = "insecureTransport";
}

View file

@ -32,4 +32,52 @@
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-minio = {
rule = "Host(`ente-minio.procopius.dk`)";
service = "ente-minio";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-minio-api = {
rule = "Host(`ente-minio-api.procopius.dk`)";
service = "ente-minio-api";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-museum = {
rule = "Host(`ente-museum.procopius.dk`)";
service = "ente-museum";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-photos = {
rule = "Host(`ente-photos.procopius.dk`) || Host(`ente-albums.procopius.dk`)";
service = "ente-photos";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-cast = {
rule = "Host(`ente-cast.procopius.dk`) ";
service = "ente-cast";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-accounts = {
rule = "Host(`ente-accounts.procopius.dk`) ";
service = "ente-accounts";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
ente-auth = {
rule = "Host(`ente-auth.procopius.dk`) ";
service = "ente-auth";
entryPoints = ["websecure"];
tls.certResolver = "letsencrypt";
};
}

View file

@ -4,4 +4,12 @@
account.loadBalancer.servers = [{url = "http://192.168.1.226:3001";}];
minio.loadBalancer.servers = [{url = "http://192.168.1.226:3201";}];
minio-api.loadBalancer.servers = [{url = "http://192.168.1.226:3200";}];
ente-minio.loadBalancer.servers = [{url = "http://photos.lab:9001";}];
ente-minio-api.loadBalancer.servers = [{url = "http://photos.lab:9000";}];
ente-museum.loadBalancer.servers = [{url = "http://photos.lab:8080";}];
ente-photos.loadBalancer.servers = [{url = "http://photos.lab:3000";}];
ente-accounts.loadBalancer.servers = [{url = "http://photos.lab:3001";}];
ente-cast.loadBalancer.servers = [{url = "http://photos.lab:3004";}];
ente-auth.loadBalancer.servers = [{url = "http://photos.lab:3003";}];
}

View file

@ -1,127 +0,0 @@
Great question — and you're absolutely right to ask.
You **dont need** Innernet or Headscale+patch *if* you're comfortable building a small self-hosted portal that handles:
* ✅ OIDC login
* ✅ WireGuard peer key management
* ✅ Config generation and download
So lets break it down:
---
## 🧩 Why *Innernet* and *Headscale+patch* Exist
Those tools solve **user/device coordination**, dynamic routing, and access control *automatically*, **with batteries included**. They're great if you want a more managed, “plug-and-play” experience. Specifically:
| Tool | Purpose |
| ------------------------ | ----------------------------------------------------------------- |
| **Innernet** | Full WireGuard-based mesh + OIDC login + per-device management |
| **Headscale** | Tailscale backend, enables user login + ACL + device coordination |
| **Headscale+OIDC patch** | Add OIDC login to Headscale (by default, only pre-shared keys) |
These tools do things like:
* Assign IPs/DNS names to users
* Regenerate keys
* Handle coordination between peers
* Gate access with identity
---
## 🛠️ Why You *Dont* Need Them (Your Custom Portal Idea)
If you're fine with:
* Managing WireGuard server and IPs manually
* Writing a small web service to issue `.conf` files after login
* Using firewall rules for access control
Then you absolutely **can** build a custom system around **WireGuard + OIDC** like this:
---
### 🧱 Architecture: WireGuard + OIDC + Config Generator
```
┌──────────────┐
│ Your OIDC │
│ provider │◄────── Google, Authentik, Keycloak
└─────┬────────┘
(OIDC Login)
┌────────▼────────┐
│ Web Portal │◄────── nginx + Go, Node.js, Flask, etc.
│ (self-hosted) │
└──┬────────────┬─┘
Gen Keys │ │ Serve config
& register │ └─────> `client.conf` / QR code
┌──────────────────────┐
│ WireGuard server │ (NixOS)
│ - Handles all peers │
└──────────────────────┘
```
---
### 🔐 What Your Portal Needs to Do
* Authenticate via OIDC (OAuth2 flow)
* Store a list of "authorized users"
* When a user logs in:
* Check if they already have a WireGuard peer entry
* If not: generate a keypair, assign IP (e.g. `10.100.0.5`)
* Append to server config (`/etc/wireguard/wg0.conf`) or use `wg set`
* Return config file or QR code for the mobile client
---
## ✅ Benefits of This DIY Approach
* 💡 Full control: you own every step
* 🔐 OIDC is the gatekeeper (centrally managed access)
* ⚡ No extra daemon/software overhead (just WireGuard + your app)
* 🧩 Easily extensible (e.g., device limits, IP lease expiration)
---
## ⚙️ Tools You Can Use
| Purpose | Tools |
| ---------------------- | -------------------------------------------------------------------------- |
| OIDC Auth | [Dex](https://github.com/dexidp/dex), Keycloak, Authentik, or Firebase |
| Web Portal Backend | Node.js (Express), Go, Flask, FastAPI |
| Config Template Engine | Go templates, EJS, Jinja2 |
| WireGuard Integration | `wg`, `wg-quick`, or directly edit `wg0.conf` |
| NixOS Support | Use `networking.wireguard.interfaces.wg0.peers` to declaratively add peers |
---
## 🧪 Bonus Features You Could Build
* 🔐 Per-user firewall rules
* 🔁 One-time config URLs (`vpn.mydomain.com/invite/xyz`)
* 🔐 Optional Authelia/Keycloak MFA
* 📉 Dashboard with currently active peers (use `wg show`)
---
## ✨ Summary
| Option | You Need Innernet / Headscale? | Login via OIDC? | DNS Routing? | Complexity |
| ----------------- | ------------------------------ | --------------- | ------------ | ---------- |
| Your own portal | ❌ No | ✅ Yes | ❌ Manual | 🟡 Medium |
| Innernet | ✅ Yes | ✅ Yes | ✅ Built-in | 🟠 Medium |
| Headscale + patch | ✅ Yes | ✅ Yes | ✅ Built-in | 🔴 High |
Youre on a great path by choosing the custom VPN portal idea.
Let me know if you'd like:
* A **sample architecture repo**
* A **NixOS module to support peer configs**
* Help building the **login + config generator backend**
I can generate a Nix flake and a working OIDC portal template to kickstart the project.

View file

@ -1,14 +0,0 @@
{
virtualisation.oci-containers.containers = {
warpgate = {
image = "ghcr.io/warp-tech/warpgate";
ports = [
"2222:2222"
"8888:8888"
];
volumes = [
"/srv/warpgate/data:/data"
];
};
};
}