diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2026-08-04 12:00:39 +0300 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2026-08-04 12:00:39 +0300 |
| commit | d38fe54ab035f8e3248b8e20a58abd89861833bf (patch) | |
| tree | fcc78428a726799568a1ae2253a16590e883c816 /roles/jail | |
| parent | 017b29539749da3c62202879d757a45ffc71208c (diff) | |
Simplify jail configuration
Diffstat (limited to 'roles/jail')
| -rw-r--r-- | roles/jail/tasks/jail_launch.yml | 7 | ||||
| -rw-r--r-- | roles/jail/tasks/jail_setup.yml | 142 | ||||
| -rw-r--r-- | roles/jail/templates/jail_conf.j2 | 32 | ||||
| -rw-r--r-- | roles/jail/templates/usr_local_bin_jail_net.j2 | 127 |
4 files changed, 175 insertions, 133 deletions
diff --git a/roles/jail/tasks/jail_launch.yml b/roles/jail/tasks/jail_launch.yml index 31ccdf2..803290c 100644 --- a/roles/jail/tasks/jail_launch.yml +++ b/roles/jail/tasks/jail_launch.yml @@ -1,5 +1,6 @@ - name: Start services - service: - name: "{{ item }}" - state: started + shell: "service {{ item }} start || service {{ item }} status" loop: "{{ services | default([]) }}" + register: _svc_start + changed_when: "'already running' not in _svc_start.stdout and 'is running' not in _svc_start.stdout" + failed_when: "_svc_start.rc != 0 and 'already running' not in _svc_start.stdout and 'is running' not in _svc_start.stdout" diff --git a/roles/jail/tasks/jail_setup.yml b/roles/jail/tasks/jail_setup.yml index f50562a..2c8581d 100644 --- a/roles/jail/tasks/jail_setup.yml +++ b/roles/jail/tasks/jail_setup.yml @@ -1,4 +1,11 @@ # Host-side setup (runs on the jail host via SSH) + +# Normalize zfs and nullfs entries: expand short names to full paths +- name: "Normalize zfs and nullfs entries for {{ jail_name }}" + set_fact: + _zfs: "{{ zfs | default([]) | normalize_zfs }}" + _nullfs: "{{ nullfs | default([]) | normalize_nullfs }}" + - name: "Check if {{ jail_name }} container exists" shell: "zfs list -o name | grep -Fxq 'zroot/jails/containers/{{ jail_name }}'" failed_when: false @@ -13,102 +20,6 @@ when: jail_exists.rc == 0 delegate_to: "{{ jail_delegate_host }}" -# ZFS volume migration -- name: "Check ZFS volume state for {{ jail_name }}" - shell: | - if zfs list {{ item.name }} >/dev/null 2>&1; then - echo "exists" - elif [ -d "/usr/local/jails/containers/{{ jail_name }}{{ item.mountpoint }}" ] && \ - [ "$(ls -A /usr/local/jails/containers/{{ jail_name }}{{ item.mountpoint }})" ]; then - echo "migrate" - else - echo "empty" - fi - loop: "{{ zfs | default([]) }}" - loop_control: - label: "{{ item.name }}" - register: _zfs_volume_state - changed_when: false - delegate_to: "{{ jail_delegate_host }}" - -- name: "*** VOLUME MIGRATION REQUIRED: {{ jail_name }} ***" - pause: - prompt: | - - ════════════════════════════════════════════════════════════════ - VOLUME MIGRATION: {{ jail_name }} - ════════════════════════════════════════════════════════════════ - The following paths contain data that will be migrated - to new ZFS volumes: - {% for result in _zfs_volume_state.results | default([]) if result.stdout == 'migrate' %} - - {{ result.item.mountpoint }} → {{ result.item.name }} - {% endfor %} - - This will: - 1. Create ZFS volumes (staged) - 2. Stop the jail - 3. Copy existing data into the volumes - ════════════════════════════════════════════════════════════════ - - Press Enter to continue or Ctrl+C to abort - when: (_zfs_volume_state.results | default([]) | selectattr('stdout', 'equalto', 'migrate') | list | length) > 0 - -- name: "Create new ZFS volumes (staged) for {{ jail_name }}" - community.general.zfs: - name: "{{ item.item.name }}" - state: present - extra_zfs_properties: - mountpoint: "/usr/local/jails/volumes/.staging/{{ jail_name }}{{ item.item.mountpoint }}" - loop: "{{ _zfs_volume_state.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - when: item.stdout == "migrate" - delegate_to: "{{ jail_delegate_host }}" - -- name: "Create staging mount points for {{ jail_name }}" - file: - path: "/usr/local/jails/volumes/.staging/{{ jail_name }}{{ item.item.mountpoint }}" - state: directory - loop: "{{ _zfs_volume_state.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - when: item.stdout == "migrate" - delegate_to: "{{ jail_delegate_host }}" - -- name: "Mount staged volumes for {{ jail_name }}" - shell: "zfs mount {{ item.item.name }} || true" - loop: "{{ _zfs_volume_state.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - when: item.stdout == "migrate" - changed_when: false - delegate_to: "{{ jail_delegate_host }}" - -- name: "Stop {{ jail_name }} jail for volume migration" - shell: "service jail stop {{ jail_name }} || true" - when: (_zfs_volume_state.results | default([]) | selectattr('stdout', 'equalto', 'migrate') | list | length) > 0 - delegate_to: "{{ jail_delegate_host }}" - -- name: "Copy data into staged volumes for {{ jail_name }}" - shell: | - cp -a "/usr/local/jails/containers/{{ jail_name }}{{ item.item.mountpoint }}/." \ - "/usr/local/jails/volumes/.staging/{{ jail_name }}{{ item.item.mountpoint }}/" - loop: "{{ _zfs_volume_state.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - when: item.stdout == "migrate" - delegate_to: "{{ jail_delegate_host }}" - -- name: "Set final mountpoint on migrated volumes for {{ jail_name }}" - shell: | - zfs unmount {{ item.item.name }} || true - zfs set mountpoint="/usr/local/jails/containers/{{ jail_name }}{{ item.item.mountpoint }}" {{ item.item.name }} - loop: "{{ _zfs_volume_state.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - when: item.stdout == "migrate" - delegate_to: "{{ jail_delegate_host }}" - # Userland migration - name: "*** USERLAND MIGRATION REQUIRED: {{ jail_name }} ***" pause: @@ -168,28 +79,41 @@ community.general.zfs: name: "{{ item.name }}" state: present - extra_zfs_properties: - mountpoint: "/usr/local/jails/containers/{{ jail_name }}{{ item.mountpoint }}" - loop: "{{ zfs | default([]) }}" + loop: "{{ _zfs }}" loop_control: label: "{{ item.name }}" delegate_to: "{{ jail_delegate_host }}" -- name: "Create mount points for ZFS volumes in {{ jail_name }}" +- name: "Inherit mountpoint on ZFS volumes for {{ jail_name }}" + shell: | + source=$(zfs get -H -o source mountpoint {{ item.name }}) + if [ "$source" != "inherited" ] && [ "$source" != "received" ]; then + zfs unmount {{ item.name }} 2>/dev/null + zfs inherit mountpoint {{ item.name }} + echo "changed" + fi + loop: "{{ _zfs }}" + loop_control: + label: "{{ item.name }}" + register: _zfs_inherit + changed_when: "'changed' in _zfs_inherit.stdout" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Set ownership on ZFS volume host mountpoints for {{ jail_name }}" file: - path: "/usr/local/jails/containers/{{ jail_name }}{{ item.mountpoint }}" + path: "/usr/local/jails/volumes/{{ item.name | basename }}" state: directory owner: "{{ item.owner | default('root') }}" group: "{{ item.group | default('wheel') }}" mode: "{{ item.mode | default('0755') }}" - loop: "{{ zfs | default([]) }}" + loop: "{{ _zfs }}" loop_control: - label: "{{ item.mountpoint }}" + label: "{{ item.name }}" delegate_to: "{{ jail_delegate_host }}" - name: "Mount ZFS volumes for {{ jail_name }}" shell: "zfs mount {{ item.name }} || true" - loop: "{{ zfs | default([]) }}" + loop: "{{ _zfs }}" loop_control: label: "{{ item.name }}" changed_when: false @@ -210,7 +134,7 @@ file: path: "{{ item.src }}" state: directory - loop: "{{ nullfs | default([]) }}" + loop: "{{ _nullfs }}" loop_control: label: "{{ item.src }}" delegate_to: "{{ jail_delegate_host }}" @@ -222,7 +146,7 @@ owner: root group: wheel mode: "0755" - loop: "{{ nullfs | default([]) }}" + loop: "{{ _nullfs }}" loop_control: label: "{{ item.dst }}" delegate_to: "{{ jail_delegate_host }}" @@ -238,17 +162,15 @@ jail: name: "{{ jail_name }}" num: "{{ jail_num }}" - ip: "{{ jail_lan_cidr | ipv4_nth_cidr(jail_num | int + jail_lan_offset | int) }}" devfs_ruleset: "{{ devfs_ruleset | default(4) }}" options: "{{ jail_conf_options | default([]) }}" - default_route: "{{ not no_default_route | default(false) }}" exec_prestart: "{{ exec_prestart | default([]) }}" exec_start: "{{ exec_start | default([]) }}" exec_poststart: "{{ exec_poststart | default([]) }}" exec_prestop: "{{ exec_prestop | default([]) }}" exec_stop: "{{ exec_stop | default([]) }}" exec_poststop: "{{ exec_poststop | default([]) }}" - mounts: "{{ nullfs | default([]) }}" + nullfs: "{{ _nullfs }}" delegate_to: "{{ jail_delegate_host }}" - name: "Start {{ jail_name }} jail" @@ -289,7 +211,7 @@ changed_when: "'Number of packages to be installed' in pkg_result.stdout" - name: Install packages from poudriere - shell: "pkg install -y -r poudriere {{ pkg_custom | join(' ') }}" + shell: "timeout 120 pkg install -y -r poudriere {{ pkg_custom | join(' ') }}" when: pkg_custom is defined and pkg_custom | length > 0 register: pkg_custom_result changed_when: "'Installing' in pkg_custom_result.stdout" diff --git a/roles/jail/templates/jail_conf.j2 b/roles/jail/templates/jail_conf.j2 index 2a0ea27..aa88049 100644 --- a/roles/jail/templates/jail_conf.j2 +++ b/roles/jail/templates/jail_conf.j2 @@ -9,28 +9,19 @@ {% endfor %} devfs_ruleset = {{ jail.devfs_ruleset }}; - host.hostname = "{{ jail.name }}"; + host.hostname = "${name}"; path = "/usr/local/jails/containers/${name}"; exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; - # LAN epair - exec.prestart += "ifconfig epl{{ jail.num }}a destroy 2>/dev/null || true"; - exec.prestart += "ifconfig epair{{ jail.num }}000 create"; - exec.prestart += "ifconfig epair{{ jail.num }}000a name epl{{ jail.num }}a"; - exec.prestart += "ifconfig epair{{ jail.num }}000b name epl{{ jail.num }}b"; - exec.prestart += "ifconfig epl{{ jail.num }}b ether random"; - exec.prestart += "ifconfig brlan0 addm epl{{ jail.num }}a"; - exec.poststart += "ifconfig epl{{ jail.num }}b vnet ${name}"; - exec.poststart += "ifconfig epl{{ jail.num }}a up"; - exec.poststart += "jexec ${name} ifconfig epl{{ jail.num }}b up"; - exec.poststart += "jexec ${name} ifconfig epl{{ jail.num }}b {{ jail.ip }}"; - exec.poststart += "jexec ${name} route delete default || true"; -{% if jail.default_route %} - exec.poststart += "jexec ${name} route add default {{ ingress_ip }} || true"; -{% endif %} - exec.poststop += "ifconfig epl{{ jail.num }}a destroy 2>/dev/null || true"; + # Networking + exec.prestart += "jail_net prestart {{ jail.num }} ${name}"; + exec.poststart += "jail_net poststart {{ jail.num }} ${name}"; + exec.prestop += "jail_net prestop {{ jail.num }} ${name}"; + exec.poststop += "jail_net poststop {{ jail.num }} ${name}"; + + # Custom hooks {% for cmd in jail.exec_prestart %} exec.prestart += "{{ cmd }}"; {% endfor %} @@ -49,8 +40,9 @@ {% for cmd in jail.exec_poststop %} exec.poststop += "{{ cmd }}"; {% endfor %} -{% for mount in jail.mounts %} - exec.prestart += "mount -t nullfs {{ mount.src }} /usr/local/jails/containers/{{ jail.name }}{{ mount.dst }} || true"; - exec.poststop += "umount /usr/local/jails/containers/{{ jail.name }}{{ mount.dst }} || true"; + + # nullfs mounts +{% for mount in jail.nullfs %} + mount += "{{ mount.src }} ${path}{{ mount.dst }} nullfs {{ mount.mode | default('rw') }} 0 0"; {% endfor %} } diff --git a/roles/jail/templates/usr_local_bin_jail_net.j2 b/roles/jail/templates/usr_local_bin_jail_net.j2 new file mode 100644 index 0000000..e9873a3 --- /dev/null +++ b/roles/jail/templates/usr_local_bin_jail_net.j2 @@ -0,0 +1,127 @@ +#!/bin/sh +# jail_net - manage jail LAN networking lifecycle +# Usage: jail_net <prestart|poststart|prestop|poststop> <jail_num> <jail_name> + +set -e + +STAGE="$1" +NUM="$2" +NAME="$3" + +BRIDGE="brlan0" +LAN_BASE="{{ jail_lan_cidr | ipv4_host }}" +LAN_PREFIX="{{ jail_lan_cidr | ipv4_prefixlen }}" +LAN_OFFSET="{{ jail_lan_offset }}" +DEFAULT_ROUTE="{{ jail_lan_cidr | ipv4_nth(1 + jail_lan_offset | int) }}" + +# Compute jail IP from jail_num + offset +_nth=$(( LAN_OFFSET + NUM )) +_a=$(echo "$LAN_BASE" | cut -d. -f1) +_b=$(echo "$LAN_BASE" | cut -d. -f2) +_c=$(echo "$LAN_BASE" | cut -d. -f3) +_d=$(echo "$LAN_BASE" | cut -d. -f4) +_total=$(( (_a << 24) + (_b << 16) + (_c << 8) + _d + _nth )) +IP="$(( (_total >> 24) & 255 )).$(( (_total >> 16) & 255 )).$(( (_total >> 8) & 255 )).$(( _total & 255 ))/${LAN_PREFIX}" + +# --- Cleanup functions --- + +_cleanup_lan_prestart() { + echo "jail_net: prestart failed for ${NAME}, cleaning up LAN interfaces" >&2 + ifconfig epair${NUM}000a destroy 2>/dev/null || true + ifconfig epl${NUM}a destroy 2>/dev/null || true +} + +_cleanup_wan_prestart() { + echo "jail_net: prestart failed for ${NAME}, cleaning up WAN interfaces" >&2 + ifconfig epair${NUM}001a destroy 2>/dev/null || true + ifconfig epw${NUM}a destroy 2>/dev/null || true +} + +_cleanup_poststart() { + echo "jail_net: poststart failed for ${NAME}" >&2 + ifconfig epl${NUM}a destroy 2>/dev/null || true + if [ "$NUM" -eq 1 ]; then + ifconfig epw${NUM}a destroy 2>/dev/null || true + fi +} + +# --- Stage functions --- + +_prestart() { + trap '_cleanup_lan_prestart' EXIT + + ifconfig epl${NUM}a destroy 2>/dev/null || true + ifconfig epair${NUM}000 create + ifconfig epair${NUM}000a name epl${NUM}a + ifconfig epair${NUM}000b name epl${NUM}b + ifconfig epl${NUM}b ether random + ifconfig ${BRIDGE} addm epl${NUM}a + + if [ "$NUM" -eq 1 ]; then + trap '_cleanup_lan_prestart; _cleanup_wan_prestart' EXIT + ifconfig epw${NUM}a destroy 2>/dev/null || true + ifconfig epair${NUM}001 create + ifconfig epair${NUM}001a name epw${NUM}a + ifconfig epair${NUM}001b name epw${NUM}b +{% if is_prod %} + ifconfig brwan0 addm epw${NUM}a +{% else %} + ifconfig brlan0 addm epw${NUM}a +{% endif %} + fi + + trap - EXIT +} + +_poststart() { + trap '_cleanup_poststart' EXIT + + ifconfig epl${NUM}b vnet ${NAME} + ifconfig epl${NUM}a up + jexec ${NAME} ifconfig epl${NUM}b up + jexec ${NAME} ifconfig epl${NUM}b ${IP} + jexec ${NAME} route delete default || true + if [ "$NUM" -ne 1 ]; then + jexec ${NAME} route add default ${DEFAULT_ROUTE} || true + fi + + if [ "$NUM" -eq 1 ]; then + ifconfig epw${NUM}b vnet ${NAME} + ifconfig epw${NUM}a up + jexec ${NAME} ifconfig epw${NUM}b up +{% if is_prod %} + jexec ${NAME} dhclient epw${NUM}b + jexec ${NAME} route add 10.6.210.0/24 {{ lan_ipv4_gateway }} || true +{% else %} + jexec ${NAME} ifconfig epw${NUM}b inet {{ ingress_wan_static }} + jexec ${NAME} route add default {{ lan_ipv4_gateway }} +{% endif %} + fi + + trap - EXIT +} + +_prestop() { + : +} + +_poststop() { + set +e + ifconfig epl${NUM}a destroy 2>/dev/null + if [ "$NUM" -eq 1 ]; then + ifconfig epw${NUM}a destroy 2>/dev/null + fi +} + +# --- Dispatch --- + +case "$STAGE" in + prestart) _prestart ;; + poststart) _poststart ;; + prestop) _prestop ;; + poststop) _poststop ;; + *) + echo "Usage: jail_net <prestart|poststart|prestop|poststop> <num> <name>" >&2 + exit 1 + ;; +esac |
