wireguard: use vpn for bastion-fleet comms
supposedly fleet will be more secure this way
This commit is contained in:
parent
1b3e800443
commit
0c8d18dcce
@ -32,6 +32,8 @@ The playbook assumes fresh Arch Linux ARM images installed on machines in your L
|
|||||||
They should start off with default credentials (i.e. `alarm:alarm`, `root:root`).
|
They should start off with default credentials (i.e. `alarm:alarm`, `root:root`).
|
||||||
This repo takes care of everything else.
|
This repo takes care of everything else.
|
||||||
The intended topology is a bastion host facing the Internet, with reverse proxies forwarding traffic to a service host inside the firewall.
|
The intended topology is a bastion host facing the Internet, with reverse proxies forwarding traffic to a service host inside the firewall.
|
||||||
|
The servers are all on a WireGuard network.
|
||||||
|
This network also serves as the typical "encrypted tunnel" for devices on the go.
|
||||||
|
|
||||||
- Flash all your machines with Arch Linux ARM.
|
- Flash all your machines with Arch Linux ARM.
|
||||||
- Copy `inventory.example.yml` to `inventory.yml`.
|
- Copy `inventory.example.yml` to `inventory.yml`.
|
||||||
|
@ -28,14 +28,20 @@ form_secret: ""
|
|||||||
paperless_secret: ""
|
paperless_secret: ""
|
||||||
|
|
||||||
wireguard_secret:
|
wireguard_secret:
|
||||||
# server secret
|
# server secrets
|
||||||
# generate with `wg genkey`, available in the 'wireguard-tools' package
|
# generate with `wg genkey`, available in the 'wireguard-tools' package
|
||||||
server_key: ""
|
servers:
|
||||||
# pipe the secret key (see secret_template in group_vars/) into `wg pubkey` to get this
|
your_bastion_host:
|
||||||
server_pub_key: ""
|
# see inventory.yml to set the vpn address
|
||||||
|
priv: ""
|
||||||
|
# pipe the secret key (see secret_template in group_vars/) into `wg pubkey` to get this
|
||||||
|
pub: ""
|
||||||
|
your_fleet_host:
|
||||||
|
priv: ""
|
||||||
|
pub: ""
|
||||||
|
|
||||||
# list of clients to generate configs for
|
# list of clients to generate configs for on the bastion host
|
||||||
peers:
|
clients:
|
||||||
# name of the client
|
# name of the client
|
||||||
- name: test_client
|
- name: test_client
|
||||||
addr: "10.66.77.2"
|
addr: "10.66.77.2"
|
||||||
|
@ -43,3 +43,6 @@ escalation_method: doas
|
|||||||
|
|
||||||
# set up static IP
|
# set up static IP
|
||||||
enable_connection: yes
|
enable_connection: yes
|
||||||
|
|
||||||
|
# use a wireguard network between bastion and fleet host for the reverse proxy
|
||||||
|
wireguard_services: true
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
# fallback_host is only used during setup before the static IP (local_ip) is configured.
|
# fallback_host is only used during setup before the static IP (local_ip) is configured.
|
||||||
# Set fallback_host using `nmap 192.168.0.0/24 -p 22` to find the dynamic IP of your Pi
|
# Set fallback_host using `nmap 192.168.0.0/24 -p 22` to find the dynamic IP of your Pi
|
||||||
|
#
|
||||||
# local_ip is used after first setup.
|
# local_ip is used after first setup.
|
||||||
|
#
|
||||||
|
# vpn_ip is used for the WireGuard network.
|
||||||
|
|
||||||
# Make entries in your .ssh/config for ease of use
|
# Make entries in your .ssh/config for ease of use
|
||||||
# Example:
|
# Example:
|
||||||
@ -19,6 +22,7 @@ all:
|
|||||||
your_bastion_host:
|
your_bastion_host:
|
||||||
fallback_host: 192.168.0.123
|
fallback_host: 192.168.0.123
|
||||||
local_ip: 192.168.0.3
|
local_ip: 192.168.0.3
|
||||||
|
vpn_ip: 10.66.77.1
|
||||||
ansible_port: 2500
|
ansible_port: 2500
|
||||||
ansible_connection: ssh
|
ansible_connection: ssh
|
||||||
ansible_ssh_private_key_file: ~/.ssh/keys/your_bastion_host
|
ansible_ssh_private_key_file: ~/.ssh/keys/your_bastion_host
|
||||||
@ -26,6 +30,7 @@ all:
|
|||||||
your_fleet_host:
|
your_fleet_host:
|
||||||
fallback_host: 192.168.0.124
|
fallback_host: 192.168.0.124
|
||||||
local_ip: 192.168.0.86
|
local_ip: 192.168.0.86
|
||||||
|
vpn_ip: 10.66.77.86
|
||||||
ansible_port: 2500
|
ansible_port: 2500
|
||||||
ansible_connection: ssh
|
ansible_connection: ssh
|
||||||
ansible_ssh_private_key_file: ~/.ssh/keys/your_fleet_host
|
ansible_ssh_private_key_file: ~/.ssh/keys/your_fleet_host
|
||||||
@ -65,6 +70,7 @@ all:
|
|||||||
wireguard:
|
wireguard:
|
||||||
hosts:
|
hosts:
|
||||||
your_bastion_host:
|
your_bastion_host:
|
||||||
|
your_fleet_host:
|
||||||
sshd:
|
sshd:
|
||||||
hosts:
|
hosts:
|
||||||
your_bastion_host:
|
your_bastion_host:
|
||||||
|
@ -126,6 +126,10 @@
|
|||||||
register: user_synapse
|
register: user_synapse
|
||||||
when: '"synapse" in groups'
|
when: '"synapse" in groups'
|
||||||
|
|
||||||
|
- name: Figure out local IP address
|
||||||
|
set_fact:
|
||||||
|
docker_ip: "{{ vpn_ip if wireguard_services else local_ip }}"
|
||||||
|
|
||||||
- name: Generate docker-compose.yml
|
- name: Generate docker-compose.yml
|
||||||
template:
|
template:
|
||||||
src: "docker-compose.yml.j2"
|
src: "docker-compose.yml.j2"
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# vim: ft=yaml
|
# vim: ft=yaml
|
||||||
---
|
---
|
||||||
|
|
||||||
version: "3"
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
gitea:
|
gitea:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
@ -22,8 +20,8 @@ services:
|
|||||||
- GITEA__server__DOMAIN={{ gitea_domain }}
|
- GITEA__server__DOMAIN={{ gitea_domain }}
|
||||||
- GITEA__server__SSH_DOMAIN={{ gitea_domain }}
|
- GITEA__server__SSH_DOMAIN={{ gitea_domain }}
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "{{ docker_ip }}:3000:3000"
|
||||||
- "2498:22"
|
- "{{ docker_ip }}:2498:22"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- {{ dataroot }}/gitea:/data
|
- {{ dataroot }}/gitea:/data
|
||||||
@ -65,7 +63,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- navidrome
|
- navidrome
|
||||||
ports:
|
ports:
|
||||||
- "4533:4533"
|
- "{{ docker_ip }}:4533:4533"
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if "synapse" in group_names %}
|
{% if "synapse" in group_names %}
|
||||||
@ -82,7 +80,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- navidrome
|
- navidrome
|
||||||
ports:
|
ports:
|
||||||
- "8008:8008/tcp"
|
- "{{ docker_ip }}:8008:8008/tcp"
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -101,7 +99,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- paperless-broker
|
- paperless-broker
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "{{ docker_ip }}:8000:8000"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
@ -8,3 +8,4 @@ local_subnet: 192.168.0.0/24
|
|||||||
sshd_port: 2500
|
sshd_port: 2500
|
||||||
|
|
||||||
bastion_ip: "{{ hostvars[groups['bastion'][0]]['local_ip'] }}"
|
bastion_ip: "{{ hostvars[groups['bastion'][0]]['local_ip'] }}"
|
||||||
|
bastion_vpn_ip: "{{ hostvars[groups['bastion'][0]]['vpn_ip'] }}"
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
|
|
||||||
- name: Set default sources (fleet server)
|
- name: Set default sources (fleet server)
|
||||||
set_fact:
|
set_fact:
|
||||||
default_firewall_src: "{{ bastion_ip }}"
|
default_firewall_src: "{{ bastion_vpn_ip if wireguard_services else bastion_ip }}"
|
||||||
when: '"fleet" in group_names'
|
when: '"fleet" in group_names'
|
||||||
|
|
||||||
|
# this is actually kind of useless because docker bypasses this
|
||||||
- name: Allow service ports
|
- name: Allow service ports
|
||||||
community.general.ufw:
|
community.general.ufw:
|
||||||
rule: allow
|
rule: allow
|
||||||
|
@ -5,5 +5,9 @@
|
|||||||
# Modifications will be lost!
|
# Modifications will be lost!
|
||||||
|
|
||||||
{% for host in groups["all"] %}
|
{% for host in groups["all"] %}
|
||||||
|
{% if wireguard_services %}
|
||||||
|
{{ hostvars[host]["vpn_ip"] }} {{ host }}
|
||||||
|
{% else %}
|
||||||
{{ hostvars[host]["local_ip"] }} {{ host }}
|
{{ hostvars[host]["local_ip"] }} {{ host }}
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -11,8 +11,6 @@ wireguard:
|
|||||||
- "{{ dns_forward }}"
|
- "{{ dns_forward }}"
|
||||||
interface: "wg0"
|
interface: "wg0"
|
||||||
ip:
|
ip:
|
||||||
# address for the server
|
|
||||||
address: "10.66.77.1/32"
|
|
||||||
# cidr range in tunnel
|
# cidr range in tunnel
|
||||||
cidr: "10.66.77.0/24"
|
cidr: "10.66.77.0/24"
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
value: 1
|
value: 1
|
||||||
state: present
|
state: present
|
||||||
reload: yes
|
reload: yes
|
||||||
|
when: '"bastion" in group_names'
|
||||||
|
|
||||||
- name: Setup UFW rules to accept VPN traffic
|
- name: Setup UFW rules to accept VPN traffic
|
||||||
community.general.ufw:
|
community.general.ufw:
|
||||||
@ -22,6 +23,7 @@
|
|||||||
direction: in
|
direction: in
|
||||||
src: "{{ wireguard.ip.cidr }}"
|
src: "{{ wireguard.ip.cidr }}"
|
||||||
dest: any
|
dest: any
|
||||||
|
when: '"bastion" in group_names'
|
||||||
|
|
||||||
- name: Deploy wireguard server config
|
- name: Deploy wireguard server config
|
||||||
template:
|
template:
|
||||||
@ -42,6 +44,7 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: 0700
|
mode: 0700
|
||||||
state: directory
|
state: directory
|
||||||
|
when: '"bastion" in group_names'
|
||||||
|
|
||||||
- name: Create wireguard client configs
|
- name: Create wireguard client configs
|
||||||
template:
|
template:
|
||||||
@ -52,6 +55,7 @@
|
|||||||
mode: 0600
|
mode: 0600
|
||||||
lstrip_blocks: true
|
lstrip_blocks: true
|
||||||
no_log: true
|
no_log: true
|
||||||
with_items: "{{ wireguard_secret.peers }}"
|
with_items: "{{ wireguard_secret.clients }}"
|
||||||
notify:
|
notify:
|
||||||
- Start wireguard
|
- Start wireguard
|
||||||
|
when: '"bastion" in group_names'
|
||||||
|
@ -3,14 +3,12 @@
|
|||||||
Address = {{ item.addr }}
|
Address = {{ item.addr }}
|
||||||
# device privkey
|
# device privkey
|
||||||
PrivateKey = {{ item.priv_key }}
|
PrivateKey = {{ item.priv_key }}
|
||||||
DNS = {{ wireguard.ip.address }}
|
DNS = {{ hostvars[groups["bastion"][0]].vpn_ip }}
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
# server stuff
|
# server stuff
|
||||||
PublicKey = {{ wireguard_secret.server_pub_key }}
|
PublicKey = {{ wireguard_secret.servers[groups["bastion"][0]].pub }}
|
||||||
Endpoint = {{ wireguard.ip.server_public }}:{{ wireguard.ip.port }}
|
Endpoint = {{ wireguard.ip.server_public }}:{{ wireguard.ip.port }}
|
||||||
|
|
||||||
# allow traffic for all subnets into the VPN
|
# allow traffic for all subnets into the VPN
|
||||||
AllowedIPs = 0.0.0.0/0
|
AllowedIPs = 0.0.0.0/0
|
||||||
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
@ -1,14 +1,27 @@
|
|||||||
[Interface]
|
[Interface]
|
||||||
Address = {{ wireguard.ip.address }}
|
Address = {{ hostvars[inventory_hostname]["vpn_ip"] }}/32
|
||||||
PrivateKey = {{ wireguard_secret.server_key }}
|
PrivateKey = {{ wireguard_secret.servers[inventory_hostname].priv }}
|
||||||
ListenPort = {{ wireguard.ip.port }}
|
ListenPort = {{ wireguard.ip.port }}
|
||||||
|
|
||||||
|
{% if "bastion" in group_names %}
|
||||||
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ net_interface }} -j MASQUERADE
|
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ net_interface }} -j MASQUERADE
|
||||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ net_interface }} -j MASQUERADE
|
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ net_interface }} -j MASQUERADE
|
||||||
|
{% endif %}
|
||||||
SaveConfig = false
|
SaveConfig = false
|
||||||
|
|
||||||
{% for peer in wireguard_secret.peers %}
|
{% for server_peer in wireguard_secret.servers %}
|
||||||
|
{% if not server_peer == inventory_hostname %}
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {{ wireguard_secret.servers[server_peer].pub }}
|
||||||
|
AllowedIPs = {{ hostvars[server_peer]["vpn_ip"] }}
|
||||||
|
Endpoint = {{ hostvars[server_peer]["local_ip"] }}:{{ wireguard.ip.port }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if "bastion" in group_names %}
|
||||||
|
{% for peer in wireguard_secret.clients %}
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = {{ peer.pub_key }}
|
PublicKey = {{ peer.pub_key }}
|
||||||
AllowedIPs = {{ peer.addr }}
|
AllowedIPs = {{ peer.addr }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
Loading…
Reference in New Issue
Block a user