aoh

A tool for running Ansible playbooks with `curl <url> | python` commands
git clone https://git.ashermorgan.net/aoh/
Log | Files | Refs | README

commit 7c389a36d85db0d58bf9d247a4d339ed06b9d058
parent d7a87b37fed078f370a7e8e5f26e6b4490ff8d89
Author: Asher Morgan <asher@ashermorgan.net>
Date:   Tue, 18 Aug 2026 19:43:58 -0700

Pass client hostname and restrict AoH connection

Diffstat:
Maoh/connection_plugins/aoh.py | 7+++++++
Maoh/install.py | 2++
Maoh/runner.py | 20++++++++++++++++++--
Maoh/server.py | 2+-
Mdemo/ansible.cfg | 2--
Ddemo/inventory.yml | 3---
Mdemo/playbook.yml | 2+-
7 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/aoh/connection_plugins/aoh.py b/aoh/connection_plugins/aoh.py @@ -44,6 +44,13 @@ class Connection(ConnectionBase): if not self._connected: AOH_DIR = self.get_option('aoh_dir') + with open(f'{AOH_DIR}/hostname', 'r') as f: + hostname = f.readline().strip() + if hostname != self._play_context.remote_addr: + raise AnsibleError('Attempted to connect to an AoH runner ' + 'belonging to a different host ' + f'({hostname})') + display.vvv(f'ESTABLISH CONNECTION TO AOH RUNNER {AOH_DIR}', host=self._play_context.remote_addr) diff --git a/aoh/install.py b/aoh/install.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import base64 +import platform import subprocess import sys import time @@ -51,6 +52,7 @@ def fetch(args): def main(): res = requests.post(f'{API}/runners/', json={ + 'host': platform.node() or 'aoh_node', 'playbook': sys.argv[1] if len(sys.argv) >= 2 else 'main', 'args': ' '.join(sys.argv[2:]), }) diff --git a/aoh/runner.py b/aoh/runner.py @@ -14,7 +14,7 @@ class AoHError(Exception): class AoHRunner: - def __init__(self, config, playbook, args): + def __init__(self, config, playbook, host, args): self.id = str(uuid4()) self._dir = tempfile.mkdtemp() self._LOGS_PATH = f'{self._dir}/artifacts/{self.id}/stdout' @@ -27,6 +27,16 @@ class AoHRunner: self._runner = None try: + # Create hostname file so connection plugin can verify hosts + with open(f'{self._dir}/hostname', 'w') as f: + f.write(f'{host}\n') + + # Set ansible_connection=aoh for the client host only. Note that we + # don't use ansible-runners inventory directory because that will + # shadow user inventory files. + with open(f'{self._dir}/inventory.ini', 'w') as f: + f.write(f'[aoh]\n{host} ansible_connection=aoh') + self._start_runner(config, playbook, args) # We assume that ansible-runner will eventually create its log file @@ -60,16 +70,22 @@ class AoHRunner: x for x in raw_config.split('\n') if x.startswith('DEFAULT_CONNECTION_PLUGIN_PATH') ).split('= ')[1]) + inventory = eval(next( + x for x in raw_config.split('\n') + if x.startswith('DEFAULT_HOST_LIST') + ).split('= ')[1]) env = { 'ANSIBLE_CONNECTION_PLUGINS': ':'.join( [_CONNECTION_PLUGIN_DIR] + connection_plugins ), 'ANSIBLE_CONFIG': config, + 'ANSIBLE_INVENTORY': ','.join( + [f'{self._dir}/inventory.ini'] + inventory + ), } vars = { 'ansible_aoh_dir': self._dir, - 'ansible_connection': 'aoh', } self._thread, self._runner = ansible_runner.run_async( diff --git a/aoh/server.py b/aoh/server.py @@ -70,7 +70,7 @@ def job_new(): return { 'err': f"Playbook not found: {request.json['playbook']}" }, 400 runner = AoHRunner(playbook['config'], playbook['playbook'], - request.json['args']) + request.json['host'], request.json['args']) _DATA[runner.id] = runner session['runner'] = runner.id diff --git a/demo/ansible.cfg b/demo/ansible.cfg @@ -1,2 +0,0 @@ -[defaults] -inventory = inventory.yml diff --git a/demo/inventory.yml b/demo/inventory.yml @@ -1,3 +0,0 @@ -all: - hosts: - myhost: diff --git a/demo/playbook.yml b/demo/playbook.yml @@ -1,4 +1,4 @@ - name: My play - hosts: myhost + hosts: all tasks: - ping: