Implement SSH passthrough mode and refactor Gitea domain configuration
Major Changes: - Add dual SSH mode system (passthrough default, dedicated fallback) - Refactor domain configuration to use direct specification pattern - Fix critical fail2ban security gap in dedicated mode - Separate HTTP and SSH domains for cleaner Git URLs
This commit is contained in:
245
docs/gitea-ssh-migration-guide.md
Normal file
245
docs/gitea-ssh-migration-guide.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Gitea SSH Migration Guide
|
||||
|
||||
Guide for migrating between Gitea SSH modes and updating Git remote URLs.
|
||||
|
||||
## SSH Modes Overview
|
||||
|
||||
### Passthrough Mode (Default)
|
||||
- **Port**: 22 (standard SSH)
|
||||
- **URL Format**: `git@git.jnss.me:user/repo.git`
|
||||
- **Security**: System fail2ban protects all SSH traffic
|
||||
- **Recommended**: ✅ For production use
|
||||
|
||||
### Dedicated Mode (Fallback)
|
||||
- **Port**: 2222 (Gitea SSH server)
|
||||
- **URL Format**: `ssh://git@git.jnss.me:2222/user/repo.git`
|
||||
- **Security**: Separate fail2ban jail for port 2222
|
||||
- **Use Case**: Debugging or when passthrough has issues
|
||||
|
||||
---
|
||||
|
||||
## Migration: Dedicated → Passthrough (Default)
|
||||
|
||||
When you deploy the new code, Gitea will automatically switch to passthrough mode.
|
||||
|
||||
### What Happens Automatically
|
||||
|
||||
1. ✅ Gitea's SSH server stops listening on port 2222
|
||||
2. ✅ Port 2222 firewall rule removed
|
||||
3. ✅ System SSH configured for Git passthrough
|
||||
4. ✅ AuthorizedKeysCommand script deployed
|
||||
5. ✅ fail2ban switches to system `sshd` jail
|
||||
|
||||
### What You Need to Do
|
||||
|
||||
**Update your Git remote URLs** in each repository:
|
||||
|
||||
```bash
|
||||
# Check current remote URL
|
||||
git remote -v
|
||||
|
||||
# Update to new format (no port number)
|
||||
git remote set-url origin git@git.jnss.me:username/repo.git
|
||||
|
||||
# Verify new URL
|
||||
git remote -v
|
||||
|
||||
# Test connection
|
||||
git fetch
|
||||
```
|
||||
|
||||
### Bulk Update Script
|
||||
|
||||
If you have many repositories, use this script:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# migrate-git-urls.sh - Update all Git remotes from dedicated to passthrough
|
||||
|
||||
# Find all git repositories in current directory and subdirectories
|
||||
find . -type d -name '.git' | while read gitdir; do
|
||||
repo=$(dirname "$gitdir")
|
||||
echo "Processing: $repo"
|
||||
|
||||
cd "$repo"
|
||||
|
||||
# Get current origin URL
|
||||
current_url=$(git remote get-url origin 2>/dev/null)
|
||||
|
||||
# Check if it's the old format (with :2222)
|
||||
if [[ $current_url == *":2222/"* ]]; then
|
||||
# Convert to new format
|
||||
new_url=$(echo "$current_url" | sed 's|ssh://git@git.jnss.me:2222/|git@git.jnss.me:|')
|
||||
|
||||
echo " Old: $current_url"
|
||||
echo " New: $new_url"
|
||||
|
||||
git remote set-url origin "$new_url"
|
||||
echo " ✅ Updated"
|
||||
else
|
||||
echo " ℹ️ Already using correct format or not Gitea"
|
||||
fi
|
||||
|
||||
cd - > /dev/null
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "Migration complete!"
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
chmod +x migrate-git-urls.sh
|
||||
./migrate-git-urls.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration: Passthrough → Dedicated
|
||||
|
||||
If you need to switch back to dedicated mode:
|
||||
|
||||
### 1. Update Configuration
|
||||
|
||||
Edit `host_vars/arch-vps/main.yml`:
|
||||
```yaml
|
||||
gitea_ssh_mode: "dedicated"
|
||||
```
|
||||
|
||||
### 2. Deploy
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory/hosts.yml rick-infra.yml --limit arch-vps
|
||||
```
|
||||
|
||||
### 3. Update Git Remotes
|
||||
|
||||
```bash
|
||||
# Update to dedicated format (with :2222 port)
|
||||
git remote set-url origin ssh://git@git.jnss.me:2222/username/repo.git
|
||||
|
||||
# Test connection
|
||||
ssh -T -p 2222 git@git.jnss.me
|
||||
git fetch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URL Format Reference
|
||||
|
||||
### Passthrough Mode (Port 22)
|
||||
```bash
|
||||
# Clone
|
||||
git clone git@git.jnss.me:username/repo.git
|
||||
|
||||
# Add remote
|
||||
git remote add origin git@git.jnss.me:username/repo.git
|
||||
|
||||
# SSH test
|
||||
ssh -T git@git.jnss.me
|
||||
```
|
||||
|
||||
### Dedicated Mode (Port 2222)
|
||||
```bash
|
||||
# Clone
|
||||
git clone ssh://git@git.jnss.me:2222/username/repo.git
|
||||
|
||||
# Add remote
|
||||
git remote add origin ssh://git@git.jnss.me:2222/username/repo.git
|
||||
|
||||
# SSH test
|
||||
ssh -T -p 2222 git@git.jnss.me
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### After Migration, Git Operations Fail
|
||||
|
||||
**Symptom**: `git push` fails with "Permission denied" or "Connection refused"
|
||||
|
||||
**Solution**:
|
||||
1. Check your remote URL format:
|
||||
```bash
|
||||
git remote -v
|
||||
```
|
||||
|
||||
2. Update if needed:
|
||||
```bash
|
||||
# For passthrough (no port)
|
||||
git remote set-url origin git@git.jnss.me:username/repo.git
|
||||
|
||||
# For dedicated (with port)
|
||||
git remote set-url origin ssh://git@git.jnss.me:2222/username/repo.git
|
||||
```
|
||||
|
||||
3. Test SSH connection:
|
||||
```bash
|
||||
# Passthrough
|
||||
ssh -T git@git.jnss.me
|
||||
|
||||
# Dedicated
|
||||
ssh -T -p 2222 git@git.jnss.me
|
||||
```
|
||||
|
||||
### SSH Key Not Recognized After Migration
|
||||
|
||||
**Symptom**: "Permission denied (publickey)"
|
||||
|
||||
**Cause**: SSH keys are stored in Gitea's database, not affected by mode change.
|
||||
|
||||
**Solution**:
|
||||
1. Verify your SSH key is in Gitea:
|
||||
- Log into Gitea web interface
|
||||
- Go to Settings → SSH/GPG Keys
|
||||
- Check your key is listed
|
||||
|
||||
2. Test key locally:
|
||||
```bash
|
||||
ssh-add -l # List loaded keys
|
||||
```
|
||||
|
||||
3. Try with explicit key:
|
||||
```bash
|
||||
ssh -T -i ~/.ssh/id_ed25519 git@git.jnss.me
|
||||
```
|
||||
|
||||
### Port 2222 Still Open After Switching to Passthrough
|
||||
|
||||
**Symptom**: `nc -zv git.jnss.me 2222` succeeds
|
||||
|
||||
**Cause**: Gitea service may still be running on port 2222
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# On the server
|
||||
systemctl restart gitea
|
||||
ss -tlnp | grep 2222 # Should show nothing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After migration, verify:
|
||||
|
||||
- [ ] SSH connection works: `ssh -T git@git.jnss.me` (passthrough) or `ssh -T -p 2222 git@git.jnss.me` (dedicated)
|
||||
- [ ] Can clone repository with new URL format
|
||||
- [ ] Can push commits to repository
|
||||
- [ ] fail2ban is active: `fail2ban-client status sshd` (passthrough) or `fail2ban-client status gitea-ssh` (dedicated)
|
||||
- [ ] Firewall configured correctly: `nft list ruleset | grep 2222` (should show nothing in passthrough)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **Both modes are fully supported** - choose what works best for your setup
|
||||
- **No data loss** - repositories, users, and SSH keys are unaffected by mode changes
|
||||
- **Gradual migration** - you can update remote URLs at your own pace (old URLs may still work for a short time)
|
||||
- **Team coordination** - if you're in a team, coordinate the migration so everyone updates their URLs
|
||||
|
||||
---
|
||||
|
||||
**Rick-Infra Gitea SSH Migration Guide**
|
||||
Switch between passthrough and dedicated SSH modes safely.
|
||||
367
docs/service-domain-configuration.md
Normal file
367
docs/service-domain-configuration.md
Normal file
@@ -0,0 +1,367 @@
|
||||
# Service Domain Configuration Standard
|
||||
|
||||
Standard pattern for domain configuration in rick-infra service roles.
|
||||
|
||||
## Architecture Philosophy
|
||||
|
||||
Rick-infra follows a **direct domain specification** pattern for service configuration:
|
||||
|
||||
```yaml
|
||||
# Direct and explicit
|
||||
service_domain: "subdomain.jnss.me"
|
||||
|
||||
# NOT this (complex and inflexible)
|
||||
service_subdomain: "subdomain"
|
||||
service_domain: "{{ caddy_domain }}"
|
||||
service_full_domain: "{{ service_subdomain }}.{{ service_domain }}"
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Simplicity**: One variable instead of three
|
||||
2. **Flexibility**: Can use any domain (subdomain, root, or completely different)
|
||||
3. **Explicitness**: Clear what domain the service uses
|
||||
4. **No Forced Inheritance**: Not tied to infrastructure `caddy_domain`
|
||||
5. **Consistency**: All services follow the same pattern
|
||||
|
||||
---
|
||||
|
||||
## Standard Pattern
|
||||
|
||||
### Basic Service (Single Domain)
|
||||
|
||||
For services that only need one domain:
|
||||
|
||||
```yaml
|
||||
# roles/service/defaults/main.yml
|
||||
service_domain: "service.jnss.me"
|
||||
|
||||
# host_vars/host/main.yml (explicit override)
|
||||
service_domain: "service.jnss.me"
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- Authentik: `authentik_domain: "auth.jnss.me"`
|
||||
- Nextcloud: `nextcloud_domain: "cloud.jnss.me"`
|
||||
|
||||
### Advanced Service (Multiple Domains)
|
||||
|
||||
For services that need separate domains for different purposes:
|
||||
|
||||
```yaml
|
||||
# roles/service/defaults/main.yml
|
||||
service_http_domain: "service.jnss.me" # Web interface
|
||||
service_api_domain: "api.jnss.me" # API endpoint
|
||||
service_ssh_domain: "jnss.me" # SSH/CLI operations
|
||||
|
||||
# host_vars/host/main.yml (explicit override)
|
||||
service_http_domain: "service.jnss.me"
|
||||
service_api_domain: "api.jnss.me"
|
||||
service_ssh_domain: "jnss.me"
|
||||
```
|
||||
|
||||
**Example:**
|
||||
- Gitea:
|
||||
- `gitea_http_domain: "git.jnss.me"` (web interface)
|
||||
- `gitea_ssh_domain: "jnss.me"` (Git operations)
|
||||
|
||||
---
|
||||
|
||||
## Usage in Templates
|
||||
|
||||
### Caddy Configuration
|
||||
|
||||
```jinja
|
||||
# roles/service/templates/service.caddy.j2
|
||||
{{ service_domain }} {
|
||||
reverse_proxy 127.0.0.1:{{ service_port }}
|
||||
}
|
||||
```
|
||||
|
||||
### Application Configuration
|
||||
|
||||
```jinja
|
||||
# roles/service/templates/service.conf.j2
|
||||
[server]
|
||||
DOMAIN = {{ service_domain }}
|
||||
ROOT_URL = https://{{ service_domain }}/
|
||||
```
|
||||
|
||||
### Task Display Messages
|
||||
|
||||
```yaml
|
||||
# roles/service/tasks/main.yml
|
||||
- name: Display service information
|
||||
debug:
|
||||
msg: |
|
||||
🌐 Web Interface: https://{{ service_domain }}
|
||||
📍 Access your service at the domain above
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Domain Selection Guidelines
|
||||
|
||||
### Use Root Domain When:
|
||||
- Service is the primary purpose of the infrastructure
|
||||
- You want cleaner URLs (e.g., SSH: `git@jnss.me` vs `git@git.jnss.me`)
|
||||
- Industry standard uses root domain (e.g., GitHub uses `github.com` for SSH)
|
||||
|
||||
### Use Subdomain When:
|
||||
- Service is one of many
|
||||
- You want explicit service identification
|
||||
- You need clear separation between services
|
||||
|
||||
### Use Different Domain When:
|
||||
- Service needs to be on a different apex domain
|
||||
- External service integration requires specific domain
|
||||
- Multi-domain setup for geographical distribution
|
||||
|
||||
---
|
||||
|
||||
## Examples by Service Type
|
||||
|
||||
### Identity/Auth Service
|
||||
```yaml
|
||||
authentik_domain: "auth.jnss.me"
|
||||
```
|
||||
**Rationale**: Auth subdomain is an industry standard
|
||||
|
||||
### Storage Service
|
||||
```yaml
|
||||
nextcloud_domain: "cloud.jnss.me"
|
||||
```
|
||||
**Rationale**: "cloud" clearly indicates storage/sync service
|
||||
|
||||
### Git Service
|
||||
```yaml
|
||||
gitea_http_domain: "git.jnss.me" # Web UI
|
||||
gitea_ssh_domain: "jnss.me" # SSH operations
|
||||
```
|
||||
**Rationale**:
|
||||
- HTTP uses `git.` for clarity
|
||||
- SSH uses root domain to avoid `git@git.jnss.me` redundancy
|
||||
- Matches GitHub/GitLab pattern
|
||||
|
||||
### Monitoring Service
|
||||
```yaml
|
||||
grafana_domain: "monitor.jnss.me"
|
||||
prometheus_domain: "metrics.jnss.me"
|
||||
```
|
||||
**Rationale**: Different subdomains for different monitoring tools
|
||||
|
||||
---
|
||||
|
||||
## Configuration Layers
|
||||
|
||||
### 1. Role Defaults (`roles/service/defaults/main.yml`)
|
||||
|
||||
Provide sensible defaults:
|
||||
|
||||
```yaml
|
||||
# Option A: Use specific domain (explicit)
|
||||
service_domain: "service.jnss.me"
|
||||
|
||||
# Option B: Use caddy_domain if it makes sense (flexible)
|
||||
service_domain: "service.{{ caddy_domain | default('localhost') }}"
|
||||
|
||||
# Recommendation: Use Option A for clarity
|
||||
```
|
||||
|
||||
### 2. Host Variables (`host_vars/hostname/main.yml`)
|
||||
|
||||
**Always explicitly set** in production:
|
||||
|
||||
```yaml
|
||||
# =================================================================
|
||||
# Service Configuration
|
||||
# =================================================================
|
||||
service_domain: "service.jnss.me"
|
||||
```
|
||||
|
||||
**Why explicit?**
|
||||
- Clear what domain is configured
|
||||
- Easy to change without understanding defaults
|
||||
- Easier to audit configuration
|
||||
- Documentation in configuration itself
|
||||
|
||||
### 3. Group Variables (`group_vars/production/main.yml`)
|
||||
|
||||
For settings shared across production hosts:
|
||||
|
||||
```yaml
|
||||
# Common production settings
|
||||
service_enable_ssl: true
|
||||
service_require_auth: true
|
||||
|
||||
# Generally avoid setting domains in group_vars
|
||||
# (domains are usually host-specific)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
### ❌ Subdomain Composition
|
||||
|
||||
```yaml
|
||||
# DON'T DO THIS
|
||||
service_subdomain: "service"
|
||||
service_domain: "{{ caddy_domain }}"
|
||||
service_full_domain: "{{ service_subdomain }}.{{ service_domain }}"
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Complex (3 variables for 1 domain)
|
||||
- Inflexible (can't use root or different domains)
|
||||
- Forces inheritance from infrastructure variable
|
||||
- Inconsistent with other services
|
||||
|
||||
### ❌ Implicit Inheritance
|
||||
|
||||
```yaml
|
||||
# DON'T DO THIS
|
||||
service_domain: "{{ caddy_domain }}"
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Not explicit what domain is used
|
||||
- Harder to change
|
||||
- Hides actual configuration
|
||||
- Requires understanding of infrastructure variables
|
||||
|
||||
### ❌ Mixed Patterns
|
||||
|
||||
```yaml
|
||||
# DON'T DO THIS
|
||||
authentik_domain: "auth.jnss.me" # Direct
|
||||
nextcloud_subdomain: "cloud" # Composition
|
||||
service_domain: "{{ caddy_domain }}" # Inheritance
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Inconsistent
|
||||
- Confusing for maintainers
|
||||
- Different patterns for same purpose
|
||||
|
||||
---
|
||||
|
||||
## Migration from Old Pattern
|
||||
|
||||
If you have services using the old subdomain composition pattern:
|
||||
|
||||
### Step 1: Identify Current Variables
|
||||
|
||||
```yaml
|
||||
# Old pattern
|
||||
service_subdomain: "service"
|
||||
service_domain: "{{ caddy_domain }}"
|
||||
service_full_domain: "{{ service_subdomain }}.{{ service_domain }}"
|
||||
```
|
||||
|
||||
### Step 2: Replace with Direct Domain
|
||||
|
||||
```yaml
|
||||
# New pattern
|
||||
service_domain: "service.jnss.me"
|
||||
```
|
||||
|
||||
### Step 3: Update Template References
|
||||
|
||||
```jinja
|
||||
# Old
|
||||
{{ service_full_domain }}
|
||||
|
||||
# New
|
||||
{{ service_domain }}
|
||||
```
|
||||
|
||||
### Step 4: Remove Unused Variables
|
||||
|
||||
Delete `service_subdomain` and `service_full_domain` from defaults.
|
||||
|
||||
### Step 5: Add Explicit Host Configuration
|
||||
|
||||
```yaml
|
||||
# host_vars/arch-vps/main.yml
|
||||
service_domain: "service.jnss.me"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Domain Configuration
|
||||
|
||||
### Verify Caddy Configuration
|
||||
|
||||
```bash
|
||||
# Check generated Caddy config
|
||||
cat /etc/caddy/sites-enabled/service.caddy
|
||||
|
||||
# Test Caddy configuration syntax
|
||||
caddy validate --config /etc/caddy/Caddyfile
|
||||
|
||||
# Check TLS certificate
|
||||
curl -I https://service.jnss.me
|
||||
```
|
||||
|
||||
### Verify Application Configuration
|
||||
|
||||
```bash
|
||||
# Check service configuration
|
||||
cat /etc/service/config.ini | grep -i domain
|
||||
|
||||
# Test service accessibility
|
||||
curl https://service.jnss.me
|
||||
```
|
||||
|
||||
### Verify DNS Resolution
|
||||
|
||||
```bash
|
||||
# Check DNS resolution
|
||||
dig service.jnss.me
|
||||
|
||||
# Test connectivity
|
||||
nc -zv service.jnss.me 443
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist for New Services
|
||||
|
||||
When creating a new service role:
|
||||
|
||||
- [ ] Use direct domain specification (not subdomain composition)
|
||||
- [ ] Define domain(s) in `roles/service/defaults/main.yml`
|
||||
- [ ] Add explicit domain(s) to host_vars
|
||||
- [ ] Update all templates to use domain variable(s)
|
||||
- [ ] Document domain configuration in role README
|
||||
- [ ] Follow naming convention: `service_domain` or `service_[type]_domain`
|
||||
- [ ] Test with different domain configurations
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Standard Pattern:**
|
||||
```yaml
|
||||
# Defaults: Provide reasonable default
|
||||
service_domain: "service.jnss.me"
|
||||
|
||||
# Host vars: Always explicit in production
|
||||
service_domain: "service.jnss.me"
|
||||
|
||||
# Templates: Use variable directly
|
||||
{{ service_domain }}
|
||||
```
|
||||
|
||||
**Key Principles:**
|
||||
1. Direct and explicit
|
||||
2. One variable per domain
|
||||
3. No forced inheritance
|
||||
4. Consistent across all services
|
||||
5. Flexible for any domain pattern
|
||||
|
||||
---
|
||||
|
||||
**Rick-Infra Domain Configuration Standard**
|
||||
Simple, flexible, and consistent domain configuration for all services.
|
||||
Reference in New Issue
Block a user