I have achieved the same goal but a bit more securely, by forwarding the unix socket (but not a tcp socket). However it is a bit convoluted. The following example contains everything from starting an example container, forwarding the socket and tearing down everything afterwards.
In this scenario I have 3 "hosts" (in the sense of ansible hosts), localhost, the server and the container.
Docker-cli must be present on localhost.
My inventory looks like this, I've explicitly specified the forwarded docker socket for the connection to the container:
---
all:
hosts:
server:
container:
ansible_connection: docker
ansible_docker_extra_args: "-H unix:///tmp/remote_docker.sock"
vars:
docker_sock_path: "/tmp/remote_docker.sock"
The playbook consists of multiple plays, as it needs to apply to different hosts.
- At first an ssh-forwarding of the unix-socket is setup. This is done on localhost (however it's not a separate play, just delegated
- The docker container is started (this is done on the server).
The container is started with
interactive: true
so it stays alive without anything to do.
- Tasks are run in the Container
- The SSH Process is killed and the socket file is deleted (this is done on localhost)
---
- name: "Preparing SSH, starting the container"
hosts: server
tasks:
- name: Create SSH tunnel for the unix docker socket
command:
cmd: "ssh -fnNT -L {{ docker_sock_path }}:/var/run/docker.sock {{ ansible_user }}@{{ ansible_host }}"
delegate_to: localhost
- name: create connection container
community.docker.docker_container:
name: container
image: python:3.12-slim-bookworm
state: started
interactive: true
entrypoint: "/bin/sh"
- name: "Executing steps in docker container"
hosts: container
tasks:
[...]
- name: "Shutting down proxy container"
hosts: server
tasks:
- name: stop connection container
community.docker.docker_container:
name: server
state: stopped
- name: "Shutting down socket forwarding"
hosts: localhost
connection: local
tasks:
- name: get process ids
shell: ps xf
register: all_pid
- name: extract pid of our ssh process
ansible.builtin.set_fact:
ssh_pid: "{{ item | trim | split | first }}"
loop: "{{ all_pid.stdout_lines }}"
when: item is match("^.*{{docker_sock_path }}:/var/run/docker.sock.*$")
- name: kill ssh process
ansible.builtin.command:
cmd: "kill {{ ssh_pid }}"
- name: Remove socket file
ansible.builtin.file:
path: "{{ docker_sock_path }}"
state: absent