aoh

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

commit 2d99a91daee2903c47f710785649070c8c6106e5
parent ad01caecedc0faeab421ebca0f0ed8d06ca81a7b
Author: Asher Morgan <asher@ashermorgan.net>
Date:   Sun, 30 Aug 2026 14:13:21 -0700

Improve docker image

The docker image should now "just work" when the ./demo directory is
mounted at /aoh. This commit updates config defaults inside the docker
image, expands the ./demo example files, and fixes a config loading bug.

Diffstat:
M.gitignore | 8++++----
MDockerfile | 6++++++
MREADME.md | 32+++++++++++++++++++++++---------
Maoh/__init__.py | 4+++-
Rdemo/Dockerfile -> demo/client.Dockerfile | 0
Ademo/passwords.yml | 2++
Ademo/playbooks.yml | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Rdemo/ansible.cfg -> demo/playbooks/ansible.cfg | 0
Rdemo/playbook.yml -> demo/playbooks/playbook.yml | 0
9 files changed, 87 insertions(+), 14 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,6 +1,6 @@ -.env -playbooks.yml -passwords.yml -*.pyc __pycache__ .venv + +/.env +/passwords.yml +/playbooks.yml diff --git a/Dockerfile b/Dockerfile @@ -7,5 +7,11 @@ RUN python3 -m pip install --no-cache-dir -r requirements.txt COPY aoh aoh +RUN << EOF +echo 'AOH_PASSWORDS_FILE=/aoh/passwords.yml' >> .env +echo 'AOH_PLAYBOOKS_FILE=/aoh/playbooks.yml' >> .env +EOF + EXPOSE 8000 + CMD ["gunicorn", "-b", "0.0.0.0:8000", "aoh:create_app()"] diff --git a/README.md b/README.md @@ -24,15 +24,23 @@ new machines, anytime, anywhere. | Connection direction | Server &rarr; client | Client &rarr; server | Client &rarr; server | -## Setup +## Getting Started -1. Install python dependencies: `pip install -r requirements.txt` +The AoH server is packaged as a Docker image. You can quickly try it out with +the following commands: -2. Create `.env`, `playbooks.yml`, and `passwords.yml` files as described below +```sh +# Build and run the server docker image: +docker build -t aoh . +docker run --rm --detach --name aoh -p 8000:8000 -v ./demo:/aoh aoh -3. Start AoH development server: `python -m aoh` +# Run the client CLI (the AoH password is "hunter2") +curl -s 127.0.0.1:8000/run | python3 +curl -s 127.0.0.1:8000/run | python3 - --help -4. Run AoH client: `curl -s localhost:5000/run | python3 - --help` +# Stop the server docker container +docker stop aoh +``` ## Configuration @@ -48,13 +56,15 @@ environment variables or a `.env` file. received. - `AOH_PASSWORDS_FILE`: The file containing passwords for password-protected - playbooks (see below). Defaults to `./passwords.yml`. + playbooks (see below). Defaults to `/aoh/passwords.yml` when running the + Docker image and `./passwords.yml` otherwise. - `AOH_PLAYBOOKS_DIR`: The default directory to search for playbooks in. Defaults to the directory containing `AOH_PLAYBOOKS_FILE`. - `AOH_PLAYBOOKS_FILE`: The file containing playbook configuration (see below). - Defaults to `./playbooks.yml`. + Defaults to `/aoh/playbooks.yml` when running the Docker image and + `./playbooks.yml` otherwise. ### Playbooks @@ -62,15 +72,17 @@ environment variables or a `.env` file. AoH only executes playbooks that are listed in the `$AOH_PLAYBOOKS_FILE` file, which must have the following structure: +<!-- EXAMPLE COPIED FROM demo/playbooks.yml: --> + ```yml main.yml: # The playbook name # The path to the playbook, relative to $AOH_PLAYBOOKS_DIR. Defaults to the # playbook name. - path: demo/playbook.yml + path: playbooks/playbook.yml # The path to an associated Ansible config file, if one exists. - config: demo/ansible.cfg + config: playbooks/ansible.cfg # The hostname to assign to clients. If omitted or null, each client is # assigned the hostname that it reports for itself. @@ -120,6 +132,8 @@ main.yml: # The playbook name Passwords for password-protected playbooks must be specified as bcrypt hashes in the `$AOH_PASSWORDS_FILE` file. For example: +<!-- EXAMPLE COPIED FROM demo/passwords.yml: --> + ``` # Require the password "hunter2" to execute the main.yml playbook main.yml: $2a$14$LfC6Bcczc0WM.OObqgQPYeVvYa10g1Z4Z8C.eERyd.GarPmNa5/ve diff --git a/aoh/__init__.py b/aoh/__init__.py @@ -1,9 +1,11 @@ from dotenv import load_dotenv +# Load config before importing app +load_dotenv() + from .server import app, scheduler def create_app(): - load_dotenv() scheduler.start() return app diff --git a/demo/Dockerfile b/demo/client.Dockerfile diff --git a/demo/passwords.yml b/demo/passwords.yml @@ -0,0 +1,2 @@ +# Require the password "hunter2" to execute the main.yml playbook +main.yml: $2a$14$LfC6Bcczc0WM.OObqgQPYeVvYa10g1Z4Z8C.eERyd.GarPmNa5/ve diff --git a/demo/playbooks.yml b/demo/playbooks.yml @@ -0,0 +1,49 @@ +main.yml: # The playbook name + + # The path to the playbook, relative to $AOH_PLAYBOOKS_DIR. Defaults to the + # playbook name. + path: playbooks/playbook.yml + + # The path to an associated Ansible config file, if one exists. + config: playbooks/ansible.cfg + + # The hostname to assign to clients. If omitted or null, each client is + # assigned the hostname that it reports for itself. + host: my-aoh-host + + # The groups to assign to clients, in addition to the aoh group and any groups + # defined in inventory files. + groups: + - group1 + - group2 + + # Whether to require a password to execute the playbook. Passwords must be + # specified separately in the $AOH_PASSWORDS_FILE file (see below). Defaults + # to false. + password: true + + # Additional raw ansible-playbook options + cmdline: '--limit aoh' # only run tasks for the current client + + # Whether to print Ansible output on the client. Defaults to true. + output: true + + # The ansible-playbooks options that users are allowed to invoke. Short and + # long option forms must be specified separately. A reasonable set of safe + # options are allowed by default, see aoh/security.py. + allow_opts: + - '--become-method' + + # A list of ansible-playbook options that users are not allowed to invoke. + # This overrides the default allowed options and the options specified in + # allow_opts. Some options are always blocked, see aoh/security.py. + block_opts: + - '--list-tags' + + # Whether to allow clients to use Jinja expressions in ansible-playbook + # options. Defaults to false. + jinja: true + + # The description displayed on the playbook web page. Defaults to null, which + # disables the playbook web page. + web_description: 'Run the main.yml playbook with a single command:' diff --git a/demo/ansible.cfg b/demo/playbooks/ansible.cfg diff --git a/demo/playbook.yml b/demo/playbooks/playbook.yml