Anisail
Ansible connection plugin that uses Holesail P2P tunnels to reach hosts behind NAT, firewalls, or CGNAT — with no port forwarding or static IPs.
- On the target: Run
holesail --live 22(or your SSH port); copy thehs://...URL. - In inventory: Set
ansible_connectionandansible_holesail_key. - Ansible starts a local tunnel, connects SSH through it, and runs your playbooks as usual.
Requires the anisail-tunnel binary (Bare-built, shipped in releases/ or built via npm run build:tunnel) and asyncssh on the control node.
Installation
0. One-command install (recommended)
To install anisail-tunnel, the Ansible collection, and asyncssh from the latest release:
macOS / Linux:
curl -fsSL https://git.ssh.surf/snxraven/anisail/raw/branch/main/scripts/install.sh | bash
Windows (PowerShell):
irm https://git.ssh.surf/snxraven/anisail/raw/branch/main/scripts/install.ps1 | iex
The script installs the tunnel binary (to ~/.anisail/bin or %LOCALAPPDATA%\anisail\bin on Windows), installs asyncssh if missing, and installs the anisail.anisail collection from the repo archive. See docs/INSTALLATION.md for details, custom install paths, and build-from-source.
1. Install the collection manually (if needed)
If the install script could not install the collection (e.g. Ansible not installed yet), from the repo root:
ansible-galaxy collection install ansible_collections/anisail/anisail/ --force
Or link for development:
export ANSIBLE_COLLECTIONS_PATHS="$PWD/ansible_collections"
# or symlink/copy ansible_collections/anisail into your collections path
2. Tunnel binary (if not using the install script)
If you did not run the install script above, either use a prebuilt binary from the releases (download the zip for your platform and extract anisail-tunnel) or build from source (requires Node.js and npm).
Option A — Build from source (Bare binary)
npm install
npm run build:tunnel
Output: releases/<platform>/anisail-tunnel (e.g. releases/darwin-arm64/anisail-tunnel). Either add that directory to PATH or set ansible_holesail_tunnel_path in inventory to the full path.
Option B — Use collection releases
If you ship the built binary inside the collection (e.g. under ansible_collections/anisail/anisail/releases/<platform>/anisail-tunnel), the plugin will find it automatically. Copy from repo releases/ after building:
cp -r releases/* ansible_collections/anisail/anisail/releases/
Option C — System PATH
Install the binary somewhere on PATH and name it anisail-tunnel. The plugin will use it if ansible_holesail_tunnel_path is not set and no collection-relative binary is found.
3. Install asyncssh (if not installed by the script)
pip install asyncssh
Inventory
Use the holesail connection and set the Holesail key (and optionally port/timeout):
# inventory.yml
all:
hosts:
my_device:
ansible_connection: anisail.anisail.holesail # or "holesail" if collection is default
ansible_holesail_key: "hs://s000..." # from "holesail --live 22" on the target
ansible_user: admin # SSH user (optional)
# ansible_port: 22 # remote port to tunnel to (default 22)
# ansible_holesail_tunnel_path: /path/to/anisail-tunnel
# ansible_holesail_ready_timeout: 30 # seconds (default 30)
If your playbook sets collections: [anisail.anisail], you can use the short name:
ansible_connection: holesail
ansible_holesail_key: "hs://s000..."
Target setup
On each device you want to manage, run Holesail in server mode exposing the SSH port:
npm i holesail -g
holesail --live 22
Use the printed URL (e.g. hs://s000...) as ansible_holesail_key for that host.
Playbook example
---
- name: Run through Holesail tunnel
hosts: my_device
gather_facts: true
tasks:
- name: Ping
ping:
- name: Run a command
command: uname -a
register: out
- name: Print result
debug:
var: out.stdout
Run as usual:
ansible-playbook -i inventory.yml playbook.yml
Connection variables
| Variable | Description | Default |
|---|---|---|
ansible_holesail_key |
Holesail URL from target (hs://...) |
(required) |
ansible_port |
Remote port to tunnel to (e.g. SSH 22) | 22 |
ansible_holesail_tunnel_path |
Path to anisail-tunnel binary | auto (collection bin/releases or PATH) |
ansible_holesail_ready_timeout |
Seconds to wait for tunnel ready | 30 |
ansible_user |
SSH user | play context default |
ansible_private_key_file / ansible_ssh_private_key_file |
SSH key path (optional; if unset, system keys ~/.ssh/id_ed25519, id_rsa, etc. and SSH agent are used) |
— |
ansible_password / ansible_ssh_pass |
SSH password | — |
Building the tunnel binary
From the repo root:
npm install
npm run build:tunnel # current platform only
npm run build:tunnel:all # all platforms (darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64)
Output is under releases/<platform>/anisail-tunnel (or anisail-tunnel.exe on Windows). The tunnel is a statically built Bare binary; no Node.js is required at runtime.
CI and releases
The repo includes a Gitea Actions workflow (.gitea/workflows/ci.yml) that:
- Runs on push and pull requests to
main - Installs dependencies, lint-checks the tunnel and connection plugin, and builds tunnel binaries for all platforms (
node scripts/build-tunnel.js --all) - Packages each binary as
anisail-tunnel-<platform>.zipand publishes a rolling release on push
Required secret: RELEASE_TOKEN — a token with permission to push the latest-main tag and create/update releases and upload assets. Configure it in your Gitea repo Settings → Secrets.
The workflow uses the same pattern as Holesail-Browser CI: force-push tag latest-main, create or update a prerelease, and upload all zip artifacts plus SHA256SUMS.txt.
Documentation
Detailed docs live in docs/. Quick links:
- Installation — Install scripts, custom paths, build-from-source, uninstall
- Usage — Inventory, playbooks, target setup
- Architecture — System design and data flow
- Troubleshooting — Common errors and fixes
- Contributing — How to contribute code and docs
License
AGPL-3.0. See LICENSE or https://www.gnu.org/licenses/agpl-3.0.html.
References
- Holesail — P2P tunnel (npm, CLI, API)
- Ansible connection plugins
- Developing network plugins