Remove deprecated deploy workflow, update README with new features and service URLs, delete unused alertmanager and prometheus configurations, refactor Authelia configuration for improved security and functionality, add Gitea and Beszel services, and enhance health checks across services.
This commit is contained in:
43
.env.example
43
.env.example
@@ -1,20 +1,13 @@
|
|||||||
# .env.example
|
|
||||||
# Copy this file to .env and update the values below
|
|
||||||
|
|
||||||
## Domain / Timezone
|
## Domain / Timezone
|
||||||
DOMAIN=your-domain.com
|
DOMAIN=your-domain.com
|
||||||
TZ=Your/Timezone
|
DOMAIN_PREFIX=gate
|
||||||
|
|
||||||
|
TZ=Africa/Cairo
|
||||||
|
|
||||||
## ACME (Let's Encrypt)
|
## ACME (Let's Encrypt)
|
||||||
# Your email address for Let's Encrypt certificate notifications
|
# Your email address for Let's Encrypt certificate notifications
|
||||||
ACME_EMAIL=admin@your-domain.com
|
ACME_EMAIL=admin@your-domain.com
|
||||||
|
|
||||||
## Basic Authentication
|
|
||||||
# Generate password hash with: echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
|
|
||||||
# Or use online generator: https://hostingcanada.org/htpasswd-generator/
|
|
||||||
# Format: username:$$2y$$10$$hashed_password
|
|
||||||
BASIC_AUTH_USERS=admin:$$2y$$05$$HIIpotyLuLRMBX3sfUs6E.YM3lP9cpF7hK7sHaSXGs6mw/RS6MXCa
|
|
||||||
|
|
||||||
## Umami (PostgreSQL)
|
## Umami (PostgreSQL)
|
||||||
# Database user for Umami analytics
|
# Database user for Umami analytics
|
||||||
UMAMI_DB_USER=umami
|
UMAMI_DB_USER=umami
|
||||||
@@ -24,8 +17,6 @@ UMAMI_DB_PASS=your_strong_database_password
|
|||||||
UMAMI_DB_NAME=umami
|
UMAMI_DB_NAME=umami
|
||||||
# Random 64-character secret for Umami app security
|
# Random 64-character secret for Umami app security
|
||||||
UMAMI_APP_SECRET=your_64_character_random_secret_here
|
UMAMI_APP_SECRET=your_64_character_random_secret_here
|
||||||
# Website ID from Umami dashboard (use placeholder initially, update after setup)
|
|
||||||
UMAMI_WEBSITE_ID=placeholder-website-id
|
|
||||||
|
|
||||||
## pgAdmin
|
## pgAdmin
|
||||||
# Default email for pgAdmin login
|
# Default email for pgAdmin login
|
||||||
@@ -33,8 +24,26 @@ PGADMIN_DEFAULT_EMAIL=admin@your-domain.com
|
|||||||
# Strong password for pgAdmin login
|
# Strong password for pgAdmin login
|
||||||
PGADMIN_DEFAULT_PASSWORD=your_strong_pgadmin_password
|
PGADMIN_DEFAULT_PASSWORD=your_strong_pgadmin_password
|
||||||
|
|
||||||
## Grafana
|
## Authelia
|
||||||
# Admin username for Grafana dashboard
|
# Database password for Authelia PostgreSQL
|
||||||
GRAFANA_ADMIN_USER=admin
|
AUTHELIA_DB_PASSWORD=your_authelia_db_password
|
||||||
# Strong password for Grafana admin user
|
# JWT secret for Authelia (64+ character random string)
|
||||||
GRAFANA_ADMIN_PASS=your_strong_grafana_password
|
AUTHELIA_JWT_SECRET=your_64_character_jwt_secret_here
|
||||||
|
# Session secret for Authelia (64+ character random string)
|
||||||
|
AUTHELIA_SESSION_SECRET=your_64_character_session_secret_here
|
||||||
|
# Storage encryption key for Authelia (20+ character string)
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=your_storage_encryption_key_here
|
||||||
|
# SMTP password for Authelia notifier
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD=your_smtp_password_here
|
||||||
|
|
||||||
|
## Gitea
|
||||||
|
# Database user for Gitea
|
||||||
|
GITEA_DB_USER=gitea
|
||||||
|
# Database password for Gitea PostgreSQL
|
||||||
|
GITEA_DB_PASSWORD=your_gitea_db_password_here
|
||||||
|
# Database name for Gitea
|
||||||
|
GITEA_DB_NAME=gitea
|
||||||
|
# Secret key for Gitea (64+ character random string - generate with: openssl rand -hex 32)
|
||||||
|
GITEA_SECRET_KEY=your_64_character_gitea_secret_key_here
|
||||||
|
# Internal token for Gitea (generate with: docker run -it --rm docker.gitea.com/gitea:1 gitea generate secret INTERNAL_TOKEN)
|
||||||
|
GITEA_INTERNAL_TOKEN=your_gitea_internal_token_here
|
||||||
|
|||||||
60
.github/workflows/deploy.yml
vendored
60
.github/workflows/deploy.yml
vendored
@@ -1,60 +0,0 @@
|
|||||||
# .github/workflows/deploy.yml
|
|
||||||
name: Deploy Gateway to VPS
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "disbaled" ]
|
|
||||||
paths:
|
|
||||||
- "docker-compose.yml"
|
|
||||||
- ".github/workflows/deploy.yml"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REMOTE_DIR: ${{ secrets.REMOTE_DIR }}
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: deploy-prod
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: Ship to VPS
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: production
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup SSH key
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
|
||||||
chmod 600 ~/.ssh/id_rsa
|
|
||||||
ssh-keyscan -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_HOST }}" >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry on VPS
|
|
||||||
run: |
|
|
||||||
ssh -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" \
|
|
||||||
"echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin"
|
|
||||||
|
|
||||||
- name: Deploy (pull, up, prune)
|
|
||||||
run: |
|
|
||||||
ssh -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" \
|
|
||||||
"export REMOTE_DIR='${{ secrets.REMOTE_DIR }}'; bash -se" <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
cd "$REMOTE_DIR"
|
|
||||||
|
|
||||||
git pull origin main
|
|
||||||
docker compose pull
|
|
||||||
docker compose up -d --remove-orphans
|
|
||||||
docker image prune -af || true
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Post-deploy smoke checks
|
|
||||||
run: |
|
|
||||||
echo "Deployed to ${{ secrets.SSH_HOST }}:${{ secrets.SSH_PORT }} → ${REMOTE_DIR}"
|
|
||||||
echo "Traefik: https://traefik.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Portainer: https://portainer.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Uptime Kuma: https://uptime.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Umami: https://umami.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "pgAdmin: https://pgadmin.gate.${{ secrets.DOMAIN }}"
|
|
||||||
61
README.md
61
README.md
@@ -1,28 +1,35 @@
|
|||||||
# Launchpad Gateway
|
# Launchpad Gateway
|
||||||
|
|
||||||
A production-ready Traefik-based reverse proxy gateway with automatic SSL/TLS, analytics, monitoring, and container management.
|
A production-ready Traefik-based reverse proxy gateway with automatic SSL/TLS, analytics, monitoring, authentication, and container management.
|
||||||
|
|
||||||
## 🚀 Features
|
## 🚀 Features
|
||||||
|
|
||||||
- **Automatic SSL/TLS** certificates via Let's Encrypt
|
- **Automatic SSL/TLS** certificates via Let's Encrypt
|
||||||
- **Reverse Proxy** with Traefik v3.1
|
- **Reverse Proxy** with Traefik v3.1
|
||||||
|
- **Authentication** with Authelia (2FA, password reset)
|
||||||
- **Web Analytics** with Umami and PostgreSQL
|
- **Web Analytics** with Umami and PostgreSQL
|
||||||
- **Container Management** via Portainer
|
- **Container Management** via Portainer
|
||||||
- **Uptime Monitoring** with Uptime Kuma
|
- **Uptime Monitoring** with Uptime Kuma
|
||||||
- **Security Headers** and Basic Authentication
|
- **Server Monitoring** with Beszel
|
||||||
|
- **Git Repository Hosting** with Gitea
|
||||||
|
- **Security Headers** and Flexible Routing
|
||||||
- **Flexible Domain Routing** (subdomains, paths, custom rules)
|
- **Flexible Domain Routing** (subdomains, paths, custom rules)
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
Internet → Traefik (Port 80/443) → Internal Services (traefik_proxy network)
|
Internet → Traefik (Port 80/443) → Authelia (Auth) → Internal Services (traefik_proxy network)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Current Services
|
### Current Services
|
||||||
- **Traefik Dashboard**: `traefik.gate.${DOMAIN}` - Reverse proxy management
|
- **Traefik Dashboard**: `traefik.${DOMAIN_PREFIX}.${DOMAIN}` - Reverse proxy management
|
||||||
- **Portainer**: `portainer.gate.${DOMAIN}` - Docker container management
|
- **Authelia**: `auth.${DOMAIN_PREFIX}.${DOMAIN}` - Authentication portal
|
||||||
- **Uptime Kuma**: `uptime.gate.${DOMAIN}` - Service monitoring
|
- **Portainer**: `portainer.${DOMAIN_PREFIX}.${DOMAIN}` - Docker container management
|
||||||
- **Umami Analytics**: `umami.gate.${DOMAIN}` - Web analytics dashboard
|
- **Uptime Kuma**: `uptime.${DOMAIN_PREFIX}.${DOMAIN}` - Service monitoring
|
||||||
|
- **Umami Analytics**: `umami.${DOMAIN_PREFIX}.${DOMAIN}` - Web analytics dashboard
|
||||||
|
- **pgAdmin**: `pgadmin.${DOMAIN_PREFIX}.${DOMAIN}` - PostgreSQL administration
|
||||||
|
- **Beszel**: `beszel.${DOMAIN_PREFIX}.${DOMAIN}` - Server monitoring
|
||||||
|
- **Gitea**: `git.${DOMAIN_PREFIX}.${DOMAIN}` - Self-hosted Git service (SSH on port 222)
|
||||||
|
|
||||||
## 🛠️ Quick Start
|
## 🛠️ Quick Start
|
||||||
|
|
||||||
@@ -49,19 +56,31 @@ Update `.env` with your settings:
|
|||||||
```bash
|
```bash
|
||||||
# Domain and timezone
|
# Domain and timezone
|
||||||
DOMAIN=your-domain.com
|
DOMAIN=your-domain.com
|
||||||
TZ=Your/Timezone
|
DOMAIN_PREFIX=test
|
||||||
|
TZ=Africa/Cairo
|
||||||
|
|
||||||
# Let's Encrypt email
|
# Let's Encrypt email
|
||||||
ACME_EMAIL=admin@your-domain.com
|
ACME_EMAIL=admin@your-domain.com
|
||||||
|
|
||||||
# Basic auth (generate with: htpasswd -nB admin)
|
|
||||||
BASIC_AUTH_USERS=admin:$$2y$$05$$your_hashed_password
|
|
||||||
|
|
||||||
# Database credentials
|
# Database credentials
|
||||||
UMAMI_DB_USER=umami
|
UMAMI_DB_USER=umami
|
||||||
UMAMI_DB_PASS=your_secure_password
|
UMAMI_DB_PASS=your_secure_password
|
||||||
UMAMI_DB_NAME=umami
|
UMAMI_DB_NAME=umami
|
||||||
UMAMI_APP_SECRET=your_64_character_secret
|
UMAMI_APP_SECRET=your_64_character_secret
|
||||||
|
|
||||||
|
# Authelia secrets (generate with openssl rand -hex 32)
|
||||||
|
AUTHELIA_DB_PASSWORD=your_secure_password
|
||||||
|
AUTHELIA_JWT_SECRET=your_64_char_secret
|
||||||
|
AUTHELIA_SESSION_SECRET=your_64_char_secret
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=your_64_char_secret
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD=your_smtp_password
|
||||||
|
|
||||||
|
# Gitea secrets
|
||||||
|
GITEA_DB_USER=gitea
|
||||||
|
GITEA_DB_PASSWORD=your_secure_password
|
||||||
|
GITEA_DB_NAME=gitea
|
||||||
|
GITEA_SECRET_KEY=your_64_char_secret
|
||||||
|
GITEA_INTERNAL_TOKEN=your_internal_token
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -71,10 +90,14 @@ docker compose up -d
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 4. Access Services
|
### 4. Access Services
|
||||||
- **Traefik Dashboard**: `https://traefik.gate.your-domain.com`
|
- **Traefik Dashboard**: `https://traefik.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Portainer**: `https://portainer.gate.your-domain.com`
|
- **Authelia**: `https://auth.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Uptime Kuma**: `https://uptime.gate.your-domain.com`
|
- **Portainer**: `https://portainer.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Umami Analytics**: `https://umami.gate.your-domain.com`
|
- **Uptime Kuma**: `https://uptime.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Umami Analytics**: `https://umami.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **pgAdmin**: `https://pgadmin.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Beszel**: `https://beszel.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Gitea**: `https://git.${DOMAIN_PREFIX}.${DOMAIN}` (SSH: port 222)
|
||||||
|
|
||||||
## 📋 Adding New Services
|
## 📋 Adding New Services
|
||||||
|
|
||||||
@@ -231,16 +254,15 @@ services:
|
|||||||
|
|
||||||
### Available Middlewares
|
### Available Middlewares
|
||||||
- **`security-headers`**: HSTS, XSS protection, content type sniffing prevention
|
- **`security-headers`**: HSTS, XSS protection, content type sniffing prevention
|
||||||
- **`basic-auth`**: HTTP Basic Authentication for admin interfaces
|
- **`authelia`**: Authentication and authorization with 2FA support
|
||||||
- **`umami-analytics`**: Automatic web analytics tracking
|
|
||||||
|
|
||||||
### Usage Examples
|
### Usage Examples
|
||||||
```yaml
|
```yaml
|
||||||
# Public application with analytics
|
# Public application with analytics
|
||||||
- traefik.http.routers.app.middlewares=umami-analytics,security-headers
|
- traefik.http.routers.app.middlewares=security-headers
|
||||||
|
|
||||||
# Admin interface with authentication
|
# Admin interface with authentication
|
||||||
- traefik.http.routers.admin.middlewares=basic-auth,security-headers
|
- traefik.http.routers.admin.middlewares=authelia@docker,security-headers
|
||||||
|
|
||||||
# API endpoint (security headers only)
|
# API endpoint (security headers only)
|
||||||
- traefik.http.routers.api.middlewares=security-headers
|
- traefik.http.routers.api.middlewares=security-headers
|
||||||
@@ -274,6 +296,7 @@ services:
|
|||||||
|
|
||||||
### Built-in Monitoring
|
### Built-in Monitoring
|
||||||
- **Uptime Kuma**: Service availability monitoring
|
- **Uptime Kuma**: Service availability monitoring
|
||||||
|
- **Beszel**: Server resource monitoring (CPU, RAM, disk, network)
|
||||||
- **Traefik Dashboard**: Traffic and routing metrics
|
- **Traefik Dashboard**: Traffic and routing metrics
|
||||||
- **Umami Analytics**: Web traffic analytics (privacy-focused)
|
- **Umami Analytics**: Web traffic analytics (privacy-focused)
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
global:
|
|
||||||
smtp_smarthost: 'smtp.example.com:587'
|
|
||||||
smtp_from: 'alertmanager@example.com'
|
|
||||||
smtp_auth_username: 'alertmanager'
|
|
||||||
smtp_auth_password: 'dummy_password'
|
|
||||||
|
|
||||||
route:
|
|
||||||
group_by: ['alertname']
|
|
||||||
group_wait: 10s
|
|
||||||
group_interval: 10s
|
|
||||||
repeat_interval: 1h
|
|
||||||
receiver: 'email'
|
|
||||||
|
|
||||||
receivers:
|
|
||||||
- name: 'email'
|
|
||||||
email_configs:
|
|
||||||
- to: 'admin@example.com'
|
|
||||||
60
authelia/configuration.template.yml
Normal file
60
authelia/configuration.template.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
# Authelia configuration
|
||||||
|
# This is a minimal configuration for getting started with Authelia
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: 'tcp://:9091'
|
||||||
|
endpoints:
|
||||||
|
authz:
|
||||||
|
forward-auth:
|
||||||
|
implementation: 'ForwardAuth'
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: '/config/users_database.yml'
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: 'one_factor'
|
||||||
|
rules:
|
||||||
|
- domain: 'pgadmin.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'beszel.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'traefik.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: '*.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'one_factor'
|
||||||
|
|
||||||
|
session:
|
||||||
|
name: 'authelia_session'
|
||||||
|
cookies:
|
||||||
|
- domain: '${DOMAIN}'
|
||||||
|
authelia_url: 'https://auth.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
default_redirection_url: 'https://portainer.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
|
||||||
|
storage:
|
||||||
|
postgres:
|
||||||
|
address: 'tcp://authelia-db:5432'
|
||||||
|
database: 'authelia'
|
||||||
|
username: 'authelia'
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
disable_startup_check: true
|
||||||
|
# Configure SMTP for production email notifications
|
||||||
|
# For testing, you can use filesystem notifier instead:
|
||||||
|
# filesystem:
|
||||||
|
# filename: /config/notification.txt
|
||||||
|
smtp:
|
||||||
|
address: 'submissions://smtp.gmail.com:465'
|
||||||
|
username: 'your-email@gmail.com'
|
||||||
|
sender: 'Authelia <noreply@${DOMAIN}>'
|
||||||
|
# For Gmail, use an App Password (not your regular password)
|
||||||
|
# Generate at: https://myaccount.google.com/apppasswords
|
||||||
|
|
||||||
|
identity_validation:
|
||||||
|
reset_password: {}
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 120
|
||||||
|
ban_time: 300
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
# This is a minimal configuration for getting started with Authelia
|
# This is a minimal configuration for getting started with Authelia
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: 9091
|
address: 'tcp://:9091'
|
||||||
endpoints:
|
endpoints:
|
||||||
authz:
|
authz:
|
||||||
forward-auth:
|
forward-auth:
|
||||||
@@ -14,45 +14,37 @@ authentication_backend:
|
|||||||
path: '/config/users_database.yml'
|
path: '/config/users_database.yml'
|
||||||
|
|
||||||
access_control:
|
access_control:
|
||||||
default_policy: 'deny'
|
default_policy: 'one_factor'
|
||||||
rules:
|
rules:
|
||||||
- domain: 'pgadmin.gate.${DOMAIN}'
|
- domain: 'pgadmin.test.3launchpad.com'
|
||||||
policy: 'two_factor'
|
policy: 'two_factor'
|
||||||
- domain: '*.gate.${DOMAIN}'
|
- domain: '*.test.3launchpad.com'
|
||||||
policy: 'one_factor'
|
policy: 'one_factor'
|
||||||
|
|
||||||
session:
|
session:
|
||||||
name: 'authelia_session'
|
name: 'authelia_session'
|
||||||
secret: '${AUTHELIA_SESSION_SECRET}'
|
|
||||||
cookies:
|
cookies:
|
||||||
- domain: 'gate.${DOMAIN}'
|
- domain: '3launchpad.com'
|
||||||
authelia_url: 'https://auth.gate.${DOMAIN}'
|
authelia_url: 'https://auth.test.3launchpad.com'
|
||||||
default_redirection_url: 'https://login.gate.${DOMAIN}'
|
default_redirection_url: 'https://portainer.test.3launchpad.com'
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
postgres:
|
postgres:
|
||||||
host: 'authelia-db'
|
address: 'tcp://authelia-db:5432'
|
||||||
port: 5432
|
|
||||||
database: 'authelia'
|
database: 'authelia'
|
||||||
username: 'authelia'
|
username: 'authelia'
|
||||||
password: '${AUTHELIA_DB_PASSWORD}'
|
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
|
disable_startup_check: true
|
||||||
smtp:
|
smtp:
|
||||||
|
address: 'smtp://localhost:25'
|
||||||
username: 'authelia'
|
username: 'authelia'
|
||||||
password: 'dummy_password'
|
|
||||||
host: 'smtp.example.com'
|
|
||||||
port: 587
|
|
||||||
sender: 'authelia@example.com'
|
sender: 'authelia@example.com'
|
||||||
|
|
||||||
jwt_secret: '${AUTHELIA_JWT_SECRET}'
|
identity_validation:
|
||||||
|
reset_password: {}
|
||||||
|
|
||||||
api:
|
regulation:
|
||||||
endpoints:
|
|
||||||
reset_password:
|
|
||||||
disable: false
|
|
||||||
|
|
||||||
regulations:
|
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
find_time: 120
|
find_time: 120
|
||||||
ban_time: 300
|
ban_time: 300
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
---
|
---
|
||||||
# Authelia users database
|
# Authelia users database
|
||||||
# This is a simple file-based user database for testing
|
# This is a simple file-based user database
|
||||||
|
#
|
||||||
|
# To generate a new password hash, run:
|
||||||
|
# docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'YOUR_PASSWORD'
|
||||||
|
#
|
||||||
|
# IMPORTANT: Change the default password before production use!
|
||||||
|
|
||||||
users:
|
users:
|
||||||
admin:
|
admin:
|
||||||
displayname: "Administrator"
|
displayname: "Administrator"
|
||||||
password: "$argon2id$v=19$m=65536,t=3,p=4$abcdefghijklmnopqrstuvwx$abcdefghijklmnopqrstuvwxabcdefghijklmnopqrstuvwx" # Dummy hash for 'password'
|
# Default password: "Admin@123456" - CHANGE THIS!
|
||||||
email: admin@example.com
|
password: "$argon2id$v=19$m=65536,t=3,p=4$O1Qjq7AB4/xJ7Qk1dUqp/g$PhVqFWEqyQTJeSnCeiCC3lrcWcpw37kYttw4Xh/qUsk"
|
||||||
groups: []
|
email: admin@3launchpad.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
1
beszel_agent_data/fingerprint
Normal file
1
beszel_agent_data/fingerprint
Normal file
@@ -0,0 +1 @@
|
|||||||
|
c17e1b1086d55cc7d4bfacbfa6b9472ca975863413fb629f
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
########################
|
|
||||||
# Networks & Volumes
|
|
||||||
########################
|
|
||||||
networks:
|
|
||||||
traefik_proxy:
|
|
||||||
name: traefik_proxy
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
traefik_letsencrypt:
|
|
||||||
traefik_logs:
|
|
||||||
portainer_data:
|
|
||||||
uptime_kuma_data:
|
|
||||||
umami_data:
|
|
||||||
pgadmin_data:
|
|
||||||
authelia_config:
|
|
||||||
authelia_db_data:
|
|
||||||
grafana_data:
|
|
||||||
prometheus_data:
|
|
||||||
alertmanager_data:
|
|
||||||
|
|
||||||
########################
|
|
||||||
# Services
|
|
||||||
########################
|
|
||||||
services:
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Traefik — edge router + ACME (HTTP-01)
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
traefik:
|
|
||||||
image: traefik:latest
|
|
||||||
container_name: traefik
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
# Mail protocol ports for MailCow integration
|
|
||||||
- "25:25" # SMTP
|
|
||||||
- "465:465" # SMTPS
|
|
||||||
- "587:587" # Submission
|
|
||||||
- "143:143" # IMAP
|
|
||||||
- "993:993" # IMAPS
|
|
||||||
- "110:110" # POP3
|
|
||||||
- "995:995" # POP3S
|
|
||||||
- "4190:4190" # ManageSieve
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
TZ: "${TZ}"
|
|
||||||
command:
|
|
||||||
# Experimental plugins
|
|
||||||
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
|
||||||
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
|
||||||
|
|
||||||
# Providers
|
|
||||||
- --providers.docker=true
|
|
||||||
- --providers.docker.exposedbydefault=false
|
|
||||||
|
|
||||||
# Entrypoints
|
|
||||||
- --entrypoints.web.address=:80
|
|
||||||
- --entrypoints.web.http.redirections.entrypoint.to=websecure
|
|
||||||
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
||||||
- --entrypoints.websecure.address=:443
|
|
||||||
- --entrypoints.web.forwardedheaders.insecure=true
|
|
||||||
- --entrypoints.websecure.forwardedheaders.insecure=true
|
|
||||||
|
|
||||||
# Mail protocol entrypoints for MailCow integration
|
|
||||||
- --entrypoints.smtp.address=:25
|
|
||||||
- --entrypoints.smtps.address=:465
|
|
||||||
- --entrypoints.submission.address=:587
|
|
||||||
- --entrypoints.imap.address=:143
|
|
||||||
- --entrypoints.imaps.address=:993
|
|
||||||
- --entrypoints.pop3.address=:110
|
|
||||||
- --entrypoints.pop3s.address=:995
|
|
||||||
- --entrypoints.sieve.address=:4190
|
|
||||||
|
|
||||||
# Dashboard/API (internal)
|
|
||||||
- --api=true
|
|
||||||
- --api.dashboard=true
|
|
||||||
|
|
||||||
# ACME via HTTP-01 (no registrar API needed)
|
|
||||||
- --certificatesresolvers.le.acme.email=${ACME_EMAIL}
|
|
||||||
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
|
|
||||||
- --certificatesresolvers.le.acme.httpchallenge=true
|
|
||||||
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
|
|
||||||
# (Alt) Use TLS-ALPN-01 if port 80 is blocked:
|
|
||||||
# - --certificatesresolvers.le.acme.tlschallenge=true
|
|
||||||
|
|
||||||
# Global timeouts for slow backends
|
|
||||||
- --serversTransport.forwardingTimeouts.dialTimeout=30s
|
|
||||||
- --serversTransport.forwardingTimeouts.responseHeaderTimeout=60s
|
|
||||||
- --serversTransport.forwardingTimeouts.idleConnTimeout=180s
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
- --accesslog.filepath=/var/log/traefik/access.log
|
|
||||||
- --accesslog.bufferingsize=100
|
|
||||||
- --log.level=INFO
|
|
||||||
- --metrics.prometheus=true
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- traefik_letsencrypt:/letsencrypt
|
|
||||||
- traefik_logs:/var/log/traefik
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
|
|
||||||
# Reusable security headers
|
|
||||||
- traefik.http.middlewares.security-headers.headers.stsSeconds=31536000
|
|
||||||
- traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true
|
|
||||||
- traefik.http.middlewares.security-headers.headers.stsPreload=true
|
|
||||||
- traefik.http.middlewares.security-headers.headers.browserXssFilter=true
|
|
||||||
- traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true
|
|
||||||
- traefik.http.middlewares.security-headers.headers.referrerPolicy=no-referrer-when-downgrade
|
|
||||||
|
|
||||||
# # Basic Auth middleware
|
|
||||||
# - traefik.http.middlewares.basic-auth.basicauth.users=${BASIC_AUTH_USERS}
|
|
||||||
|
|
||||||
# Authelia middleware
|
|
||||||
- traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/authz/forward-auth
|
|
||||||
- traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true
|
|
||||||
- traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name
|
|
||||||
|
|
||||||
# Traefik dashboard (protected)
|
|
||||||
- traefik.http.routers.traefik.rule=Host(`traefik.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.traefik.entrypoints=websecure
|
|
||||||
- traefik.http.routers.traefik.tls.certresolver=le
|
|
||||||
- traefik.http.routers.traefik.service=api@internal
|
|
||||||
- traefik.http.routers.traefik.middlewares=authelia@docker,security-headers
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Authelia — authentication and authorization
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
authelia:
|
|
||||||
image: authelia/authelia:latest
|
|
||||||
container_name: authelia
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
volumes:
|
|
||||||
- authelia_config:/config
|
|
||||||
environment:
|
|
||||||
TZ: "${TZ}"
|
|
||||||
AUTHELIA_DB_PASSWORD: "${AUTHELIA_DB_PASSWORD}"
|
|
||||||
AUTHELIA_JWT_SECRET: "${AUTHELIA_JWT_SECRET}"
|
|
||||||
AUTHELIA_SESSION_SECRET: "${AUTHELIA_SESSION_SECRET}"
|
|
||||||
depends_on:
|
|
||||||
- authelia-db
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.authelia.rule=Host(`auth.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.authelia.entrypoints=websecure
|
|
||||||
- traefik.http.routers.authelia.tls.certresolver=le
|
|
||||||
- traefik.http.services.authelia.loadbalancer.server.port=9091
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Authelia Database — PostgreSQL
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
authelia-db:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: authelia-db
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: authelia
|
|
||||||
POSTGRES_USER: authelia
|
|
||||||
POSTGRES_PASSWORD: ${AUTHELIA_DB_PASSWORD}
|
|
||||||
volumes:
|
|
||||||
- authelia_db_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Portainer — Docker control plane
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
portainer:
|
|
||||||
image: portainer/portainer-ce:latest
|
|
||||||
container_name: portainer
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- portainer_data:/data
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.portainer.rule=Host(`portainer.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.portainer.entrypoints=websecure
|
|
||||||
- traefik.http.routers.portainer.tls.certresolver=le
|
|
||||||
- traefik.http.routers.portainer.middlewares=security-headers
|
|
||||||
- traefik.http.services.portainer.loadbalancer.server.port=9000
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Uptime Kuma — status page / checks
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
uptime-kuma:
|
|
||||||
image: louislam/uptime-kuma:latest
|
|
||||||
container_name: uptime-kuma
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- uptime_kuma_data:/app/data
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.kuma.rule=Host(`uptime.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.kuma.entrypoints=websecure
|
|
||||||
- traefik.http.routers.kuma.tls.certresolver=le
|
|
||||||
- traefik.http.routers.kuma.middlewares=security-headers
|
|
||||||
- traefik.http.services.kuma.loadbalancer.server.port=3001
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Umami — web analytics
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
umami:
|
|
||||||
image: ghcr.io/umami-software/umami:postgresql-latest
|
|
||||||
container_name: umami
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
DATABASE_URL: postgresql://${UMAMI_DB_USER}:${UMAMI_DB_PASS}@umami-db:5432/${UMAMI_DB_NAME}
|
|
||||||
DATABASE_TYPE: postgresql
|
|
||||||
APP_SECRET: ${UMAMI_APP_SECRET}
|
|
||||||
depends_on:
|
|
||||||
- umami-db
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.umami.rule=Host(`umami.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.umami.entrypoints=websecure
|
|
||||||
- traefik.http.routers.umami.tls.certresolver=le
|
|
||||||
- traefik.http.routers.umami.middlewares=security-headers
|
|
||||||
- traefik.http.services.umami.loadbalancer.server.port=3000
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Umami Database — PostgreSQL
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
umami-db:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: umami-db
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ${UMAMI_DB_NAME}
|
|
||||||
POSTGRES_USER: ${UMAMI_DB_USER}
|
|
||||||
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
|
||||||
volumes:
|
|
||||||
- umami_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
|
||||||
# pgAdmin — PostgreSQL administration
|
|
||||||
# ─────────────────────────────────────────────
|
|
||||||
pgadmin:
|
|
||||||
image: dpage/pgadmin4:latest
|
|
||||||
container_name: pgadmin
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
|
|
||||||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
|
||||||
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
|
||||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
|
||||||
# Fix CSRF issues behind reverse proxy
|
|
||||||
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
|
||||||
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
|
||||||
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
|
||||||
# Trust proxy headers
|
|
||||||
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
|
||||||
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
|
||||||
volumes:
|
|
||||||
- pgadmin_data:/var/lib/pgadmin
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.pgadmin.rule=Host(`pgadmin.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.pgadmin.entrypoints=websecure
|
|
||||||
- traefik.http.routers.pgadmin.tls.certresolver=le
|
|
||||||
- traefik.http.services.pgadmin.loadbalancer.server.port=80
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Prometheus — monitoring
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
prometheus:
|
|
||||||
image: prom/prometheus:latest
|
|
||||||
container_name: prometheus
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
volumes:
|
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
|
||||||
- ./rules.yml:/etc/prometheus/rules.yml
|
|
||||||
- prometheus_data:/prometheus
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
command:
|
|
||||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
||||||
- '--storage.tsdb.path=/prometheus'
|
|
||||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
|
||||||
- '--web.console.templates=/etc/prometheus/consoles'
|
|
||||||
- '--storage.tsdb.retention.time=200h'
|
|
||||||
- '--web.enable-lifecycle'
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.prometheus.rule=Host(`prometheus.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.prometheus.entrypoints=websecure
|
|
||||||
- traefik.http.routers.prometheus.tls.certresolver=le
|
|
||||||
- traefik.http.routers.prometheus.middlewares=authelia@docker,security-headers
|
|
||||||
- traefik.http.services.prometheus.loadbalancer.server.port=9090
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Grafana — visualization
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
grafana:
|
|
||||||
image: grafana/grafana:latest
|
|
||||||
container_name: grafana
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
environment:
|
|
||||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
|
|
||||||
volumes:
|
|
||||||
- grafana_data:/var/lib/grafana
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.grafana.rule=Host(`grafana.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.grafana.entrypoints=websecure
|
|
||||||
- traefik.http.routers.grafana.tls.certresolver=le
|
|
||||||
- traefik.http.routers.grafana.middlewares=authelia@docker,security-headers
|
|
||||||
- traefik.http.services.grafana.loadbalancer.server.port=3000
|
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
## Alertmanager — alert handling
|
|
||||||
## ─────────────────────────────────────────────
|
|
||||||
alertmanager:
|
|
||||||
image: prom/alertmanager:latest
|
|
||||||
container_name: alertmanager
|
|
||||||
restart: unless-stopped
|
|
||||||
networks: [traefik_proxy]
|
|
||||||
volumes:
|
|
||||||
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
|
||||||
- alertmanager_data:/alertmanager
|
|
||||||
command:
|
|
||||||
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
|
||||||
- '--storage.path=/alertmanager'
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.http.routers.alertmanager.rule=Host(`alertmanager.test.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.alertmanager.entrypoints=websecure
|
|
||||||
- traefik.http.routers.alertmanager.tls.certresolver=le
|
|
||||||
- traefik.http.routers.alertmanager.middlewares=authelia@docker,security-headers
|
|
||||||
- traefik.http.services.alertmanager.loadbalancer.server.port=9093
|
|
||||||
@@ -12,11 +12,10 @@ volumes:
|
|||||||
uptime_kuma_data:
|
uptime_kuma_data:
|
||||||
umami_data:
|
umami_data:
|
||||||
pgadmin_data:
|
pgadmin_data:
|
||||||
authelia_config:
|
|
||||||
authelia_db_data:
|
authelia_db_data:
|
||||||
grafana_data:
|
beszel_data:
|
||||||
prometheus_data:
|
gitea_data:
|
||||||
alertmanager_data:
|
gitea_db_data:
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# Services
|
# Services
|
||||||
@@ -46,6 +45,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
TZ: "${TZ}"
|
TZ: "${TZ}"
|
||||||
command:
|
command:
|
||||||
|
# Enable ping endpoint for health checks
|
||||||
|
- --ping=true
|
||||||
|
|
||||||
# Experimental plugins
|
# Experimental plugins
|
||||||
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
||||||
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
||||||
@@ -118,11 +120,17 @@ services:
|
|||||||
- traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name
|
- traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name
|
||||||
|
|
||||||
# Traefik dashboard (protected)
|
# Traefik dashboard (protected)
|
||||||
- traefik.http.routers.traefik.rule=Host(`traefik.gate.${DOMAIN}`)
|
- traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.traefik.entrypoints=websecure
|
- traefik.http.routers.traefik.entrypoints=websecure
|
||||||
- traefik.http.routers.traefik.tls.certresolver=le
|
- traefik.http.routers.traefik.tls.certresolver=le
|
||||||
- traefik.http.routers.traefik.service=api@internal
|
- traefik.http.routers.traefik.service=api@internal
|
||||||
- traefik.http.routers.traefik.middlewares=authelia@docker,security-headers
|
- traefik.http.routers.traefik.middlewares=authelia@docker,security-headers
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Authelia — authentication and authorization
|
## Authelia — authentication and authorization
|
||||||
@@ -133,17 +141,27 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
volumes:
|
volumes:
|
||||||
- authelia_config:/config
|
- ./authelia:/config
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
eval "echo \"$(cat /config/configuration.template.yml)\"" > /config/configuration.yml
|
||||||
|
exec /app/entrypoint.sh
|
||||||
environment:
|
environment:
|
||||||
TZ: "${TZ}"
|
TZ: "${TZ}"
|
||||||
AUTHELIA_DB_PASSWORD: "${AUTHELIA_DB_PASSWORD}"
|
DOMAIN: "${DOMAIN}"
|
||||||
AUTHELIA_JWT_SECRET: "${AUTHELIA_JWT_SECRET}"
|
DOMAIN_PREFIX: "${DOMAIN_PREFIX}"
|
||||||
AUTHELIA_SESSION_SECRET: "${AUTHELIA_SESSION_SECRET}"
|
AUTHELIA_SESSION_SECRET: '${AUTHELIA_SESSION_SECRET}'
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY: '${AUTHELIA_STORAGE_ENCRYPTION_KEY}'
|
||||||
|
AUTHELIA_STORAGE_POSTGRES_PASSWORD: '${AUTHELIA_DB_PASSWORD}'
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD: '${AUTHELIA_NOTIFIER_SMTP_PASSWORD}'
|
||||||
|
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET: '${AUTHELIA_JWT_SECRET}'
|
||||||
depends_on:
|
depends_on:
|
||||||
- authelia-db
|
- authelia-db
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.authelia.rule=Host(`auth.gate.${DOMAIN}`)
|
- traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.authelia.entrypoints=websecure
|
- traefik.http.routers.authelia.entrypoints=websecure
|
||||||
- traefik.http.routers.authelia.tls.certresolver=le
|
- traefik.http.routers.authelia.tls.certresolver=le
|
||||||
- traefik.http.services.authelia.loadbalancer.server.port=9091
|
- traefik.http.services.authelia.loadbalancer.server.port=9091
|
||||||
@@ -162,6 +180,12 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${AUTHELIA_DB_PASSWORD}
|
POSTGRES_PASSWORD: ${AUTHELIA_DB_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- authelia_db_data:/var/lib/postgresql/data
|
- authelia_db_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U authelia -d authelia"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Portainer — Docker control plane
|
## Portainer — Docker control plane
|
||||||
@@ -171,12 +195,13 @@ services:
|
|||||||
container_name: portainer
|
container_name: portainer
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
|
command: --host unix:///var/run/docker.sock
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- portainer_data:/data
|
- portainer_data:/data
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.portainer.rule=Host(`portainer.gate.${DOMAIN}`)
|
- traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.portainer.entrypoints=websecure
|
- traefik.http.routers.portainer.entrypoints=websecure
|
||||||
- traefik.http.routers.portainer.tls.certresolver=le
|
- traefik.http.routers.portainer.tls.certresolver=le
|
||||||
- traefik.http.routers.portainer.middlewares=security-headers
|
- traefik.http.routers.portainer.middlewares=security-headers
|
||||||
@@ -194,7 +219,7 @@ services:
|
|||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.kuma.rule=Host(`uptime.gate.${DOMAIN}`)
|
- traefik.http.routers.kuma.rule=Host(`uptime.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.kuma.entrypoints=websecure
|
- traefik.http.routers.kuma.entrypoints=websecure
|
||||||
- traefik.http.routers.kuma.tls.certresolver=le
|
- traefik.http.routers.kuma.tls.certresolver=le
|
||||||
- traefik.http.routers.kuma.middlewares=security-headers
|
- traefik.http.routers.kuma.middlewares=security-headers
|
||||||
@@ -216,11 +241,17 @@ services:
|
|||||||
- umami-db
|
- umami-db
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.umami.rule=Host(`umami.gate.${DOMAIN}`)
|
- traefik.http.routers.umami.rule=Host(`umami.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.umami.entrypoints=websecure
|
- traefik.http.routers.umami.entrypoints=websecure
|
||||||
- traefik.http.routers.umami.tls.certresolver=le
|
- traefik.http.routers.umami.tls.certresolver=le
|
||||||
- traefik.http.routers.umami.middlewares=security-headers
|
- traefik.http.routers.umami.middlewares=security-headers
|
||||||
- traefik.http.services.umami.loadbalancer.server.port=3000
|
- traefik.http.services.umami.loadbalancer.server.port=3000
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Umami Database — PostgreSQL
|
## Umami Database — PostgreSQL
|
||||||
@@ -236,6 +267,12 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- umami_data:/var/lib/postgresql/data
|
- umami_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${UMAMI_DB_USER} -d ${UMAMI_DB_NAME}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
# pgAdmin — PostgreSQL administration
|
# pgAdmin — PostgreSQL administration
|
||||||
@@ -250,88 +287,113 @@ services:
|
|||||||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
||||||
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
||||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
||||||
# Fix CSRF issues behind reverse proxy
|
|
||||||
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
||||||
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
||||||
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
||||||
# Trust proxy headers
|
|
||||||
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
||||||
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
||||||
volumes:
|
volumes:
|
||||||
- pgadmin_data:/var/lib/pgadmin
|
- pgadmin_data:/var/lib/pgadmin
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.pgadmin.rule=Host(`pgadmin.gate.${DOMAIN}`)
|
- traefik.http.routers.pgadmin.rule=Host(`pgadmin.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.pgadmin.entrypoints=websecure
|
- traefik.http.routers.pgadmin.entrypoints=websecure
|
||||||
- traefik.http.routers.pgadmin.tls.certresolver=le
|
- traefik.http.routers.pgadmin.tls.certresolver=le
|
||||||
|
- traefik.http.routers.pgadmin.middlewares=security-headers
|
||||||
- traefik.http.services.pgadmin.loadbalancer.server.port=80
|
- traefik.http.services.pgadmin.loadbalancer.server.port=80
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Prometheus — monitoring
|
## Beszel Hub — lightweight server monitoring
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
prometheus:
|
beszel:
|
||||||
image: prom/prometheus:latest
|
image: henrygd/beszel:latest
|
||||||
container_name: prometheus
|
container_name: beszel
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- beszel_data:/beszel_data
|
||||||
- ./rules.yml:/etc/prometheus/rules.yml
|
|
||||||
- prometheus_data:/prometheus
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
command:
|
|
||||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
||||||
- '--storage.tsdb.path=/prometheus'
|
|
||||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
|
||||||
- '--web.console.templates=/etc/prometheus/consoles'
|
|
||||||
- '--storage.tsdb.retention.time=200h'
|
|
||||||
- '--web.enable-lifecycle'
|
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.prometheus.rule=Host(`prometheus.gate.${DOMAIN}`)
|
- traefik.http.routers.beszel.rule=Host(`beszel.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.prometheus.entrypoints=websecure
|
- traefik.http.routers.beszel.entrypoints=websecure
|
||||||
- traefik.http.routers.prometheus.tls.certresolver=le
|
- traefik.http.routers.beszel.tls.certresolver=le
|
||||||
- traefik.http.routers.prometheus.middlewares=authelia@docker,security-headers
|
- traefik.http.routers.beszel.middlewares=security-headers
|
||||||
- traefik.http.services.prometheus.loadbalancer.server.port=9090
|
- traefik.http.services.beszel.loadbalancer.server.port=8090
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Grafana — visualization
|
## Gitea — self-hosted Git service
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
grafana:
|
gitea:
|
||||||
image: grafana/grafana:latest
|
image: docker.gitea.com/gitea:latest
|
||||||
container_name: grafana
|
container_name: gitea
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
environment:
|
environment:
|
||||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__database__DB_TYPE=postgres
|
||||||
|
- GITEA__database__HOST=gitea-db:5432
|
||||||
|
- GITEA__database__NAME=${GITEA_DB_NAME}
|
||||||
|
- GITEA__database__USER=${GITEA_DB_USER}
|
||||||
|
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
|
||||||
|
- GITEA__server__DOMAIN=git.${DOMAIN_PREFIX}.${DOMAIN}
|
||||||
|
- GITEA__server__SSH_DOMAIN=git.${DOMAIN_PREFIX}.${DOMAIN}
|
||||||
|
- GITEA__server__ROOT_URL=https://git.${DOMAIN_PREFIX}.${DOMAIN}/
|
||||||
|
- GITEA__server__SSH_PORT=222
|
||||||
|
- GITEA__server__SSH_LISTEN_PORT=22
|
||||||
|
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY}
|
||||||
|
- GITEA__security__INTERNAL_TOKEN=${GITEA_INTERNAL_TOKEN}
|
||||||
volumes:
|
volumes:
|
||||||
- grafana_data:/var/lib/grafana
|
- gitea_data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "222:22"
|
||||||
|
depends_on:
|
||||||
|
- gitea-db
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.grafana.rule=Host(`grafana.gate.${DOMAIN}`)
|
- traefik.http.routers.gitea.rule=Host(`git.${DOMAIN_PREFIX}.${DOMAIN}`)
|
||||||
- traefik.http.routers.grafana.entrypoints=websecure
|
- traefik.http.routers.gitea.entrypoints=websecure
|
||||||
- traefik.http.routers.grafana.tls.certresolver=le
|
- traefik.http.routers.gitea.tls.certresolver=le
|
||||||
- traefik.http.routers.grafana.middlewares=authelia@docker,security-headers
|
- traefik.http.routers.gitea.middlewares=security-headers
|
||||||
- traefik.http.services.grafana.loadbalancer.server.port=3000
|
- traefik.http.services.gitea.loadbalancer.server.port=3000
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Alertmanager — alert handling
|
## Gitea Database — PostgreSQL
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
alertmanager:
|
gitea-db:
|
||||||
image: prom/alertmanager:latest
|
image: postgres:15-alpine
|
||||||
container_name: alertmanager
|
container_name: gitea-db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${GITEA_DB_NAME}
|
||||||
|
POSTGRES_USER: ${GITEA_DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${GITEA_DB_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
- gitea_db_data:/var/lib/postgresql/data
|
||||||
- alertmanager_data:/alertmanager
|
healthcheck:
|
||||||
command:
|
test: ["CMD-SHELL", "pg_isready -U ${GITEA_DB_USER} -d ${GITEA_DB_NAME}"]
|
||||||
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
interval: 30s
|
||||||
- '--storage.path=/alertmanager'
|
timeout: 10s
|
||||||
labels:
|
retries: 3
|
||||||
- traefik.enable=true
|
start_period: 30s
|
||||||
- traefik.http.routers.alertmanager.rule=Host(`alertmanager.gate.${DOMAIN}`)
|
|
||||||
- traefik.http.routers.alertmanager.entrypoints=websecure
|
|
||||||
- traefik.http.routers.alertmanager.tls.certresolver=le
|
|
||||||
- traefik.http.routers.alertmanager.middlewares=authelia@docker,security-headers
|
|
||||||
- traefik.http.services.alertmanager.loadbalancer.server.port=9093
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
global:
|
|
||||||
scrape_interval: 15s
|
|
||||||
evaluation_interval: 15s
|
|
||||||
|
|
||||||
rule_files:
|
|
||||||
- rules.yml
|
|
||||||
|
|
||||||
alerting:
|
|
||||||
alertmanagers:
|
|
||||||
- static_configs:
|
|
||||||
- targets:
|
|
||||||
- alertmanager:9093
|
|
||||||
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: 'prometheus'
|
|
||||||
static_configs:
|
|
||||||
- targets: ['localhost:9090']
|
|
||||||
|
|
||||||
- job_name: 'traefik'
|
|
||||||
static_configs:
|
|
||||||
- targets: ['traefik:8080']
|
|
||||||
metrics_path: /metrics
|
|
||||||
|
|
||||||
- job_name: 'authelia'
|
|
||||||
static_configs:
|
|
||||||
- targets: ['authelia:9091']
|
|
||||||
|
|
||||||
- job_name: 'grafana'
|
|
||||||
static_configs:
|
|
||||||
- targets: ['grafana:3000']
|
|
||||||
|
|
||||||
- job_name: 'docker'
|
|
||||||
docker_sd_configs:
|
|
||||||
- host: unix:///var/run/docker.sock
|
|
||||||
relabel_configs:
|
|
||||||
- source_labels: [__meta_docker_container_name]
|
|
||||||
regex: '/(.*)'
|
|
||||||
target_label: container_name
|
|
||||||
- source_labels: [__meta_docker_container_label_com_docker_compose_service]
|
|
||||||
target_label: service
|
|
||||||
- action: keep
|
|
||||||
source_labels: [__meta_docker_container_label_com_docker_compose_service]
|
|
||||||
regex: '.*'
|
|
||||||
36
rules.yml
36
rules.yml
@@ -1,36 +0,0 @@
|
|||||||
groups:
|
|
||||||
- name: recording_rules
|
|
||||||
rules:
|
|
||||||
- record: job:up:sum
|
|
||||||
expr: sum(up) by (job)
|
|
||||||
- record: job:up:count
|
|
||||||
expr: count(up) by (job)
|
|
||||||
|
|
||||||
- name: alerting_rules
|
|
||||||
rules:
|
|
||||||
- alert: InstanceDown
|
|
||||||
expr: up == 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Instance {{ $labels.instance }} down"
|
|
||||||
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
|
|
||||||
|
|
||||||
- alert: TraefikDown
|
|
||||||
expr: up{job="traefik"} == 0
|
|
||||||
for: 2m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
summary: "Traefik is down"
|
|
||||||
description: "Traefik has been down for more than 2 minutes."
|
|
||||||
|
|
||||||
- alert: AutheliaDown
|
|
||||||
expr: up{job="authelia"} == 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Authelia is down"
|
|
||||||
description: "Authelia authentication service is unavailable."
|
|
||||||
Reference in New Issue
Block a user