--- - name: Reach Portainer via SSH tunnel through bdc (fixed local port 9443) hosts: localhost connection: local gather_facts: false vars: bastion_host: bdc.cci17.fr bastion_port: 17100 bastion_user: ansible portainer_ip: 10.30.0.151 portainer_port: 9443 # Port local fixe demandé local_port: 9443 ssh_control_socket: "/tmp/ssh-tunnel-portainer-9443.sock" ssh_log: "/tmp/ssh-tunnel-portainer-9443.log" tasks: - block: - name: Check if local port 9443 is already in use shell: "ss -lnt | grep -q ':{{ local_port }} '" register: port_in_use changed_when: false failed_when: port_in_use.rc == 0 - name: Open SSH tunnel (local 127.0.0.1:9443 -> portainer via bastion) shell: | rm -f "{{ ssh_log }}" ssh -p {{ bastion_port }} \ -o ExitOnForwardFailure=yes \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o ServerAliveInterval=10 \ -o ServerAliveCountMax=3 \ -o ConnectTimeout=10 \ -o LogLevel=DEBUG2 \ -vv \ -M -S {{ ssh_control_socket }} \ -f -N \ -L 127.0.0.1:{{ local_port }}:{{ portainer_ip }}:{{ portainer_port }} \ {{ bastion_user }}@{{ bastion_host }} \ > "{{ ssh_log }}" 2>&1 register: tunnel_open changed_when: true failed_when: tunnel_open.rc != 0 - name: Wait for local tunnel port 9443 to be listening wait_for: host: 127.0.0.1 port: "{{ local_port }}" delay: 1 timeout: 20 - name: HTTPS check Portainer through tunnel uri: url: "https://127.0.0.1:{{ local_port }}/" method: GET validate_certs: false status_code: [200, 301, 302, 401, 403] register: portainer_check - name: OK debug: msg: "✅ Portainer joignable via tunnel (status {{ portainer_check.status }})" rescue: - name: Show last 120 lines of SSH verbose log shell: "tail -n 120 {{ ssh_log }} || true" register: sshlog_tail changed_when: false - name: Failure details debug: msg: - "❌ Tunnel/Portainer non joignable via 127.0.0.1:9443" - "Extrait log SSH:" - "{{ sshlog_tail.stdout | default('') }}" - "Indications :" - "- 'administratively prohibited' => port forwarding désactivé côté bdc/OPNsense" - "- 'connect to host 10.30.0.151 port 9443: ...' => bdc n'atteint pas Portainer (routage/firewall/service)" always: - name: Close tunnel if opened shell: > test -S {{ ssh_control_socket }} && ssh -p {{ bastion_port }} -S {{ ssh_control_socket }} -O exit {{ bastion_user }}@{{ bastion_host }} || true ignore_errors: true