Updated text
This commit is contained in:
parent
99996be1a3
commit
7ac09ff6da
312
README.md
312
README.md
|
|
@ -1,3 +1,313 @@
|
||||||
# VHostLoom
|
# VHostLoom
|
||||||
|
|
||||||
Software stack for virtual host serving based on Docker + Traefik + Authelia.
|
A Docker Hosting Framework: Traefik + Authelia + Modular Sites
|
||||||
|
|
||||||
|
This repository provides a reusable framework for running multiple
|
||||||
|
virtual-hosted web services on a single server using:
|
||||||
|
|
||||||
|
- [Traefik](https://traefik.io/) as a reverse proxy and TLS terminator
|
||||||
|
- [Authelia](https://www.authelia.com/) as an SSO / authentication gateway
|
||||||
|
- Docker Compose for container orchestration
|
||||||
|
- Modular per-site stacks with bind-mounted volumes
|
||||||
|
|
||||||
|
The design goals are:
|
||||||
|
|
||||||
|
> **Add or change sites without constantly editing core proxy config**
|
||||||
|
> and keep application data available natively on the host filesystem.
|
||||||
|
|
||||||
|
Under the view that hacking is inevitable, it seemed to me that making
|
||||||
|
it so a hacker has to hack each container separately makes the system
|
||||||
|
less brittle and less exposed to threats like ransomware. With modest
|
||||||
|
backup discipline, this system should permit porting or getting back
|
||||||
|
up expeditiously.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Core proxy stack** (`core-proxy/`) with Traefik and Authelia
|
||||||
|
- Let’s Encrypt HTTP-01 certificates
|
||||||
|
- Traefik dashboard at `https://traefik.example.com`, protected by Authelia
|
||||||
|
- Authelia at `https://auth.example.com` for login
|
||||||
|
|
||||||
|
- **Modular site stacks** under `sites/`:
|
||||||
|
- `static-site`: static file hosting at `https://example.com`
|
||||||
|
- `wordpress-site`: WordPress at `https://example.com/wp`
|
||||||
|
- `forgejo`: Forgejo (Git hosting) + CI runner at `https://git.example.com`
|
||||||
|
- `nextcloud`: Nextcloud cloud storage at `https://cloud.example.com`
|
||||||
|
|
||||||
|
- **Security model**
|
||||||
|
- Default-deny firewall template (`firewall/nftables.conf.example`)
|
||||||
|
- Only 80/443 exposed publicly (and optionally a few others)
|
||||||
|
- Authelia used to put an extra auth layer in front of sensitive apps
|
||||||
|
- Easy integration with ZeroTier or other VPNs for “VPN-only” services
|
||||||
|
|
||||||
|
- **Host-friendly data layout**
|
||||||
|
- Each site’s data lives in bind-mounted directories under `sites/`
|
||||||
|
- Easy to back up, rsync, or inspect without entering containers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Linux host (e.g., Ubuntu Server 22.04+)
|
||||||
|
- Docker and Docker Compose plugin installed
|
||||||
|
- A public IP address (static or effectively static)
|
||||||
|
- Control over DNS records for your domains
|
||||||
|
|
||||||
|
For HTTPS, you’ll need DNS `A` (and/or `AAAA`) records pointing to your server:
|
||||||
|
|
||||||
|
- `example.com` → server IP
|
||||||
|
- `auth.example.com` → server IP
|
||||||
|
- `traefik.example.com` → server IP
|
||||||
|
- `git.example.com` → server IP
|
||||||
|
- `cloud.example.com` → server IP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Clone the repo**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://example.com/your/hosting-framework.git
|
||||||
|
cd hosting-framework
|
||||||
|
````
|
||||||
|
|
||||||
|
2. **Create shared Docker network**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network create traefik_proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Prepare Traefik ACME storage**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd core-proxy
|
||||||
|
mkdir -p traefik/dynamic
|
||||||
|
touch traefik/acme.json
|
||||||
|
chmod 600 traefik/acme.json
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Edit core config**
|
||||||
|
|
||||||
|
* `core-proxy/docker-compose.yml`
|
||||||
|
|
||||||
|
* Change `admin@example.com` to a real email
|
||||||
|
* Adjust domains in Traefik labels for your use case
|
||||||
|
* `core-proxy/traefik/dynamic/authelia.yml`
|
||||||
|
|
||||||
|
* Set `auth.example.com` (or equivalent)
|
||||||
|
* `core-proxy/authelia/configuration.yml`
|
||||||
|
|
||||||
|
* Change `example.com`, secrets, etc.
|
||||||
|
* `core-proxy/authelia/users_database.yml`
|
||||||
|
|
||||||
|
* Generate a password hash:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm authelia/authelia:latest authelia hash-password 'yourpassword'
|
||||||
|
```
|
||||||
|
|
||||||
|
Paste the hash into `password:`.
|
||||||
|
|
||||||
|
5. **Start the core stack**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd core-proxy
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Configure DNS**
|
||||||
|
|
||||||
|
Create DNS records pointing your domains to the server’s public IP.
|
||||||
|
Let’s Encrypt will fail if DNS is wrong or not propagated.
|
||||||
|
|
||||||
|
7. **Bring up example sites**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Static site
|
||||||
|
cd ../sites/static-site
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# WordPress
|
||||||
|
cd ../wordpress-site
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Forgejo
|
||||||
|
cd ../forgejo
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Nextcloud
|
||||||
|
cd ../nextcloud
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
8. **Test**
|
||||||
|
|
||||||
|
* `https://traefik.<your-domain>` → Authelia login → Traefik dashboard
|
||||||
|
* `https://auth.<your-domain>` → Authelia portal
|
||||||
|
* `https://example.com` → static site
|
||||||
|
* `https://example.com/wp` → WordPress installer
|
||||||
|
* `https://git.<your-domain>` → Authelia login → Forgejo setup
|
||||||
|
* `https://cloud.<your-domain>` → Authelia login → Nextcloud setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rationale & Design
|
||||||
|
|
||||||
|
### Separation of concerns
|
||||||
|
|
||||||
|
* **Core proxy** (Traefik + Authelia) is stable and rarely changed.
|
||||||
|
* **Sites** live in separate directories with their own `docker-compose.yml`.
|
||||||
|
* **Firewall** is independent of Docker and enforces network boundaries.
|
||||||
|
|
||||||
|
This makes it easy to:
|
||||||
|
|
||||||
|
* Add new virtual hosts (just add a new `sites/<name>/docker-compose.yml`)
|
||||||
|
* Share the framework without embedding secrets
|
||||||
|
* Back up only what matters (`sites/**`, Authelia DB, maybe Traefik `acme.json`)
|
||||||
|
|
||||||
|
### Minimal coupling
|
||||||
|
|
||||||
|
The only shared assumptions between stacks:
|
||||||
|
|
||||||
|
* A Docker network named `traefik_proxy`
|
||||||
|
* Authelia’s forward-auth middleware named `authelia-auth@file`
|
||||||
|
* Traefik’s Let’s Encrypt resolver named `letsencrypt`
|
||||||
|
|
||||||
|
Everything else is per-site.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Details
|
||||||
|
|
||||||
|
### Traefik labels
|
||||||
|
|
||||||
|
Each service defines its routing behavior entirely via labels:
|
||||||
|
|
||||||
|
* Match host and path:
|
||||||
|
`traefik.http.routers.<name>.rule=Host(`example.com`) && PathPrefix(`/wp`)`
|
||||||
|
* Bind to entrypoint:
|
||||||
|
`...entrypoints=web` or `websecure`
|
||||||
|
* Enable HTTPS / certificates:
|
||||||
|
`...tls.certresolver=letsencrypt`
|
||||||
|
* HTTP→HTTPS redirect using a middleware:
|
||||||
|
|
||||||
|
* Define middleware:
|
||||||
|
`traefik.http.middlewares.foo-https-redirect.redirectscheme.scheme=https`
|
||||||
|
* Attach it to an HTTP router:
|
||||||
|
`traefik.http.routers.foo-http.middlewares=foo-https-redirect`
|
||||||
|
|
||||||
|
### Authelia protection
|
||||||
|
|
||||||
|
To require login via Authelia before an app:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.<name>-https.middlewares=authelia-auth@file"
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove or comment this label to make a site public.
|
||||||
|
|
||||||
|
### Authelia users
|
||||||
|
|
||||||
|
Defined in `core-proxy/authelia/users_database.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
users:
|
||||||
|
admin:
|
||||||
|
displayname: "Admin User"
|
||||||
|
email: "admin@example.com"
|
||||||
|
groups: [admins]
|
||||||
|
password: "<argon2id hash>"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use groups and more complex access_control rules if desired; the default config in this repo simply treats any authenticated user as allowed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security & Firewall
|
||||||
|
|
||||||
|
The `firewall/nftables.conf.example` file contains a default-deny firewall with:
|
||||||
|
|
||||||
|
* Loopback and established connections allowed
|
||||||
|
* ICMP allowed (optional but recommended)
|
||||||
|
* SSH allowed only over a VPN interface (e.g., ZeroTier)
|
||||||
|
* 80/443 open for Traefik
|
||||||
|
* Example ports restricted to the VPN interface
|
||||||
|
|
||||||
|
You can adapt it and enable with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp firewall/nftables.conf.example /etc/nftables.conf
|
||||||
|
sudo nft -f /etc/nftables.conf
|
||||||
|
sudo systemctl enable nftables
|
||||||
|
```
|
||||||
|
|
||||||
|
Always test SSH access before locking down too far.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Updating containers
|
||||||
|
|
||||||
|
For each stack:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd core-proxy
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
cd ../sites/<site-name>
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backups
|
||||||
|
|
||||||
|
At minimum, back up:
|
||||||
|
|
||||||
|
* `core-proxy/traefik/acme.json` (certificates)
|
||||||
|
* `core-proxy/authelia/` (configuration + DB)
|
||||||
|
* `sites/**/` data directories:
|
||||||
|
|
||||||
|
* `sites/static-site/html/`
|
||||||
|
* `sites/wordpress-site/db/` and `wp/`
|
||||||
|
* `sites/forgejo/data/`, `db/`, `runner/`
|
||||||
|
* `sites/nextcloud/nextcloud/`, `db/`, `redis/`
|
||||||
|
|
||||||
|
Use `rsync`, `borg`, `restic`, or your favorite solution.
|
||||||
|
|
||||||
|
### Logs
|
||||||
|
|
||||||
|
* Traefik logs: stdout (use `docker logs traefik` or a log collector)
|
||||||
|
* Authelia logs: stdout + `notification.log` (if configured)
|
||||||
|
* Site logs: as exposed via each container
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extending the Framework
|
||||||
|
|
||||||
|
To add a new site:
|
||||||
|
|
||||||
|
1. Create `sites/<new-site>/docker-compose.yml`.
|
||||||
|
2. Attach it to `traefik_proxy`.
|
||||||
|
3. Add Traefik labels for hostnames, HTTPS, redirect, and optionally Authelia.
|
||||||
|
4. Bind volumes for data so they live on the host.
|
||||||
|
5. `docker compose up -d` in that directory.
|
||||||
|
6. Add DNS records for the new hostname(s).
|
||||||
|
|
||||||
|
You never need to edit the core proxy configuration for new sites.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This software is licensed under the MIT license.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
Beyond the MIT license statement, this software was produced by
|
||||||
|
iterative prompting of OpenAI's GPT 5.1 LLM.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue