Connection vbotka.freebsd.jailexec
The jailexec connection plugin connects to FreeBSD jails without requiring
SSH inside the jail. It uses the host-level jexec utility to execute
commands within target jails.
Hint
View the documentation from the command line:
shell> ansible-doc -t connection vbotka.freebsd.jailexec
Execution Architecture
When a task executes against a host configured with
vbotka.freebsd.jailexec, the plugin performs the following
pipeline:
Opens a transport connection (typically SSH) to the jail host specified by
ansible_jail_hostusingansible_ssh_user.Applies the privilege escalation specified by
ansible_jail_privilege_escalation(such assudoordoas), since invokingjexec(8)requires root privileges on the FreeBSD host.Spawns the command inside the jail identified by
ansible_jail_nameusingjexec [-u <jail_user>] <jid_or_name> <command>.Transfers files and temporary script modules to and from the jail.
Configuration Options
The four plugin options can be set via ansible_jail_*
variables. The variable ansible_ssh_user is an option for the
ansible.builtin.ssh connection plugin.
Variable |
Type |
Default |
Description |
|---|---|---|---|
|
string |
(required) |
Must be set to |
|
string |
(required) |
Hostname or IP of the FreeBSD host running the jail. |
|
string |
|
Target jail name or numeric JID (Jail ID). Overrides
|
|
string |
|
Host-level wrapper to invoke |
|
string |
|
Target user inside the jail (resolved via |
|
string |
(inherited) |
User account for SSH login to |
Note
Jail Identification: The plugin defaults
ansible_jail_nametoinventory_hostnameif unset. However,jexec(8)requires a numeric Jail ID (JID) or a native FreeBSD jail name. Frameworks likeiocagemaintain internal aliases that might not match the system jail name known to the FreeBSD kernel. When managingiocageenvironments, explicitly setansible_jail_name: iocage_jid.Host vs. Jail Escalation:
ansible_jail_privilege_escalation(doasorsudo) executes on the jail host, not inside the jail. Neither tool is needed inside the jail itself for privilege escalation; the plugin invokesjexec -U <ansible_jail_user>directly from the host as root. Set this tononeif connecting to the jail host directly asroot.Inherited SSH Options: Because
jailexecsubclasses the built-insshplugin, all standard SSH connection variables (such asansible_ssh_user,ansible_ssh_private_key_file,ansible_ssh_common_args, andansible_port) apply to the connection established withansible_jail_host.
Usage Examples
Given the running jails:
shell> ssh admin@iocage_06 iocage list
+-----+---------------+-------+--------------+--------------+
| JID | NAME | STATE | RELEASE | IP4 |
+=====+===============+=======+==============+==============+
| 1 | log-server-01 | up | 15.1-RELEASE | 172.16.99.10 |
+-----+---------------+-------+--------------+--------------+
| 2 | pkg-repo | up | 15.1-RELEASE | 172.16.99.23 |
+-----+---------------+-------+--------------+--------------+
| 3 | repos | up | 15.1-RELEASE | 172.16.99.21 |
+-----+---------------+-------+--------------+--------------+
Static Inventory
The configuration stored in hosts.ini:
log_server_01 ansible_jail_name=1
pkg_repo ansible_jail_name=2
repos ansible_jail_name=3
[vmm_iocage_06]
log_server_01
pkg_repo
repos
[vmm_iocage_06:vars]
ansible_connection=vbotka.freebsd.jailexec
ansible_ssh_user=admin
ansible_jail_host=iocage_06
ansible_jail_privilege_escalation=sudo
creates the following inventory:
shell> ansible-inventory -i hosts.ini --graph
@all:
|--@ungrouped:
|--@vmm_iocage_06:
| |--log_server_01
| |--pkg_repo
| |--repos
Note
jexec does not work with iocage jail names. If you use the
NAME instead of the JID in hosts.ini:
log_server_01 ansible_jail_name=log-server-01
pkg_repo ansible_jail_name=pkg-repo
repos ansible_jail_name=repos
the inventory remains valid, but the playbook below will fail.
Dynamic Inventory
When used with the inventory plugin vbotka.freebsd.iocage2, the
connection variables ansible_jail_host and ansible_jail_name
are dynamically composed in hosts.iocage2.yml:
plugin: vbotka.freebsd.iocage2
host: iocage_06
user: admin
get_properties: true
inventory_hostname_tag: alias
compose:
iocage_tags: dict(iocage_properties.notes | regex_findall('(\\w+)=([\\w\\-]+)'))
ansible_connection: "'vbotka.freebsd.jailexec'"
ansible_ssh_user: "'admin'"
ansible_jail_host: dict(iocage_properties.notes | regex_findall('(\\w+)=([\\w\\-]+)')).vmm | d('none')
ansible_jail_name: iocage_jid
ansible_jail_privilege_escalation: "'sudo'"
keyed_groups:
- prefix: vmm
key: iocage_tags.vmm
This creates the corresponding inventory:
shell> ansible-inventory -i hosts.iocage2.yml --graph
@all:
|--@ungrouped:
|--@vmm_iocage_06:
| |--pkg_repo
| |--log_server_01
| |--repos
Note
The iocage tag vmm is used to define ansible_jail_host, and
the tag alias is used to define inventory_hostname. For
example, inspecting the jail notes shows:
shell> ssh admin@iocage_06 iocage get notes log-server-01
vmm=iocage_06 class=log-server alias=log_server_01
Playbook
The playbook pb-test-connection.yml:
- name: Test connection and get hostname
hosts: all
tasks:
- command: hostname
register:
hostname: _task.result.stdout
- debug:
var: hostname
works with both inventory sources:
shell> ansible-playbook -i hosts.ini pb-test-connection.yml
shell> ansible-playbook -i hosts.iocage2.yml pb-test-connection.yml
Execution output:
PLAY [Test connection and get hostname] ****************************************
TASK [command] *****************************************************************
changed: [log_server_01]
changed: [pkg_repo]
changed: [repos]
TASK [debug] *******************************************************************
ok: [pkg_repo] =>
hostname: pkg-repo
ok: [log_server_01] =>
hostname: log-server-01
ok: [repos] =>
hostname: repos
PLAY RECAP *********************************************************************
log_server_01 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
pkg_repo : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
repos : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Host-Level Options
Because jexec is invoked via
ansible_jail_privilege_escalation, the account defined in
ansible_ssh_user requires elevated privileges on the jail host.
Depending on host management policies, configure sudo permissions in
/usr/local/etc/sudoers.d/admin:
Host managed by Ansible (recommended): If the FreeBSD host itself is already managed via Ansible, grant full passwordless privilege escalation:
admin ALL=(ALL) NOPASSWD: ALL
Host unmanaged / restricted: If you prefer least-privilege access restricted solely to jail execution, limit the rule to the
jexecbinary:admin ALL=(ALL) NOPASSWD: /usr/sbin/jexec