Major Changes: - Configure private Git server with OAuth-preferred authentication - Integrate Titan Email for notifications and OAuth workflows - Enable CI/CD Actions and repository mirroring - Implement enhanced security hardening Authentication & Access Control: - Require sign-in for all access (unauthorized users blocked) - OAuth via Authentik as primary login method (password form hidden) - Password authentication still functional as backup via direct URL - Registration disabled (admin-only user creation) - Auto-registration for OAuth users with account linking support Email Configuration (Titan Email): - SMTP: smtp.titan.email:587 (STARTTLS) - From address: hello@jnss.me - Used for: OAuth account linking, notifications, confirmations - Subject prefix: [Gitea] Repository Privacy & Features: - Private repositories by default (public repos allowed) - Unauthorized users cannot view any content (must sign in) - External integrations disabled (ext_issues, ext_wiki) - Manual repository creation required (no push-to-create) - LFS enabled for large file storage Features Enabled: - CI/CD Actions with GitHub actions support - Repository mirroring (pull/push mirrors enabled) - User organization creation - Webhook security (restricted to private/loopback) Security Enhancements: - HTTPS-only session cookies with strict SameSite policy - CSRF cookie HTTP-only protection - Password breach checking (HaveIBeenPwned) - 1-hour session timeout (reduced from 24h) - Reverse proxy trust limited to Caddy only - API Swagger docs disabled in production Configuration Sections Added: - [oauth2_client] - OAuth integration settings - [mailer] - Email/SMTP configuration - [session] - Enhanced session security - [actions] - CI/CD workflow configuration - [mirror] - Repository mirroring settings - [api] - API access configuration - [webhook] - Webhook security restrictions - [service.explore] - Public content settings Files Changed: - roles/gitea/defaults/main.yml: +97 lines (OAuth, email, security vars) - roles/gitea/templates/app.ini.j2: +94 lines (config sections) - host_vars/arch-vps/vault.yml: +1 line (SMTP password - not committed) Deployment Status: - Successfully deployed to arch-vps - Service running and healthy - Ready for OAuth provider configuration in Authentik - Tested: HTTP access, configuration generation, service health
Gitea Git Service Role
Self-contained Gitea Git service for rick-infra following the established architectural patterns.
Features
- ✅ Self-contained: Manages its own database and configuration
- ✅ Native Arch installation: Uses pacman packages
- ✅ PostgreSQL integration: Uses shared PostgreSQL infrastructure
- ✅ Caddy integration: Deploys reverse proxy configuration
- ✅ Dual SSH modes: Passthrough (default) or dedicated SSH server
- ✅ Flexible domains: Separate HTTP and SSH domains
- ✅ Security hardened: SystemD restrictions and secure defaults
- ✅ Firewall management: Automatic nftables configuration per mode
- ✅ fail2ban protection: Brute force protection for SSH authentication
- ✅ Production ready: HTTPS, SSH access, LFS support
Architecture
- Dependencies: PostgreSQL infrastructure role
- Database: Self-managed gitea database and user
- Network: HTTP on :3000 (localhost), SSH via system SSH (port 22) or dedicated (port 2222)
- Web access: https://git.jnss.me (via Caddy reverse proxy)
- SSH access: git@jnss.me:user/repo.git (passthrough mode, default)
- Firewall: Managed per SSH mode (no extra ports in passthrough)
- Security: fail2ban protects SSH authentication (system or dedicated jail)
Configuration
Key variables (defaults in defaults/main.yml):
# Service
gitea_service_enabled: true
gitea_http_port: 3000
# Domain Configuration
gitea_http_domain: "git.jnss.me" # Web interface
gitea_ssh_domain: "jnss.me" # SSH/Git operations
# SSH Mode
gitea_ssh_mode: "passthrough" # or "dedicated"
# Database (self-managed)
gitea_db_name: "gitea"
gitea_db_user: "gitea"
gitea_db_password: "{{ vault_gitea_db_password }}"
# Application
gitea_app_name: "Gitea: Git with a cup of tea"
gitea_disable_registration: false
gitea_enable_lfs: true
Domain Configuration
Gitea uses separate domains for HTTP and SSH access, providing flexibility and cleaner URLs:
HTTP Domain (gitea_http_domain)
- Purpose: Web interface access
- Default:
git.jnss.me - Example:
https://git.jnss.me - Used for: Browsing repos, managing settings, viewing commits
SSH Domain (gitea_ssh_domain)
- Purpose: Git clone/push/pull operations
- Default:
jnss.me(root domain) - Example:
git@jnss.me:user/repo.git - Used for: Git operations over SSH
Why Separate Domains?
- ✅ Cleaner SSH URLs (no redundant "git" subdomain)
- ✅ Flexibility to use completely different domains
- ✅ Matches industry standards (GitHub, GitLab use root domain for SSH)
- ✅ Professional appearance
Configuration Examples:
# Standard setup (recommended)
gitea_http_domain: "git.jnss.me"
gitea_ssh_domain: "jnss.me"
# Result: git@jnss.me:user/repo.git
# Same domain for both
gitea_http_domain: "git.jnss.me"
gitea_ssh_domain: "git.jnss.me"
# Result: git@git.jnss.me:user/repo.git
# Completely custom
gitea_http_domain: "code.jnss.me"
gitea_ssh_domain: "git.example.com"
# Result: git@git.example.com:user/repo.git
SSH Modes
Passthrough Mode (Default - Recommended)
System SSH handles Git operations via AuthorizedKeysCommand:
# Clone repository (standard Git URL, no port number)
git clone git@jnss.me:username/repository.git
# Add as remote
git remote add origin git@jnss.me:username/repository.git
# Test SSH connection
ssh -T git@jnss.me
Features:
- ✅ Standard Git URLs (no :2222 port)
- ✅ Single SSH daemon (smaller attack surface)
- ✅ System fail2ban protects everything
- ✅ Port 22 only (no extra firewall rules)
- ✅ Matches GitHub/GitLab pattern
Dedicated Mode (Fallback)
Gitea runs its own SSH server on port 2222:
# Clone repository (with port number)
git clone ssh://git@jnss.me:2222/username/repository.git
# Add as remote
git remote add origin ssh://git@jnss.me:2222/username/repository.git
# Test SSH connection
ssh -T -p 2222 git@jnss.me
Features:
- ✅ Complete isolation from system SSH
- ✅ Independent configuration
- ⚠️ Requires port 2222 open in firewall
- ⚠️ Non-standard URLs (requires :2222)
To switch modes:
# host_vars/arch-vps/main.yml
gitea_ssh_mode: "dedicated" # or "passthrough"
Then re-run the playbook.
Usage
- Add vault password: Set
vault_gitea_db_passwordin host_vars vault - Configure domains (optional): Override
gitea_http_domainandgitea_ssh_domainin host_vars - Deploy:
ansible-playbook site.yml --tags gitea - Access: Visit https://git.jnss.me to set up admin account
SSH Key Setup
-
Generate SSH key (if you don't have one):
ssh-keygen -t ed25519 -C "your_email@example.com" -
Copy your public key:
cat ~/.ssh/id_ed25519.pub -
Add to Gitea:
- Log into Gitea web interface
- Go to Settings → SSH/GPG Keys
- Click "Add Key"
- Paste your public key
-
Test SSH connection:
# Passthrough mode ssh -T git@jnss.me # Dedicated mode ssh -T -p 2222 git@jnss.me
Firewall and Security
Automatic Firewall Management
Firewall configuration is mode-aware:
Passthrough Mode:
- No extra firewall rules needed (uses port 22)
- System SSH already configured in security playbook
Dedicated Mode:
- Port 2222 automatically opened via nftables
- Firewall rules stored in
/etc/nftables.d/50-gitea.nft - Rules integrated with main security playbook
- Automatically removed when switching to passthrough
fail2ban Protection
Protection is mode-aware:
Passthrough Mode:
- System
sshdjail protects all SSH traffic (port 22) - Covers admin SSH + Git operations automatically
- No separate Gitea jail needed
Dedicated Mode:
gitea-sshjail monitors Gitea logs (port 2222)- Max retries: 5 failed attempts
- Find time: 10 minutes (600 seconds)
- Ban time: 1 hour (3600 seconds)
- Action: IP banned via nftables
Check fail2ban status:
# Passthrough mode
fail2ban-client status sshd
# Dedicated mode
fail2ban-client status gitea-ssh
Firewall Verification
# List active nftables rules
nft list ruleset
# Check for Gitea SSH port (should be empty in passthrough)
nft list ruleset | grep 2222
# Verify SSH connectivity
nc -zv jnss.me 22 # Passthrough
nc -zv jnss.me 2222 # Dedicated
Dependencies
- PostgreSQL infrastructure role (auto-included)
- Caddy web server (for HTTPS access)
- Vault password:
vault_gitea_db_password
Self-Contained Design
This role follows rick-infra's self-contained service pattern:
- Creates its own database and user via PostgreSQL infrastructure
- Manages its own configuration and data
- Deploys its own Caddy reverse proxy config
- Manages its own firewall rules and security (nftables, fail2ban)
- Flexible domain configuration (not tied to infrastructure variables)
- Independent lifecycle from other services
Migration Guide
See docs/gitea-ssh-migration-guide.md for:
- Switching between SSH modes
- Updating Git remote URLs
- Bulk migration scripts
- Troubleshooting
Rick-Infra Gitea Service
Git repository management with flexible SSH modes and domain configuration.