0937
This commit is contained in:
@@ -589,7 +589,7 @@
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0 (LAST, BEFORE PPP CHECK): IN-PROGRESS MARKER GUARD ============================
|
||||
- name: "PHASE 0 (LAST) | DEV2 check: /tmp/ptsd.inprogress marker exists (show-stopper) via wrapper"
|
||||
- name: "PHASE 0 (LAST) | DEV2 check /tmp/ptsd.inprogress marker exists (show-stopper) via wrapper"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
export DEV2_CMD="test -f /tmp/ptsd.inprogress && echo YES || echo NO"
|
||||
@@ -615,7 +615,7 @@
|
||||
|
||||
|
||||
# ============================ PHASE 0 (LAST): DEV2 STATE GUARD (OLD PPP EXPECTED) ============================
|
||||
- name: "PHASE 0 (LAST) | DEV2 check: ppp0 has IPv4 (old expected) via wrapper"
|
||||
- name: "PHASE 0 (LAST) | DEV2 check ppp0 has IPv4 (old expected) via wrapper"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
export DEV2_CMD="ip -4 addr show dev ppp0 2>/dev/null | grep -m1 'inet ' >/dev/null 2>&1 && echo YES || echo NO"
|
||||
@@ -625,7 +625,7 @@
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: "PHASE 0 (LAST) | DEV2 check: eth0.4000 exists (new/migrated indicator) via wrapper"
|
||||
- name: "PHASE 0 (LAST) | DEV2 check eth0.4000 exists (new/migrated indicator) via wrapper"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
export DEV2_CMD="ip link show eth0.4000 >/dev/null 2>&1 && echo YES || echo NO"
|
||||
@@ -635,7 +635,7 @@
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: "PHASE 0 (LAST) | Detect mixed state: ppp0 present AND eth0.4000 present (should not happen)"
|
||||
- name: "PHASE 0 (LAST) | Detect mixed state ppp0 present AND eth0.4000 present (should not happen)"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_ppp_present: "{{ (dev2_ppp0_has_ipv4.stdout | default('') | trim) == 'YES' }}"
|
||||
@@ -648,7 +648,7 @@
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: "PHASE 0 (LAST) | Graceful stop: DEV2 has BOTH ppp0 and eth0.4000 (mixed/partial migration)"
|
||||
- name: "PHASE 0 (LAST) | Graceful stop DEV2 has BOTH ppp0 and eth0.4000 (mixed/partial migration)"
|
||||
when: dev2_mixed_state | bool
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
|
||||
519
files/ansible-playbooks/redirect_and_block.yml
Normal file
519
files/ansible-playbooks/redirect_and_block.yml
Normal file
@@ -0,0 +1,519 @@
|
||||
# ptsd-stage-blockscripts.yml — Connectivity only + stage block scripts
|
||||
# Scope:
|
||||
# a) Thorough DEV2 connectivity via DEV1 (LLDP first, then tunnel, then fallbacks)
|
||||
# b) Copy startblock.sh and stopblock.sh from controller to DEV2
|
||||
#
|
||||
# Everything else from ptsd-migrate.yml intentionally removed.
|
||||
|
||||
- name: "PTSD | Stage block scripts | Connectivity + copy only"
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
vars:
|
||||
pathprefix: "PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH; "
|
||||
|
||||
dev1_user: "root"
|
||||
dev1_pass: "wavewave"
|
||||
|
||||
dev2_host: "192.168.1.1"
|
||||
dev2_port: 22
|
||||
|
||||
dev2_side_ip: "192.168.1.11/24"
|
||||
dev2_side_ip_addr: "{{ dev2_side_ip.split('/')[0] }}"
|
||||
dev1_iface: "br-wan"
|
||||
|
||||
arping_iface: "eth0"
|
||||
|
||||
dev2_ssh_user: "root"
|
||||
dev2_passfiles:
|
||||
- "basicpass"
|
||||
- "basicpass2"
|
||||
|
||||
ssh_opts_common: >-
|
||||
-o PreferredAuthentications=password
|
||||
-o PubkeyAuthentication=no
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o NumberOfPasswordPrompts=1
|
||||
-o ConnectTimeout=30
|
||||
-o ConnectionAttempts=1
|
||||
-o LogLevel=ERROR
|
||||
|
||||
debugging: true
|
||||
ssh_timeout: 30
|
||||
|
||||
block_src_dir: "/opt/containers/ansible-worker/app"
|
||||
block_dst_dir: "/root/"
|
||||
block_files:
|
||||
- "startblock.sh"
|
||||
- "stopblock.sh"
|
||||
|
||||
tasks:
|
||||
- name: Initialize passfile facts defensively (avoid undefined vars later)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_direct: "NONE"
|
||||
dev2_passfile_used_tunnel: "NONE"
|
||||
dev2_passfile_used_lldp4: "NONE"
|
||||
dev2_passfile_used_lldp6: "NONE"
|
||||
changed_when: false
|
||||
|
||||
- name: Read DEV1 hostname (busybox-safe)
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(cat /proc/sys/kernel/hostname 2>/dev/null || echo "")
|
||||
register: dev1_host_read
|
||||
changed_when: false
|
||||
|
||||
- name: Stop early if connected DEV1 hostname != inventory (guard)
|
||||
ansible.builtin.meta: end_host
|
||||
when: (dev1_host_read.stdout | trim | length > 0) and
|
||||
((dev1_host_read.stdout | trim) != (inventory_hostname | string))
|
||||
|
||||
# ============================ PHASE 0: LLDP FIRST ============================
|
||||
|
||||
- name: Compute DEV2 LLDP digits from inventory hostname (DEV1 side)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_lldp_digits: "{{ (inventory_hostname | string | regex_search('([0-9]+)$')) | default('') }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Read LLDP JSON on DEV1 (best-effort)
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(cat /var/run/lldp_server.json 2>/dev/null || echo "")
|
||||
register: lldp_json
|
||||
changed_when: false
|
||||
|
||||
- name: Parse LLDP IPv4 and IPv6 candidates for DEV2 (best-effort)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
lldp_dev2_ip: >-
|
||||
{{
|
||||
(
|
||||
lldp_json.stdout | default('') |
|
||||
regex_search('"name"\\s*:\\s*"dev2-?'+(dev2_lldp_digits|default(''))+'".*?"ip"\\s*:\\s*"([^"]+)"', '\\1')
|
||||
) | default('')
|
||||
}}
|
||||
lldp_dev2_ip6: >-
|
||||
{{
|
||||
(
|
||||
lldp_json.stdout | default('') |
|
||||
regex_search('"name"\\s*:\\s*"dev2-?'+(dev2_lldp_digits|default(''))+'".*?"ip6"\\s*:\\s*"([^"]+)"', '\\1')
|
||||
) | default('')
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Classify LLDP IPv4 (none, 10, 192_168, other)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
lldp_ipv4_class: >-
|
||||
{% set ip = (lldp_dev2_ip | default('')) %}
|
||||
{% if ip == '' %}none
|
||||
{% elif ip.startswith('10.') %}10
|
||||
{% elif ip.startswith('192.168.') %}192_168
|
||||
{% else %}other{% endif %}
|
||||
changed_when: false
|
||||
|
||||
- name: Debug LLDP candidates
|
||||
when: debugging | bool
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "LLDP digits={{ dev2_lldp_digits | default('') }}"
|
||||
- "LLDP IPv4 candidate={{ lldp_dev2_ip | default('<none>') }}"
|
||||
- "LLDP IPv4 class={{ lldp_ipv4_class | default('none') }}"
|
||||
- "LLDP IPv6 candidate={{ lldp_dev2_ip6 | default('<none>') }}"
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0: DEV2 MAC DISCOVERY VIA DEV1 (FOR TUNNEL SAFETY) ============================
|
||||
|
||||
- name: Find DEV2 MAC via DEV1 bridge fdb (best-effort)
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(bridge fdb show br br-lan 2>/dev/null || echo "")
|
||||
register: bridge_fdb
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Parse DEV2 MAC from bridge fdb output (best-effort)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_mac_from_fdb: >-
|
||||
{{
|
||||
(bridge_fdb.stdout | default('') |
|
||||
regex_search('([0-9a-f]{2}(:[0-9a-f]{2}){5})\\s+dev\\s+{{ arping_iface }}', '\\1')) | default('')
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Debug DEV2 MAC candidate from DEV1
|
||||
when: debugging | bool
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "DEV2 MAC from fdb={{ dev2_mac_from_fdb | default('<none>') }}"
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0: CONNECTION METHOD SELECTION (LLDP DIRECT IF 10.X ELSE TUNNEL) ============================
|
||||
|
||||
- name: Prefer LLDP IPv4 if 192.168.x.x as dev2_host override (tunnel path still allowed)
|
||||
when: lldp_ipv4_class | default('none') == "192_168"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_host: "{{ lldp_dev2_ip }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Set connection method initial (direct_lldp if 10.x else tunnel)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_conn_method: "{{ 'direct_lldp' if (lldp_ipv4_class | default('none') == '10') else 'tunnel' }}"
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0: DIRECT LLDP AUTH (CONTROLLER -> DEV2) ============================
|
||||
|
||||
- name: Try direct LLDP IPv4 auth with basicpass
|
||||
when: dev2_conn_method == "direct_lldp"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f basicpass ssh {{ ssh_opts_common }} -p {{ dev2_port }} "{{ dev2_ssh_user }}@{{ lldp_dev2_ip }}" "true"
|
||||
args: { executable: /bin/bash }
|
||||
register: direct_try_basicpass
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Record direct LLDP passfile used basicpass
|
||||
when: dev2_conn_method == "direct_lldp" and direct_try_basicpass.rc == 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_direct: "basicpass"
|
||||
changed_when: false
|
||||
|
||||
- name: Try direct LLDP IPv4 auth with basicpass2
|
||||
when: dev2_conn_method == "direct_lldp" and dev2_passfile_used_direct == "NONE"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f basicpass2 ssh {{ ssh_opts_common }} -p {{ dev2_port }} "{{ dev2_ssh_user }}@{{ lldp_dev2_ip }}" "true"
|
||||
args: { executable: /bin/bash }
|
||||
register: direct_try_basicpass2
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Record direct LLDP passfile used basicpass2
|
||||
when: dev2_conn_method == "direct_lldp" and direct_try_basicpass2.rc == 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_direct: "basicpass2"
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0: TUNNEL SETUP (CONTROLLER -> DEV1 -> DEV2) ============================
|
||||
|
||||
- name: Prepare tunnel temp dir and choose local port
|
||||
when: dev2_conn_method == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
TMPDIR="$(mktemp -d -t ptsd-tunnel.XXXXXX)"
|
||||
PORT="2222"
|
||||
for p in $(seq 2222 2299); do
|
||||
if ! ss -lnt | awk '{print $4}' | grep -q ":$p$"; then
|
||||
PORT="$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "$TMPDIR" > "$TMPDIR/tmpdir"
|
||||
echo "$PORT" > "$TMPDIR/port"
|
||||
echo "$TMPDIR"
|
||||
args: { executable: /bin/bash }
|
||||
register: tunnel_tmpdir
|
||||
changed_when: false
|
||||
|
||||
- name: Set tunnel facts
|
||||
when: dev2_conn_method == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
_tmpdir: "{{ tunnel_tmpdir.stdout | trim }}"
|
||||
_local_port: "{{ (lookup('file', (tunnel_tmpdir.stdout | trim) ~ '/port') | trim) }}"
|
||||
_ctrl_sock: "{{ (tunnel_tmpdir.stdout | trim) ~ '/cm.sock' }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Add temporary IP on DEV1 for DEV2 side (best-effort)
|
||||
when: dev2_conn_method == "tunnel"
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
ip a add {{ dev2_side_ip }} dev {{ dev1_iface }}
|
||||
register: add_ip
|
||||
changed_when: add_ip.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Start SSH ControlMaster and forward 127.0.0.1 local_port to DEV2 port 22 via DEV1
|
||||
when: dev2_conn_method == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f "{{ dev1_passfiles_used | default('') }}" true
|
||||
args: { executable: /bin/bash }
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Probe local forwarded port readiness (best-effort)
|
||||
when: dev2_conn_method == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
for i in $(seq 1 30); do
|
||||
(echo >/dev/tcp/127.0.0.1/{{ _local_port }}) >/dev/null 2>&1 && exit 0
|
||||
sleep 1
|
||||
done
|
||||
exit 1
|
||||
args: { executable: /bin/bash }
|
||||
register: tunnel_port_ready
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Try tunnel auth with basicpass
|
||||
when: dev2_conn_method == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f basicpass ssh {{ ssh_opts_common }} -p {{ _local_port }} "{{ dev2_ssh_user }}@127.0.0.1" "true"
|
||||
args: { executable: /bin/bash }
|
||||
register: tunnel_try_basicpass
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Record tunnel passfile used basicpass
|
||||
when: dev2_conn_method == "tunnel" and tunnel_try_basicpass.rc == 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_tunnel: "basicpass"
|
||||
changed_when: false
|
||||
|
||||
- name: Try tunnel auth with basicpass2
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f basicpass2 ssh {{ ssh_opts_common }} -p {{ _local_port }} "{{ dev2_ssh_user }}@127.0.0.1" "true"
|
||||
args: { executable: /bin/bash }
|
||||
register: tunnel_try_basicpass2
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Record tunnel passfile used basicpass2
|
||||
when: dev2_conn_method == "tunnel" and tunnel_try_basicpass2.rc == 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_tunnel: "basicpass2"
|
||||
changed_when: false
|
||||
|
||||
- name: Read DEV2 eth0 MAC through tunnel (best-effort)
|
||||
when: dev2_conn_method == "tunnel" and (dev2_mac_from_fdb | default('') | length > 0) and (dev2_passfile_used_tunnel != "NONE")
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
sshpass -f "{{ dev2_passfile_used_tunnel }}" ssh {{ ssh_opts_common }} -p {{ _local_port }} "{{ dev2_ssh_user }}@127.0.0.1" "cat /sys/class/net/eth0/address 2>/dev/null || echo ''"
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_eth0_mac_through_tunnel
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Fail if tunnel lands on unexpected DEV2 MAC (safety guard)
|
||||
when: dev2_conn_method == "tunnel" and (dev2_mac_from_fdb | default('') | length > 0) and (dev2_passfile_used_tunnel != "NONE")
|
||||
delegate_to: localhost
|
||||
ansible.builtin.fail:
|
||||
msg: "Tunnel connected to unexpected DEV2 MAC. Expected={{ dev2_mac_from_fdb }}, Got={{ dev2_eth0_mac_through_tunnel.stdout | trim }}"
|
||||
when: (dev2_eth0_mac_through_tunnel.stdout | trim | lower) != (dev2_mac_from_fdb | trim | lower)
|
||||
|
||||
# ============================ PHASE 0: FALLBACKS IF TUNNEL AUTH FAILED ============================
|
||||
|
||||
- name: Fallback controller direct LLDP IPv4 if tunnel failed and LLDP IPv4 is 10.x
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_ipv4_class | default('none') == "10")
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
for pf in {{ dev2_passfiles | join(' ') }}; do
|
||||
if sshpass -f "$pf" ssh {{ ssh_opts_common }} -p {{ dev2_port }} "{{ dev2_ssh_user }}@{{ lldp_dev2_ip }}" "true" >/dev/null 2>&1; then
|
||||
echo "$pf"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
echo "NONE"
|
||||
exit 0
|
||||
args: { executable: /bin/bash }
|
||||
register: lldp4_pf
|
||||
changed_when: false
|
||||
|
||||
- name: Record LLDP IPv4 fallback passfile
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_ipv4_class | default('none') == "10")
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_lldp4: "{{ lldp4_pf.stdout | trim }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Stage passfiles to DEV1 for IPv6 nested fallback (best-effort)
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_dev2_ip6 | default('') | length > 0)
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(mkdir -p /tmp/ptsd_passfiles && true)
|
||||
register: stage_dir
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Copy passfiles to DEV1 (best-effort)
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_dev2_ip6 | default('') | length > 0)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
for pf in {{ dev2_passfiles | join(' ') }}; do
|
||||
sshpass -f "{{ dev1_pass }}" scp {{ ssh_opts_common }} "$pf" "{{ dev1_user }}@{{ inventory_hostname }}:/tmp/ptsd_passfiles/$pf" >/dev/null 2>&1 || true
|
||||
done
|
||||
args: { executable: /bin/bash }
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Try IPv6 nested fallback via DEV1 to DEV2 (best-effort)
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_dev2_ip6 | default('') | length > 0)
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(for pf in basicpass basicpass2; do
|
||||
sshpass -f /tmp/ptsd_passfiles/$pf ssh {{ ssh_opts_common }} -6 "{{ dev2_ssh_user }}@{{ lldp_dev2_ip6 }}" "true" >/dev/null 2>&1 && echo $pf && exit 0;
|
||||
done;
|
||||
echo NONE;
|
||||
exit 0)
|
||||
register: lldp6_pf
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Record LLDP IPv6 via DEV1 fallback passfile
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel == "NONE" and (lldp_dev2_ip6 | default('') | length > 0)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_passfile_used_lldp6: "{{ lldp6_pf.stdout | trim }}"
|
||||
changed_when: false
|
||||
|
||||
# ============================ PHASE 0: FINAL CONNECTION MODE + WRAPPER BUILD ============================
|
||||
|
||||
- name: Decide final connection mode
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_conn_final: >-
|
||||
{% if dev2_conn_method == 'direct_lldp' and dev2_passfile_used_direct != 'NONE' %}
|
||||
direct_lldp
|
||||
{% elif dev2_conn_method == 'tunnel' and dev2_passfile_used_tunnel != 'NONE' %}
|
||||
tunnel
|
||||
{% elif dev2_passfile_used_lldp4 != 'NONE' %}
|
||||
lldp4_fallback
|
||||
{% elif dev2_passfile_used_lldp6 != 'NONE' %}
|
||||
lldp6_via_dev1
|
||||
{% else %}
|
||||
none
|
||||
{% endif %}
|
||||
changed_when: false
|
||||
|
||||
- name: Fail if no connectivity method succeeded
|
||||
delegate_to: localhost
|
||||
ansible.builtin.fail:
|
||||
msg: "No DEV2 connectivity method succeeded (direct_lldp/tunnel/lldp4/lldp6)."
|
||||
when: dev2_conn_final == "none"
|
||||
|
||||
- name: Build DEV2 wrapper command (controller-side) for staged commands
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_exec_cmd: |
|
||||
set -e
|
||||
MODE="{{ dev2_conn_final }}"
|
||||
if [ -z "${DEV2_CMD:-}" ]; then
|
||||
echo "ERROR: DEV2_CMD is empty" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
tunnel)
|
||||
PORT="{{ _local_port | default('') }}"
|
||||
PASS="{{ dev2_passfile_used_tunnel }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} -p "$PORT" "{{ dev2_ssh_user }}@127.0.0.1" '{{ pathprefix }} '"${DEV2_CMD}" 2>&1"
|
||||
;;
|
||||
direct_lldp)
|
||||
HOST="{{ lldp_dev2_ip }}"
|
||||
PASS="{{ dev2_passfile_used_direct }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} -p "{{ dev2_port }}" "{{ dev2_ssh_user }}@${HOST}" '{{ pathprefix }} '"${DEV2_CMD}" 2>&1"
|
||||
;;
|
||||
lldp4_fallback)
|
||||
HOST="{{ lldp_dev2_ip }}"
|
||||
PASS="{{ dev2_passfile_used_lldp4 }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} -p "{{ dev2_port }}" "{{ dev2_ssh_user }}@${HOST}" '{{ pathprefix }} '"${DEV2_CMD}" 2>&1"
|
||||
;;
|
||||
lldp6_via_dev1)
|
||||
HOST6="{{ lldp_dev2_ip6 }}"
|
||||
PASS="{{ dev2_passfile_used_lldp6 }}"
|
||||
sshpass -f "{{ dev1_pass }}" ssh {{ ssh_opts_common }} "{{ dev1_user }}@{{ inventory_hostname }}" \
|
||||
"sshpass -f /tmp/ptsd_passfiles/${PASS} ssh {{ ssh_opts_common }} -6 {{ dev2_ssh_user }}@${HOST6} '{{ pathprefix }} '"${DEV2_CMD}" 2>&1'"
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown MODE=$MODE" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
changed_when: false
|
||||
|
||||
# ============================ COPY BLOCK SCRIPTS TO DEV2 ============================
|
||||
|
||||
- name: Copy block scripts to DEV2 via wrapper
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
export DEV2_CMD='mkdir -p "{{ block_dst_dir }}" \
|
||||
&& cat > "{{ block_dst_dir }}/{{ item }}" <<'__PTSD_EOF__'
|
||||
{{ lookup('file', block_src_dir ~ '/' ~ item) }}
|
||||
__PTSD_EOF__
|
||||
&& chmod 0755 "{{ block_dst_dir }}/{{ item }}"'
|
||||
{{ dev2_exec_cmd }}
|
||||
args: { executable: /bin/bash }
|
||||
loop: "{{ block_files }}"
|
||||
changed_when: true
|
||||
|
||||
post_tasks:
|
||||
- name: Cleanup note
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
msg: "Cleanup: closing tunnel, removing temp IP, removing staged passfiles (best-effort)"
|
||||
changed_when: false
|
||||
|
||||
- name: Close SSH ControlMaster (best-effort)
|
||||
when: dev2_conn_method | default('') == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
ssh -S "{{ _ctrl_sock | default('/dev/null') }}" -O exit 2>/dev/null || true
|
||||
args: { executable: /bin/bash }
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove tunnel temp dir (best-effort)
|
||||
when: dev2_conn_method | default('') == "tunnel"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
rm -rf "{{ _tmpdir | default('') }}" 2>/dev/null || true
|
||||
args: { executable: /bin/bash }
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove staged passfiles on DEV1 (best-effort)
|
||||
when: dev2_conn_method | default('') == "tunnel"
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
(rm -rf /tmp/ptsd_passfiles 2>/dev/null || true)
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Remove temporary IP on DEV1 (tolerate 'Cannot assign requested address')
|
||||
when: dev2_conn_method | default('') == "tunnel"
|
||||
ansible.builtin.raw: >
|
||||
{{ pathprefix }}
|
||||
ip a del {{ dev2_side_ip }} dev {{ dev1_iface }}
|
||||
register: del_ip
|
||||
changed_when: del_ip.rc == 0
|
||||
failed_when: >
|
||||
del_ip.rc != 0
|
||||
and ('Cannot assign requested address' not in (del_ip.stdout | default('')))
|
||||
and ('Cannot assign requested address' not in (del_ip.stderr | default('')))
|
||||
58
files/startblock.sh
Normal file
58
files/startblock.sh
Normal file
@@ -0,0 +1,58 @@
|
||||
LAN_IF="br-lan"
|
||||
LAN_NET="192.168.2.0/24"
|
||||
EXCLUDE_IP="13.244.149.112/32"
|
||||
|
||||
PORTAL_IP="102.38.126.180"
|
||||
PORTAL_HTTP_PORT="8082"
|
||||
PORTAL_HTTPS_PORT="8083"
|
||||
|
||||
# -------------------------
|
||||
# NAT CAPTURE
|
||||
# -------------------------
|
||||
|
||||
iptables -t nat -N CAPTIVE 2>/dev/null || true
|
||||
iptables -t nat -F CAPTIVE
|
||||
|
||||
while iptables -t nat -C PREROUTING -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE 2>/dev/null; do
|
||||
iptables -t nat -D PREROUTING -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE
|
||||
done
|
||||
iptables -t nat -I PREROUTING 1 -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE
|
||||
|
||||
iptables -t nat -A CAPTIVE -d "$EXCLUDE_IP" -j RETURN
|
||||
|
||||
iptables -t nat -A CAPTIVE -p udp --dport 53 -j REDIRECT --to-ports 53
|
||||
iptables -t nat -A CAPTIVE -p tcp --dport 53 -j REDIRECT --to-ports 53
|
||||
|
||||
iptables -t nat -A CAPTIVE -p tcp --dport 80 -j DNAT --to-destination ${PORTAL_IP}:${PORTAL_HTTP_PORT}
|
||||
iptables -t nat -A CAPTIVE -p tcp --dport 443 -j DNAT --to-destination ${PORTAL_IP}:${PORTAL_HTTPS_PORT}
|
||||
|
||||
# -------------------------
|
||||
# FILTER ENFORCEMENT (BLOCK EVERYTHING ELSE)
|
||||
# -------------------------
|
||||
|
||||
iptables -N CAPTIVE_BLOCK 2>/dev/null || true
|
||||
iptables -F CAPTIVE_BLOCK
|
||||
|
||||
# Allow established traffic
|
||||
iptables -A CAPTIVE_BLOCK -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Allow DNS to router itself (client -> router)
|
||||
iptables -A CAPTIVE_BLOCK -p udp -d 192.168.2.1 --dport 53 -j ACCEPT
|
||||
iptables -A CAPTIVE_BLOCK -p tcp -d 192.168.2.1 --dport 53 -j ACCEPT
|
||||
|
||||
# Allow access to captive portal server (after DNAT)
|
||||
iptables -A CAPTIVE_BLOCK -p tcp -d "$PORTAL_IP" --dport "$PORTAL_HTTP_PORT" -j ACCEPT
|
||||
iptables -A CAPTIVE_BLOCK -p tcp -d "$PORTAL_IP" --dport "$PORTAL_HTTPS_PORT" -j ACCEPT
|
||||
|
||||
# Allow bypass destination fully (optional but usually desired)
|
||||
iptables -A CAPTIVE_BLOCK -d "$EXCLUDE_IP" -j ACCEPT
|
||||
|
||||
# Block everything else from LAN clients
|
||||
iptables -A CAPTIVE_BLOCK -j REJECT --reject-with icmp-admin-prohibited
|
||||
|
||||
# Hook CAPTIVE_BLOCK into FORWARD (ensure single jump)
|
||||
while iptables -C FORWARD -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE_BLOCK 2>/dev/null; do
|
||||
iptables -D FORWARD -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE_BLOCK
|
||||
done
|
||||
iptables -I FORWARD 1 -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE_BLOCK
|
||||
|
||||
33
files/stopblock.sh
Normal file
33
files/stopblock.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
LAN_IF="br-lan"
|
||||
LAN_NET="192.168.2.0/24"
|
||||
|
||||
# -------------------------
|
||||
# FILTER: remove CAPTIVE_BLOCK hook + chain
|
||||
# -------------------------
|
||||
|
||||
# Remove any FORWARD jumps that match our hook exactly
|
||||
while iptables -C FORWARD -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE_BLOCK 2>/dev/null; do
|
||||
iptables -D FORWARD -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE_BLOCK
|
||||
done
|
||||
|
||||
# Flush/delete CAPTIVE_BLOCK chain if present
|
||||
iptables -F CAPTIVE_BLOCK 2>/dev/null || true
|
||||
iptables -X CAPTIVE_BLOCK 2>/dev/null || true
|
||||
|
||||
# -------------------------
|
||||
# NAT: remove CAPTIVE hook + chain
|
||||
# -------------------------
|
||||
|
||||
# Remove any PREROUTING jumps that match our hook exactly
|
||||
while iptables -t nat -C PREROUTING -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE 2>/dev/null; do
|
||||
iptables -t nat -D PREROUTING -i "$LAN_IF" -s "$LAN_NET" -j CAPTIVE
|
||||
done
|
||||
|
||||
# Flush/delete CAPTIVE chain if present
|
||||
iptables -t nat -F CAPTIVE 2>/dev/null || true
|
||||
iptables -t nat -X CAPTIVE 2>/dev/null || true
|
||||
|
||||
echo "OK: captive NAT + filter rules removed (baseline preserved)."
|
||||
Reference in New Issue
Block a user