Implement modular nftables architecture and Gitea SSH firewall management
- Restructure security playbook with modular nftables loader - Base rules loaded first, service rules second, drop rule last - Add Gitea self-contained firewall management (port 2222) - Add fail2ban protection for Gitea SSH brute force attacks - Update documentation with new firewall architecture - Create comprehensive Gitea deployment and testing guide This enables self-contained service roles to manage their own firewall rules without modifying the central security playbook. Each service deploys rules to /etc/nftables.d/ which are loaded before the final drop rule, maintaining the defense-in-depth security model.
This commit is contained in:
@@ -9,15 +9,19 @@ Self-contained Gitea Git service for rick-infra following the established archit
|
||||
- ✅ **PostgreSQL integration**: Uses shared PostgreSQL infrastructure
|
||||
- ✅ **Caddy integration**: Deploys reverse proxy configuration
|
||||
- ✅ **Security hardened**: SystemD restrictions and secure defaults
|
||||
- ✅ **Firewall management**: Automatically configures nftables for SSH access
|
||||
- ✅ **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, SSH on :2222 (localhost)
|
||||
- **Web access**: https://git.domain.com (via Caddy)
|
||||
- **Network**: HTTP on :3000 (localhost), SSH on :2222 (public)
|
||||
- **Web access**: https://git.domain.com (via Caddy reverse proxy)
|
||||
- **SSH access**: ssh://git@git.domain.com:2222
|
||||
- **Firewall**: Port 2222 automatically opened via nftables
|
||||
- **Security**: fail2ban monitors and blocks SSH brute force attempts
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -42,6 +46,9 @@ gitea_db_password: "{{ vault_gitea_db_password }}"
|
||||
gitea_app_name: "Gitea: Git with a cup of tea"
|
||||
gitea_disable_registration: false
|
||||
gitea_enable_lfs: true
|
||||
|
||||
# Firewall and Security
|
||||
gitea_manage_firewall: true # Automatically manage nftables rules
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -56,12 +63,94 @@ gitea_enable_lfs: true
|
||||
- Caddy web server (for HTTPS access)
|
||||
- Vault password: `vault_gitea_db_password`
|
||||
|
||||
## SSH Access
|
||||
|
||||
Gitea provides Git repository access via SSH on port 2222:
|
||||
|
||||
```bash
|
||||
# Clone a repository
|
||||
git clone ssh://git@git.jnss.me:2222/username/repository.git
|
||||
|
||||
# Or add as remote
|
||||
git remote add origin ssh://git@git.jnss.me:2222/username/repository.git
|
||||
```
|
||||
|
||||
### SSH Key Setup
|
||||
|
||||
1. **Generate SSH key** (if you don't have one):
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "your_email@example.com"
|
||||
```
|
||||
|
||||
2. **Copy your public key**:
|
||||
```bash
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
3. **Add to Gitea**:
|
||||
- Log into Gitea web interface
|
||||
- Go to Settings → SSH/GPG Keys
|
||||
- Click "Add Key"
|
||||
- Paste your public key
|
||||
|
||||
4. **Test SSH connection**:
|
||||
```bash
|
||||
ssh -T -p 2222 git@git.jnss.me
|
||||
```
|
||||
|
||||
## Firewall and Security
|
||||
|
||||
### Automatic Firewall Management
|
||||
|
||||
The Gitea role automatically manages firewall rules via nftables:
|
||||
|
||||
- **Port 2222** is opened automatically when Gitea is deployed
|
||||
- Firewall rules are stored in `/etc/nftables.d/gitea.nft`
|
||||
- Rules are integrated with the main security playbook configuration
|
||||
- To disable automatic firewall management, set `gitea_manage_firewall: false`
|
||||
|
||||
### fail2ban Protection
|
||||
|
||||
SSH brute force protection is automatically configured:
|
||||
|
||||
- **Jail**: `gitea-ssh` monitors Gitea SSH authentication attempts
|
||||
- **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:
|
||||
```bash
|
||||
# Check Gitea SSH jail status
|
||||
fail2ban-client status gitea-ssh
|
||||
|
||||
# View banned IPs
|
||||
fail2ban-client get gitea-ssh banned
|
||||
|
||||
# Unban an IP if needed
|
||||
fail2ban-client set gitea-ssh unbanip 203.0.113.100
|
||||
```
|
||||
|
||||
### Firewall Verification
|
||||
|
||||
```bash
|
||||
# List active nftables rules
|
||||
nft list ruleset
|
||||
|
||||
# Check if Gitea SSH port is open
|
||||
nft list ruleset | grep 2222
|
||||
|
||||
# Verify from external machine
|
||||
nc -zv git.jnss.me 2222
|
||||
```
|
||||
|
||||
## 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)
|
||||
- Independent lifecycle from other services
|
||||
|
||||
---
|
||||
|
||||
@@ -66,6 +66,13 @@ gitea_require_signin: false
|
||||
# SSH settings
|
||||
gitea_start_ssh_server: true
|
||||
|
||||
# =================================================================
|
||||
# Firewall Configuration
|
||||
# =================================================================
|
||||
|
||||
# Firewall management
|
||||
gitea_manage_firewall: true # Set to false if firewall is managed externally
|
||||
|
||||
# =================================================================
|
||||
# Infrastructure Dependencies (Read-only)
|
||||
# =================================================================
|
||||
|
||||
@@ -15,4 +15,16 @@
|
||||
systemd:
|
||||
name: caddy
|
||||
state: reloaded
|
||||
when: caddy_service_enabled | default(false)
|
||||
when: caddy_service_enabled | default(false)
|
||||
|
||||
- name: reload nftables
|
||||
systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
# Safety: only reload if service is active
|
||||
when: ansible_connection != 'local'
|
||||
|
||||
- name: restart fail2ban
|
||||
systemd:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
75
roles/gitea/tasks/fail2ban.yml
Normal file
75
roles/gitea/tasks/fail2ban.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
# Gitea fail2ban Configuration - Rick-Infra
|
||||
# Protects Gitea SSH from brute force attacks
|
||||
# Integrates with system fail2ban service
|
||||
|
||||
- name: Install fail2ban
|
||||
pacman:
|
||||
name: fail2ban
|
||||
state: present
|
||||
|
||||
- name: Create Gitea fail2ban filter
|
||||
copy:
|
||||
content: |
|
||||
# Fail2ban filter for Gitea SSH authentication failures
|
||||
# Rick-Infra: Gitea role
|
||||
|
||||
[Definition]
|
||||
# Match failed authentication attempts in Gitea logs
|
||||
failregex = .*(Failed authentication attempt|authentication failed|Invalid user|Failed login attempt).*from\s+<HOST>
|
||||
.*level=warning.*msg=.*authentication.*failed.*ip=<HOST>
|
||||
|
||||
ignoreregex =
|
||||
dest: /etc/fail2ban/filter.d/gitea-ssh.conf
|
||||
mode: '0644'
|
||||
backup: yes
|
||||
notify: restart fail2ban
|
||||
|
||||
- name: Ensure fail2ban jail.local exists
|
||||
file:
|
||||
path: /etc/fail2ban/jail.local
|
||||
state: touch
|
||||
mode: '0644'
|
||||
modification_time: preserve
|
||||
access_time: preserve
|
||||
|
||||
- name: Add Gitea SSH jail to fail2ban
|
||||
blockinfile:
|
||||
path: /etc/fail2ban/jail.local
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - Gitea SSH"
|
||||
block: |
|
||||
# Gitea SSH Protection - Rick-Infra
|
||||
[gitea-ssh]
|
||||
enabled = true
|
||||
port = {{ gitea_ssh_port }}
|
||||
filter = gitea-ssh
|
||||
logpath = {{ gitea_home }}/log/gitea.log
|
||||
maxretry = 5
|
||||
findtime = 600
|
||||
bantime = 3600
|
||||
banaction = nftables
|
||||
backup: yes
|
||||
notify: restart fail2ban
|
||||
|
||||
- name: Enable and start fail2ban service
|
||||
systemd:
|
||||
name: fail2ban
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: Add fail2ban restart handler
|
||||
meta: flush_handlers
|
||||
|
||||
- name: Display fail2ban status for Gitea
|
||||
debug:
|
||||
msg: |
|
||||
🛡️ fail2ban configured for Gitea SSH
|
||||
📍 Filter: /etc/fail2ban/filter.d/gitea-ssh.conf
|
||||
📍 Jail: gitea-ssh (in /etc/fail2ban/jail.local)
|
||||
🔒 Protection: Port {{ gitea_ssh_port }}
|
||||
⏱️ Ban time: 1 hour (3600 seconds)
|
||||
🔢 Max retries: 5 attempts in 10 minutes
|
||||
|
||||
Check status: fail2ban-client status gitea-ssh
|
||||
|
||||
# Rick-Infra: Self-contained fail2ban protection per role
|
||||
51
roles/gitea/tasks/firewall.yml
Normal file
51
roles/gitea/tasks/firewall.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
# Gitea Firewall Configuration - Rick-Infra
|
||||
# Self-contained firewall management for Gitea SSH access
|
||||
# Opens port 2222 for Gitea's SSH server
|
||||
|
||||
- name: Install nftables (if not present)
|
||||
pacman:
|
||||
name: nftables
|
||||
state: present
|
||||
|
||||
- name: Create nftables rules directory
|
||||
file:
|
||||
path: /etc/nftables.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy Gitea nftables rules
|
||||
template:
|
||||
src: gitea.nft.j2
|
||||
dest: /etc/nftables.d/50-gitea.nft
|
||||
mode: '0644'
|
||||
notify: reload nftables
|
||||
register: gitea_nft_deployed
|
||||
|
||||
- name: Validate nftables loader configuration
|
||||
command: nft -c -f /etc/nftables-load.conf
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
register: nft_validation
|
||||
|
||||
- name: Display nftables validation results
|
||||
debug:
|
||||
msg: "{{ 'nftables configuration valid' if nft_validation.rc == 0 else 'nftables validation failed: ' + nft_validation.stderr }}"
|
||||
when: nft_validation is defined
|
||||
|
||||
- name: Enable and start nftables service
|
||||
systemd:
|
||||
name: nftables
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: Display Gitea firewall status
|
||||
debug:
|
||||
msg: |
|
||||
🔥 Gitea firewall configuration deployed
|
||||
📍 Rule file: /etc/nftables.d/50-gitea.nft
|
||||
🔓 Port opened: {{ gitea_ssh_port }} (Gitea SSH)
|
||||
|
||||
⚠️ Note: nftables will reload automatically via handler
|
||||
|
||||
# Rick-Infra: Self-contained firewall management per role
|
||||
@@ -16,6 +16,18 @@
|
||||
name: gitea
|
||||
state: present
|
||||
|
||||
# Firewall configuration - self-managed by Gitea role
|
||||
- name: Configure firewall for Gitea SSH
|
||||
import_tasks: firewall.yml
|
||||
tags: ['firewall']
|
||||
when: gitea_manage_firewall | default(true)
|
||||
|
||||
# fail2ban protection - self-managed by Gitea role
|
||||
- name: Configure fail2ban for Gitea SSH
|
||||
import_tasks: fail2ban.yml
|
||||
tags: ['fail2ban', 'security']
|
||||
when: gitea_manage_firewall | default(true)
|
||||
|
||||
- name: Install Git
|
||||
pacman:
|
||||
name: git
|
||||
|
||||
11
roles/gitea/templates/gitea.nft.j2
Normal file
11
roles/gitea/templates/gitea.nft.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
# Gitea SSH Firewall Rules - Rick-Infra
|
||||
# Generated by Ansible Gitea role
|
||||
# Allows incoming SSH connections on port {{ gitea_ssh_port }}
|
||||
#
|
||||
# This file is loaded BEFORE the final drop rule (99-drop.nft)
|
||||
# Filename: 50-gitea.nft (ensures proper load order)
|
||||
|
||||
# Add Gitea SSH port to the input chain
|
||||
add rule inet filter input tcp dport {{ gitea_ssh_port }} ct state new accept comment "Gitea SSH (Port {{ gitea_ssh_port }})"
|
||||
|
||||
# Rick-Infra: Self-contained firewall rule for Gitea SSH access
|
||||
Reference in New Issue
Block a user