docs: update claude setup
refactor: Move some things to roles refactor: fix some linting
This commit is contained in:
23
roles/openclaw/defaults/main.yml
Normal file
23
roles/openclaw/defaults/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
# OpenClaw service user
|
||||
openclaw_user: openclaw
|
||||
openclaw_group: openclaw
|
||||
openclaw_home: /opt/openclaw
|
||||
openclaw_state_dir: /opt/openclaw/.openclaw
|
||||
openclaw_node_version: "24"
|
||||
|
||||
# Model provider
|
||||
openclaw_model_provider: anthropic
|
||||
openclaw_api_key: "{{ vault_openclaw_api_key }}"
|
||||
|
||||
# Signal channel
|
||||
openclaw_signal_enabled: false
|
||||
openclaw_signal_account: "{{ vault_openclaw_signal_phone | default('') }}"
|
||||
openclaw_signal_cli_version: "0.13.15"
|
||||
openclaw_signal_cli_path: /usr/local/bin/signal-cli
|
||||
openclaw_signal_dm_policy: pairing
|
||||
openclaw_signal_allow_from: [] # list of E.164 numbers permitted to DM
|
||||
|
||||
# Firewall
|
||||
openclaw_ssh_port: 22
|
||||
openclaw_gateway_port: 18789
|
||||
10
roles/openclaw/handlers/main.yml
Normal file
10
roles/openclaw/handlers/main.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart openclaw
|
||||
ansible.builtin.systemd:
|
||||
name: openclaw
|
||||
state: restarted
|
||||
listen: Restart openclaw
|
||||
16
roles/openclaw/meta/main.yml
Normal file
16
roles/openclaw/meta/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: ptoal
|
||||
description: Install and configure OpenClaw AI gateway on Ubuntu
|
||||
license: MIT
|
||||
min_ansible_version: "2.16"
|
||||
platforms:
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- noble
|
||||
galaxy_tags:
|
||||
- openclaw
|
||||
- ai
|
||||
- signal
|
||||
|
||||
dependencies: []
|
||||
122
roles/openclaw/tasks/install.yml
Normal file
122
roles/openclaw/tasks/install.yml
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
# ---------------------------------------------------------------------------
|
||||
# System user and directories
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Create openclaw group
|
||||
ansible.builtin.group:
|
||||
name: "{{ openclaw_group }}"
|
||||
system: false
|
||||
state: present
|
||||
|
||||
- name: Create openclaw user
|
||||
ansible.builtin.user:
|
||||
name: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_group }}"
|
||||
home: "{{ openclaw_home }}"
|
||||
shell: /sbin/nologin
|
||||
system: false # must be non-system: subuid/subgid entries required for rootless Podman
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Get openclaw user UID
|
||||
ansible.builtin.command:
|
||||
cmd: "id -u {{ openclaw_user }}"
|
||||
register: __openclaw_uid_result
|
||||
changed_when: false
|
||||
|
||||
- name: Set openclaw UID fact
|
||||
ansible.builtin.set_fact:
|
||||
__openclaw_uid: "{{ __openclaw_uid_result.stdout }}"
|
||||
|
||||
- name: Enable lingering for openclaw user
|
||||
ansible.builtin.command:
|
||||
cmd: "loginctl enable-linger {{ openclaw_user }}"
|
||||
register: __openclaw_linger
|
||||
changed_when: __openclaw_linger.rc == 0
|
||||
|
||||
- name: Enable rootless Podman socket for openclaw user
|
||||
ansible.builtin.systemd:
|
||||
name: podman.socket
|
||||
enabled: true
|
||||
state: started
|
||||
scope: user
|
||||
become: true
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ __openclaw_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ __openclaw_uid }}/bus"
|
||||
|
||||
- name: Create OpenClaw state directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ openclaw_state_dir }}"
|
||||
state: directory
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_group }}"
|
||||
mode: "0750"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Node.js
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Add NodeSource apt signing key
|
||||
ansible.builtin.apt_key:
|
||||
url: "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key"
|
||||
state: present
|
||||
|
||||
- name: Add NodeSource apt repository
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "deb https://deb.nodesource.com/node_{{ openclaw_node_version }}.x nodistro main"
|
||||
state: present
|
||||
filename: nodesource
|
||||
|
||||
- name: Install Node.js
|
||||
ansible.builtin.apt:
|
||||
name: nodejs
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install pnpm globally
|
||||
community.general.npm:
|
||||
name: pnpm
|
||||
global: true
|
||||
state: present
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OpenClaw binary
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Install OpenClaw via npm
|
||||
community.general.npm:
|
||||
name: openclaw
|
||||
global: true
|
||||
state: "{{ 'latest' if openclaw_version == 'latest' else 'present' }}"
|
||||
notify: Restart openclaw
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Template OpenClaw config
|
||||
ansible.builtin.template:
|
||||
src: openclaw-config.yaml.j2
|
||||
dest: "{{ openclaw_state_dir }}/config.yaml"
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_group }}"
|
||||
mode: "0640"
|
||||
notify: Restart openclaw
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Systemd service with hardening
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Template openclaw systemd service
|
||||
ansible.builtin.template:
|
||||
src: openclaw.service.j2
|
||||
dest: /etc/systemd/system/openclaw.service
|
||||
mode: "0644"
|
||||
notify:
|
||||
- Reload systemd
|
||||
- Restart openclaw
|
||||
|
||||
- name: Enable and start openclaw service
|
||||
ansible.builtin.systemd:
|
||||
name: openclaw
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
10
roles/openclaw/tasks/main.yml
Normal file
10
roles/openclaw/tasks/main.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Configure security (UFW, Tailscale, Docker)
|
||||
ansible.builtin.include_tasks: security.yml
|
||||
|
||||
- name: Install OpenClaw
|
||||
ansible.builtin.include_tasks: install.yml
|
||||
|
||||
- name: Configure Signal channel
|
||||
ansible.builtin.include_tasks: signal.yml
|
||||
when: openclaw_signal_enabled | bool
|
||||
49
roles/openclaw/tasks/security.yml
Normal file
49
roles/openclaw/tasks/security.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
# ---------------------------------------------------------------------------
|
||||
# UFW firewall — defense-in-depth behind OPNsense perimeter
|
||||
# Allows SSH and the OpenClaw gateway port; blocks everything else inbound
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Install UFW
|
||||
ansible.builtin.apt:
|
||||
name: ufw
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Set UFW default policies
|
||||
community.general.ufw:
|
||||
direction: "{{ item.direction }}"
|
||||
policy: "{{ item.policy }}"
|
||||
loop:
|
||||
- { direction: incoming, policy: deny }
|
||||
- { direction: outgoing, policy: allow }
|
||||
- { direction: routed, policy: deny }
|
||||
|
||||
- name: Allow SSH
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ openclaw_ssh_port | string }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow OpenClaw gateway port
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ openclaw_gateway_port | string }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Enable UFW
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rootless Podman — used exclusively for agent sandbox isolation
|
||||
# Runs as the openclaw user; no root daemon, no exposed sockets
|
||||
# podman-docker provides a docker-compatible CLI shim for OpenClaw tooling
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Install Podman and dependencies
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- podman
|
||||
- podman-docker
|
||||
- uidmap
|
||||
state: present
|
||||
update_cache: true
|
||||
72
roles/openclaw/tasks/signal.yml
Normal file
72
roles/openclaw/tasks/signal.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
# ---------------------------------------------------------------------------
|
||||
# signal-cli — Java-based CLI bridge required by OpenClaw's Signal channel.
|
||||
# Docs: https://docs.openclaw.ai/channels/signal
|
||||
#
|
||||
# MANUAL STEP REQUIRED after first deploy:
|
||||
# Option A (link existing account):
|
||||
# sudo -i -u openclaw
|
||||
# signal-cli link -n "OpenClaw" # scan QR code with Signal app
|
||||
#
|
||||
# Option B (register dedicated number):
|
||||
# sudo -i -u openclaw
|
||||
# signal-cli -a {{ openclaw_signal_account }} register --captcha <token>
|
||||
# signal-cli -a {{ openclaw_signal_account }} verify <sms-code>
|
||||
#
|
||||
# Then approve DM access:
|
||||
# openclaw pairing approve signal
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
- name: Install Java runtime (required by signal-cli)
|
||||
ansible.builtin.apt:
|
||||
name: default-jre-headless
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create signal-cli install directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/signal-cli
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Download signal-cli archive
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/AsamK/signal-cli/releases/download/v{{ openclaw_signal_cli_version }}/signal-cli-{{ openclaw_signal_cli_version }}-Linux.tar.gz"
|
||||
dest: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}.tar.gz"
|
||||
mode: "0644"
|
||||
register: __openclaw_signal_cli_download
|
||||
|
||||
- name: Extract signal-cli
|
||||
ansible.builtin.unarchive:
|
||||
src: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}.tar.gz"
|
||||
dest: /opt/signal-cli
|
||||
remote_src: true
|
||||
creates: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}/bin/signal-cli"
|
||||
|
||||
- name: Symlink signal-cli to PATH
|
||||
ansible.builtin.file:
|
||||
src: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}/bin/signal-cli"
|
||||
dest: "{{ openclaw_signal_cli_path }}"
|
||||
state: link
|
||||
|
||||
- name: Set ownership of signal-cli data directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ openclaw_home }}/.local/share/signal-cli"
|
||||
state: directory
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_group }}"
|
||||
mode: "0700"
|
||||
|
||||
- name: Display Signal registration reminder
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "*** MANUAL STEP REQUIRED: Signal account not yet registered ***"
|
||||
- "Switch to the openclaw user and register signal-cli:"
|
||||
- " sudo -i -u {{ openclaw_user }}"
|
||||
- " # Option A — link existing account (recommended):"
|
||||
- " signal-cli link -n 'OpenClaw' # scan QR with Signal app"
|
||||
- " # Option B — register a dedicated number:"
|
||||
- " signal-cli -a {{ openclaw_signal_account }} register --captcha <token>"
|
||||
- " signal-cli -a {{ openclaw_signal_account }} verify <sms-code>"
|
||||
- "After registration, approve pairing:"
|
||||
- " openclaw pairing approve signal"
|
||||
24
roles/openclaw/templates/openclaw-config.yaml.j2
Normal file
24
roles/openclaw/templates/openclaw-config.yaml.j2
Normal file
@@ -0,0 +1,24 @@
|
||||
# OpenClaw configuration — managed by Ansible, do not edit manually
|
||||
# Ref: https://docs.openclaw.ai
|
||||
|
||||
gateway:
|
||||
port: 18789
|
||||
# Gateway binds localhost only; Tailscale is the remote access path
|
||||
|
||||
providers:
|
||||
- type: {{ openclaw_model_provider }}
|
||||
apiKey: "{{ openclaw_api_key }}"
|
||||
|
||||
{% if openclaw_signal_enabled | bool %}
|
||||
channels:
|
||||
signal:
|
||||
account: "{{ openclaw_signal_account }}"
|
||||
cliPath: "{{ openclaw_signal_cli_path }}"
|
||||
dmPolicy: {{ openclaw_signal_dm_policy }}
|
||||
{% if openclaw_signal_allow_from | length > 0 %}
|
||||
allowFrom:
|
||||
{% for number in openclaw_signal_allow_from %}
|
||||
- "{{ number }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
29
roles/openclaw/templates/openclaw.service.j2
Normal file
29
roles/openclaw/templates/openclaw.service.j2
Normal file
@@ -0,0 +1,29 @@
|
||||
[Unit]
|
||||
Description=OpenClaw AI Gateway
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ openclaw_user }}
|
||||
Group={{ openclaw_group }}
|
||||
WorkingDirectory={{ openclaw_home }}
|
||||
|
||||
Environment=OPENCLAW_STATE_DIR={{ openclaw_state_dir }}
|
||||
Environment=OPENCLAW_CONFIG_PATH={{ openclaw_state_dir }}/config.yaml
|
||||
Environment=DOCKER_HOST=unix:/run/user/{{ __openclaw_uid }}/podman/podman.sock
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/{{ __openclaw_uid }}
|
||||
|
||||
ExecStart=/usr/bin/openclaw gateway run
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# Hardening
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths={{ openclaw_state_dir }} {{ openclaw_home }}
|
||||
ProtectHome=read-only
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user