This commit is contained in:
2025-10-23 19:28:27 +03:00
parent 6383760052
commit c7586c38e7

View File

@@ -1,5 +1,5 @@
--- ---
# update-secondline.yml # update-indoor.yml (conservative, minimal fixes)
- name: Second-line indoor upgrade via DEV1 → tunnel → DEV2 (non-invasive control path) - name: Second-line indoor upgrade via DEV1 → tunnel → DEV2 (non-invasive control path)
hosts: all hosts: all
gather_facts: no gather_facts: no
@@ -50,7 +50,6 @@
control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}" control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}"
afterupgrade_routing_key: "{{ lookup('env','AFTERUP_ROUTING_KEY') | default('deviceconfig', true) }}" afterupgrade_routing_key: "{{ lookup('env','AFTERUP_ROUTING_KEY') | default('deviceconfig', true) }}"
pre_tasks: pre_tasks:
# ------------------------------- Hostname sanity DEV1 ------------------------------- # ------------------------------- Hostname sanity DEV1 -------------------------------
- name: Read DEV1 hostname (busybox-safe) - name: Read DEV1 hostname (busybox-safe)
@@ -158,13 +157,31 @@
echo "$p"; return 0 echo "$p"; return 0
fi fi
fi fi
done end
return 1 return 1
} }
pick pick
register: pick_port register: pick_port
changed_when: false changed_when: false
# (moved up) Stop immediately if no free local port was found
- name: Stop if no free local port was found
ansible.builtin.meta: end_host
when: (pick_port.stdout | trim | length) == 0
# (moved up) Set chosen port and control socket path
- name: Record chosen local port and create control dir for SSH ControlMaster
delegate_to: localhost
ansible.builtin.set_fact:
_local_port: "{{ pick_port.stdout | trim }}"
_ctrl_dir: "{{ lookup('ansible.builtin.pipe', 'mktemp -d') }}"
- name: Build path for SSH ControlMaster socket
delegate_to: localhost
ansible.builtin.set_fact:
_ctrl_sock: "{{ _ctrl_dir }}/ssh_tunnel_ctl"
# (moved down) Now its safe to reference _local_port/_ctrl_sock
- name: Debug picked local port (controller) - name: Debug picked local port (controller)
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: ansible.builtin.debug:
@@ -191,30 +208,6 @@
msg: msg:
- "listeners_before:\n{{ (port_listeners_before.stdout | default('')) | trim }}" - "listeners_before:\n{{ (port_listeners_before.stdout | default('')) | trim }}"
- name: Stop if no free local port was found
ansible.builtin.meta: end_host
when: (pick_port.stdout | trim | length) == 0
- name: Record chosen local port and create control dir for SSH ControlMaster
delegate_to: localhost
ansible.builtin.set_fact:
_local_port: "{{ pick_port.stdout | trim }}"
_ctrl_dir: "{{ lookup('ansible.builtin.pipe', 'mktemp -d') }}"
- name: Build path for SSH ControlMaster socket
delegate_to: localhost
ansible.builtin.set_fact:
_ctrl_sock: "{{ _ctrl_dir }}/ssh_tunnel_ctl"
- name: Debug tunnel parameters (controller side)
delegate_to: localhost
ansible.builtin.debug:
msg:
- "local_port={{ _local_port }}"
- "ctrl_dir={{ _ctrl_dir }}"
- "ctrl_sock={{ _ctrl_sock }}"
- name: Refresh ARP on DEV1s LAN (send unsolicited ARP from temporary IP) - name: Refresh ARP on DEV1s LAN (send unsolicited ARP from temporary IP)
ansible.builtin.raw: arping -U -I eth0 192.168.1.11 -c 3 ansible.builtin.raw: arping -U -I eth0 192.168.1.11 -c 3
@@ -251,8 +244,8 @@
{ ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}' { ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}'
echo "== lsof LISTEN on :${P} ==" echo "== lsof LISTEN on :${P} =="
{ lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; } { lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; }
echo "== grep ControlMaster PID ==" echo "== ps/grep ControlMaster by ControlPath =="
pgrep -fa "ssh.*-S {{ _ctrl_sock | regex_escape }}" || true ps -ef | grep -F " -S {{ _ctrl_sock }}" | grep -v grep || true
register: port_listeners_after register: port_listeners_after
changed_when: false changed_when: false
failed_when: false failed_when: false
@@ -263,7 +256,6 @@
msg: msg:
- "{{ (port_listeners_after.stdout | default('')) | trim }}" - "{{ (port_listeners_after.stdout | default('')) | trim }}"
- name: Wait a moment for tunnel to settle - name: Wait a moment for tunnel to settle
delegate_to: localhost delegate_to: localhost
ansible.builtin.wait_for: ansible.builtin.wait_for:
@@ -286,7 +278,6 @@
- "tunnel_check.rc={{ tun_check.rc }}" - "tunnel_check.rc={{ tun_check.rc }}"
- "tunnel_check.out={{ (tun_check.stdout | default('')) | trim }}" - "tunnel_check.out={{ (tun_check.stdout | default('')) | trim }}"
# ADD THIS RIGHT AFTER THE EXISTING DEBUG:
- name: Debug ControlMaster check (full rc/stdout/stderr) - name: Debug ControlMaster check (full rc/stdout/stderr)
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: ansible.builtin.debug:
@@ -332,33 +323,7 @@
- "{{ (dev2_ls.stdout | default('')) | trim }}" - "{{ (dev2_ls.stdout | default('')) | trim }}"
- "{{ (dev2_ls.stderr | default('')) | trim }}" - "{{ (dev2_ls.stderr | default('')) | trim }}"
# ---------------------------- Single banner probe (kept) ----------------------------
- name: Probe SSH banner through tunnel without auth (quick)
delegate_to: localhost
ansible.builtin.shell: |
set -e
PORT="{{ _local_port }}"
# No password, no interactive auth; prints preauth/banners then exits
ssh -p "$PORT" \
-o PreferredAuthentications=none \
-o PubkeyAuthentication=no \
-o KbdInteractiveAuthentication=no \
-o PasswordAuthentication=no \
-o NumberOfPasswordPrompts=0 \
-o ConnectTimeout=5 \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-vvv root@127.0.0.1 true 2>&1 || true
register: tunnel_banner_probe
changed_when: false
failed_when: false
- name: Debug SSH preauth probe output (first 40 lines)
delegate_to: localhost
ansible.builtin.debug:
msg: "{{ (tunnel_banner_probe.stdout | default('')) | regex_replace('^(?:.*\\n){,40}\\K[\\s\\S]*','[...truncated...]') }}"
# ---------------------------- Pick DEV2 password for root ----------------------------
- name: Probe SSH banner through tunnel (pre-auth, quick) - name: Probe SSH banner through tunnel (pre-auth, quick)
delegate_to: localhost delegate_to: localhost
ansible.builtin.shell: | ansible.builtin.shell: |
@@ -380,9 +345,9 @@
- name: Debug SSH preauth probe (first 40 lines) - name: Debug SSH preauth probe (first 40 lines)
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: ansible.builtin.debug:
msg: "{{ (tunnel_banner_probe.stdout | default('')) | regex_replace('^(?:.*\\n){,40}\\K[\\s\\S]*','[...truncated...]') }}" msg: "{{ (tunnel_banner_probe.stdout | default('') | split('\n'))[:40] | join('\n') }}"
# ---------------------------- Pick DEV2 password for root ----------------------------
- name: Try DEV2 login with 'basicpass' (root) - name: Try DEV2 login with 'basicpass' (root)
delegate_to: localhost delegate_to: localhost
ansible.builtin.shell: | ansible.builtin.shell: |
@@ -407,8 +372,8 @@
{ ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}' { ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}'
echo "== lsof LISTEN on :${P} ==" echo "== lsof LISTEN on :${P} =="
{ lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; } { lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; }
echo "== pgrep ControlMaster by socket ==" echo "== ps/grep ControlMaster by ControlPath =="
pgrep -fa "ssh.*-S {{ _ctrl_sock | regex_escape }}" || true ps -ef | grep -F " -S {{ _ctrl_sock }}" | grep -v grep || true
register: listeners_after_basicpass register: listeners_after_basicpass
changed_when: false changed_when: false
failed_when: false failed_when: false
@@ -422,7 +387,6 @@
- "ctrl_sock={{ _ctrl_sock }}" - "ctrl_sock={{ _ctrl_sock }}"
- "listeners:\n{{ (listeners_after_basicpass.stdout | default('')) | trim }}" - "listeners:\n{{ (listeners_after_basicpass.stdout | default('')) | trim }}"
- name: Select 'basicpass' if previous login succeeded - name: Select 'basicpass' if previous login succeeded
when: dev2_try_basicpass.rc == 0 when: dev2_try_basicpass.rc == 0
delegate_to: localhost delegate_to: localhost
@@ -455,8 +419,8 @@
{ ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}' { ss -ltnp 2>/dev/null || true; } | awk -v p=":${P}$" '$0 ~ p {print}'
echo "== lsof LISTEN on :${P} ==" echo "== lsof LISTEN on :${P} =="
{ lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; } { lsof -nP -iTCP:"${P}" -sTCP:LISTEN 2>/dev/null || true; }
echo "== pgrep ControlMaster by socket ==" echo "== ps/grep ControlMaster by ControlPath =="
pgrep -fa "ssh.*-S {{ _ctrl_sock | regex_escape }}" || true ps -ef | grep -F " -S {{ _ctrl_sock }}" | grep -v grep || true
register: listeners_after_basicpass2 register: listeners_after_basicpass2
changed_when: false changed_when: false
failed_when: false failed_when: false
@@ -471,7 +435,6 @@
- "listeners:\n{{ (listeners_after_basicpass2.stdout | default('')) | trim }}" - "listeners:\n{{ (listeners_after_basicpass2.stdout | default('')) | trim }}"
when: dev2_try_basicpass2 is defined when: dev2_try_basicpass2 is defined
- name: Select 'basicpass2' if second login succeeded - name: Select 'basicpass2' if second login succeeded
when: dev2_passfile_used is not defined and dev2_try_basicpass2.rc == 0 when: dev2_passfile_used is not defined and dev2_try_basicpass2.rc == 0
delegate_to: localhost delegate_to: localhost
@@ -1148,8 +1111,6 @@
register: dev2_marker_write register: dev2_marker_write
changed_when: true changed_when: true
ignore_errors: true ignore_errors: true
# ---------------------------- Reboot scheduling (normalized) ---------------------------- # ---------------------------- Reboot scheduling (normalized) ----------------------------
- name: Schedule DEV2 reboot after computed delay (seconds) - name: Schedule DEV2 reboot after computed delay (seconds)