mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-11 12:24:42 +02:00
feat(wireguard): accès distant au lab via VPN (Freebox 51820/udp → s01) (#80)
- rôle wireguard : serveur wg0 (10.99.0.0/24) sur storage-01, profils clients générés dans /etc/wireguard/clients/ (fichier .conf / QR code) - gateway : filtrage nftables par pair — full (tout le lab) / portal (uniquement Traefik :80 = portail Guacamole), masquerade VPN→cluster - dnsmasq : bind-dynamic + écoute wg0 → *.lab.local résolu dans le tunnel - pairs initiaux : alkatrazz (full), invite-01 (portal) - vault : clés WireGuard + reprise de l'archive Authentik (remplace #78) - doc : admin/infra/wireguard.md (onboarding pair, pièges, exploitation) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
fbec998c21
commit
bc0e1dd53e
13 changed files with 388 additions and 123 deletions
|
|
@ -1,8 +1,10 @@
|
|||
# Géré par Ansible — ne pas modifier manuellement
|
||||
|
||||
# Écoute uniquement sur le LAN cluster
|
||||
# Écoute sur le LAN cluster + tunnel WireGuard (DNS des clients VPN).
|
||||
# bind-dynamic (et non bind-interfaces) : wg0 peut apparaître après le démarrage
|
||||
interface={{ lan_interface }}
|
||||
bind-interfaces
|
||||
interface=wg0
|
||||
bind-dynamic
|
||||
|
||||
# Domaine local
|
||||
domain={{ dns_domain }}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ table inet filter {
|
|||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
# SSH depuis le LAN domestique et le LAN cluster
|
||||
tcp dport 22 ip saddr { 192.168.1.0/24, 192.168.10.0/24 } accept
|
||||
# SSH depuis le LAN domestique, le LAN cluster et le tunnel WireGuard
|
||||
tcp dport 22 ip saddr { 192.168.1.0/24, 192.168.10.0/24, 10.99.0.0/24 } accept
|
||||
|
||||
# DNS + DHCP (dnsmasq)
|
||||
udp dport 53 ip saddr 192.168.10.0/24 accept
|
||||
tcp dport 53 ip saddr 192.168.10.0/24 accept
|
||||
# WireGuard — handshake/transport (Freebox 51820/udp → funk-s01)
|
||||
udp dport 51820 accept
|
||||
|
||||
# DNS + DHCP (dnsmasq) — cluster + clients VPN (dnsmasq écoute aussi sur wg0)
|
||||
udp dport 53 ip saddr { 192.168.10.0/24, 10.99.0.0/24 } accept
|
||||
tcp dport 53 ip saddr { 192.168.10.0/24, 10.99.0.0/24 } accept
|
||||
udp dport 67 iif {{ lan_interface }} accept
|
||||
|
||||
# NFS
|
||||
|
|
@ -73,6 +76,18 @@ table inet filter {
|
|||
# 7777 = jeu/API ; 8888 = reliable messaging TCP (1.1) — bloqué = kick à ~60 s
|
||||
ip daddr 192.168.10.20 tcp dport { 7777, 8888 } ct state new,established,related accept
|
||||
ip daddr 192.168.10.20 udp dport 7777 ct state new,established,related accept
|
||||
|
||||
# WireGuard → lab : filtrage par pair (host_vars wireguard_peers)
|
||||
{% for peer in wireguard_peers | selectattr('access', 'equalto', 'full') | list %}
|
||||
# {{ peer.name }} — accès complet
|
||||
iif "wg0" ip saddr {{ peer.ip }} ct state new,established,related accept
|
||||
{% endfor %}
|
||||
{% for peer in wireguard_peers | selectattr('access', 'equalto', 'portal') | list %}
|
||||
# {{ peer.name }} — portail Guacamole uniquement (Traefik)
|
||||
iif "wg0" ip saddr {{ peer.ip }} ip daddr 192.168.10.200 tcp dport 80 ct state new,established,related accept
|
||||
{% endfor %}
|
||||
# Retours vers le tunnel
|
||||
oif "wg0" ct state established,related accept
|
||||
}
|
||||
|
||||
chain output {
|
||||
|
|
@ -95,5 +110,9 @@ table ip nat {
|
|||
|
||||
# NAT masquerade pour le LAN cluster
|
||||
oif {{ wan_interface }} ip saddr 192.168.10.0/24 masquerade
|
||||
|
||||
# Clients VPN → LAN cluster : masquerade en IP s01 (retours garantis même si
|
||||
# un nœud ne route pas 10.99.0.0/24)
|
||||
oif {{ lan_interface }} ip saddr 10.99.0.0/24 masquerade
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
ansible/roles/wireguard/README.md
Normal file
36
ansible/roles/wireguard/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Rôle `wireguard`
|
||||
|
||||
Serveur **WireGuard** sur storage-01 : accès distant au lab (portail Guacamole, voire
|
||||
tout le LAN cluster) via `kaya.freeboxos.fr:51820/udp` (redirection Freebox → funk-s01).
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Défaut | Rôle |
|
||||
|---|---|---|
|
||||
| `wireguard_port` | `51820` | Port UDP d'écoute (= redirection Freebox) |
|
||||
| `wireguard_subnet` / `wireguard_server_ip` | `10.99.0.0/24` / `10.99.0.1` | Réseau du tunnel |
|
||||
| `wireguard_endpoint` | `kaya.freeboxos.fr:51820` | Endpoint public côté clients |
|
||||
| `wireguard_client_allowed_ips` | `192.168.10.0/24, 10.99.0.0/24` | Split tunnel (lab uniquement) |
|
||||
| `wireguard_peers` | `[]` (host_vars) | Pairs : `{name, ip, public_key, access}` |
|
||||
|
||||
`access` : `full` (tout le lab — admin) ou `portal` (uniquement Traefik :80 → portail
|
||||
Guacamole). Le filtrage est fait **par nftables** (rôle `gateway`), pas par WireGuard.
|
||||
|
||||
Secrets vault : `vault_wireguard_server_private_key`,
|
||||
`vault_wireguard_peer_private_keys` (dict `name: clé`).
|
||||
|
||||
## Ajouter un pair
|
||||
|
||||
Procédure complète (clés openssl, vault, host_vars, QR code) :
|
||||
**`admin/infra/wireguard.md`**. Résumé : générer une paire X25519, clé privée dans le
|
||||
vault, entrée dans `wireguard_peers` (host_vars storage-01), ré-appliquer
|
||||
`--tags wireguard,gateway`, distribuer `/etc/wireguard/clients/<name>.conf`.
|
||||
|
||||
## Caveats
|
||||
|
||||
- Les profils clients dans `/etc/wireguard/clients/` contiennent la **clé privée du
|
||||
client** — à transmettre par canal raisonnable, jamais commités.
|
||||
- Le DNS du tunnel = dnsmasq s01 (`10.99.0.1`) : nécessite `bind-dynamic` + `interface=wg0`
|
||||
dans le rôle `dnsmasq`, et l'ouverture 53 depuis `10.99.0.0/24` dans `gateway`.
|
||||
- Retirer un pair : le supprimer de `wireguard_peers` + du vault → re-apply
|
||||
`--tags wireguard,gateway` (la règle nftables et le [Peer] disparaissent).
|
||||
23
ansible/roles/wireguard/defaults/main.yml
Normal file
23
ansible/roles/wireguard/defaults/main.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
wireguard_interface: wg0
|
||||
wireguard_port: 51820
|
||||
wireguard_subnet: 10.99.0.0/24
|
||||
wireguard_server_ip: 10.99.0.1
|
||||
wireguard_server_public_key: "NCneTVY6UlvaVY9L6dgP+w+w8pheQS30d08NHn9jBk8="
|
||||
# Clé privée serveur : vault_wireguard_server_private_key
|
||||
|
||||
# Endpoint public que les clients contactent (redirection Freebox 51820/udp → funk-s01)
|
||||
wireguard_endpoint: "kaya.freeboxos.fr:{{ wireguard_port }}"
|
||||
|
||||
# DNS poussé aux clients = dnsmasq de s01 (résout *.lab.local → Traefik)
|
||||
wireguard_client_dns: "{{ wireguard_server_ip }}"
|
||||
|
||||
# Split tunnel : seuls le LAN cluster et le tunnel passent dans le VPN,
|
||||
# le reste du trafic client sort par sa connexion normale
|
||||
wireguard_client_allowed_ips: "192.168.10.0/24, {{ wireguard_subnet }}"
|
||||
|
||||
# Pairs — définis dans host_vars/storage-01/vars.yml (clés privées dans le vault :
|
||||
# vault_wireguard_peer_private_keys.<name>). access :
|
||||
# full → tout le LAN cluster (admin)
|
||||
# portal → uniquement le portail web Guacamole (Traefik :80) — invités
|
||||
wireguard_peers: []
|
||||
5
ansible/roles/wireguard/handlers/main.yml
Normal file
5
ansible/roles/wireguard/handlers/main.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: Restart wireguard
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
state: restarted
|
||||
42
ansible/roles/wireguard/tasks/main.yml
Normal file
42
ansible/roles/wireguard/tasks/main.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
- name: Install wireguard-tools
|
||||
ansible.builtin.dnf:
|
||||
name: wireguard-tools
|
||||
state: present
|
||||
|
||||
- name: Deploy wg0 server config
|
||||
ansible.builtin.template:
|
||||
src: wg0.conf.j2
|
||||
dest: "/etc/wireguard/{{ wireguard_interface }}.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
notify: Restart wireguard
|
||||
|
||||
- name: Create clients config directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/wireguard/clients
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0700'
|
||||
|
||||
# Profils clients prêts à distribuer (fichier .conf à envoyer / QR code —
|
||||
# voir admin/infra/wireguard.md)
|
||||
- name: Generate client configs
|
||||
ansible.builtin.template:
|
||||
src: client.conf.j2
|
||||
dest: "/etc/wireguard/clients/{{ item.name }}.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
loop: "{{ wireguard_peers }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
no_log: true
|
||||
|
||||
- name: Enable and start wg-quick
|
||||
ansible.builtin.systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
enabled: true
|
||||
state: started
|
||||
11
ansible/roles/wireguard/templates/client.conf.j2
Normal file
11
ansible/roles/wireguard/templates/client.conf.j2
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Profil WireGuard « {{ item.name }} » — accès {{ item.access }} au lab Funk
|
||||
[Interface]
|
||||
PrivateKey = {{ vault_wireguard_peer_private_keys[item.name] }}
|
||||
Address = {{ item.ip }}/32
|
||||
DNS = {{ wireguard_client_dns }}
|
||||
|
||||
[Peer]
|
||||
PublicKey = {{ wireguard_server_public_key }}
|
||||
Endpoint = {{ wireguard_endpoint }}
|
||||
AllowedIPs = {{ wireguard_client_allowed_ips }}
|
||||
PersistentKeepalive = 25
|
||||
13
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
13
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Géré par Ansible — ne pas modifier manuellement
|
||||
[Interface]
|
||||
Address = {{ wireguard_server_ip }}/{{ wireguard_subnet.split('/')[1] }}
|
||||
ListenPort = {{ wireguard_port }}
|
||||
PrivateKey = {{ vault_wireguard_server_private_key }}
|
||||
|
||||
{% for peer in wireguard_peers %}
|
||||
# {{ peer.name }} — accès {{ peer.access }}
|
||||
[Peer]
|
||||
PublicKey = {{ peer.public_key }}
|
||||
AllowedIPs = {{ peer.ip }}/32
|
||||
|
||||
{% endfor %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue