ansible
This commit is contained in:
parent
7974e36c1b
commit
b81f7e15b2
41 changed files with 1632 additions and 0 deletions
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