Updated text
This commit is contained in:
parent
99996be1a3
commit
7ac09ff6da
312
README.md
312
README.md
|
|
@ -1,3 +1,313 @@
|
|||
# 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