Updated CLAUDE.md and README.md to document security features: **CLAUDE.md updates:** - Expanded Traefik section with security architecture details - Added Security Configuration section with detailed guides - Documented HTTP Basic Auth setup and credential management - Added security testing commands and procedures - Included TLS/header configuration instructions **README.md updates:** - Enhanced PROTOCOLS & SECURITY section - Added TLS 1.2+ and cipher suite information - Listed security headers (HSTS, X-Frame-Options, etc.) - Documented HTTP Basic Auth and rate limiting - Updated access control list Documentation now reflects all security hardening applied to Traefik reverse proxy and service authentication. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
276 lines
9.5 KiB
Markdown
276 lines
9.5 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Overview
|
|
|
|
Multi-service Docker Compose stack named "falcon" managing production services on pivoine.art domain. Uses Arty for configuration management with centralized environment variables and custom scripts.
|
|
|
|
## Architecture
|
|
|
|
### Compose Include Pattern
|
|
Root `compose.yaml` uses Docker Compose's `include` directive to orchestrate multiple service stacks:
|
|
- **core**: Shared PostgreSQL 16 + Redis 7 infrastructure
|
|
- **proxy**: Traefik reverse proxy with Let's Encrypt SSL
|
|
- **sexy**: Directus 11 CMS + SvelteKit frontend
|
|
- **awsm**: Next.js application with SQLite
|
|
- **track**: Umami analytics (PostgreSQL)
|
|
- **gotify**: Push notification server
|
|
- **scrapy**: Scrapyd web scraping cluster (scrapyd, scrapy, scrapyrt)
|
|
- **n8n**: Workflow automation platform (PostgreSQL)
|
|
- **stash**: Filestash web-based file manager
|
|
- **vpn**: WireGuard VPN (wg-easy)
|
|
|
|
All services connect to a single external Docker network (`falcon_network` by default, defined by `$NETWORK_NAME`).
|
|
|
|
### Environment Management via Arty
|
|
Configuration is centralized in `arty.yml`:
|
|
- **envs.default**: All environment variables with sensible defaults
|
|
- **scripts**: Common Docker Compose and operational commands
|
|
- Variables follow naming pattern: `{SERVICE}_COMPOSE_PROJECT_NAME`, `{SERVICE}_TRAEFIK_HOST`, etc.
|
|
|
|
Sensitive values (passwords, secrets) live in `.env` and override arty.yml defaults.
|
|
|
|
### Traefik Routing & Security Architecture
|
|
Services expose themselves via Docker labels:
|
|
- HTTP → HTTPS redirect on `web` entrypoint (port 80)
|
|
- SSL termination on `web-secure` entrypoint (port 443)
|
|
- Let's Encrypt certificates stored in `proxy` volume
|
|
- Path-based routing: `/api` routes to Directus backend, root to frontend
|
|
- Compression middleware applied via labels
|
|
- All routers scoped to `$NETWORK_NAME` network
|
|
|
|
**Security Features:**
|
|
- **TLS Security**: Minimum TLS 1.2, strong cipher suites (ECDHE, AES-GCM, ChaCha20), SNI strict mode
|
|
- **Security Headers**: HSTS (1-year), X-Frame-Options, X-XSS-Protection, Content-Type-Options, Referrer-Policy, Permissions-Policy
|
|
- **Dynamic Configuration**: Security settings in `proxy/dynamic/security.yaml` with auto-reload
|
|
- **Rate Limiting**: Available middlewares (100 req/s general, 30 req/s API)
|
|
- **HTTP Basic Auth**: Scrapyd protected with username/password authentication
|
|
|
|
### Database Initialization
|
|
`core/postgres/init/01-init-databases.sh` runs on first PostgreSQL startup:
|
|
- Creates `directus` database for Sexy CMS
|
|
- Creates `umami` database for Track analytics
|
|
- Creates `n8n` database for workflow automation
|
|
- Grants privileges to `$DB_USER`
|
|
|
|
## Common Commands
|
|
|
|
All commands use `pnpm arty` (leveraging scripts in arty.yml):
|
|
|
|
### Stack Management
|
|
```bash
|
|
# Start all services
|
|
pnpm arty up
|
|
|
|
# Stop all services
|
|
pnpm arty down
|
|
|
|
# View running containers
|
|
pnpm arty ps
|
|
|
|
# Follow logs for all services
|
|
pnpm arty logs
|
|
|
|
# Restart all services
|
|
pnpm arty restart
|
|
|
|
# Pull latest images
|
|
pnpm arty pull
|
|
|
|
# View rendered configuration (with variables substituted)
|
|
pnpm arty config
|
|
```
|
|
|
|
### Network Setup
|
|
```bash
|
|
# Create external Docker network (required before first up)
|
|
pnpm arty net/create
|
|
```
|
|
|
|
### Database Operations (Sexy/Directus)
|
|
```bash
|
|
# Export Directus database + schema snapshot
|
|
pnpm arty db/dump
|
|
|
|
# Import database dump + apply schema snapshot
|
|
pnpm arty db/import
|
|
|
|
# Export Directus uploads directory
|
|
pnpm arty uploads/export
|
|
|
|
# Import Directus uploads directory
|
|
pnpm arty uploads/import
|
|
```
|
|
|
|
### Deployment
|
|
```bash
|
|
# Sync .env file to remote VPS
|
|
pnpm arty env/sync
|
|
```
|
|
|
|
## Service-Specific Details
|
|
|
|
### Core Services (core/compose.yaml)
|
|
- **postgres**: PostgreSQL 16 Alpine, exposed on host port 5432
|
|
- Uses scram-sha-256 authentication
|
|
- Health check via `pg_isready`
|
|
- Init scripts mounted from `./postgres/init/`
|
|
- **redis**: Redis 7 Alpine for caching
|
|
- Used by Directus for cache and websocket storage
|
|
|
|
### Sexy (sexy/compose.yaml)
|
|
Directus headless CMS + SvelteKit frontend:
|
|
- **directus**: Directus 11.12.0
|
|
- Admin panel + GraphQL/REST API
|
|
- Traefik routes `/api` path to port 8055
|
|
- Volumes: `directus_uploads` for media, `directus_bundle` for extensions
|
|
- Email via SMTP (IONOS configuration in .env)
|
|
- **frontend**: Custom SvelteKit app from ghcr.io/valknarxxx/sexy
|
|
- Serves on port 3000
|
|
- Shared `directus_bundle` volume for Directus extensions
|
|
|
|
### Proxy (proxy/compose.yaml)
|
|
Traefik 3.x reverse proxy:
|
|
- Global HTTP→HTTPS redirect
|
|
- Let's Encrypt via TLS challenge
|
|
- Dashboard disabled (`api.dashboard=false`)
|
|
- Reads labels from Docker socket (`/var/run/docker.sock`)
|
|
- Scoped to `$NETWORK_NAME` network via provider configuration
|
|
|
|
### AWSM (awsm/compose.yaml)
|
|
Next.js app with embedded SQLite:
|
|
- Serves awesome-app list directory
|
|
- Optional GitHub token for API rate limits
|
|
- Optional webhook secret for database updates
|
|
- Database persisted in `awesome_data` volume
|
|
|
|
### Scrapy (scrapy/compose.yaml)
|
|
Web scraping cluster with three services:
|
|
- **scrapyd**: Scrapyd daemon exposed at `scrapy.pivoine.art:6800`
|
|
- Web interface for deploying and managing spiders
|
|
- Protected by HTTP Basic Auth (credentials in `.env`)
|
|
- Data persisted in `scrapyd_data` volume
|
|
- **scrapy**: Development container for running Scrapy commands
|
|
- Shared `scrapy_code` volume for spider projects
|
|
- **scrapyrt**: Scrapyd Real-Time API on port 9080
|
|
- Run spiders via HTTP API without scheduling
|
|
|
|
**Authentication**: Access requires username/password (stored as `SCRAPY_AUTH_USERS` in `.env` using htpasswd format)
|
|
|
|
### n8n (n8n/compose.yaml)
|
|
Workflow automation platform:
|
|
- **n8n**: n8n application exposed at `n8n.pivoine.art:5678`
|
|
- Visual workflow builder with 200+ integrations
|
|
- PostgreSQL backend for workflow persistence
|
|
- Runners enabled for task execution
|
|
- Webhook support for external triggers
|
|
- Data persisted in `n8n_data` volume
|
|
|
|
### Stash (stash/compose.yaml)
|
|
Web-based file manager:
|
|
- **filestash**: Filestash app exposed at `stash.pivoine.art:8334`
|
|
- Support for multiple storage backends (SFTP, S3, Dropbox, Google Drive, FTP, WebDAV)
|
|
- In-browser file viewer and media player
|
|
- File sharing capabilities
|
|
- Data persisted in `filestash_data` volume
|
|
|
|
## Important Environment Variables
|
|
|
|
Key variables defined in `arty.yml` and overridden in `.env`:
|
|
- `NETWORK_NAME`: Docker network name (default: `falcon_network`)
|
|
- `ADMIN_EMAIL`: Used for Let's Encrypt and service admin accounts
|
|
- `DB_USER`, `DB_PASSWORD`: PostgreSQL credentials
|
|
- `CORE_DB_HOST`, `CORE_DB_PORT`: PostgreSQL connection (default: `postgres:5432`)
|
|
- `CORE_REDIS_HOST`, `CORE_REDIS_PORT`: Redis connection (default: `redis:6379`)
|
|
- `{SERVICE}_TRAEFIK_HOST`: Domain for each service
|
|
- `{SERVICE}_TRAEFIK_ENABLED`: Toggle Traefik exposure
|
|
- `SEXY_DIRECTUS_SECRET`: Directus security secret
|
|
- `TRACK_APP_SECRET`: Umami analytics secret
|
|
|
|
## Volume Management
|
|
|
|
Each service uses named volumes prefixed with project name:
|
|
- `core_postgres_data`, `core_redis_data`: Database persistence
|
|
- `core_directus_uploads`, `core_directus_bundle`: Directus media/extensions
|
|
- `awesome_data`: AWSM SQLite database
|
|
- `scrapy_scrapyd_data`, `scrapy_scrapy_code`: Scrapy spider data and code
|
|
- `n8n_n8n_data`: n8n workflow data
|
|
- `stash_filestash_data`: Filestash configuration and state
|
|
- `proxy_letsencrypt_data`: SSL certificates
|
|
|
|
Volumes can be inspected with:
|
|
```bash
|
|
docker volume ls | grep falcon
|
|
docker volume inspect <volume_name>
|
|
```
|
|
|
|
## Security Configuration
|
|
|
|
### HTTP Basic Authentication
|
|
Scrapyd is protected with HTTP Basic Auth via Traefik middleware:
|
|
- Credentials stored in `.env` as `SCRAPY_AUTH_USERS`
|
|
- Format: `username:$apr1$hash` (Apache htpasswd format)
|
|
- Generate new hash: `openssl passwd -apr1 'your_password'`
|
|
- Remember to escape `$` signs with `$$` in `.env` files
|
|
|
|
**To update credentials:**
|
|
```bash
|
|
# Generate hash
|
|
echo "username:$(openssl passwd -apr1 'new_password')"
|
|
|
|
# Update .env
|
|
SCRAPY_AUTH_USERS=username:$$apr1$$hash$$here
|
|
|
|
# Sync to VPS
|
|
rsync -avzhe ssh .env root@vps:~/Projects/docker-compose/
|
|
|
|
# Restart services
|
|
ssh -A root@vps "cd ~/Projects/docker-compose && arty restart"
|
|
```
|
|
|
|
### Security Headers & TLS
|
|
Global security settings applied via `proxy/dynamic/security.yaml`:
|
|
- **TLS**: Minimum TLS 1.2, strong ciphers only, SNI strict mode
|
|
- **Headers**: HSTS, X-Frame-Options, CSP, Referrer-Policy, etc.
|
|
- **Rate Limiting**: Available middlewares for DDoS protection
|
|
|
|
Test security:
|
|
```bash
|
|
# Check headers
|
|
curl -I https://scrapy.pivoine.art
|
|
|
|
# SSL Labs test
|
|
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=scrapy.pivoine.art
|
|
```
|
|
|
|
### Modifying Security Settings
|
|
Edit `proxy/dynamic/security.yaml` to customize:
|
|
- TLS versions and cipher suites
|
|
- Security header values
|
|
- Rate limiting thresholds
|
|
|
|
Traefik automatically reloads changes (no restart needed).
|
|
|
|
## Troubleshooting
|
|
|
|
### Services won't start
|
|
1. Ensure external network exists: `pnpm arty net/create`
|
|
2. Check if services reference correct `$NETWORK_NAME` in labels
|
|
3. Verify `.env` has required secrets (compare with `arty.yml` envs.default)
|
|
|
|
### SSL certificates failing
|
|
1. Check `ADMIN_EMAIL` is set in `.env`
|
|
2. Ensure ports 80/443 are accessible from internet
|
|
3. Inspect Traefik logs: `docker logs proxy_app`
|
|
|
|
### Database connection errors
|
|
1. Check PostgreSQL is healthy: `docker ps` (should show healthy status)
|
|
2. Verify database exists: `docker exec core_postgres psql -U $DB_USER -l`
|
|
3. Check credentials match between `.env` and service configs
|
|
|
|
### Directus schema migration
|
|
- Export schema: `pnpm arty db/dump` (creates `sexy/directus.yaml`)
|
|
- Import to new instance: `pnpm arty db/import` (applies schema snapshot)
|
|
- Schema file stored in `sexy/directus.yaml` for version control
|