Files
infra/ansible/playbooks/openvpn-laptop.yml
T
DmitryandClaude Sonnet 5 d45391a261
lint / yamllint + ansible-lint + syntax-check (push) Canceled after 0s
Revert: drop the phone road-warrior OpenVPN attempt
Rolled back per the user's request. Three variants were tried on ru-vps
(static key; TLS peer-fingerprint p2p; server mode with push routes and an
inline <ca>). The server side worked each time, but the "OpenVPN for
Android" client consistently failed at config build ("Used 101 tries to
get current version of the profile"), which looks like an app/OS issue
rather than the config.

Repo: remove playbooks/openvpn-phone.yml, its Make target, and the shared
homelab_vpn_client_routes var; restore openvpn-laptop.yml to its prior
state (its pre-existing `become: false` on delegate_to: localhost is noted
in plan.md, left untouched). ru-vps teardown done out of band: unit, tun2,
ufw/nat rules for 9444 and 10.80.0.0/29, and /etc/openvpn/homelab-phone
removed; the site tunnel (homelab-openvpn, tun0) was not touched and is
verified active.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbuZrUoevfBgCpf5DCF4DG
2026-09-03 10:17:26 +03:00

198 lines
6.4 KiB
YAML

- name: Configure OpenVPN laptop access through ru-vps
hosts: ru-vps
gather_facts: true
become: true
vars:
laptop_profile_name: ada-x1
laptop_openvpn_dir: /etc/openvpn/homelab-laptop
laptop_openvpn_config: /etc/openvpn/homelab-laptop/server.conf
laptop_openvpn_key: /etc/openvpn/homelab-laptop/static.key
laptop_openvpn_service: homelab-openvpn-laptop
laptop_openvpn_port: 9443
laptop_openvpn_interface: tun1
laptop_openvpn_site_interface: tun0
laptop_openvpn_network_cidr: 10.79.0.0/30
laptop_openvpn_server_ip: 10.79.0.1
laptop_openvpn_client_ip: 10.79.0.2
laptop_openvpn_remote: 157.22.231.198
laptop_openvpn_client_config_local: "{{ playbook_dir }}/../generated/{{ laptop_profile_name }}.ovpn"
laptop_openvpn_home_routes:
- network: 192.168.1.5
netmask: 255.255.255.255
- network: 192.168.1.6
netmask: 255.255.255.254
- network: 192.168.1.8
netmask: 255.255.255.248
- network: 192.168.1.16
netmask: 255.255.255.240
- network: 192.168.1.32
netmask: 255.255.255.248
- network: 192.168.1.40
netmask: 255.255.255.255
tasks:
- name: Install OpenVPN packages
ansible.builtin.apt:
name:
- openvpn
- iptables
state: present
update_cache: true
- name: Ensure laptop OpenVPN config directory exists
ansible.builtin.file:
path: "{{ laptop_openvpn_dir }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Generate laptop static key if missing
ansible.builtin.command: "openvpn --genkey secret {{ laptop_openvpn_key }}"
args:
creates: "{{ laptop_openvpn_key }}"
no_log: true
- name: Enable IPv4 forwarding on ru-vps
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: "1"
state: present
reload: true
- name: Allow laptop OpenVPN TCP in UFW
ansible.builtin.command: "ufw allow {{ laptop_openvpn_port }}/tcp"
register: ufw_allow_laptop_openvpn
changed_when: "'Rule added' in ufw_allow_laptop_openvpn.stdout or 'Rules updated' in ufw_allow_laptop_openvpn.stdout"
failed_when: false
- name: Render laptop OpenVPN server config
ansible.builtin.copy:
dest: "{{ laptop_openvpn_config }}"
owner: root
group: root
mode: "0600"
content: |
dev {{ laptop_openvpn_interface }}
ifconfig {{ laptop_openvpn_server_ip }} {{ laptop_openvpn_client_ip }}
secret {{ laptop_openvpn_key }}
cipher AES-256-CBC
auth SHA256
proto tcp-server
port {{ laptop_openvpn_port }}
persist-key
persist-tun
verb 3
keepalive 10 60
notify: restart laptop openvpn
- name: Render laptop OpenVPN up script
ansible.builtin.copy:
dest: "{{ laptop_openvpn_dir }}/up.sh"
owner: root
group: root
mode: "0700"
content: |
#!/bin/sh
iptables -t nat -C POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE
notify: restart laptop openvpn
- name: Render laptop OpenVPN down script
ansible.builtin.copy:
dest: "{{ laptop_openvpn_dir }}/down.sh"
owner: root
group: root
mode: "0700"
content: |
#!/bin/sh
iptables -t nat -D POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE 2>/dev/null || true
notify: restart laptop openvpn
- name: Render laptop OpenVPN systemd service
ansible.builtin.copy:
dest: "/etc/systemd/system/{{ laptop_openvpn_service }}.service"
owner: root
group: root
mode: "0644"
content: |
[Unit]
Description=HomeLab OpenVPN laptop access
After=network-online.target homelab-openvpn.service
Wants=network-online.target
Requires=homelab-openvpn.service
[Service]
Type=simple
ExecStartPre={{ laptop_openvpn_dir }}/up.sh
ExecStart=/usr/sbin/openvpn --config {{ laptop_openvpn_config }}
ExecStopPost={{ laptop_openvpn_dir }}/down.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
notify:
- reload systemd
- restart laptop openvpn
- name: Flush handlers before starting laptop OpenVPN
ansible.builtin.meta: flush_handlers
- name: Enable and start laptop OpenVPN
ansible.builtin.systemd:
name: "{{ laptop_openvpn_service }}"
state: started
enabled: true
- name: Read laptop static key for client profile
ansible.builtin.slurp:
src: "{{ laptop_openvpn_key }}"
register: laptop_openvpn_static_key_raw
no_log: true
- name: Ensure local generated directory exists
ansible.builtin.file:
path: "{{ playbook_dir }}/../generated"
state: directory
mode: "0700"
become: false
delegate_to: localhost
- name: Write local laptop OpenVPN profile
ansible.builtin.copy:
dest: "{{ laptop_openvpn_client_config_local }}"
mode: "0600"
content: |
client
dev tun
proto tcp-client
remote {{ laptop_openvpn_remote }} {{ laptop_openvpn_port }}
ifconfig {{ laptop_openvpn_client_ip }} {{ laptop_openvpn_server_ip }}
cipher AES-256-CBC
auth SHA256
persist-key
persist-tun
resolv-retry infinite
nobind
verb 3
{% for route in laptop_openvpn_home_routes %}
route {{ route.network }} {{ route.netmask }}
{% endfor %}
<secret>
{{ laptop_openvpn_static_key_raw.content | b64decode }}
</secret>
become: false
delegate_to: localhost
no_log: true
handlers:
- name: reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: restart laptop openvpn
ansible.builtin.systemd:
name: "{{ laptop_openvpn_service }}"
state: restarted