Add remainging rate limits. Add docker compose production example. Update beta-checklist.
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

This commit is contained in:
Dan Milne
2026-01-02 12:14:13 +11:00
parent fed7c3cedb
commit 5137a25626
4 changed files with 116 additions and 64 deletions

145
README.md
View File

@@ -306,21 +306,100 @@ bin/dev
## Production Deployment ## Production Deployment
### Docker ### Docker Compose (Recommended)
Create a `docker-compose.yml` file:
```yaml
services:
clinch:
image: ghcr.io/dkam/clinch:latest
ports:
- "127.0.0.1:3000:3000" # Bind to localhost only (reverse proxy on same host)
# Use "3000:3000" if reverse proxy is in Docker network or different host
environment:
# Rails Configuration
RAILS_ENV: production
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
# Application Configuration
CLINCH_HOST: ${CLINCH_HOST}
CLINCH_FROM_EMAIL: ${CLINCH_FROM_EMAIL:-noreply@example.com}
# SMTP Configuration
SMTP_ADDRESS: ${SMTP_ADDRESS}
SMTP_PORT: ${SMTP_PORT}
SMTP_DOMAIN: ${SMTP_DOMAIN}
SMTP_USERNAME: ${SMTP_USERNAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_AUTHENTICATION: ${SMTP_AUTHENTICATION:-plain}
SMTP_ENABLE_STARTTLS: ${SMTP_ENABLE_STARTTLS:-true}
# OIDC Configuration (optional - generates temporary key if not provided)
OIDC_PRIVATE_KEY: ${OIDC_PRIVATE_KEY}
# Optional Configuration
FORCE_SSL: ${FORCE_SSL:-false}
volumes:
- ./storage:/rails/storage
restart: unless-stopped
```
Create a `.env` file in the same directory:
```bash ```bash
# Build image # Generate with: openssl rand -hex 64
docker build -t clinch . SECRET_KEY_BASE=your-secret-key-here
# Run container # Application URLs
docker run -p 3000:3000 \ CLINCH_HOST=https://auth.yourdomain.com
-v clinch-storage:/rails/storage \ CLINCH_FROM_EMAIL=noreply@yourdomain.com
-e SECRET_KEY_BASE=your-secret-key \
-e SMTP_ADDRESS=smtp.example.com \ # SMTP Settings
-e SMTP_PORT=587 \ SMTP_ADDRESS=smtp.example.com
-e SMTP_USERNAME=your-username \ SMTP_PORT=587
-e SMTP_PASSWORD=your-password \ SMTP_DOMAIN=yourdomain.com
clinch SMTP_USERNAME=your-smtp-username
SMTP_PASSWORD=your-smtp-password
# OIDC (optional - generates temporary key if not set)
# Generate with: openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
# Then: OIDC_PRIVATE_KEY=$(cat private_key.pem)
OIDC_PRIVATE_KEY=
# Optional: Force SSL redirects (if not behind a reverse proxy handling SSL)
FORCE_SSL=false
```
Start Clinch:
```bash
docker compose up -d
```
**First Run:**
1. Visit `http://localhost:3000` (or your configured domain)
2. Complete the first-run wizard to create your admin account
3. Configure applications and invite users
**Upgrading:**
```bash
# Pull latest image
docker compose pull
# Restart with new image (migrations run automatically)
docker compose up -d
```
**Logs:**
```bash
# View logs
docker compose logs -f clinch
# View last 100 lines
docker compose logs --tail=100 clinch
``` ```
### Backup & Restore ### Backup & Restore
@@ -351,9 +430,9 @@ sqlite3 storage/production.sqlite3 "VACUUM INTO 'backup-$(date +%Y%m%d).sqlite3'
# 2. Backup uploaded files (ActiveStorage files are immutable) # 2. Backup uploaded files (ActiveStorage files are immutable)
tar -czf uploads-backup-$(date +%Y%m%d).tar.gz storage/uploads/ tar -czf uploads-backup-$(date +%Y%m%d).tar.gz storage/uploads/
# Docker equivalent # Docker Compose equivalent
docker exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup-$(date +%Y%m%d).sqlite3';" docker compose exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup-$(date +%Y%m%d).sqlite3';"
docker exec clinch tar -czf /rails/storage/uploads-backup-$(date +%Y%m%d).tar.gz /rails/storage/uploads/ docker compose exec clinch tar -czf /rails/storage/uploads-backup-$(date +%Y%m%d).tar.gz /rails/storage/uploads/
``` ```
**Restore:** **Restore:**
@@ -380,13 +459,13 @@ sqlite3 /host/path/production.sqlite3 "VACUUM INTO '/host/path/backup-$(date +%Y
rsync -av /host/path/backup-*.sqlite3 /host/path/uploads/ remote:/backups/clinch/ rsync -av /host/path/backup-*.sqlite3 /host/path/uploads/ remote:/backups/clinch/
``` ```
b) **Docker volumes** (e.g., `-v clinch_storage:/rails/storage`): b) **Docker volumes** (e.g., using named volumes in compose):
```bash ```bash
# Database backup (safe while running) # Database backup (safe while running)
docker exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup.sqlite3';" docker compose exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup.sqlite3';"
# Copy out of container # Copy out of container
docker cp clinch:/rails/storage/backup.sqlite3 ./backup-$(date +%Y%m%d).sqlite3 docker compose cp clinch:/rails/storage/backup.sqlite3 ./backup-$(date +%Y%m%d).sqlite3
``` ```
**Option 2: While Stopped (Offline Backup)** **Option 2: While Stopped (Offline Backup)**
@@ -411,35 +490,7 @@ docker compose up -d
## Configuration ## Configuration
### Environment Variables All configuration is handled via environment variables (see the `.env` file in the Docker Compose section above).
Create a `.env` file (see `.env.example`):
```bash
# Rails
SECRET_KEY_BASE=generate-with-bin-rails-secret
RAILS_ENV=production
# Database
# SQLite database stored in storage/ directory (Docker volume mount point)
# SMTP (for sending emails)
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_DOMAIN=example.com
SMTP_USERNAME=your-username
SMTP_PASSWORD=your-password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS=true
# Application
CLINCH_HOST=https://auth.example.com
CLINCH_FROM_EMAIL=noreply@example.com
# OIDC (optional - generates temporary key in development)
# Generate with: openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
OIDC_PRIVATE_KEY=<contents-of-private-key.pem>
```
### First Run ### First Run
1. Visit Clinch at `http://localhost:3000` (or your configured domain) 1. Visit Clinch at `http://localhost:3000` (or your configured domain)

View File

@@ -3,6 +3,7 @@ class InvitationsController < ApplicationController
allow_unauthenticated_access allow_unauthenticated_access
before_action :set_user_by_invitation_token, only: %i[show update] before_action :set_user_by_invitation_token, only: %i[show update]
rate_limit to: 10, within: 10.minutes, only: :update, with: -> { redirect_to signin_path, alert: "Too many attempts. Try again later." }
def show def show
# Show the password setup form # Show the password setup form

View File

@@ -2,6 +2,7 @@ class PasswordsController < ApplicationController
allow_unauthenticated_access allow_unauthenticated_access
before_action :set_user_by_token, only: %i[edit update] before_action :set_user_by_token, only: %i[edit update]
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." } rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." }
rate_limit to: 10, within: 10.minutes, only: :update, with: -> { redirect_to new_password_path, alert: "Too many attempts. Try again later." }
def new def new
end end

View File

@@ -153,7 +153,7 @@ This checklist ensures Clinch meets security, quality, and documentation standar
### Deployment ### Deployment
- [x] Docker support - [x] Docker support
- [x] Docker Compose example - [x] Docker Compose example
- [ ] Production deployment guide - [x] Production deployment guide (Docker Compose with .env configuration, upgrading, logs)
- [x] Backup and restore documentation - [x] Backup and restore documentation
## Security Hardening ## Security Hardening
@@ -165,10 +165,13 @@ This checklist ensures Clinch meets security, quality, and documentation standar
- [x] Referrer-Policy (strict-origin-when-cross-origin in production config) - [x] Referrer-Policy (strict-origin-when-cross-origin in production config)
### Rate Limiting ### Rate Limiting
- [ ] Login attempt rate limiting - [x] Login attempt rate limiting (20/3min on sessions#create)
- [ ] API endpoint rate limiting - [x] TOTP verification rate limiting (10/3min on sessions#verify_totp)
- [ ] Token endpoint rate limiting - [x] WebAuthn rate limiting (10/1min on webauthn endpoints, 10/3min on session endpoints)
- [ ] Password reset rate limiting - [x] Password reset rate limiting (10/3min on request, 10/10min on completion)
- [x] Invitation acceptance rate limiting (10/10min)
- [x] OAuth token endpoint rate limiting (60/1min on token, 30/1min on authorize)
- [x] Backup code rate limiting (5 failed attempts per hour, model-level)
### Secrets Management ### Secrets Management
- [x] No secrets in code - [x] No secrets in code
@@ -222,15 +225,15 @@ To move from "experimental" to "Beta", the following must be completed:
- [x] All tests passing - [x] All tests passing
- [x] Core features implemented and tested - [x] Core features implemented and tested
- [x] Basic documentation complete - [x] Basic documentation complete
- [x] Backup/restore documentation
- [x] Production deployment guide
- [ ] At least one external security review or penetration test - [ ] At least one external security review or penetration test
- [ ] Production deployment guide
- [ ] Backup/restore documentation
**Important (Should have for Beta):** **Important (Should have for Beta):**
- [ ] Rate limiting on auth endpoints - [x] Rate limiting on auth endpoints
- [ ] Security headers configuration documented - [x] Security headers configuration documented (CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
- [x] Known limitations documented (ForwardAuth same-domain requirement in README)
- [ ] Admin audit logging - [ ] Admin audit logging
- [ ] Known limitations documented
**Nice to have (Can defer to post-Beta):** **Nice to have (Can defer to post-Beta):**
- [ ] Bug bounty program - [ ] Bug bounty program
@@ -250,16 +253,12 @@ To move from "experimental" to "Beta", the following must be completed:
**Before Beta Release:** **Before Beta Release:**
- 🔶 External security review recommended - 🔶 External security review recommended
- 🔶 Rate limiting implementation needed - 🔶 Admin audit logging (optional)
- 🔶 Production deployment documentation
- 🔶 Security hardening checklist completion
**Recommendation:** Consider Beta status after: **Recommendation:** Consider Beta status after:
1. External security review or penetration testing 1. External security review or penetration testing
2. Rate limiting implementation 2. Real-world testing period
3. Production hardening documentation
4. 1-2 months of real-world testing
--- ---
Last updated: 2026-01-01 Last updated: 2026-01-02