feat: Add truecommand; update VPN config

This commit is contained in:
Tony Du 2025-02-26 20:04:52 -08:00
parent dde508f88b
commit 8aba7c5c7e
6 changed files with 104 additions and 48 deletions

View File

@ -2,29 +2,33 @@
wireguard_remote_directory: /etc/wireguard wireguard_remote_directory: /etc/wireguard
wireguard_interface_restart: false wireguard_interface_restart: false
wireguard_service_enabled: false wireguard_service_enabled: true
wireguard_service_state: started wireguard_service_state: started
# Keep the NAT mapping open. Should only be needed for server -> client, but # We need to keep the NAT mapping open:
# if the server disconnects, we may never be able to re-establish a connection. # https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence
# So this is on both client and server just in case that happens. # I've tested 25 seconds, which seems to be too low. The mapping still seems
wireguard_persistent_keepalive: 25 # to be broken every once in a while.
# Or, it might be because PersistentKeepalive is actually also needed on the
# server but it's being omitted currently. See the issue I opened:
# https://github.com/githubixx/ansible-role-wireguard/issues/217#issue-2871281915
wireguard_persistent_keepalive: 15
wireguard_ipv6_subnet: "fde0:fb5b:2593::/64" wireguard_ipv6_subnet: "fde0:fb5b:2593::/64"
# Setting this here doesn't seem to work. We set it in a playbook later # Setting this here doesn't seem to work. We set it during runtime later
# public_ipv6_subnet: "{{ hostvars[groups['embassy'][0]].ipv6_subnet }}" # public_ipv6_subnet: "{{ hostvars[groups['embassy'][0]].ipv6_subnet }}"
# We can generate this dynamically, but it really doesn't seem like it's worth # We can generate this dynamically, but it really doesn't seem like it's worth
# the work. # the work.
nat_map: nat_map:
moirai-clotho.local: moirai-clotho.local:
vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('16') | ansible.utils.ipaddr('address') }}" vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('16') }}"
vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('16') | ansible.utils.ipaddr('address') }}" vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('16') }}"
moirai-lachesis.local: moirai-lachesis.local:
vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('17') | ansible.utils.ipaddr('address') }}" vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('17') }}"
vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('17') | ansible.utils.ipaddr('address') }}" vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('17') }}"
moirai-atropos.local: moirai-atropos.local:
vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('18') | ansible.utils.ipaddr('address') }}" vpn_ipv6: "{{ wireguard_ipv6_subnet | ansible.utils.ipaddr('18') }}"
vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('18') | ansible.utils.ipaddr('address') }}" vps_ipv6: "{{ public_ipv6_subnet | ansible.utils.ipaddr('18') }}"

View File

@ -1,5 +1,5 @@
wireguard_addresses: wireguard_addresses:
- "{{ nat_map[inventory_hostname].vpn_ipv6 }}" - "{{ nat_map[inventory_hostname].vpn_ipv6 | ansible.utils.ipaddr('address') }}"
# wireguard_endpoint: "{{ nat_map[inventory_hostname].vpn_ipv6 }}" # wireguard_endpoint: "{{ nat_map[inventory_hostname].vpn_ipv6 }}"
wireguard_endpoint: "" wireguard_endpoint: ""

View File

@ -8,6 +8,9 @@ wireguard_endpoint: "{{ inventory_hostname }}"
wireguard_preup: wireguard_preup:
- echo 1 > /proc/sys/net/ipv4/ip_forward - echo 1 > /proc/sys/net/ipv4/ip_forward
- echo 1 > /proc/sys/net/ipv6/conf/all/forwarding - echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
# Disable autoconf while running. We assign some IP addresses that get removed
# by autoconf otherwise.
- echo 0 > /proc/sys/net/ipv6/conf/eth0/autoconf
wireguard_postup: | wireguard_postup: |
{% filter from_yaml %} {% filter from_yaml %}
@ -16,17 +19,19 @@ wireguard_postup: |
# Incoming packets to this node's public IP are DNAT'd and forwarded to the # Incoming packets to this node's public IP are DNAT'd and forwarded to the
# matching internal VPN IP # matching internal VPN IP
- ip6tables -t nat -A PREROUTING -p tcp -d {{ value.vps_ipv6 }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 }} - ip6tables -t nat -A PREROUTING -p tcp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
# Same for SFTP over TCP.
- ip6tables -t nat -A PREROUTING -p tcp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 2022 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
# Incoming packets to an internal VPN IP are SNAT'd to use this node's public # Incoming packets to an internal VPN IP are SNAT'd to use this node's public
# IP. I think `-j MASQUERADE` might work here rather than doing the SNAT # IP. I think `-j MASQUERADE` might work here rather than doing the SNAT
# manually(?), but I don't mind being explicit here. # manually(?), but I don't mind being explicit here.
- ip6tables -t nat -A POSTROUTING -p tcp -s {{ value.vpn_ipv6 }} -j SNAT --to-source {{ value.vps_ipv6 }} - ip6tables -t nat -A POSTROUTING -p tcp -s {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }} -j SNAT --to-source {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }}
# Same thing with UDP. We do this selectively so we don't mess with things # Same thing with UDP. We do this selectively so we don't mess with things
# like ICMP6 and whatnot. # like ICMP6 and whatnot.
- ip6tables -t nat -A PREROUTING -p udp -d {{ value.vps_ipv6 }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 }} - ip6tables -t nat -A PREROUTING -p udp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
- ip6tables -t nat -A POSTROUTING -p udp -s {{ value.vpn_ipv6 }} -j SNAT --to-source {{ value.vps_ipv6 }} - ip6tables -t nat -A POSTROUTING -p udp -s {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }} -j SNAT --to-source {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }}
{% endfor %} {% endfor %}
{% endfilter %} {% endfilter %}
@ -34,15 +39,17 @@ wireguard_postup: |
wireguard_predown: | wireguard_predown: |
{% filter from_yaml %} {% filter from_yaml %}
{% for value in (nat_map | dict2items | map(attribute='value') | reverse) %} {% for value in (nat_map | dict2items | map(attribute='value') | reverse) %}
- ip6tables -t nat -D POSTROUTING -p udp -s {{ value.vpn_ipv6 }} -j SNAT --to-source {{ value.vps_ipv6 }} - ip6tables -t nat -D POSTROUTING -p udp -s {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }} -j SNAT --to-source {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }}
- ip6tables -t nat -D PREROUTING -p udp -d {{ value.vps_ipv6 }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 }} - ip6tables -t nat -D PREROUTING -p udp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
- ip6tables -t nat -D POSTROUTING -p tcp -s {{ value.vpn_ipv6 }} -j SNAT --to-source {{ value.vps_ipv6 }} - ip6tables -t nat -D POSTROUTING -p tcp -s {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }} -j SNAT --to-source {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }}
- ip6tables -t nat -D PREROUTING -p tcp -d {{ value.vps_ipv6 }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 }} - ip6tables -t nat -D PREROUTING -p tcp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 2022 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
- ip6tables -t nat -D PREROUTING -p tcp -d {{ value.vps_ipv6 | ansible.utils.ipaddr('address') }} --dport 20000:20100 -j DNAT --to-destination {{ value.vpn_ipv6 | ansible.utils.ipaddr('address') }}
- ip -6 addr del {{ value.vps_ipv6 }} dev eth0 - ip -6 addr del {{ value.vps_ipv6 }} dev eth0
{% endfor %} {% endfor %}
{% endfilter %} {% endfilter %}
wireguard_postdown: wireguard_postdown:
- echo 1 > /proc/sys/net/ipv6/conf/eth0/autoconf
- echo 0 > /proc/sys/net/ipv6/conf/all/forwarding - echo 0 > /proc/sys/net/ipv6/conf/all/forwarding
- echo 0 > /proc/sys/net/ipv4/ip_forward - echo 0 > /proc/sys/net/ipv4/ip_forward

View File

@ -9,6 +9,7 @@
groups['vpn_server'] | length == 1 and groups['vpn_server'] | length == 1 and
groups['vpn_server'] | intersect(groups['embassy']) | length == 1 groups['vpn_server'] | intersect(groups['embassy']) | length == 1
msg: Expected only one embassy host msg: Expected only one embassy host
- name: Verify ipv6_subnet is set - name: Verify ipv6_subnet is set
when: inventory_hostname == groups['embassy'][0] when: inventory_hostname == groups['embassy'][0]
ansible.builtin.assert: ansible.builtin.assert:
@ -22,25 +23,27 @@
- set_fact: - set_fact:
public_ipv6_subnet: "{{ hostvars[groups['embassy'][0]].ipv6_subnet }}" public_ipv6_subnet: "{{ hostvars[groups['embassy'][0]].ipv6_subnet }}"
- name: Prepare embassy - when: inventory_hostname == groups['embassy'][0]
hosts: embassy name: Disable password-based authentication
become: true become: true
tasks:
- name: Disable password-based authentication
lineinfile: lineinfile:
path: "/etc/ssh/sshd_config" path: "/etc/ssh/sshd_config"
regexp: '^()PasswordAuthentication yes()$' regexp: '^()PasswordAuthentication yes()$'
line: 'PasswordAuthentication no' line: 'PasswordAuthentication no'
register: passwordauthentication register: passwordauthentication
- name: Enable public key authentication in SSH - when: inventory_hostname == groups['embassy'][0]
name: Enable public key authentication in SSH
become: true
lineinfile: lineinfile:
path: "/etc/ssh/sshd_config" path: "/etc/ssh/sshd_config"
regexp: '^()PubkeyAuthentication()$' regexp: '^()PubkeyAuthentication()$'
line: 'PubkeyAuthentication yes' line: 'PubkeyAuthentication yes'
register: publickeyauthentication register: publickeyauthentication
- name: Restart SSH - when: inventory_hostname == groups['embassy'][0]
name: Restart SSH
become: true
service: service:
name: ssh name: ssh
state: restarted state: restarted
@ -52,24 +55,24 @@
roles: roles:
- githubixx.ansible_role_wireguard - githubixx.ansible_role_wireguard
# - name: Install wings - name: Install wings
# hosts: moirai_wings hosts: moirai_wings
# remote_user: ubuntu remote_user: ubuntu
# # Don't forget to create a new disk if creating new wings. This is # Don't forget to create a new disk if creating new wings. This is
# # purposefully manual to give more fine-grained control # purposefully manual to give more fine-grained control
# vars: vars:
# pv_disks: pv_disks:
# - /dev/sda - /dev/sda
# vg_name: vg1 vg_name: vg1
# lv_name: pvs lv_name: pvs
# lv_size: +100%FREE lv_size: +100%FREE
# fs_type: ext4 fs_type: ext4
# mount_path: /var/lib/pterodactyl mount_path: /var/lib/pterodactyl
# extra_docker_daemon_options: | extra_docker_daemon_options: |
# "dns": ["10.0.123.123"], "dns": ["10.0.123.123"],
# roles: roles:
# - dns-client - dns-client
# - lvm - lvm
# - docker - docker
# - wings - wings

View File

@ -0,0 +1,33 @@
---
version: '3.9'
networks:
traefik:
external: true
services:
ixsystems:
image: 'ghcr.io/ixsystems/truecommand:v3.0.2'
volumes:
- ${TRUECOMMAND_DATA_DIRECTORY:-/mnt/stingray/truecommand/data}:/data
networks:
- traefik
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.truecommand.rule=Host(`${TRUECOMMAND_HOST:-truecommand.stingray.mnke.org}`)"
- "traefik.http.routers.truecommand.entrypoints=websecure"
- "traefik.http.routers.truecommand.tls.certresolver=letsencrypt"
- "traefik.http.services.truecommand.loadbalancer.server.port=80"
- "traefik.swarm.network=traefik"
mode: replicated
replicas: 1
placement:
constraints: [node.role != manager]
# resources:
# limits:
# cpus: '0.50'
# memory: 1G
# reservations:
# cpus: '0.25'
# memory: 128M

View File

@ -56,6 +56,15 @@ resource "linode_firewall" "embassy" {
ipv6 = ["::/0"] ipv6 = ["::/0"]
} }
inbound {
label = "allow-sftp"
action = "ACCEPT"
protocol = "TCP"
ports = "2022"
ipv4 = ["0.0.0.0/0"]
ipv6 = ["::/0"]
}
inbound { inbound {
label = "allow-wireguard" label = "allow-wireguard"
action = "ACCEPT" action = "ACCEPT"