fix: align bridge OpenClaw protocol 4 deployment
This commit is contained in:
parent
402faa02e1
commit
ba4daa3597
15
README.md
15
README.md
@ -1,5 +1,20 @@
|
|||||||
# playbooks
|
# playbooks
|
||||||
|
|
||||||
|
## XWorkmate Bridge Distributed VPN
|
||||||
|
|
||||||
|
The bidirectional WireGuard-over-VLESS transport for the two XWorkmate bridge
|
||||||
|
nodes is deployed by:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory.ini vpn-wireguard-over-vless.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
The implementation uses split bridge groups (`xworkmate_bridge` and
|
||||||
|
`cn_xworkmate_bridge`) under `xworkmate_bridge_distributed`, stores private keys
|
||||||
|
and the shared management-side Xray UUID in `https://vault.svc.plus`, and keeps
|
||||||
|
the host's default `xray.service` untouched. The runbook lives in
|
||||||
|
[`roles/vhosts/xworkmate_bridge_distributed_vpn/README.md`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/xworkmate_bridge_distributed_vpn/README.md).
|
||||||
|
|
||||||
## Cloud Dev Desktop
|
## Cloud Dev Desktop
|
||||||
|
|
||||||
The cloud dev desktop flow lives here as two playbooks:
|
The cloud dev desktop flow lives here as two playbooks:
|
||||||
|
|||||||
36
group_vars/xworkmate_bridge_distributed.yml
Normal file
36
group_vars/xworkmate_bridge_distributed.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
xworkmate_bridge_distributed_topology: dual-node
|
||||||
|
xworkmate_bridge_distributed_nodes:
|
||||||
|
- id: xworkmate-bridge
|
||||||
|
role: primary
|
||||||
|
public_base_url: https://xworkmate-bridge.svc.plus
|
||||||
|
bridge_endpoint: http://172.29.10.1:8787
|
||||||
|
- id: cn-xworkmate-bridge
|
||||||
|
role: edge
|
||||||
|
public_base_url: https://cn-xworkmate-bridge.svc.plus
|
||||||
|
bridge_endpoint: http://172.29.10.2:8787
|
||||||
|
|
||||||
|
xworkmate_bridge_distributed_vpn_interface: wg-xwm
|
||||||
|
xworkmate_bridge_distributed_vpn_wireguard_port: 51820
|
||||||
|
xworkmate_bridge_distributed_vpn_local_tproxy_port: 51830
|
||||||
|
xworkmate_bridge_distributed_vpn_vless_port: 2443
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_port: 8787
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_target: 127.0.0.1:8787
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_addr: "{{ lookup('ansible.builtin.env', 'VAULT_SERVER_URL') | default('https://vault.svc.plus', true) }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_token: "{{ lookup('ansible.builtin.env', 'VAULT_SERVER_ROOT_ACCESS_TOKEN') | default(lookup('ansible.builtin.env', 'VAULT_TOKEN'), true) }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_mount: kv
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_base_path: xworkmate-bridge/distributed/wireguard-over-vless
|
||||||
|
|
||||||
|
xworkmate_bridge_distributed_vpn_nodes:
|
||||||
|
jp-xhttp-contabo.svc.plus:
|
||||||
|
node_id: xworkmate-bridge
|
||||||
|
domain: xworkmate-bridge.svc.plus
|
||||||
|
wg_ip: 172.29.10.1
|
||||||
|
public_key: 1staGq8lmHFRFRFNj2QOFx/MPxb/1fFV4tawC6xSi1Q=
|
||||||
|
peer: cn-xworkmate-bridge.svc.plus
|
||||||
|
cn-xworkmate-bridge.svc.plus:
|
||||||
|
node_id: cn-xworkmate-bridge
|
||||||
|
domain: cn-xworkmate-bridge.svc.plus
|
||||||
|
wg_ip: 172.29.10.2
|
||||||
|
public_key: iYlnFaWiMfMelpiN8ZV2SwCDrLihqtJXvHUsM3BN9zU=
|
||||||
|
peer: jp-xhttp-contabo.svc.plus
|
||||||
@ -12,3 +12,5 @@ xworkmate_bridge_required_listeners:
|
|||||||
- host: 127.0.0.1
|
- host: 127.0.0.1
|
||||||
port: "8787"
|
port: "8787"
|
||||||
name: bridge
|
name: bridge
|
||||||
|
xworkmate_bridge_distributed_local_node_id: cn-xworkmate-bridge
|
||||||
|
xworkmate_bridge_distributed_task_forward_peer_id: xworkmate-bridge
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
xworkmate_bridge_distributed_local_node_id: xworkmate-bridge
|
||||||
|
xworkmate_bridge_distributed_task_forward_peer_id: ""
|
||||||
@ -43,6 +43,16 @@ jp-xhttp-contabo.svc.plus
|
|||||||
tky-proxy.svc.plus
|
tky-proxy.svc.plus
|
||||||
jp-xhttp-contabo.svc.plus
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
|
[xworkmate_bridge]
|
||||||
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
|
[cn_xworkmate_bridge]
|
||||||
|
cn-xworkmate-bridge.svc.plus
|
||||||
|
|
||||||
|
[xworkmate_bridge_distributed:children]
|
||||||
|
xworkmate_bridge
|
||||||
|
cn_xworkmate_bridge
|
||||||
|
|
||||||
[billing_service]
|
[billing_service]
|
||||||
jp-xhttp-contabo.svc.plus
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,8 @@ gateway_openclaw_profile_script_path: /etc/profile.d/openclaw-user-systemd.sh
|
|||||||
gateway_openclaw_home: "/home/{{ gateway_openclaw_service_user }}"
|
gateway_openclaw_home: "/home/{{ gateway_openclaw_service_user }}"
|
||||||
gateway_openclaw_binary_path: "{{ gateway_openclaw_home }}/.local/bin/openclaw"
|
gateway_openclaw_binary_path: "{{ gateway_openclaw_home }}/.local/bin/openclaw"
|
||||||
gateway_openclaw_install_dir: "{{ gateway_openclaw_home }}/.local/lib/node_modules/openclaw"
|
gateway_openclaw_install_dir: "{{ gateway_openclaw_home }}/.local/lib/node_modules/openclaw"
|
||||||
|
gateway_openclaw_required_version: "2026.5.28"
|
||||||
|
gateway_openclaw_npm_package_spec: "openclaw@{{ gateway_openclaw_required_version }}"
|
||||||
gateway_openclaw_extension_dependency_dirs:
|
gateway_openclaw_extension_dependency_dirs:
|
||||||
- "{{ gateway_openclaw_install_dir }}/dist/extensions/acpx"
|
- "{{ gateway_openclaw_install_dir }}/dist/extensions/acpx"
|
||||||
gateway_openclaw_config_path: "{{ gateway_openclaw_home }}/.openclaw/openclaw.json"
|
gateway_openclaw_config_path: "{{ gateway_openclaw_home }}/.openclaw/openclaw.json"
|
||||||
@ -24,6 +26,7 @@ gateway_openclaw_workspace_mode: "0775"
|
|||||||
gateway_openclaw_compile_cache_dir: /var/tmp/openclaw-compile-cache
|
gateway_openclaw_compile_cache_dir: /var/tmp/openclaw-compile-cache
|
||||||
gateway_openclaw_service_path: "{{ gateway_openclaw_home }}/.nix-profile/bin:{{ gateway_openclaw_home }}/.local/bin:{{ gateway_openclaw_home }}/.npm-global/bin:{{ gateway_openclaw_home }}/bin:/usr/local/bin:/usr/bin:/bin"
|
gateway_openclaw_service_path: "{{ gateway_openclaw_home }}/.nix-profile/bin:{{ gateway_openclaw_home }}/.local/bin:{{ gateway_openclaw_home }}/.npm-global/bin:{{ gateway_openclaw_home }}/bin:/usr/local/bin:/usr/bin:/bin"
|
||||||
gateway_openclaw_extension_backup_dir: "{{ gateway_openclaw_home }}/.openclaw/backups/extensions"
|
gateway_openclaw_extension_backup_dir: "{{ gateway_openclaw_home }}/.openclaw/backups/extensions"
|
||||||
|
gateway_openclaw_doctor_repair_enabled: true
|
||||||
|
|
||||||
gateway_openclaw_upstream_host: 127.0.0.1
|
gateway_openclaw_upstream_host: 127.0.0.1
|
||||||
gateway_openclaw_upstream_port: 18789
|
gateway_openclaw_upstream_port: 18789
|
||||||
@ -53,7 +56,9 @@ gateway_openclaw_default_models:
|
|||||||
nvidia/nemotron-3-super-120b-a12b: {}
|
nvidia/nemotron-3-super-120b-a12b: {}
|
||||||
nvidia/minimaxai/minimax-m2.5: {}
|
nvidia/minimaxai/minimax-m2.5: {}
|
||||||
nvidia/z-ai/glm5: {}
|
nvidia/z-ai/glm5: {}
|
||||||
openai-codex/gpt-5.5: {}
|
openai/gpt-5.5:
|
||||||
|
agentRuntime:
|
||||||
|
id: codex
|
||||||
gateway_openclaw_main_agent_model: nvidia/nemotron-3-super-120b-a12b
|
gateway_openclaw_main_agent_model: nvidia/nemotron-3-super-120b-a12b
|
||||||
|
|
||||||
gateway_openclaw_main_agent_skills:
|
gateway_openclaw_main_agent_skills:
|
||||||
|
|||||||
@ -68,6 +68,28 @@
|
|||||||
group: "{{ gateway_openclaw_service_group }}"
|
group: "{{ gateway_openclaw_service_group }}"
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
|
||||||
|
- name: Install required OpenClaw gateway package version
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
npm install --global --omit=dev --no-audit --no-fund
|
||||||
|
--prefix "{{ gateway_openclaw_home }}/.local"
|
||||||
|
"{{ gateway_openclaw_npm_package_spec }}"
|
||||||
|
environment:
|
||||||
|
HOME: "{{ gateway_openclaw_home }}"
|
||||||
|
PATH: "{{ gateway_openclaw_service_path }}"
|
||||||
|
OPENCLAW_NO_RESPAWN: "1"
|
||||||
|
NODE_COMPILE_CACHE: "{{ gateway_openclaw_compile_cache_dir }}"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ gateway_openclaw_service_user }}"
|
||||||
|
register: gateway_openclaw_package_install
|
||||||
|
changed_when: >-
|
||||||
|
'added ' in (gateway_openclaw_package_install.stdout | default('')) or
|
||||||
|
'removed ' in (gateway_openclaw_package_install.stdout | default('')) or
|
||||||
|
'changed ' in (gateway_openclaw_package_install.stdout | default(''))
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
notify: Restart openclaw gateway
|
||||||
|
|
||||||
- name: Move stale OpenClaw plugin backups out of extension scan path
|
- name: Move stale OpenClaw plugin backups out of extension scan path
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.shell: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@ -116,13 +138,6 @@
|
|||||||
diff: false
|
diff: false
|
||||||
notify: Restart openclaw gateway
|
notify: Restart openclaw gateway
|
||||||
|
|
||||||
- name: Restore immutable flag on OpenClaw gateway JSON config
|
|
||||||
ansible.builtin.command:
|
|
||||||
cmd: chattr +i "{{ gateway_openclaw_config_path }}"
|
|
||||||
when:
|
|
||||||
- "'i' in (gateway_openclaw_config_attrs.stdout | default(''))"
|
|
||||||
changed_when: true
|
|
||||||
|
|
||||||
- name: Inspect OpenClaw package manifest
|
- name: Inspect OpenClaw package manifest
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ gateway_openclaw_install_dir }}/package.json"
|
path: "{{ gateway_openclaw_install_dir }}/package.json"
|
||||||
@ -183,6 +198,35 @@
|
|||||||
- item.stat.exists | default(false)
|
- item.stat.exists | default(false)
|
||||||
- not ansible_check_mode
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Repair OpenClaw 2026.5 route state as service user
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ gateway_openclaw_binary_path }} doctor --fix --non-interactive"
|
||||||
|
environment:
|
||||||
|
HOME: "{{ gateway_openclaw_home }}"
|
||||||
|
PATH: "{{ gateway_openclaw_service_path }}"
|
||||||
|
OPENCLAW_NO_RESPAWN: "1"
|
||||||
|
NODE_COMPILE_CACHE: "{{ gateway_openclaw_compile_cache_dir }}"
|
||||||
|
OPENCLAW_SERVICE_REPAIR_POLICY: external
|
||||||
|
become: true
|
||||||
|
become_user: "{{ gateway_openclaw_service_user }}"
|
||||||
|
register: gateway_openclaw_doctor_repair
|
||||||
|
changed_when: >-
|
||||||
|
((gateway_openclaw_doctor_repair.stdout | default('')) ~ '\n' ~
|
||||||
|
(gateway_openclaw_doctor_repair.stderr | default('')))
|
||||||
|
is search('(?i)(fixed|repaired|migrated|rewrote|removed|set |moved |updated|archived)')
|
||||||
|
when:
|
||||||
|
- gateway_openclaw_doctor_repair_enabled | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
notify: Restart openclaw gateway
|
||||||
|
|
||||||
|
- name: Restore immutable flag on OpenClaw gateway JSON config
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: chattr +i "{{ gateway_openclaw_config_path }}"
|
||||||
|
when:
|
||||||
|
- "'i' in (gateway_openclaw_config_attrs.stdout | default(''))"
|
||||||
|
- not ansible_check_mode
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
- name: Inspect OpenClaw gateway binary
|
- name: Inspect OpenClaw gateway binary
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ gateway_openclaw_binary_path }}"
|
path: "{{ gateway_openclaw_binary_path }}"
|
||||||
@ -196,6 +240,31 @@
|
|||||||
- gateway_openclaw_binary.stat.executable | default(false)
|
- gateway_openclaw_binary.stat.executable | default(false)
|
||||||
fail_msg: "OpenClaw gateway binary is missing or not executable: {{ gateway_openclaw_binary_path }}"
|
fail_msg: "OpenClaw gateway binary is missing or not executable: {{ gateway_openclaw_binary_path }}"
|
||||||
|
|
||||||
|
- name: Check OpenClaw gateway version
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ gateway_openclaw_binary_path }} --version"
|
||||||
|
environment:
|
||||||
|
HOME: "{{ gateway_openclaw_home }}"
|
||||||
|
PATH: "{{ gateway_openclaw_service_path }}"
|
||||||
|
OPENCLAW_NO_RESPAWN: "1"
|
||||||
|
NODE_COMPILE_CACHE: "{{ gateway_openclaw_compile_cache_dir }}"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ gateway_openclaw_service_user }}"
|
||||||
|
register: gateway_openclaw_version
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Assert OpenClaw gateway version is pinned
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- gateway_openclaw_version.stdout is search('OpenClaw ' ~ gateway_openclaw_required_version)
|
||||||
|
fail_msg: >-
|
||||||
|
OpenClaw gateway must run {{ gateway_openclaw_required_version }} after the
|
||||||
|
package upgrade. Actual version output: {{ gateway_openclaw_version.stdout | default('') }}
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
- name: Ensure OpenClaw user systemd unit directory exists
|
- name: Ensure OpenClaw user systemd unit directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ gateway_openclaw_user_service_unit_path | dirname }}"
|
path: "{{ gateway_openclaw_user_service_unit_path | dirname }}"
|
||||||
|
|||||||
@ -91,13 +91,13 @@
|
|||||||
},
|
},
|
||||||
"wizard": {
|
"wizard": {
|
||||||
"lastRunAt": "2026-04-19T10:52:37.655Z",
|
"lastRunAt": "2026-04-19T10:52:37.655Z",
|
||||||
"lastRunVersion": "2026.4.15",
|
"lastRunVersion": "2026.5.28",
|
||||||
"lastRunCommand": "configure",
|
"lastRunCommand": "configure",
|
||||||
"lastRunMode": "local"
|
"lastRunMode": "local"
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"lastTouchedVersion": "2026.4.26",
|
"lastTouchedVersion": "2026.5.28",
|
||||||
"lastTouchedAt": "2026-04-29T04:41:12.010Z"
|
"lastTouchedAt": "2026-06-01T00:00:00.000Z"
|
||||||
},
|
},
|
||||||
"acp": {
|
"acp": {
|
||||||
"enabled": {{ gateway_openclaw_acp_enabled | bool | to_json }},
|
"enabled": {{ gateway_openclaw_acp_enabled | bool | to_json }},
|
||||||
|
|||||||
@ -6,6 +6,15 @@ This document records the current real deployment and runtime validation state f
|
|||||||
|
|
||||||
`roles/vhosts/xworkmate_bridge` owns the public ingress and validation contract for `xworkmate-bridge.svc.plus`.
|
`roles/vhosts/xworkmate_bridge` owns the public ingress and validation contract for `xworkmate-bridge.svc.plus`.
|
||||||
|
|
||||||
|
The private distributed bridge transport is managed by
|
||||||
|
[`roles/vhosts/xworkmate_bridge_distributed_vpn`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/xworkmate_bridge_distributed_vpn/README.md)
|
||||||
|
and deployed through
|
||||||
|
[`vpn-wireguard-over-vless.yml`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/vpn-wireguard-over-vless.yml).
|
||||||
|
That role provides `wg-xwm`, `xray-wg-tproxy.service`, and the VPN-only
|
||||||
|
`172.29.10.0/24` bridge forwarders. This bridge role consumes the resulting
|
||||||
|
distributed topology config; CN forwards tasks to the primary private endpoint,
|
||||||
|
while the primary node keeps reverse task forwarding disabled.
|
||||||
|
|
||||||
The provider runtimes remain separate sibling roles:
|
The provider runtimes remain separate sibling roles:
|
||||||
|
|
||||||
- [`roles/vhosts/acp_codex`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_codex)
|
- [`roles/vhosts/acp_codex`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_codex)
|
||||||
|
|||||||
@ -12,17 +12,23 @@ xworkmate_bridge_base_dir: /opt/cloud-neutral/xworkmate-bridge
|
|||||||
xworkmate_bridge_config_file: "{{ xworkmate_bridge_base_dir }}/config.yaml"
|
xworkmate_bridge_config_file: "{{ xworkmate_bridge_base_dir }}/config.yaml"
|
||||||
xworkmate_bridge_binary_path: /usr/local/bin/xworkmate-go-core
|
xworkmate_bridge_binary_path: /usr/local/bin/xworkmate-go-core
|
||||||
xworkmate_bridge_systemd_unit_path: "/etc/systemd/system/{{ xworkmate_bridge_service_name }}.service"
|
xworkmate_bridge_systemd_unit_path: "/etc/systemd/system/{{ xworkmate_bridge_service_name }}.service"
|
||||||
xworkmate_bridge_runtime_image_ref: "{{ service_compose_image | default(lookup('ansible.builtin.env', 'SERVICE_COMPOSE_IMAGE') | default(lookup('ansible.builtin.env', 'XWORKMATE_BRIDGE_IMAGE') | default('', true), true), true) }}"
|
|
||||||
xworkmate_bridge_deprecated_container_name: xworkmate-bridge-managed
|
xworkmate_bridge_deprecated_container_name: xworkmate-bridge-managed
|
||||||
xworkmate_bridge_deprecated_compose_file: "{{ xworkmate_bridge_base_dir }}/docker-compose.yml"
|
xworkmate_bridge_deprecated_compose_file: "{{ xworkmate_bridge_base_dir }}/docker-compose.yml"
|
||||||
|
xworkmate_bridge_obsolete_systemd_dropin_paths:
|
||||||
|
- "/etc/systemd/system/{{ xworkmate_bridge_service_name }}.service.d/20-distributed-forward.conf"
|
||||||
xworkmate_bridge_service_environment:
|
xworkmate_bridge_service_environment:
|
||||||
BRIDGE_AUTH_TOKEN: "{{ xworkmate_bridge_effective_auth_token | default(xworkmate_bridge_auth_token) }}"
|
BRIDGE_AUTH_TOKEN: "{{ xworkmate_bridge_effective_auth_token | default(xworkmate_bridge_auth_token) }}"
|
||||||
BRIDGE_REVIEW_AUTH_TOKEN: "{{ xworkmate_bridge_effective_review_auth_token | default(xworkmate_bridge_review_auth_token) }}"
|
BRIDGE_REVIEW_AUTH_TOKEN: "{{ xworkmate_bridge_effective_review_auth_token | default(xworkmate_bridge_review_auth_token) }}"
|
||||||
BRIDGE_CONFIG_PATH: "{{ xworkmate_bridge_config_file }}"
|
BRIDGE_CONFIG_PATH: "{{ xworkmate_bridge_config_file }}"
|
||||||
IMAGE: "{{ xworkmate_bridge_effective_runtime_image_ref | default(xworkmate_bridge_runtime_image_ref) }}"
|
|
||||||
xworkmate_bridge_openclaw_gateway_max_active: 5
|
xworkmate_bridge_openclaw_gateway_max_active: 5
|
||||||
xworkmate_bridge_openclaw_gateway_max_queued: 20
|
xworkmate_bridge_openclaw_gateway_max_queued: 20
|
||||||
xworkmate_bridge_openclaw_gateway_queue_timeout: 10m
|
xworkmate_bridge_openclaw_gateway_queue_timeout: 10m
|
||||||
|
xworkmate_bridge_distributed_topology: ""
|
||||||
|
xworkmate_bridge_distributed_local_node_id: ""
|
||||||
|
xworkmate_bridge_distributed_task_forward_peer_id: ""
|
||||||
|
xworkmate_bridge_distributed_task_forward_endpoint: ""
|
||||||
|
xworkmate_bridge_distributed_task_forward_token: ""
|
||||||
|
xworkmate_bridge_distributed_nodes: []
|
||||||
xworkmate_bridge_required_services:
|
xworkmate_bridge_required_services:
|
||||||
- acp-codex.service
|
- acp-codex.service
|
||||||
- acp-opencode.service
|
- acp-opencode.service
|
||||||
|
|||||||
@ -40,19 +40,6 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
- name: Read existing xworkmate-bridge image ref from systemd unit
|
|
||||||
ansible.builtin.shell: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -f "{{ xworkmate_bridge_systemd_unit_path }}" ]; then
|
|
||||||
sed -n 's/^Environment="IMAGE=\(.*\)"$/\1/p' "{{ xworkmate_bridge_systemd_unit_path }}" | head -n 1
|
|
||||||
fi
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
register: xworkmate_bridge_existing_image_ref
|
|
||||||
check_mode: false
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Resolve xworkmate-bridge auth token
|
- name: Resolve xworkmate-bridge auth token
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
xworkmate_bridge_effective_auth_token: >-
|
xworkmate_bridge_effective_auth_token: >-
|
||||||
@ -69,15 +56,6 @@
|
|||||||
}}
|
}}
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
- name: Resolve xworkmate-bridge runtime image ref
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
xworkmate_bridge_effective_runtime_image_ref: >-
|
|
||||||
{{
|
|
||||||
xworkmate_bridge_runtime_image_ref
|
|
||||||
if (xworkmate_bridge_runtime_image_ref | trim | length > 0)
|
|
||||||
else (xworkmate_bridge_existing_image_ref.stdout | default(''))
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Assert xworkmate-bridge binary exists
|
- name: Assert xworkmate-bridge binary exists
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ xworkmate_bridge_binary_path }}"
|
path: "{{ xworkmate_bridge_binary_path }}"
|
||||||
@ -117,6 +95,13 @@
|
|||||||
path: "{{ xworkmate_bridge_deprecated_compose_file }}"
|
path: "{{ xworkmate_bridge_deprecated_compose_file }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
|
- name: Remove obsolete xworkmate-bridge systemd drop-ins
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ xworkmate_bridge_obsolete_systemd_dropin_paths }}"
|
||||||
|
notify: Reload bridge
|
||||||
|
|
||||||
- name: Deploy xworkmate-bridge runtime configuration
|
- name: Deploy xworkmate-bridge runtime configuration
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: config.yaml.j2
|
src: config.yaml.j2
|
||||||
@ -147,6 +132,8 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
diff: false
|
||||||
|
no_log: true
|
||||||
register: xworkmate_bridge_systemd_unit
|
register: xworkmate_bridge_systemd_unit
|
||||||
notify: Reload bridge
|
notify: Reload bridge
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,16 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
|
- name: Assert xworkmate-bridge ping reports native binary metadata
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- xworkmate_bridge_service_ping.json.version | default('') | trim | length > 0
|
||||||
|
- xworkmate_bridge_service_ping.json.image | default('') | trim | length == 0
|
||||||
|
- xworkmate_bridge_service_ping.json.tag | default('') | trim | length == 0
|
||||||
|
fail_msg: >-
|
||||||
|
xworkmate-bridge /api/ping must report native binary metadata and must
|
||||||
|
not expose stale Docker image/tag metadata.
|
||||||
|
|
||||||
- name: Check xworkmate-bridge capabilities contract
|
- name: Check xworkmate-bridge capabilities contract
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "https://{{ xworkmate_bridge_service_domain }}/acp/rpc"
|
url: "https://{{ xworkmate_bridge_service_domain }}/acp/rpc"
|
||||||
|
|||||||
@ -15,6 +15,20 @@ openclaw_gateway:
|
|||||||
max_queued: {{ xworkmate_bridge_openclaw_gateway_max_queued | int }}
|
max_queued: {{ xworkmate_bridge_openclaw_gateway_max_queued | int }}
|
||||||
queue_timeout: "{{ xworkmate_bridge_openclaw_gateway_queue_timeout }}"
|
queue_timeout: "{{ xworkmate_bridge_openclaw_gateway_queue_timeout }}"
|
||||||
|
|
||||||
|
distributed:
|
||||||
|
topology: "{{ xworkmate_bridge_distributed_topology }}"
|
||||||
|
local_node_id: "{{ xworkmate_bridge_distributed_local_node_id }}"
|
||||||
|
task_forward_peer_id: "{{ xworkmate_bridge_distributed_task_forward_peer_id }}"
|
||||||
|
task_forward_endpoint: "{{ xworkmate_bridge_distributed_task_forward_endpoint }}"
|
||||||
|
task_forward_token: "{{ xworkmate_bridge_distributed_task_forward_token }}"
|
||||||
|
nodes:
|
||||||
|
{% for node in xworkmate_bridge_distributed_nodes %}
|
||||||
|
- id: "{{ node.id }}"
|
||||||
|
role: "{{ node.role }}"
|
||||||
|
public_base_url: "{{ node.public_base_url }}"
|
||||||
|
bridge_endpoint: "{{ node.bridge_endpoint }}"
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
# Server orchestration settings
|
# Server orchestration settings
|
||||||
bridge:
|
bridge:
|
||||||
listenAddr: "{{ xworkmate_bridge_listen_addr }}"
|
listenAddr: "{{ xworkmate_bridge_listen_addr }}"
|
||||||
@ -26,4 +40,3 @@ bridge:
|
|||||||
# Operational notes for this deployment
|
# Operational notes for this deployment
|
||||||
notes:
|
notes:
|
||||||
- Bridge Auth Token is managed via BRIDGE_AUTH_TOKEN environment variable.
|
- Bridge Auth Token is managed via BRIDGE_AUTH_TOKEN environment variable.
|
||||||
- Deployed on: {{ ansible_date_time.iso8601 | default('N/A') }}
|
|
||||||
|
|||||||
172
roles/vhosts/xworkmate_bridge_distributed_vpn/README.md
Normal file
172
roles/vhosts/xworkmate_bridge_distributed_vpn/README.md
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# xworkmate_bridge_distributed_vpn
|
||||||
|
|
||||||
|
This role deploys the private transport used by the XWorkmate bridge distributed extension.
|
||||||
|
|
||||||
|
## Topology
|
||||||
|
|
||||||
|
The current implementation is a two-node `dual-node` topology:
|
||||||
|
|
||||||
|
- `jp-xhttp-contabo.svc.plus` is the primary node for `xworkmate-bridge.svc.plus`.
|
||||||
|
- `cn-xworkmate-bridge.svc.plus` is the CN edge node for `cn-xworkmate-bridge.svc.plus`.
|
||||||
|
|
||||||
|
Both nodes run the same private network path:
|
||||||
|
|
||||||
|
```text
|
||||||
|
WireGuard peer -> 127.0.0.1:51830 -> xray-wg-tproxy -> VLESS/TLS -> peer xray-wg-tproxy -> peer UDP 51820
|
||||||
|
```
|
||||||
|
|
||||||
|
The role intentionally does not manage the host's default `xray.service` or
|
||||||
|
`/usr/local/etc/xray/config.json`. WireGuard-over-VLESS uses its own config and
|
||||||
|
service:
|
||||||
|
|
||||||
|
- `/usr/local/etc/xray/wireguard-over-vless.json`
|
||||||
|
- `xray-wg-tproxy.service`
|
||||||
|
|
||||||
|
## Managed Services
|
||||||
|
|
||||||
|
Each node gets:
|
||||||
|
|
||||||
|
- WireGuard interface: `wg-xwm`
|
||||||
|
- WireGuard listen port: UDP `51820`
|
||||||
|
- local Xray dokodemo-door ingress: `127.0.0.1:51830`
|
||||||
|
- VLESS/TLS listen port: TCP `2443`
|
||||||
|
- VPN-only bridge forwarder: `<wg_ip>:8787 -> 127.0.0.1:8787`
|
||||||
|
|
||||||
|
Systemd units:
|
||||||
|
|
||||||
|
- `wg-quick@wg-xwm.service`
|
||||||
|
- `xray-wg-tproxy.service`
|
||||||
|
- `xworkmate-bridge-vpn-forwarder.service`
|
||||||
|
|
||||||
|
The WireGuard peer endpoint on both sides is local:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
Endpoint = 127.0.0.1:51830
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inventory And Variables
|
||||||
|
|
||||||
|
The inventory uses split bridge groups and one distributed parent group:
|
||||||
|
|
||||||
|
- `xworkmate_bridge`
|
||||||
|
- `cn_xworkmate_bridge`
|
||||||
|
- `xworkmate_bridge_distributed`
|
||||||
|
|
||||||
|
Shared topology and VPN variables live in
|
||||||
|
[`group_vars/xworkmate_bridge_distributed.yml`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/group_vars/xworkmate_bridge_distributed.yml).
|
||||||
|
|
||||||
|
Host-specific distributed bridge behavior lives in:
|
||||||
|
|
||||||
|
- [`host_vars/jp-xhttp-contabo.svc.plus/xworkmate_bridge_distributed.yml`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/host_vars/jp-xhttp-contabo.svc.plus/xworkmate_bridge_distributed.yml)
|
||||||
|
- [`host_vars/cn-xworkmate-bridge.svc.plus.yml`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/host_vars/cn-xworkmate-bridge.svc.plus.yml)
|
||||||
|
|
||||||
|
Important defaults:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
xworkmate_bridge_distributed_vpn_interface: wg-xwm
|
||||||
|
xworkmate_bridge_distributed_vpn_wireguard_port: 51820
|
||||||
|
xworkmate_bridge_distributed_vpn_local_tproxy_port: 51830
|
||||||
|
xworkmate_bridge_distributed_vpn_vless_port: 2443
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_port: 8787
|
||||||
|
```
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
This role reads secrets from the Vault service, not from a local Ansible Vault
|
||||||
|
password file.
|
||||||
|
|
||||||
|
Required controller environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export VAULT_SERVER_URL=https://vault.svc.plus
|
||||||
|
export VAULT_SERVER_ROOT_ACCESS_TOKEN=...
|
||||||
|
```
|
||||||
|
|
||||||
|
`VAULT_TOKEN` is also accepted when `VAULT_SERVER_ROOT_ACCESS_TOKEN` is not set.
|
||||||
|
Do not commit Vault tokens, WireGuard private keys, or the shared Xray UUID.
|
||||||
|
|
||||||
|
Vault KV base path:
|
||||||
|
|
||||||
|
```text
|
||||||
|
kv/xworkmate-bridge/distributed/wireguard-over-vless
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected secret layout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
common
|
||||||
|
xray_uuid
|
||||||
|
hosts/<inventory_hostname>
|
||||||
|
wireguard_private_key
|
||||||
|
```
|
||||||
|
|
||||||
|
The Xray UUID is the shared management-side UUID for this bridge transport. It
|
||||||
|
is not derived from tenant accounts or Xray account sync.
|
||||||
|
|
||||||
|
## Bridge Forwarding
|
||||||
|
|
||||||
|
The VPN forwarder exposes each bridge only on the WireGuard address:
|
||||||
|
|
||||||
|
- primary: `172.29.10.1:8787 -> 127.0.0.1:8787`
|
||||||
|
- CN edge: `172.29.10.2:8787 -> 127.0.0.1:8787`
|
||||||
|
|
||||||
|
Distributed task forwarding is configured through bridge topology. CN sets
|
||||||
|
`task_forward_peer_id: xworkmate-bridge`, so the bridge resolves the primary
|
||||||
|
private endpoint from `xworkmate_bridge_distributed_nodes`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://172.29.10.1:8787
|
||||||
|
```
|
||||||
|
|
||||||
|
The primary node leaves `task_forward_peer_id` empty. That keeps the reverse
|
||||||
|
WireGuard/VLESS path available for private network reachability without sending
|
||||||
|
primary runtime tasks back to CN.
|
||||||
|
|
||||||
|
Both sides use the same `BRIDGE_AUTH_TOKEN`. CN does not configure a separate
|
||||||
|
forwarding token; an empty forwarding token means the bridge reuses its local
|
||||||
|
auth token.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Run from the playbooks repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks
|
||||||
|
export VAULT_SERVER_URL=https://vault.svc.plus
|
||||||
|
export VAULT_SERVER_ROOT_ACCESS_TOKEN=...
|
||||||
|
|
||||||
|
ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i inventory.ini vpn-wireguard-over-vless.yml --check --diff
|
||||||
|
ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i inventory.ini vpn-wireguard-over-vless.yml -f 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `-f 1` for this two-host path when long SSH control sessions are unstable.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
On both hosts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl is-active xray-wg-tproxy wg-quick@wg-xwm xworkmate-bridge-vpn-forwarder xworkmate-bridge
|
||||||
|
xray run -test -config /usr/local/etc/xray/wireguard-over-vless.json
|
||||||
|
wg show wg-xwm
|
||||||
|
```
|
||||||
|
|
||||||
|
From the primary node:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ping -c 3 172.29.10.2
|
||||||
|
curl -H "Authorization: Bearer $BRIDGE_AUTH_TOKEN" http://172.29.10.2:8787/api/ping
|
||||||
|
```
|
||||||
|
|
||||||
|
From the CN edge node:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ping -c 3 172.29.10.1
|
||||||
|
curl -H "Authorization: Bearer $BRIDGE_AUTH_TOKEN" http://172.29.10.1:8787/api/ping
|
||||||
|
```
|
||||||
|
|
||||||
|
Regression checks:
|
||||||
|
|
||||||
|
- the primary host's `xray.service` still starts the original `/usr/local/etc/xray/config.json`
|
||||||
|
- both public bridge HTTPS endpoints still return `/api/ping`
|
||||||
|
- CN task forwarding resolves to the private `http://172.29.10.1:8787` endpoint
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
xworkmate_bridge_distributed_vpn_interface: wg-xwm
|
||||||
|
xworkmate_bridge_distributed_vpn_wireguard_port: 51820
|
||||||
|
xworkmate_bridge_distributed_vpn_local_tproxy_port: 51830
|
||||||
|
xworkmate_bridge_distributed_vpn_vless_port: 2443
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_port: 8787
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_target: 127.0.0.1:8787
|
||||||
|
xworkmate_bridge_distributed_vpn_mtu: 1400
|
||||||
|
xworkmate_bridge_distributed_vpn_persistent_keepalive: 25
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_bin_path: /usr/local/bin/xray
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_local_cache_dir: /tmp/xray-wireguard-over-vless
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_config_dir: /usr/local/etc/xray
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_config_path: "{{ xworkmate_bridge_distributed_vpn_xray_config_dir }}/wireguard-over-vless.json"
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_service_name: xray-wg-tproxy
|
||||||
|
xworkmate_bridge_distributed_vpn_forwarder_service_name: xworkmate-bridge-vpn-forwarder
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_addr: "{{ lookup('ansible.builtin.env', 'VAULT_SERVER_URL') | default('https://vault.svc.plus', true) }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_token: "{{ lookup('ansible.builtin.env', 'VAULT_SERVER_ROOT_ACCESS_TOKEN') | default(lookup('ansible.builtin.env', 'VAULT_TOKEN'), true) }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_mount: kv
|
||||||
|
xworkmate_bridge_distributed_vpn_vault_base_path: xworkmate-bridge/distributed/wireguard-over-vless
|
||||||
|
xworkmate_bridge_distributed_vpn_nodes: {}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
- name: Restart xray-wg-tproxy
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ xworkmate_bridge_distributed_vpn_xray_service_name }}"
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Restart wg-xwm
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "wg-quick@{{ xworkmate_bridge_distributed_vpn_interface }}"
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Restart xworkmate-bridge-vpn-forwarder
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ xworkmate_bridge_distributed_vpn_forwarder_service_name }}"
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
286
roles/vhosts/xworkmate_bridge_distributed_vpn/tasks/main.yml
Normal file
286
roles/vhosts/xworkmate_bridge_distributed_vpn/tasks/main.yml
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
---
|
||||||
|
- name: Assert distributed VPN node is defined for this host
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- inventory_hostname in xworkmate_bridge_distributed_vpn_nodes
|
||||||
|
- xworkmate_bridge_distributed_vpn_nodes[inventory_hostname].peer in xworkmate_bridge_distributed_vpn_nodes
|
||||||
|
fail_msg: "Missing xworkmate_bridge_distributed_vpn_nodes entry for {{ inventory_hostname }} or its peer"
|
||||||
|
|
||||||
|
- name: Resolve distributed VPN node facts
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_vpn_current_node: "{{ xworkmate_bridge_distributed_vpn_nodes[inventory_hostname] }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_peer_node: "{{ xworkmate_bridge_distributed_vpn_nodes[xworkmate_bridge_distributed_vpn_nodes[inventory_hostname].peer] }}"
|
||||||
|
|
||||||
|
- name: Resolve Caddy-managed TLS certificate paths for VLESS
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_vpn_tls_cert_path: "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{ xworkmate_bridge_distributed_vpn_current_node.domain }}/{{ xworkmate_bridge_distributed_vpn_current_node.domain }}.crt"
|
||||||
|
xworkmate_bridge_distributed_vpn_tls_key_path: "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{ xworkmate_bridge_distributed_vpn_current_node.domain }}/{{ xworkmate_bridge_distributed_vpn_current_node.domain }}.key"
|
||||||
|
|
||||||
|
- name: Assert Vault access is configured for distributed VPN secrets
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- xworkmate_bridge_distributed_vpn_vault_addr | trim | length > 0
|
||||||
|
- xworkmate_bridge_distributed_vpn_vault_token | trim | length > 0
|
||||||
|
fail_msg: "Set VAULT_SERVER_URL and VAULT_SERVER_ROOT_ACCESS_TOKEN, or VAULT_TOKEN, before deploying distributed VPN secrets"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Read shared distributed VPN Xray UUID from Vault
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ xworkmate_bridge_distributed_vpn_vault_addr }}/v1/{{ xworkmate_bridge_distributed_vpn_vault_mount }}/data/{{ xworkmate_bridge_distributed_vpn_vault_base_path }}/common"
|
||||||
|
headers:
|
||||||
|
X-Vault-Token: "{{ xworkmate_bridge_distributed_vpn_vault_token }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
register: xworkmate_bridge_distributed_vpn_common_secret
|
||||||
|
until: xworkmate_bridge_distributed_vpn_common_secret.status | default(0) == 200
|
||||||
|
retries: 5
|
||||||
|
delay: 3
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
check_mode: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Read host distributed VPN WireGuard secret from Vault
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ xworkmate_bridge_distributed_vpn_vault_addr }}/v1/{{ xworkmate_bridge_distributed_vpn_vault_mount }}/data/{{ xworkmate_bridge_distributed_vpn_vault_base_path }}/{{ inventory_hostname }}"
|
||||||
|
headers:
|
||||||
|
X-Vault-Token: "{{ xworkmate_bridge_distributed_vpn_vault_token }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
register: xworkmate_bridge_distributed_vpn_host_secret
|
||||||
|
until: xworkmate_bridge_distributed_vpn_host_secret.status | default(0) == 200
|
||||||
|
retries: 5
|
||||||
|
delay: 3
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
check_mode: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Resolve distributed VPN secrets
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_uuid: "{{ xworkmate_bridge_distributed_vpn_common_secret.json.data.data.xray_uuid }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_wireguard_private_key: "{{ xworkmate_bridge_distributed_vpn_host_secret.json.data.data.wireguard_private_key }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Install distributed VPN packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- unzip
|
||||||
|
- wireguard-tools
|
||||||
|
- socat
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
when:
|
||||||
|
- ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Check Xray binary
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ xworkmate_bridge_distributed_vpn_xray_bin_path }}"
|
||||||
|
register: xworkmate_bridge_distributed_vpn_xray_binary
|
||||||
|
|
||||||
|
- name: Resolve Xray release asset architecture
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_asset_arch: "{{ 'arm64-v8a' if ansible_architecture in ['aarch64', 'arm64'] else '64' }}"
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Resolve controller-side Xray cache paths
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_local_cache_path: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_dir }}-{{ xworkmate_bridge_distributed_vpn_xray_asset_arch }}"
|
||||||
|
xworkmate_bridge_distributed_vpn_xray_local_archive_path: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_dir }}-{{ xworkmate_bridge_distributed_vpn_xray_asset_arch }}.zip"
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Check controller-side cached Xray binary
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_path }}/xray"
|
||||||
|
register: xworkmate_bridge_distributed_vpn_xray_cached_binary
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Download Xray release archive
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-{{ xworkmate_bridge_distributed_vpn_xray_asset_arch }}.zip"
|
||||||
|
dest: "{{ xworkmate_bridge_distributed_vpn_xray_local_archive_path }}"
|
||||||
|
mode: "0644"
|
||||||
|
timeout: 120
|
||||||
|
register: xworkmate_bridge_distributed_vpn_xray_download
|
||||||
|
until: xworkmate_bridge_distributed_vpn_xray_download is succeeded
|
||||||
|
retries: 3
|
||||||
|
delay: 5
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not (xworkmate_bridge_distributed_vpn_xray_cached_binary.stat.exists | default(false))
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Create temporary Xray install directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_path }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not (xworkmate_bridge_distributed_vpn_xray_cached_binary.stat.exists | default(false))
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Extract Xray release archive
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "{{ xworkmate_bridge_distributed_vpn_xray_local_archive_path }}"
|
||||||
|
dest: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_path }}"
|
||||||
|
remote_src: true
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not (xworkmate_bridge_distributed_vpn_xray_cached_binary.stat.exists | default(false))
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Install Xray binary without creating the default xray.service
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ xworkmate_bridge_distributed_vpn_xray_local_cache_path }}/xray"
|
||||||
|
dest: "{{ xworkmate_bridge_distributed_vpn_xray_bin_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
when:
|
||||||
|
- not xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Ensure distributed VPN config directories exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- /etc/wireguard
|
||||||
|
- "{{ xworkmate_bridge_distributed_vpn_xray_config_dir }}"
|
||||||
|
|
||||||
|
- name: Check VLESS TLS certificate files
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item }}"
|
||||||
|
register: xworkmate_bridge_distributed_vpn_tls_files
|
||||||
|
loop:
|
||||||
|
- "{{ xworkmate_bridge_distributed_vpn_tls_cert_path }}"
|
||||||
|
- "{{ xworkmate_bridge_distributed_vpn_tls_key_path }}"
|
||||||
|
|
||||||
|
- name: Assert VLESS TLS certificate files exist
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.stat.exists
|
||||||
|
- item.stat.isreg
|
||||||
|
fail_msg: "Missing VLESS TLS certificate file: {{ item.item }}"
|
||||||
|
loop: "{{ xworkmate_bridge_distributed_vpn_tls_files.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
|
|
||||||
|
- name: Deploy Xray WireGuard over VLESS config
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: wireguard-over-vless.json.j2
|
||||||
|
dest: "{{ xworkmate_bridge_distributed_vpn_xray_config_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
notify: Restart xray-wg-tproxy
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Deploy Xray WireGuard over VLESS service
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: xray-wg-tproxy.service.j2
|
||||||
|
dest: "/etc/systemd/system/{{ xworkmate_bridge_distributed_vpn_xray_service_name }}.service"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart xray-wg-tproxy
|
||||||
|
|
||||||
|
- name: Test Xray WireGuard over VLESS config
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ xworkmate_bridge_distributed_vpn_xray_bin_path }} run -test -config {{ xworkmate_bridge_distributed_vpn_xray_config_path }}"
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode or xworkmate_bridge_distributed_vpn_xray_binary.stat.exists
|
||||||
|
|
||||||
|
- name: Deploy WireGuard distributed bridge config
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: wg-xwm.conf.j2
|
||||||
|
dest: "/etc/wireguard/{{ xworkmate_bridge_distributed_vpn_interface }}.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
notify: Restart wg-xwm
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Deploy VPN-only bridge forwarder service
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: xworkmate-bridge-vpn-forwarder.service.j2
|
||||||
|
dest: "/etc/systemd/system/{{ xworkmate_bridge_distributed_vpn_forwarder_service_name }}.service"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart xworkmate-bridge-vpn-forwarder
|
||||||
|
|
||||||
|
- name: Apply distributed VPN unit changes before service checks
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Enable and start Xray WireGuard over VLESS service
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ xworkmate_bridge_distributed_vpn_xray_service_name }}"
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Enable and start WireGuard distributed bridge interface
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "wg-quick@{{ xworkmate_bridge_distributed_vpn_interface }}"
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Enable and start VPN-only bridge forwarder
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ xworkmate_bridge_distributed_vpn_forwarder_service_name }}"
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Check distributed VPN service active states
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ item }}"
|
||||||
|
register: xworkmate_bridge_distributed_vpn_service_status
|
||||||
|
until: xworkmate_bridge_distributed_vpn_service_status.status.ActiveState | default('') == "active"
|
||||||
|
retries: 6
|
||||||
|
delay: 2
|
||||||
|
loop:
|
||||||
|
- "{{ xworkmate_bridge_distributed_vpn_xray_service_name }}"
|
||||||
|
- "wg-quick@{{ xworkmate_bridge_distributed_vpn_interface }}"
|
||||||
|
- "{{ xworkmate_bridge_distributed_vpn_forwarder_service_name }}"
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Show WireGuard distributed interface state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "wg show {{ xworkmate_bridge_distributed_vpn_interface }}"
|
||||||
|
register: xworkmate_bridge_distributed_vpn_wg_show
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
[Interface]
|
||||||
|
PrivateKey = {{ xworkmate_bridge_distributed_vpn_wireguard_private_key }}
|
||||||
|
Address = {{ xworkmate_bridge_distributed_vpn_current_node.wg_ip }}/32
|
||||||
|
ListenPort = {{ xworkmate_bridge_distributed_vpn_wireguard_port }}
|
||||||
|
MTU = {{ xworkmate_bridge_distributed_vpn_mtu }}
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {{ xworkmate_bridge_distributed_vpn_peer_node.public_key }}
|
||||||
|
AllowedIPs = {{ xworkmate_bridge_distributed_vpn_peer_node.wg_ip }}/32
|
||||||
|
Endpoint = 127.0.0.1:{{ xworkmate_bridge_distributed_vpn_local_tproxy_port }}
|
||||||
|
PersistentKeepalive = {{ xworkmate_bridge_distributed_vpn_persistent_keepalive }}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"log": {
|
||||||
|
"loglevel": "warning"
|
||||||
|
},
|
||||||
|
"routing": {
|
||||||
|
"domainStrategy": "IPIfNonMatch",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"inboundTag": ["wireguard-local-udp"],
|
||||||
|
"outboundTag": "wireguard-peer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"inboundTag": ["wireguard-vless"],
|
||||||
|
"outboundTag": "direct"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"tag": "wireguard-vless",
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"port": {{ xworkmate_bridge_distributed_vpn_vless_port | int }},
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "{{ xworkmate_bridge_distributed_vpn_xray_uuid }}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decryption": "none"
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"serverName": "{{ xworkmate_bridge_distributed_vpn_current_node.domain }}",
|
||||||
|
"minVersion": "1.2",
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "{{ xworkmate_bridge_distributed_vpn_tls_cert_path }}",
|
||||||
|
"keyFile": "{{ xworkmate_bridge_distributed_vpn_tls_key_path }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "wireguard-local-udp",
|
||||||
|
"listen": "127.0.0.1",
|
||||||
|
"port": {{ xworkmate_bridge_distributed_vpn_local_tproxy_port | int }},
|
||||||
|
"protocol": "dokodemo-door",
|
||||||
|
"settings": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port": {{ xworkmate_bridge_distributed_vpn_wireguard_port | int }},
|
||||||
|
"network": "udp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"tag": "wireguard-peer",
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"vnext": [
|
||||||
|
{
|
||||||
|
"address": "{{ xworkmate_bridge_distributed_vpn_peer_node.domain }}",
|
||||||
|
"port": {{ xworkmate_bridge_distributed_vpn_vless_port | int }},
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": "{{ xworkmate_bridge_distributed_vpn_xray_uuid }}",
|
||||||
|
"encryption": "none",
|
||||||
|
"packetEncoding": "xudp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"serverName": "{{ xworkmate_bridge_distributed_vpn_peer_node.domain }}",
|
||||||
|
"allowInsecure": false,
|
||||||
|
"fingerprint": "chrome"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "direct",
|
||||||
|
"protocol": "freedom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "block",
|
||||||
|
"protocol": "blackhole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Xray WireGuard over VLESS/TLS transport
|
||||||
|
Documentation=https://github.com/XTLS/Xray-core
|
||||||
|
After=network-online.target nss-lookup.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStartPre={{ xworkmate_bridge_distributed_vpn_xray_bin_path }} run -test -config {{ xworkmate_bridge_distributed_vpn_xray_config_path }}
|
||||||
|
ExecStart={{ xworkmate_bridge_distributed_vpn_xray_bin_path }} run -config {{ xworkmate_bridge_distributed_vpn_xray_config_path }}
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
RestartPreventExitStatus=23
|
||||||
|
LimitNPROC=10000
|
||||||
|
LimitNOFILE=1000000
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=XWorkmate bridge VPN-only private forwarder
|
||||||
|
After=network-online.target wg-quick@{{ xworkmate_bridge_distributed_vpn_interface }}.service
|
||||||
|
Requires=wg-quick@{{ xworkmate_bridge_distributed_vpn_interface }}.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/socat TCP-LISTEN:{{ xworkmate_bridge_distributed_vpn_forwarder_port }},bind={{ xworkmate_bridge_distributed_vpn_current_node.wg_ip }},fork,reuseaddr TCP:{{ xworkmate_bridge_distributed_vpn_forwarder_target }}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@ -21,15 +21,11 @@ if [ -z "$INTERNAL_TOKEN" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Resolve Image (get latest from online if not provided)
|
# 3. Run Ansible
|
||||||
IMAGE="${SERVICE_COMPOSE_IMAGE:-ghcr.io/x-evor/xworkmate-bridge:f30c8d481615933448535b15c0ed9099ed7c4ac9}"
|
|
||||||
|
|
||||||
# 4. Run Ansible
|
|
||||||
echo "[Ansible] Starting dry-run validation..."
|
echo "[Ansible] Starting dry-run validation..."
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
ansible-playbook -i inventory.ini deploy_xworkmate_bridge_vhosts.yml \
|
ansible-playbook -i inventory.ini deploy_xworkmate_bridge_vhosts.yml \
|
||||||
-l jp-xhttp-contabo.svc.plus \
|
-l jp-xhttp-contabo.svc.plus \
|
||||||
-e "INTERNAL_SERVICE_TOKEN=$INTERNAL_TOKEN" \
|
-e "INTERNAL_SERVICE_TOKEN=$INTERNAL_TOKEN" \
|
||||||
-e "xworkmate_bridge_auth_token=$INTERNAL_TOKEN" \
|
-e "xworkmate_bridge_auth_token=$INTERNAL_TOKEN" \
|
||||||
-e "service_compose_image=$IMAGE" \
|
|
||||||
"$@"
|
"$@"
|
||||||
|
|||||||
65
vpn-wireguard-over-vless.yml
Normal file
65
vpn-wireguard-over-vless.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy bidirectional WireGuard over VLESS for XWorkmate bridge distribution
|
||||||
|
hosts: xworkmate_bridge_distributed
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: roles/vhosts/xworkmate_bridge_distributed_vpn/
|
||||||
|
|
||||||
|
- name: Refresh xworkmate-bridge distributed runtime config
|
||||||
|
hosts: xworkmate_bridge_distributed
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: roles/vhosts/xworkmate_bridge/
|
||||||
|
tags: [xworkmate_bridge]
|
||||||
|
|
||||||
|
- name: Validate XWorkmate bridge private distributed path
|
||||||
|
hosts: xworkmate_bridge_distributed
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Resolve current distributed VPN node
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_current_node: "{{ xworkmate_bridge_distributed_vpn_nodes[inventory_hostname] }}"
|
||||||
|
|
||||||
|
- name: Resolve peer distributed VPN node
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
xworkmate_bridge_distributed_peer_node: "{{ xworkmate_bridge_distributed_vpn_nodes[xworkmate_bridge_distributed_current_node.peer] }}"
|
||||||
|
|
||||||
|
- name: Verify peer WireGuard address is reachable
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "ping -c 3 -W 2 {{ xworkmate_bridge_distributed_peer_node.wg_ip }}"
|
||||||
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Read local bridge auth token for private peer validation
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
systemctl cat xworkmate-bridge.service |
|
||||||
|
sed -n 's/^Environment="BRIDGE_AUTH_TOKEN=\(.*\)"$/\1/p' |
|
||||||
|
head -n 1
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: xworkmate_bridge_private_validation_token
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Verify peer bridge API through WireGuard private endpoint
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ xworkmate_bridge_distributed_peer_node.wg_ip }}:{{ xworkmate_bridge_distributed_vpn_forwarder_port }}/api/ping"
|
||||||
|
headers:
|
||||||
|
Authorization: "Bearer {{ xworkmate_bridge_private_validation_token.stdout }}"
|
||||||
|
return_content: true
|
||||||
|
register: xworkmate_bridge_private_peer_ping
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Assert peer bridge private ping succeeded
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- xworkmate_bridge_private_peer_ping.status == 200
|
||||||
|
- xworkmate_bridge_private_peer_ping.json.status | default('') == 'ok'
|
||||||
|
when: not ansible_check_mode
|
||||||
Loading…
Reference in New Issue
Block a user