ansible
This commit is contained in:
parent
7974e36c1b
commit
b81f7e15b2
41 changed files with 1632 additions and 0 deletions
215
ansible/README.md
Normal file
215
ansible/README.md
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# ASXP.IO Ansible
|
||||
|
||||
Post-cloud-init configuration for asxpio servers.
|
||||
|
||||
## Playbooks
|
||||
|
||||
### setup-servers.yaml
|
||||
Post-deployment verification (cloud-init handles base setup).
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/setup-servers.yaml
|
||||
```
|
||||
|
||||
### wireguard.yaml
|
||||
Deploy WireGuard VPN server on asxpio-ops (10.20.0.10).
|
||||
|
||||
```bash
|
||||
# Initial deployment
|
||||
ansible-playbook playbooks/wireguard.yaml
|
||||
|
||||
# Add new clients to inventory/host_vars/asxpio-ops.yaml, then re-run
|
||||
ansible-playbook playbooks/wireguard.yaml
|
||||
```
|
||||
|
||||
Client configs fetched to `./wireguard-configs/asxpio-ops/`
|
||||
|
||||
### wireguard-client.yaml
|
||||
Deploy WireGuard client configs to servers.
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/wireguard-client.yaml
|
||||
```
|
||||
|
||||
Deploys asxpio-services.conf (10.20.0.20) to asxpio-services server.
|
||||
|
||||
### ssh-harden.yaml
|
||||
Harden SSH configuration with network restrictions and strong cryptography.
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/ssh-harden.yaml
|
||||
```
|
||||
|
||||
**Security measures:**
|
||||
- Shell access (asxpi/deploy/root): Only from VPC (10.10.0.0/20) or WireGuard (10.20.0.0/24)
|
||||
- Forgejo git user: Public access on asxpio-ops for git operations (ssh://forgejo@git.asxp.io/...)
|
||||
- Strong crypto: ChaCha20-Poly1305, AES-GCM, RSA-SHA2, Ed25519
|
||||
- iptables: SSH blocked on asxpio-services except from trusted networks
|
||||
- Enhanced sshd_config: MaxAuthTries 3, MaxSessions 5, no X11 forwarding
|
||||
|
||||
**Access after hardening:**
|
||||
- Admin access: Connect via WireGuard VPN first, then SSH
|
||||
- Git operations: Public access to asxpio-ops (Forgejo handles authentication)
|
||||
- Internal VPC: Servers can SSH to each other via 10.10.0.0/20
|
||||
|
||||
### deploy-traefik.yaml
|
||||
Deploy Traefik reverse proxy with Let's Encrypt on asxpio-ops.
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/deploy-traefik.yaml
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Automatic HTTPS with Let's Encrypt (HTTP challenge)
|
||||
- Dashboard accessible only via WireGuard (http://10.20.0.10:8080)
|
||||
- Shared Docker network for service discovery
|
||||
- Certificates stored in `/opt/traefik/letsencrypt/`
|
||||
- Logs in `/opt/traefik/logs/`
|
||||
|
||||
### deploy-forgejo.yaml
|
||||
Deploy fresh Forgejo Git server on asxpio-ops (no data migration).
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/deploy-forgejo.yaml
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
- Forgejo 12.x with SQLite database
|
||||
- HTTP: port 3000 (internal), exposed via Traefik at git.asxp.io
|
||||
- SSH: port 2222 (external) → 22 (container)
|
||||
- Actions enabled with 365-day artifact retention
|
||||
- LFS enabled, OAuth2 enabled
|
||||
- Data stored in `/opt/forgejo/data/`
|
||||
|
||||
**Important:** Configure secrets in `inventory/host_vars/asxpio-ops.yaml`:
|
||||
```yaml
|
||||
forgejo_secret_key: "YOUR-SECRET-KEY"
|
||||
forgejo_internal_token: "YOUR-INTERNAL-TOKEN"
|
||||
forgejo_jwt_secret: "YOUR-JWT-SECRET"
|
||||
forgejo_lfs_jwt_secret: "YOUR-LFS-JWT-SECRET"
|
||||
letsencrypt_email: "admin@asxp.io"
|
||||
```
|
||||
|
||||
### deploy-forgejo-runner.yaml
|
||||
Deploy Forgejo Runner on asxpio-services for CI/CD.
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/deploy-forgejo-runner.yaml
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
- Forgejo Runner 11.x with Docker execution
|
||||
- 14 pre-configured labels (ubuntu, debian, arch, node, python, nginx, alpine)
|
||||
- Connects to Forgejo via VPC (http://10.10.0.10:3000)
|
||||
- Requires manual registration after deployment
|
||||
|
||||
**Post-deployment registration:**
|
||||
```bash
|
||||
# 1. Get registration token from Forgejo:
|
||||
# Site Administration > Actions > Runners > Generate Token
|
||||
|
||||
# 2. Register the runner:
|
||||
ssh asxpio-services
|
||||
docker exec -it forgejo-runner forgejo-runner register --no-interactive \
|
||||
--token YOUR_REGISTRATION_TOKEN \
|
||||
--name runner-asxpio-services \
|
||||
--instance http://10.10.0.10:3000
|
||||
|
||||
# 3. Restart runner:
|
||||
cd /opt/forgejo-runner && docker compose restart
|
||||
```
|
||||
|
||||
### backup-forgejo.yaml
|
||||
Backup Forgejo from old server (10.0.0.1).
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/backup-forgejo.yaml
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
1. Stops Forgejo service temporarily
|
||||
2. Creates dump: `forgejo dump -c /etc/forgejo/app.ini`
|
||||
3. Creates additional archive of:
|
||||
- Database: `/srv/gitea/gitea.db`
|
||||
- Repositories: `/srv/gitea/data/`
|
||||
- SSH keys: `/var/lib/forgejo/.ssh/`
|
||||
- Runner registration: `/var/lib/forgejo-runner/.runner`
|
||||
4. Fetches backups to local machine: `/tmp/forgejo-backups/`
|
||||
5. Restarts Forgejo service
|
||||
|
||||
**Backup includes:** Database, repositories (~513MB), configuration, SSH keys, runner registration.
|
||||
|
||||
### restore-forgejo.yaml
|
||||
Restore Forgejo backup to new server (10.20.0.10 via WireGuard).
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/restore-forgejo.yaml
|
||||
```
|
||||
|
||||
**Prerequisites:**
|
||||
- Traefik deployed and running
|
||||
- Forgejo deployed (fresh instance)
|
||||
- Backup files in `/tmp/forgejo-backups/`
|
||||
|
||||
**What it does:**
|
||||
1. Stops Forgejo container
|
||||
2. Extracts dump backup to `/opt/forgejo/`
|
||||
3. Restores database, repositories, SSH keys
|
||||
4. Sets correct ownership (UID 1000)
|
||||
5. Starts Forgejo container
|
||||
6. Waits for health check
|
||||
|
||||
**Post-restore verification:**
|
||||
```bash
|
||||
# Test web interface
|
||||
curl -I https://git.asxp.io
|
||||
|
||||
# Test git clone
|
||||
git clone https://git.asxp.io/asxpi/infrastructure.git
|
||||
|
||||
# Test SSH access
|
||||
git clone git@git.asxp.io:asxpi/infrastructure.git
|
||||
```
|
||||
|
||||
## Deployment Order
|
||||
|
||||
### Phase 1: Infrastructure (Prerequisites)
|
||||
```bash
|
||||
# 1. Deploy VPN
|
||||
ansible-playbook playbooks/wireguard.yaml
|
||||
ansible-playbook playbooks/wireguard-client.yaml
|
||||
|
||||
# 2. Harden SSH
|
||||
ansible-playbook playbooks/ssh-harden.yaml
|
||||
|
||||
# 3. Deploy reverse proxy
|
||||
ansible-playbook playbooks/deploy-traefik.yaml
|
||||
```
|
||||
|
||||
### Phase 2: Forgejo Fresh Deployment
|
||||
```bash
|
||||
# 4. Deploy Forgejo (empty instance)
|
||||
ansible-playbook playbooks/deploy-forgejo.yaml
|
||||
|
||||
# 5. Deploy runner
|
||||
ansible-playbook playbooks/deploy-forgejo-runner.yaml
|
||||
|
||||
# 6. Register runner (manual step - see instructions above)
|
||||
```
|
||||
|
||||
### Phase 3: Data Migration (When Ready)
|
||||
```bash
|
||||
# 7. Backup from old server
|
||||
ansible-playbook playbooks/backup-forgejo.yaml
|
||||
|
||||
# 8. Restore to new server
|
||||
ansible-playbook playbooks/restore-forgejo.yaml
|
||||
|
||||
# 9. Update DNS: git.asxp.io → 65.109.14.216
|
||||
# 10. Re-register runner with migrated instance
|
||||
```
|
||||
|
||||
## Inventory
|
||||
|
||||
- **asxpio-ops** (65.109.14.216) - Traefik, Forgejo, monitoring, WireGuard server
|
||||
- **asxpio-services** (95.217.19.240) - Forgejo Runner, Immich, game servers, XMPP
|
||||
20
ansible/ansible.cfg
Normal file
20
ansible/ansible.cfg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[defaults]
|
||||
interpreter_python = auto_silent
|
||||
gathering = smart
|
||||
pipelining = True
|
||||
retry_files_enabled = False
|
||||
inventory = inventory/hosts.yaml
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
module_name = shell
|
||||
display_skipped_hosts = no
|
||||
timeout=30
|
||||
remote_user = root
|
||||
|
||||
[connection]
|
||||
pipelining=True
|
||||
|
||||
[ssh_connection]
|
||||
pipelining=True
|
||||
control_path = /tmp/%%r@%%h:%%p
|
||||
ssh_args = -o ControlPersist=10m -o PreferredAuthentications=publickey -o ControlMaster=no -o ForwardAgent=yes
|
||||
20
ansible/group_vars/all.yaml
Normal file
20
ansible/group_vars/all.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
# Shared configuration for all servers
|
||||
|
||||
# Docker network for Traefik
|
||||
traefik_network: traefik
|
||||
|
||||
# Forgejo configuration
|
||||
forgejo_version: "12"
|
||||
forgejo_domain: git.asxp.io
|
||||
forgejo_http_port: 3000
|
||||
forgejo_ssh_port: 2222 # External port (container uses 22)
|
||||
|
||||
# Forgejo Runner configuration
|
||||
forgejo_runner_version: "11"
|
||||
forgejo_runner_capacity: 1
|
||||
forgejo_runner_timeout: 3h
|
||||
|
||||
# Backup configuration
|
||||
backup_dest_dir: /tmp/forgejo-backups
|
||||
backup_old_server: 10.0.0.1
|
||||
47
ansible/inventory/host_vars/asxpio-ops.yaml
Normal file
47
ansible/inventory/host_vars/asxpio-ops.yaml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
# WireGuard VPN Configuration for asxpio-ops
|
||||
wireguard_port: 60101
|
||||
wireguard_interface: wg0
|
||||
wireguard_ipv4: 10.20.0.10
|
||||
wireguard_ipv6: fd69:69:69::1
|
||||
wireguard_ipv4_network: 10.20.0.0/24
|
||||
wireguard_ipv6_network: fd69:69:69::/64
|
||||
|
||||
# Enable UFW firewall rules (set to false to use iptables PostUp/PostDown)
|
||||
wireguard_enable_ufw: false
|
||||
|
||||
# WireGuard clients
|
||||
wireguard_clients:
|
||||
- name: asxpio-services
|
||||
ipv4: 10.20.0.20
|
||||
ipv6: fd69:69:69::20
|
||||
preshared_key: true
|
||||
persistent_keepalive: 25
|
||||
- name: laptop
|
||||
ipv4: 10.20.0.2
|
||||
ipv6: fd69:69:69::2
|
||||
preshared_key: true
|
||||
persistent_keepalive: 25
|
||||
- name: phone
|
||||
ipv4: 10.20.0.3
|
||||
ipv6: fd69:69:69::3
|
||||
preshared_key: true
|
||||
persistent_keepalive: 25
|
||||
- name: pixel9
|
||||
ipv4: 10.20.0.4
|
||||
ipv6: fd69:69:69::4
|
||||
preshared_key: true
|
||||
persistent_keepalive: 25
|
||||
|
||||
# Optional: DNS servers for clients
|
||||
wireguard_dns_servers:
|
||||
- 1.1.1.1
|
||||
- 1.0.0.1
|
||||
|
||||
# Optional: Additional routes for split tunneling
|
||||
# wireguard_additional_routes:
|
||||
# - 192.168.1.0/24
|
||||
|
||||
# SSH Hardening Configuration
|
||||
# Allow public SSH access for Forgejo git operations
|
||||
ssh_allow_public_git: true
|
||||
4
ansible/inventory/host_vars/asxpio-services.yaml
Normal file
4
ansible/inventory/host_vars/asxpio-services.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
# SSH Hardening Configuration
|
||||
# Restrict SSH to VPC and WireGuard networks only
|
||||
ssh_allow_public_git: false
|
||||
45
ansible/inventory/hosts.yaml
Normal file
45
ansible/inventory/hosts.yaml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
# Ansible inventory for asxpio infrastructure
|
||||
# Update IP addresses after running terraform apply
|
||||
|
||||
all:
|
||||
vars:
|
||||
ansible_user: root
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
children:
|
||||
asxpio:
|
||||
vars:
|
||||
# Common variables for all asxpio servers
|
||||
timezone: UTC
|
||||
locale: en_US.UTF-8
|
||||
|
||||
children:
|
||||
ops_servers:
|
||||
hosts:
|
||||
asxpio-ops:
|
||||
ansible_host: 10.20.0.10 #65.109.14.216
|
||||
server_role: ops
|
||||
server_type: cx22
|
||||
services:
|
||||
- traefik
|
||||
- forgejo
|
||||
- grafana
|
||||
- prometheus
|
||||
- loki
|
||||
- static_sites
|
||||
|
||||
services_servers:
|
||||
hosts:
|
||||
asxpio-services:
|
||||
ansible_host: 10.20.0.20 #95.217.15.42
|
||||
server_role: services
|
||||
server_type: cx43
|
||||
services:
|
||||
- forgejo_runner
|
||||
- immich
|
||||
- minecraft
|
||||
- valheim
|
||||
- factorio
|
||||
- prosody
|
||||
- coturn
|
||||
8
ansible/playbooks/backup-forgejo.yaml
Normal file
8
ansible/playbooks/backup-forgejo.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
- name: Backup Forgejo from old server
|
||||
hosts: "{{ backup_old_server }}"
|
||||
become: true
|
||||
vars:
|
||||
ansible_host: "{{ backup_old_server }}"
|
||||
roles:
|
||||
- forgejo_backup
|
||||
6
ansible/playbooks/deploy-forgejo-runner.yaml
Normal file
6
ansible/playbooks/deploy-forgejo-runner.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Deploy Forgejo Runner
|
||||
hosts: asxpio-services
|
||||
become: true
|
||||
roles:
|
||||
- forgejo_runner
|
||||
6
ansible/playbooks/deploy-forgejo.yaml
Normal file
6
ansible/playbooks/deploy-forgejo.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Deploy Forgejo Git server
|
||||
hosts: asxpio-ops
|
||||
become: true
|
||||
roles:
|
||||
- forgejo
|
||||
6
ansible/playbooks/deploy-traefik.yaml
Normal file
6
ansible/playbooks/deploy-traefik.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Deploy Traefik reverse proxy
|
||||
hosts: asxpio-ops
|
||||
become: true
|
||||
roles:
|
||||
- traefik
|
||||
6
ansible/playbooks/restore-forgejo.yaml
Normal file
6
ansible/playbooks/restore-forgejo.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Restore Forgejo to new server
|
||||
hosts: asxpio-ops
|
||||
become: true
|
||||
roles:
|
||||
- forgejo_restore
|
||||
32
ansible/playbooks/setup-servers.yaml
Normal file
32
ansible/playbooks/setup-servers.yaml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# Main playbook for setting up asxpio servers
|
||||
# Usage: ansible-playbook playbooks/setup-servers.yml
|
||||
#
|
||||
# NOTE: Cloud-init handles base system setup:
|
||||
# - System packages (fail2ban, curl, htop, ncdu, tree, etc.)
|
||||
# - User management (asxpi + deploy users with sudo and docker access)
|
||||
# - SSH hardening (key-only auth, prohibit root password)
|
||||
# - Docker installation (docker-ce, docker-ce-cli, containerd.io, docker-compose-plugin)
|
||||
# - Bash prompt styling for asxpi user
|
||||
# - Docker image pruning cron job
|
||||
# - Directory structure creation
|
||||
#
|
||||
# This playbook handles post-cloud-init verification and additional configuration.
|
||||
# Application deployment (Traefik, Forgejo, monitoring, etc.)
|
||||
# will be handled via Forgejo Actions pipelines
|
||||
|
||||
- name: Setup asxpio infrastructure servers
|
||||
hosts: asxpio
|
||||
become: yes
|
||||
gather_facts: yes
|
||||
|
||||
roles:
|
||||
- common
|
||||
|
||||
post_tasks:
|
||||
- name: Display server info
|
||||
debug:
|
||||
msg: |
|
||||
Server {{ inventory_hostname }} post-cloud-init setup complete!
|
||||
Role: {{ server_role }}
|
||||
Next: Deploy services via Forgejo Actions pipelines
|
||||
6
ansible/playbooks/ssh-harden.yaml
Normal file
6
ansible/playbooks/ssh-harden.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Harden SSH configuration
|
||||
hosts: asxpio
|
||||
become: true
|
||||
roles:
|
||||
- ssh_hardening
|
||||
59
ansible/playbooks/wireguard-client.yaml
Normal file
59
ansible/playbooks/wireguard-client.yaml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
- name: Deploy WireGuard client configuration
|
||||
hosts: services_servers
|
||||
become: true
|
||||
vars:
|
||||
# Map each server to its client config name
|
||||
wireguard_client_configs:
|
||||
asxpio-services: asxpio-services
|
||||
tasks:
|
||||
- name: Get client config name for this host
|
||||
ansible.builtin.set_fact:
|
||||
client_config_name: "{{ wireguard_client_configs[inventory_hostname] }}"
|
||||
when: inventory_hostname in wireguard_client_configs
|
||||
|
||||
- name: Install WireGuard
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- wireguard
|
||||
- wireguard-tools
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create WireGuard config directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/wireguard
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Copy WireGuard client configuration
|
||||
ansible.builtin.copy:
|
||||
src: "../playbooks/wireguard-configs/asxpio-ops/{{ client_config_name }}.conf"
|
||||
dest: "/etc/wireguard/{{ client_config_name }}.conf"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
when: client_config_name is defined
|
||||
notify: restart wireguard client
|
||||
|
||||
- name: Enable WireGuard interface
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ client_config_name }}"
|
||||
enabled: true
|
||||
when: client_config_name is defined
|
||||
|
||||
- name: Start WireGuard interface
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ client_config_name }}"
|
||||
state: started
|
||||
when: client_config_name is defined
|
||||
|
||||
handlers:
|
||||
- name: restart wireguard client
|
||||
ansible.builtin.shell: |
|
||||
wg-quick down {{ client_config_name }} || true
|
||||
wg-quick up {{ client_config_name }}
|
||||
args:
|
||||
executable: /bin/bash
|
||||
2
ansible/playbooks/wireguard-configs/.gitignore
vendored
Normal file
2
ansible/playbooks/wireguard-configs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
6
ansible/playbooks/wireguard.yaml
Normal file
6
ansible/playbooks/wireguard.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Setup WireGuard VPN
|
||||
hosts: ops_servers
|
||||
become: true
|
||||
roles:
|
||||
- wireguard
|
||||
16
ansible/roles/common/tasks/main.yaml
Normal file
16
ansible/roles/common/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Cloud-init already handles:
|
||||
# - Package installation (fail2ban, curl, git, htop, ncdu, tree, tmux, etc.)
|
||||
# - fail2ban service enabled and started
|
||||
# - User creation (asxpi with sudo + docker, deploy with sudo + docker)
|
||||
# - SSH hardening (key-only auth, prohibit root password)
|
||||
# - Docker installation and configuration
|
||||
# - Docker image pruning cron job (weekly at 3am Monday)
|
||||
# - Directory structure creation
|
||||
#
|
||||
# This role is now for post-cloud-init configuration only
|
||||
|
||||
- name: Verify cloud-init completed successfully
|
||||
command: cloud-init status --wait
|
||||
register: cloud_init_status
|
||||
changed_when: false
|
||||
failed_when: "'done' not in cloud_init_status.stdout"
|
||||
5
ansible/roles/forgejo/handlers/main.yaml
Normal file
5
ansible/roles/forgejo/handlers/main.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: Restart Forgejo
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo
|
||||
state: restarted
|
||||
39
ansible/roles/forgejo/tasks/main.yaml
Normal file
39
ansible/roles/forgejo/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
- name: Create Forgejo directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop:
|
||||
- /opt/forgejo
|
||||
- /opt/forgejo/data
|
||||
- /opt/forgejo/config
|
||||
|
||||
- name: Deploy Forgejo app.ini configuration
|
||||
template:
|
||||
src: app.ini.j2
|
||||
dest: /opt/forgejo/config/app.ini
|
||||
mode: '0644'
|
||||
notify: Restart Forgejo
|
||||
|
||||
- name: Deploy Forgejo docker-compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: /opt/forgejo/docker-compose.yml
|
||||
mode: '0644'
|
||||
notify: Restart Forgejo
|
||||
|
||||
- name: Start Forgejo
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo
|
||||
state: present
|
||||
register: forgejo_output
|
||||
|
||||
- name: Wait for Forgejo to be healthy
|
||||
uri:
|
||||
url: http://localhost:{{ forgejo_http_port }}/
|
||||
status_code: 200
|
||||
retries: 15
|
||||
delay: 5
|
||||
register: result
|
||||
until: result.status == 200
|
||||
83
ansible/roles/forgejo/templates/app.ini.j2
Normal file
83
ansible/roles/forgejo/templates/app.ini.j2
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
APP_NAME = Gitea: Git with a cup of tea together with asxpi
|
||||
RUN_MODE = prod
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
DOMAIN = {{ forgejo_domain }}
|
||||
ROOT_URL = https://{{ forgejo_domain }}/
|
||||
HTTP_PORT = 3000
|
||||
DISABLE_SSH = false
|
||||
SSH_DOMAIN = {{ forgejo_domain }}
|
||||
SSH_PORT = {{ forgejo_ssh_port }}
|
||||
SSH_LISTEN_PORT = 22
|
||||
START_SSH_SERVER = true
|
||||
LFS_START_SERVER = true
|
||||
OFFLINE_MODE = false
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = /data/gitea/gitea.db
|
||||
LOG_SQL = false
|
||||
|
||||
[repository]
|
||||
ROOT = /data/git/repositories
|
||||
SCRIPT_TYPE = bash
|
||||
DEFAULT_BRANCH = main
|
||||
DEFAULT_PRIVATE = private
|
||||
ENABLE_PUSH_CREATE_USER = true
|
||||
ENABLE_PUSH_CREATE_ORG = false
|
||||
DISABLED_REPO_UNITS =
|
||||
DEFAULT_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages,repo.actions
|
||||
|
||||
[ui]
|
||||
THEMES = forgejo-auto,forgejo-light,forgejo-dark,gitea-auto,gitea-light,gitea-dark
|
||||
DEFAULT_THEME = forgejo-auto
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = {{ forgejo_secret_key | default('CHANGE-ME-PLEASE-GENERATE-NEW-SECRET') }}
|
||||
INTERNAL_TOKEN = {{ forgejo_internal_token | default('CHANGE-ME-PLEASE-GENERATE-NEW-TOKEN') }}
|
||||
PASSWORD_HASH_ALGO = pbkdf2
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = false
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
DEFAULT_KEEP_EMAIL_PRIVATE = true
|
||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||
DEFAULT_ENABLE_TIMETRACKING = true
|
||||
NO_REPLY_ADDRESS = noreply.{{ forgejo_domain }}
|
||||
|
||||
[oauth2]
|
||||
ENABLED = true
|
||||
JWT_SECRET = {{ forgejo_jwt_secret | default('CHANGE-ME-PLEASE-GENERATE-NEW-JWT-SECRET') }}
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
||||
[session]
|
||||
PROVIDER = file
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = info
|
||||
ROOT_PATH = /data/gitea/log
|
||||
|
||||
[actions]
|
||||
ENABLED = true
|
||||
DEFAULT_ACTIONS_URL = https://github.com
|
||||
ARTIFACT_RETENTION_DAYS = 365
|
||||
|
||||
[repository.upload]
|
||||
ENABLED = true
|
||||
|
||||
[repository.pull-request]
|
||||
DEFAULT_MERGE_STYLE = merge
|
||||
|
||||
[repository.signing]
|
||||
DEFAULT_TRUST_MODEL = collaborator
|
||||
|
||||
[lfs]
|
||||
PATH = /data/git/lfs
|
||||
LFS_JWT_SECRET = {{ forgejo_lfs_jwt_secret | default('CHANGE-ME-PLEASE-GENERATE-NEW-LFS-JWT-SECRET') }}
|
||||
31
ansible/roles/forgejo/templates/docker-compose.yml.j2
Normal file
31
ansible/roles/forgejo/templates/docker-compose.yml.j2
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
services:
|
||||
forgejo:
|
||||
image: codeberg.org/forgejo/forgejo:{{ forgejo_version }}
|
||||
container_name: forgejo
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- FORGEJO__database__DB_TYPE=sqlite3
|
||||
- FORGEJO__database__PATH=/data/gitea/gitea.db
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./config/app.ini:/data/gitea/conf/app.ini
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "{{ forgejo_http_port }}:3000"
|
||||
- "{{ forgejo_ssh_port }}:22"
|
||||
networks:
|
||||
- {{ traefik_network }}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.forgejo.rule=Host(`{{ forgejo_domain }}`)"
|
||||
- "traefik.http.routers.forgejo.entrypoints=websecure"
|
||||
- "traefik.http.routers.forgejo.tls=true"
|
||||
- "traefik.http.routers.forgejo.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.forgejo.loadbalancer.server.port=3000"
|
||||
|
||||
networks:
|
||||
{{ traefik_network }}:
|
||||
external: true
|
||||
79
ansible/roles/forgejo_backup/tasks/main.yaml
Normal file
79
ansible/roles/forgejo_backup/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
- name: Create local backup directory
|
||||
file:
|
||||
path: "{{ backup_dest_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Check if Forgejo service is running on old server
|
||||
systemd:
|
||||
name: forgejo
|
||||
register: forgejo_service
|
||||
|
||||
- name: Stop Forgejo service temporarily for backup
|
||||
systemd:
|
||||
name: forgejo
|
||||
state: stopped
|
||||
when: forgejo_service.status.ActiveState == "active"
|
||||
|
||||
- name: Create Forgejo dump backup
|
||||
command: >
|
||||
/usr/bin/forgejo dump
|
||||
-c /etc/forgejo/app.ini
|
||||
-w /srv/gitea
|
||||
--tempdir /tmp
|
||||
args:
|
||||
chdir: /srv/gitea
|
||||
register: dump_output
|
||||
|
||||
- name: Get dump filename
|
||||
find:
|
||||
paths: /srv/gitea
|
||||
patterns: "gitea-dump-*.zip"
|
||||
register: dump_file
|
||||
|
||||
- name: Create additional backup archive
|
||||
archive:
|
||||
path:
|
||||
- /srv/gitea/gitea.db
|
||||
- /srv/gitea/data
|
||||
- /var/lib/forgejo/.ssh
|
||||
- /var/lib/forgejo-runner/.runner
|
||||
dest: /tmp/forgejo-additional-{{ ansible_date_time.date }}.tar.gz
|
||||
format: gz
|
||||
ignore_errors: true
|
||||
|
||||
- name: Fetch dump file to local machine
|
||||
fetch:
|
||||
src: "{{ dump_file.files[0].path }}"
|
||||
dest: "{{ backup_dest_dir }}/"
|
||||
flat: true
|
||||
when: dump_file.files | length > 0
|
||||
|
||||
- name: Fetch additional backup to local machine
|
||||
fetch:
|
||||
src: /tmp/forgejo-additional-{{ ansible_date_time.date }}.tar.gz
|
||||
dest: "{{ backup_dest_dir }}/"
|
||||
flat: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: Start Forgejo service
|
||||
systemd:
|
||||
name: forgejo
|
||||
state: started
|
||||
when: forgejo_service.status.ActiveState == "active"
|
||||
|
||||
- name: Display backup information
|
||||
debug:
|
||||
msg: |
|
||||
Forgejo backup completed successfully!
|
||||
|
||||
Backup files saved to: {{ backup_dest_dir }}/
|
||||
- Forgejo dump: {{ dump_file.files[0].path | basename }}
|
||||
- Additional data: forgejo-additional-{{ ansible_date_time.date }}.tar.gz
|
||||
|
||||
Next steps:
|
||||
1. Verify backup integrity
|
||||
2. Run restore playbook: ansible-playbook playbooks/restore-forgejo.yaml
|
||||
129
ansible/roles/forgejo_restore/tasks/main.yaml
Normal file
129
ansible/roles/forgejo_restore/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
- name: Stop Forgejo container
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo
|
||||
state: stopped
|
||||
ignore_errors: true
|
||||
|
||||
- name: Find backup dump file locally
|
||||
find:
|
||||
paths: "{{ backup_dest_dir }}"
|
||||
patterns: "gitea-dump-*.zip"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
register: local_dump_file
|
||||
|
||||
- name: Find additional backup file locally
|
||||
find:
|
||||
paths: "{{ backup_dest_dir }}"
|
||||
patterns: "forgejo-additional-*.tar.gz"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
register: local_additional_file
|
||||
|
||||
- name: Fail if no backup files found
|
||||
fail:
|
||||
msg: "No backup files found in {{ backup_dest_dir }}. Please run backup-forgejo.yaml first."
|
||||
when: local_dump_file.files | length == 0
|
||||
|
||||
- name: Copy dump backup to server
|
||||
copy:
|
||||
src: "{{ local_dump_file.files[0].path }}"
|
||||
dest: /tmp/forgejo-dump.zip
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy additional backup to server
|
||||
copy:
|
||||
src: "{{ local_additional_file.files[0].path }}"
|
||||
dest: /tmp/forgejo-additional.tar.gz
|
||||
mode: '0644'
|
||||
when: local_additional_file.files | length > 0
|
||||
|
||||
- name: Extract Forgejo dump
|
||||
unarchive:
|
||||
src: /tmp/forgejo-dump.zip
|
||||
dest: /opt/forgejo/
|
||||
remote_src: true
|
||||
creates: /opt/forgejo/app.ini
|
||||
|
||||
- name: Extract additional backup
|
||||
unarchive:
|
||||
src: /tmp/forgejo-additional.tar.gz
|
||||
dest: /tmp/forgejo-restore/
|
||||
remote_src: true
|
||||
when: local_additional_file.files | length > 0
|
||||
|
||||
- name: Ensure data directory exists
|
||||
file:
|
||||
path: /opt/forgejo/data
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy database from dump to data directory
|
||||
copy:
|
||||
src: /opt/forgejo/data/gitea.db
|
||||
dest: /opt/forgejo/data/gitea.db
|
||||
remote_src: true
|
||||
force: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: Copy repository data from additional backup
|
||||
synchronize:
|
||||
src: /tmp/forgejo-restore/srv/gitea/data/
|
||||
dest: /opt/forgejo/data/
|
||||
delegate_to: "{{ inventory_hostname }}"
|
||||
when: local_additional_file.files | length > 0
|
||||
ignore_errors: true
|
||||
|
||||
- name: Copy SSH keys from additional backup
|
||||
synchronize:
|
||||
src: /tmp/forgejo-restore/var/lib/forgejo/.ssh/
|
||||
dest: /opt/forgejo/data/.ssh/
|
||||
delegate_to: "{{ inventory_hostname }}"
|
||||
when: local_additional_file.files | length > 0
|
||||
ignore_errors: true
|
||||
|
||||
- name: Set correct ownership for Forgejo data
|
||||
file:
|
||||
path: /opt/forgejo/data
|
||||
owner: 1000
|
||||
group: 1000
|
||||
recurse: true
|
||||
|
||||
- name: Start Forgejo container
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo
|
||||
state: present
|
||||
|
||||
- name: Wait for Forgejo to be healthy
|
||||
uri:
|
||||
url: http://localhost:{{ forgejo_http_port }}/
|
||||
status_code: 200
|
||||
retries: 15
|
||||
delay: 5
|
||||
register: result
|
||||
until: result.status == 200
|
||||
|
||||
- name: Clean up temporary files
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/forgejo-dump.zip
|
||||
- /tmp/forgejo-additional.tar.gz
|
||||
- /tmp/forgejo-restore
|
||||
|
||||
- name: Display restore information
|
||||
debug:
|
||||
msg: |
|
||||
Forgejo restore completed successfully!
|
||||
|
||||
Forgejo is now running at:
|
||||
- Internal: http://localhost:{{ forgejo_http_port }}
|
||||
- External: https://{{ forgejo_domain }}
|
||||
|
||||
Next steps:
|
||||
1. Test git operations: git clone https://{{ forgejo_domain }}/user/repo.git
|
||||
2. Test SSH access: git clone git@{{ forgejo_domain }}:user/repo.git
|
||||
3. Test web interface at https://{{ forgejo_domain }}
|
||||
4. Register runners with the new instance
|
||||
5
ansible/roles/forgejo_runner/handlers/main.yaml
Normal file
5
ansible/roles/forgejo_runner/handlers/main.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: Restart Forgejo Runner
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo-runner
|
||||
state: restarted
|
||||
45
ansible/roles/forgejo_runner/tasks/main.yaml
Normal file
45
ansible/roles/forgejo_runner/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
- name: Create Forgejo Runner directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop:
|
||||
- /opt/forgejo-runner
|
||||
- /opt/forgejo-runner/data
|
||||
- /opt/forgejo-runner/config
|
||||
|
||||
- name: Deploy Forgejo Runner configuration
|
||||
template:
|
||||
src: config.yaml.j2
|
||||
dest: /opt/forgejo-runner/config/config.yaml
|
||||
mode: '0644'
|
||||
notify: Restart Forgejo Runner
|
||||
|
||||
- name: Deploy Forgejo Runner docker-compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: /opt/forgejo-runner/docker-compose.yml
|
||||
mode: '0644'
|
||||
notify: Restart Forgejo Runner
|
||||
|
||||
- name: Start Forgejo Runner
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/forgejo-runner
|
||||
state: present
|
||||
register: runner_output
|
||||
|
||||
- name: Display runner registration instructions
|
||||
debug:
|
||||
msg: |
|
||||
Forgejo Runner deployed successfully!
|
||||
|
||||
To register the runner with Forgejo:
|
||||
1. Log in to Forgejo at https://{{ forgejo_domain }}
|
||||
2. Go to Site Administration > Actions > Runners
|
||||
3. Generate a registration token
|
||||
4. Run: docker exec -it forgejo-runner forgejo-runner register --no-interactive \
|
||||
--token YOUR_TOKEN \
|
||||
--name runner-{{ inventory_hostname }} \
|
||||
--instance http://10.10.0.10:{{ forgejo_http_port }}
|
||||
5. Restart the runner: cd /opt/forgejo-runner && docker compose restart
|
||||
43
ansible/roles/forgejo_runner/templates/config.yaml.j2
Normal file
43
ansible/roles/forgejo_runner/templates/config.yaml.j2
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
log:
|
||||
level: info
|
||||
job_level: info
|
||||
|
||||
runner:
|
||||
file: /data/.runner
|
||||
capacity: {{ forgejo_runner_capacity }}
|
||||
envs: {}
|
||||
env_file: .env
|
||||
timeout: {{ forgejo_runner_timeout }}
|
||||
shutdown_timeout: {{ forgejo_runner_timeout }}
|
||||
insecure: false
|
||||
fetch_timeout: 5s
|
||||
fetch_interval: 2s
|
||||
report_interval: 1s
|
||||
labels:
|
||||
- "ubuntu-latest:docker://node:20-bookworm"
|
||||
- "ubuntu-22.04:docker://node:20-bookworm"
|
||||
- "ubuntu-24.04:docker://node:22-bookworm"
|
||||
- "debian-latest:docker://debian:bookworm"
|
||||
- "debian-12:docker://debian:bookworm"
|
||||
- "debian-11:docker://debian:bullseye"
|
||||
- "arch-latest:docker://archlinux:latest"
|
||||
- "nginx-latest:docker://nginx:latest"
|
||||
- "nginx-alpine:docker://nginx:alpine"
|
||||
- "node-20:docker://node:20"
|
||||
- "node-22:docker://node:22"
|
||||
- "python-3.11:docker://python:3.11"
|
||||
- "python-3.12:docker://python:3.12"
|
||||
- "alpine-latest:docker://alpine:latest"
|
||||
|
||||
cache:
|
||||
enabled: true
|
||||
port: 0
|
||||
dir: ""
|
||||
|
||||
container:
|
||||
network: "host"
|
||||
enable_ipv6: false
|
||||
privileged: false
|
||||
docker_host: "-"
|
||||
force_pull: false
|
||||
force_rebuild: false
|
||||
11
ansible/roles/forgejo_runner/templates/docker-compose.yml.j2
Normal file
11
ansible/roles/forgejo_runner/templates/docker-compose.yml.j2
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
services:
|
||||
forgejo-runner:
|
||||
image: code.forgejo.org/forgejo/runner:{{ forgejo_runner_version }}
|
||||
container_name: forgejo-runner
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./config/config.yaml:/config.yaml
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- CONFIG_FILE=/config.yaml
|
||||
60
ansible/roles/ssh_hardening/defaults/main.yaml
Normal file
60
ansible/roles/ssh_hardening/defaults/main.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
# SSH Hardening Role Default Variables
|
||||
|
||||
# Networks allowed to access SSH (VPC and WireGuard)
|
||||
ssh_allowed_networks:
|
||||
- 10.20.0.0/24 # WireGuard VPN
|
||||
- 10.10.0.0/20 # Internal VPC
|
||||
|
||||
# Whether to allow public SSH access for git operations (Forgejo)
|
||||
# Set to true on Forgejo host, false on other servers
|
||||
ssh_allow_public_git: false
|
||||
|
||||
# Shell users that require network restrictions
|
||||
ssh_shell_users:
|
||||
- asxpi
|
||||
- deploy
|
||||
- root
|
||||
|
||||
# Git user for Forgejo (allowed from anywhere when ssh_allow_public_git is true)
|
||||
ssh_git_user: forgejo
|
||||
|
||||
# SSH hardening settings
|
||||
ssh_max_auth_tries: 3
|
||||
ssh_max_sessions: 5
|
||||
ssh_client_alive_interval: 300
|
||||
ssh_client_alive_count_max: 2
|
||||
|
||||
# Strong cryptography settings
|
||||
ssh_ciphers:
|
||||
- chacha20-poly1305@openssh.com
|
||||
- aes256-gcm@openssh.com
|
||||
- aes128-gcm@openssh.com
|
||||
- aes256-ctr
|
||||
- aes192-ctr
|
||||
- aes128-ctr
|
||||
|
||||
ssh_macs:
|
||||
- hmac-sha2-512-etm@openssh.com
|
||||
- hmac-sha2-256-etm@openssh.com
|
||||
- hmac-sha2-512
|
||||
- hmac-sha2-256
|
||||
|
||||
ssh_kex_algorithms:
|
||||
- sntrup761x25519-sha512@openssh.com # Post-quantum hybrid (OpenSSH 9.0+)
|
||||
- curve25519-sha256
|
||||
- curve25519-sha256@libssh.org
|
||||
- diffie-hellman-group-exchange-sha256
|
||||
- diffie-hellman-group16-sha512
|
||||
- diffie-hellman-group18-sha512
|
||||
|
||||
# Public key algorithms (includes RSA-SHA2 for existing keys)
|
||||
ssh_pubkey_accepted_algorithms:
|
||||
- ssh-ed25519
|
||||
- ssh-ed25519-cert-v01@openssh.com
|
||||
- sk-ssh-ed25519@openssh.com
|
||||
- ecdsa-sha2-nistp256
|
||||
- ecdsa-sha2-nistp384
|
||||
- ecdsa-sha2-nistp521
|
||||
- rsa-sha2-512
|
||||
- rsa-sha2-256
|
||||
6
ansible/roles/ssh_hardening/handlers/main.yaml
Normal file
6
ansible/roles/ssh_hardening/handlers/main.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: restart sshd
|
||||
ansible.builtin.systemd:
|
||||
name: sshd
|
||||
state: restarted
|
||||
enabled: true
|
||||
80
ansible/roles/ssh_hardening/tasks/main.yaml
Normal file
80
ansible/roles/ssh_hardening/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
- name: Install iptables and iptables-persistent
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- iptables
|
||||
- iptables-persistent
|
||||
- netfilter-persistent
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Deploy SSH hardening configuration
|
||||
ansible.builtin.template:
|
||||
src: 99-ssh-hardening.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/99-ssh-hardening.conf
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
validate: 'sshd -t -f %s'
|
||||
notify: restart sshd
|
||||
|
||||
- name: Configure iptables for SSH restrictions (non-Forgejo servers)
|
||||
block:
|
||||
- name: Accept SSH from WireGuard network
|
||||
ansible.builtin.iptables:
|
||||
chain: INPUT
|
||||
protocol: tcp
|
||||
destination_port: '22'
|
||||
source: 10.20.0.0/24
|
||||
jump: ACCEPT
|
||||
comment: Allow SSH from WireGuard VPN
|
||||
|
||||
- name: Accept SSH from VPC network
|
||||
ansible.builtin.iptables:
|
||||
chain: INPUT
|
||||
protocol: tcp
|
||||
destination_port: '22'
|
||||
source: 10.10.0.0/20
|
||||
jump: ACCEPT
|
||||
comment: Allow SSH from internal VPC
|
||||
|
||||
- name: Accept established SSH connections
|
||||
ansible.builtin.iptables:
|
||||
chain: INPUT
|
||||
protocol: tcp
|
||||
destination_port: '22'
|
||||
ctstate: ESTABLISHED,RELATED
|
||||
jump: ACCEPT
|
||||
comment: Allow established SSH connections
|
||||
|
||||
- name: Drop all other SSH connections
|
||||
ansible.builtin.iptables:
|
||||
chain: INPUT
|
||||
protocol: tcp
|
||||
destination_port: '22'
|
||||
jump: DROP
|
||||
comment: Drop SSH from untrusted networks
|
||||
|
||||
when: not ssh_allow_public_git
|
||||
|
||||
- name: Save iptables rules
|
||||
ansible.builtin.shell: |
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
netfilter-persistent save
|
||||
args:
|
||||
executable: /bin/bash
|
||||
changed_when: true
|
||||
when: not ssh_allow_public_git
|
||||
|
||||
- name: Display SSH access information
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
SSH hardening applied to {{ inventory_hostname }}:
|
||||
- Shell users ({{ ssh_shell_users | join(', ') }}): Only from {{ ssh_allowed_networks | join(', ') }}
|
||||
{% if ssh_allow_public_git %}
|
||||
- Git user ({{ ssh_git_user }}): Public access allowed for Forgejo
|
||||
- No iptables restrictions (sshd AllowUsers controls access)
|
||||
{% else %}
|
||||
- iptables rules: SSH blocked except from trusted networks
|
||||
{% endif %}
|
||||
- Strong cryptography enforced (RSA-SHA2, Ed25519, ChaCha20, AES-GCM)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# SSH Hardening Configuration
|
||||
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
|
||||
# Server: {{ inventory_hostname }}
|
||||
|
||||
# Connection settings
|
||||
MaxAuthTries {{ ssh_max_auth_tries }}
|
||||
MaxSessions {{ ssh_max_sessions }}
|
||||
ClientAliveInterval {{ ssh_client_alive_interval }}
|
||||
ClientAliveCountMax {{ ssh_client_alive_count_max }}
|
||||
|
||||
# Security hardening
|
||||
StrictModes yes
|
||||
X11Forwarding no
|
||||
PrintMotd no
|
||||
|
||||
# Strong cryptography only
|
||||
Ciphers {{ ssh_ciphers | join(',') }}
|
||||
MACs {{ ssh_macs | join(',') }}
|
||||
KexAlgorithms {{ ssh_kex_algorithms | join(',') }}
|
||||
PubkeyAcceptedAlgorithms {{ ssh_pubkey_accepted_algorithms | join(',') }}
|
||||
|
||||
# Access control - restrict shell users to trusted networks only
|
||||
{% for user in ssh_shell_users %}
|
||||
{% for network in ssh_allowed_networks %}
|
||||
AllowUsers {{ user }}@{{ network }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% if ssh_allow_public_git %}
|
||||
# Allow Forgejo git user from anywhere for git operations
|
||||
AllowUsers {{ ssh_git_user }}
|
||||
{% endif %}
|
||||
5
ansible/roles/traefik/handlers/main.yaml
Normal file
5
ansible/roles/traefik/handlers/main.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: Restart Traefik
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/traefik
|
||||
state: restarted
|
||||
53
ansible/roles/traefik/tasks/main.yaml
Normal file
53
ansible/roles/traefik/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
- name: Create Traefik directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop:
|
||||
- /opt/traefik
|
||||
- /opt/traefik/config
|
||||
- /opt/traefik/letsencrypt
|
||||
- /opt/traefik/logs
|
||||
|
||||
- name: Create Traefik Docker network
|
||||
community.docker.docker_network:
|
||||
name: "{{ traefik_network }}"
|
||||
driver: bridge
|
||||
state: present
|
||||
|
||||
- name: Deploy Traefik static configuration
|
||||
template:
|
||||
src: traefik.yml.j2
|
||||
dest: /opt/traefik/traefik.yml
|
||||
mode: '0644'
|
||||
notify: Restart Traefik
|
||||
|
||||
- name: Deploy Traefik dynamic configuration
|
||||
template:
|
||||
src: dynamic.yml.j2
|
||||
dest: /opt/traefik/config/dynamic.yml
|
||||
mode: '0644'
|
||||
notify: Restart Traefik
|
||||
|
||||
- name: Deploy Traefik docker-compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: /opt/traefik/docker-compose.yml
|
||||
mode: '0644'
|
||||
notify: Restart Traefik
|
||||
|
||||
- name: Start Traefik
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/traefik
|
||||
state: present
|
||||
register: traefik_output
|
||||
|
||||
- name: Wait for Traefik to be healthy
|
||||
uri:
|
||||
url: http://localhost:8080/ping
|
||||
status_code: 200
|
||||
retries: 10
|
||||
delay: 3
|
||||
register: result
|
||||
until: result.status == 200
|
||||
26
ansible/roles/traefik/templates/docker-compose.yml.j2
Normal file
26
ansible/roles/traefik/templates/docker-compose.yml.j2
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- {{ traefik_network }}
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "10.20.0.10:8080:8080" # Dashboard only on WireGuard IP
|
||||
environment:
|
||||
- TZ=UTC
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./traefik.yml:/traefik.yml:ro
|
||||
- ./config:/config:ro
|
||||
- ./letsencrypt:/letsencrypt
|
||||
- ./logs:/var/log/traefik
|
||||
|
||||
networks:
|
||||
{{ traefik_network }}:
|
||||
external: true
|
||||
17
ansible/roles/traefik/templates/dynamic.yml.j2
Normal file
17
ansible/roles/traefik/templates/dynamic.yml.j2
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
http:
|
||||
middlewares:
|
||||
# Security headers
|
||||
secure-headers:
|
||||
headers:
|
||||
sslRedirect: true
|
||||
forceSTSHeader: true
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
stsSeconds: 31536000
|
||||
|
||||
# Rate limiting
|
||||
rate-limit:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 50
|
||||
period: 1s
|
||||
40
ansible/roles/traefik/templates/traefik.yml.j2
Normal file
40
ansible/roles/traefik/templates/traefik.yml.j2
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
api:
|
||||
dashboard: true
|
||||
insecure: true # Dashboard on port 8080 (bound to WireGuard IP only)
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "unix:///var/run/docker.sock"
|
||||
exposedByDefault: false
|
||||
network: {{ traefik_network }}
|
||||
file:
|
||||
directory: /config
|
||||
watch: true
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: {{ letsencrypt_email | default('admin@asxp.io') }}
|
||||
storage: /letsencrypt/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
|
||||
log:
|
||||
level: INFO
|
||||
filePath: /var/log/traefik/traefik.log
|
||||
|
||||
accessLog:
|
||||
filePath: /var/log/traefik/access.log
|
||||
bufferingSize: 100
|
||||
39
ansible/roles/wireguard/defaults/main.yaml
Normal file
39
ansible/roles/wireguard/defaults/main.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
# WireGuard default configuration
|
||||
wireguard_interface: wg0
|
||||
wireguard_port: 60101
|
||||
wireguard_ipv4: 10.20.0.10
|
||||
wireguard_ipv6: fd69:69:69::1
|
||||
wireguard_ipv4_network: 10.20.0.0/24
|
||||
wireguard_ipv6_network: fd69:69:69::/64
|
||||
|
||||
# Directory paths
|
||||
wireguard_config_dir: /etc/wireguard
|
||||
wireguard_keys_dir: "{{ wireguard_config_dir }}/keys"
|
||||
wireguard_clients_dir: "{{ wireguard_config_dir }}/clients"
|
||||
|
||||
# Enable IPv4 forwarding
|
||||
wireguard_enable_forwarding: true
|
||||
|
||||
# UFW configuration (if false, will use iptables rules in PostUp/PostDown)
|
||||
# Default to false since most Debian servers don't have UFW enabled by default
|
||||
wireguard_enable_ufw: false
|
||||
|
||||
# Client configurations
|
||||
wireguard_clients: []
|
||||
# Example client structure:
|
||||
# wireguard_clients:
|
||||
# - name: laptop
|
||||
# ipv4: 10.20.0.2
|
||||
# ipv6: fd69:69:69::2
|
||||
# preshared_key: true # Optional: generate preshared key for extra security
|
||||
# - name: phone
|
||||
# ipv4: 10.20.0.3
|
||||
# ipv6: fd69:69:69::3
|
||||
# preshared_key: true
|
||||
|
||||
# DNS servers for clients (optional)
|
||||
wireguard_dns_servers: []
|
||||
|
||||
# Additional routes for split tunneling (optional)
|
||||
wireguard_additional_routes: []
|
||||
21
ansible/roles/wireguard/handlers/main.yaml
Normal file
21
ansible/roles/wireguard/handlers/main.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
- name: restart wireguard
|
||||
ansible.builtin.shell: |
|
||||
wg syncconf {{ wireguard_interface }} <(wg-quick strip {{ wireguard_interface }})
|
||||
args:
|
||||
executable: /bin/bash
|
||||
ignore_errors: true
|
||||
register: wg_sync_result
|
||||
|
||||
- name: restart wireguard (fallback)
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
state: restarted
|
||||
enabled: true
|
||||
when: wg_sync_result is failed
|
||||
|
||||
- name: reload wireguard
|
||||
ansible.builtin.shell: |
|
||||
wg syncconf {{ wireguard_interface }} <(wg-quick strip {{ wireguard_interface }})
|
||||
args:
|
||||
executable: /bin/bash
|
||||
219
ansible/roles/wireguard/tasks/main.yaml
Normal file
219
ansible/roles/wireguard/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
---
|
||||
- name: Install WireGuard
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- wireguard
|
||||
- wireguard-tools
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install UFW (if needed)
|
||||
ansible.builtin.apt:
|
||||
name: ufw
|
||||
state: present
|
||||
when: wireguard_enable_ufw
|
||||
|
||||
- name: Create WireGuard directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
loop:
|
||||
- "{{ wireguard_config_dir }}"
|
||||
- "{{ wireguard_keys_dir }}"
|
||||
- "{{ wireguard_clients_dir }}"
|
||||
|
||||
- name: Check if server private key exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ wireguard_keys_dir }}/server_private.key"
|
||||
register: server_private_key_stat
|
||||
|
||||
- name: Generate server private key
|
||||
ansible.builtin.shell: |
|
||||
wg genkey > {{ wireguard_keys_dir }}/server_private.key
|
||||
chmod 600 {{ wireguard_keys_dir }}/server_private.key
|
||||
when: not server_private_key_stat.stat.exists
|
||||
|
||||
- name: Generate server public key
|
||||
ansible.builtin.shell: |
|
||||
wg pubkey < {{ wireguard_keys_dir }}/server_private.key > {{ wireguard_keys_dir }}/server_public.key
|
||||
chmod 600 {{ wireguard_keys_dir }}/server_public.key
|
||||
when: not server_private_key_stat.stat.exists
|
||||
|
||||
- name: Read server private key
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ wireguard_keys_dir }}/server_private.key"
|
||||
register: server_private_key_content
|
||||
|
||||
- name: Read server public key
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ wireguard_keys_dir }}/server_public.key"
|
||||
register: server_public_key_content
|
||||
|
||||
- name: Set server key facts
|
||||
ansible.builtin.set_fact:
|
||||
wireguard_server_private_key: "{{ server_private_key_content.content | b64decode | trim }}"
|
||||
wireguard_server_public_key: "{{ server_public_key_content.content | b64decode | trim }}"
|
||||
|
||||
- name: Generate client keys
|
||||
block:
|
||||
- name: Check if client private key exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ wireguard_keys_dir }}/{{ item.name }}_private.key"
|
||||
register: client_private_key_stats
|
||||
loop: "{{ wireguard_clients }}"
|
||||
|
||||
- name: Generate client private keys
|
||||
ansible.builtin.shell: |
|
||||
wg genkey > {{ wireguard_keys_dir }}/{{ item.item.name }}_private.key
|
||||
chmod 600 {{ wireguard_keys_dir }}/{{ item.item.name }}_private.key
|
||||
loop: "{{ client_private_key_stats.results }}"
|
||||
when: not item.stat.exists
|
||||
|
||||
- name: Check if client public key exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ wireguard_keys_dir }}/{{ item.name }}_public.key"
|
||||
register: client_public_key_stats
|
||||
loop: "{{ wireguard_clients }}"
|
||||
|
||||
- name: Generate client public keys
|
||||
ansible.builtin.shell: |
|
||||
wg pubkey < {{ wireguard_keys_dir }}/{{ item.item.name }}_private.key > {{ wireguard_keys_dir }}/{{ item.item.name }}_public.key
|
||||
chmod 600 {{ wireguard_keys_dir }}/{{ item.item.name }}_public.key
|
||||
loop: "{{ client_public_key_stats.results }}"
|
||||
when: not item.stat.exists
|
||||
|
||||
- name: Check if client preshared key exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ wireguard_keys_dir }}/{{ item.name }}_preshared.key"
|
||||
register: client_preshared_key_stats
|
||||
loop: "{{ wireguard_clients }}"
|
||||
when: item.preshared_key | default(false)
|
||||
|
||||
- name: Generate client preshared keys (if enabled)
|
||||
ansible.builtin.shell: |
|
||||
wg genpsk > {{ wireguard_keys_dir }}/{{ item.item.name }}_preshared.key
|
||||
chmod 600 {{ wireguard_keys_dir }}/{{ item.item.name }}_preshared.key
|
||||
loop: "{{ client_preshared_key_stats.results | default([]) }}"
|
||||
when:
|
||||
- client_preshared_key_stats.results is defined
|
||||
- not item.stat.exists
|
||||
|
||||
- name: Read client private keys
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ wireguard_keys_dir }}/{{ item.name }}_private.key"
|
||||
register: client_private_keys
|
||||
loop: "{{ wireguard_clients }}"
|
||||
|
||||
- name: Read client public keys
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ wireguard_keys_dir }}/{{ item.name }}_public.key"
|
||||
register: client_public_keys
|
||||
loop: "{{ wireguard_clients }}"
|
||||
|
||||
- name: Read client preshared keys
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ wireguard_keys_dir }}/{{ item.name }}_preshared.key"
|
||||
register: client_preshared_keys
|
||||
loop: "{{ wireguard_clients }}"
|
||||
when: item.preshared_key | default(false)
|
||||
ignore_errors: true
|
||||
|
||||
when: wireguard_clients | length > 0
|
||||
|
||||
- name: Process client keys into structured data
|
||||
ansible.builtin.set_fact:
|
||||
wireguard_client_configs: "{{ wireguard_client_configs | default([]) + [client_config] }}"
|
||||
loop: "{{ wireguard_clients }}"
|
||||
loop_control:
|
||||
index_var: client_index
|
||||
vars:
|
||||
private_key: "{{ client_private_keys.results[client_index].content | b64decode | trim }}"
|
||||
public_key: "{{ client_public_keys.results[client_index].content | b64decode | trim }}"
|
||||
preshared_key: "{{ client_preshared_keys.results[client_index].content | b64decode | trim if (item.preshared_key | default(false) and client_preshared_keys.results[client_index] is defined and not client_preshared_keys.results[client_index].failed | default(false)) else '' }}"
|
||||
client_config:
|
||||
name: "{{ item.name }}"
|
||||
ipv4: "{{ item.ipv4 }}"
|
||||
ipv6: "{{ item.ipv6 }}"
|
||||
private_key: "{{ private_key }}"
|
||||
public_key: "{{ public_key }}"
|
||||
preshared_key: "{{ preshared_key }}"
|
||||
persistent_keepalive: "{{ item.persistent_keepalive | default(25) }}"
|
||||
when: wireguard_clients | length > 0
|
||||
|
||||
- name: Enable IPv4 forwarding
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
sysctl_set: true
|
||||
state: present
|
||||
reload: true
|
||||
when: wireguard_enable_forwarding
|
||||
|
||||
- name: Enable IPv6 forwarding
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv6.conf.all.forwarding
|
||||
value: '1'
|
||||
sysctl_set: true
|
||||
state: present
|
||||
reload: true
|
||||
when: wireguard_enable_forwarding
|
||||
|
||||
- name: Get default network interface
|
||||
ansible.builtin.shell: ip route | grep default | awk '{print $5}' | head -n1
|
||||
register: default_interface
|
||||
changed_when: false
|
||||
|
||||
- name: Set default interface fact
|
||||
ansible.builtin.set_fact:
|
||||
wireguard_default_interface: "{{ default_interface.stdout }}"
|
||||
|
||||
- name: Deploy WireGuard server configuration
|
||||
ansible.builtin.template:
|
||||
src: wg0.conf.j2
|
||||
dest: "{{ wireguard_config_dir }}/{{ wireguard_interface }}.conf"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
notify: restart wireguard
|
||||
|
||||
- name: Configure UFW for WireGuard
|
||||
block:
|
||||
- name: Allow WireGuard port through UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ wireguard_port }}"
|
||||
proto: udp
|
||||
|
||||
- name: Allow forwarding on WireGuard interface
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
route: true
|
||||
interface_in: "{{ wireguard_interface }}"
|
||||
when: wireguard_enable_ufw
|
||||
|
||||
- name: Enable and start WireGuard service
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Generate client configuration files
|
||||
ansible.builtin.template:
|
||||
src: client.conf.j2
|
||||
dest: "{{ wireguard_clients_dir }}/{{ item.name }}.conf"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
loop: "{{ wireguard_client_configs | default([]) }}"
|
||||
when: wireguard_clients | length > 0
|
||||
|
||||
- name: Fetch client configuration files to local machine
|
||||
ansible.builtin.fetch:
|
||||
src: "{{ wireguard_clients_dir }}/{{ item.name }}.conf"
|
||||
dest: "./wireguard-configs/{{ inventory_hostname }}/{{ item.name }}.conf"
|
||||
flat: true
|
||||
loop: "{{ wireguard_clients }}"
|
||||
when: wireguard_clients | length > 0
|
||||
21
ansible/roles/wireguard/templates/client.conf.j2
Normal file
21
ansible/roles/wireguard/templates/client.conf.j2
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# WireGuard Client Configuration - {{ item.name }}
|
||||
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
|
||||
# Server: {{ inventory_hostname }}
|
||||
|
||||
[Interface]
|
||||
PrivateKey = {{ item.private_key }}
|
||||
Address = {{ item.ipv4 }}/32,{{ item.ipv6 }}/128
|
||||
{% if wireguard_dns_servers | length > 0 %}
|
||||
DNS = {{ wireguard_dns_servers | join(', ') }}
|
||||
{% endif %}
|
||||
|
||||
[Peer]
|
||||
PublicKey = {{ wireguard_server_public_key }}
|
||||
{% if item.preshared_key %}
|
||||
PresharedKey = {{ item.preshared_key }}
|
||||
{% endif %}
|
||||
Endpoint = {{ ansible_host }}:{{ wireguard_port }}
|
||||
# Allow all VPN traffic (peer-to-peer connectivity)
|
||||
AllowedIPs = {{ wireguard_ipv4_network }},{{ wireguard_ipv6_network }}{% if wireguard_additional_routes | length > 0 %},{{ wireguard_additional_routes | join(',') }}{% endif %}
|
||||
|
||||
PersistentKeepalive = {{ item.persistent_keepalive | default(25) }}
|
||||
39
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
39
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# WireGuard Server Configuration - {{ wireguard_interface }}
|
||||
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
|
||||
|
||||
[Interface]
|
||||
Address = {{ wireguard_ipv4 }}/{{ wireguard_ipv4_network.split('/')[-1] }},{{ wireguard_ipv6 }}/{{ wireguard_ipv6_network.split('/')[-1] }}
|
||||
ListenPort = {{ wireguard_port }}
|
||||
PrivateKey = {{ wireguard_server_private_key }}
|
||||
|
||||
{% if wireguard_enable_ufw %}
|
||||
# UFW handles firewall rules
|
||||
{% else %}
|
||||
# Firewall rules for iptables
|
||||
PostUp = iptables -I INPUT -p udp --dport {{ wireguard_port }} -j ACCEPT
|
||||
PostUp = iptables -I FORWARD -i {{ wireguard_default_interface }} -o {{ wireguard_interface }} -j ACCEPT
|
||||
PostUp = iptables -I FORWARD -i {{ wireguard_interface }} -j ACCEPT
|
||||
PostUp = iptables -t nat -A POSTROUTING -o {{ wireguard_default_interface }} -j MASQUERADE
|
||||
PostUp = ip6tables -I FORWARD -i {{ wireguard_interface }} -j ACCEPT
|
||||
PostUp = ip6tables -t nat -A POSTROUTING -o {{ wireguard_default_interface }} -j MASQUERADE
|
||||
PostDown = iptables -D INPUT -p udp --dport {{ wireguard_port }} -j ACCEPT
|
||||
PostDown = iptables -D FORWARD -i {{ wireguard_default_interface }} -o {{ wireguard_interface }} -j ACCEPT
|
||||
PostDown = iptables -D FORWARD -i {{ wireguard_interface }} -j ACCEPT
|
||||
PostDown = iptables -t nat -D POSTROUTING -o {{ wireguard_default_interface }} -j MASQUERADE
|
||||
PostDown = ip6tables -D FORWARD -i {{ wireguard_interface }} -j ACCEPT
|
||||
PostDown = ip6tables -t nat -D POSTROUTING -o {{ wireguard_default_interface }} -j MASQUERADE
|
||||
{% endif %}
|
||||
|
||||
{% for client in wireguard_client_configs | default([]) %}
|
||||
### Client {{ client.name }}
|
||||
[Peer]
|
||||
PublicKey = {{ client.public_key }}
|
||||
{% if client.preshared_key %}
|
||||
PresharedKey = {{ client.preshared_key }}
|
||||
{% endif %}
|
||||
AllowedIPs = {{ client.ipv4 }}/32,{{ client.ipv6 }}/128
|
||||
{% if client.persistent_keepalive | default(25) %}
|
||||
PersistentKeepalive = {{ client.persistent_keepalive }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue