diff options
131 files changed, 1581 insertions, 1325 deletions
@@ -1,3 +1,4 @@ .envrc secrets.yml vm/ +__pycache__/ @@ -37,10 +37,10 @@ Use a `secrets.yml` file to provide secrets for Ansible to use. Required variabl Run the playbook or only some tagged sub-playbooks: ```shell -ansible-playbook -i inventory playbook.yml -t [tag1] [tag2] ... +ansible-playbook -i inventory.prod site.yml ``` -See `playbook.yml` for available tags. +See `site.yml` for available tags. ## Author diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..803d273 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +roles_path = roles +connection_plugins = connection_plugins diff --git a/connection_plugins/__pycache__/jailexec.cpython-314.pyc b/connection_plugins/__pycache__/jailexec.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..c1fe98b --- /dev/null +++ b/connection_plugins/__pycache__/jailexec.cpython-314.pyc diff --git a/connection_plugins/jailexec.py b/connection_plugins/jailexec.py new file mode 100644 index 0000000..4f4fab3 --- /dev/null +++ b/connection_plugins/jailexec.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +# Copyright (c) 2025 Christian Hofstede-Kuhn <christian@hofstede.it> +# SPDX-License-Identifier: BSD-2-Clause + +"""FreeBSD jail connection plugin for Ansible. + +Opens an SSH session to a FreeBSD jail host (inheriting Ansible's built-in +ssh connection plugin) and wraps every command with ``jexec`` so Ansible +operates *inside* the target jail without needing direct SSH access to it. +""" + +from __future__ import annotations + +import os +import posixpath +import re +import shlex + +import yaml +from ansible.errors import AnsibleConnectionFailure, AnsibleError +from ansible.plugins.connection import ssh as _ssh_module +from ansible.plugins.connection.ssh import Connection as SSHConnection +from ansible.utils.display import Display + +display = Display() + +# Static stub so ``ansible-doc -t connection jailexec`` can read the plugin +# (ansible-doc parses the source file as AST and only understands literal +# strings). The full option set is built below and assigned over the top via +# ``globals()`` -- the AST walker only inspects ``ast.Assign`` nodes with a +# simple Name target, so a plain function-call *expression* statement is +# invisible to it. At runtime, the plugin loader reads the merged version. +DOCUMENTATION = """ + name: jailexec + short_description: Execute tasks in FreeBSD jails via jexec over SSH + description: + - Opens an SSH session to a FreeBSD jail host and wraps every command + with jexec so Ansible runs inside the target jail without needing + direct SSH into the jail. + - Inherits all options from the built-in ssh connection plugin. + author: Christian Hofstede-Kuhn <christian@hofstede.it> + version_added: "1.1.0" + options: + jail_name: + description: Jail name. Defaults to the inventory hostname. + type: str + vars: + - name: ansible_jail_name + jail_host: + description: Hostname or IP of the FreeBSD host that runs the jail. + type: str + required: true + vars: + - name: ansible_jail_host + jail_root: + description: + - Absolute on-host filesystem path of the jail, used as the + base for put_file and fetch_file. + - If unset, the plugin probes the host with + ``jls -j <name> path`` on the first file transfer. + - Set this for nested or VNET jail setups where the probe + does not return the expected path. + type: str + version_added: "1.2.0" + vars: + - name: ansible_jail_root + jail_user: + description: User to run commands as inside the jail. + type: str + default: root + vars: + - name: ansible_jail_user + privilege_escalation: + description: Command used on the jail host to run jexec as root. + type: str + default: doas + choices: [doas, sudo, none] + vars: + - name: ansible_jail_privilege_escalation +""" + + +def _extend_with_ssh_options(doc): + """Merge SSH plugin options into our DOCUMENTATION at import time. + + Pulling options from the live SSH plugin (rather than freezing a copy) + keeps us in sync with whichever ansible-core version is installed; newer + ansible-core releases have added options (e.g. ``password_mechanism``) + that older snapshots didn't know about, and a frozen list would cause + ``get_option`` to return None and trigger type errors downstream. + """ + ssh_doc = yaml.safe_load(_ssh_module.DOCUMENTATION) or {} + our_doc = yaml.safe_load(doc) or {} + merged = dict(ssh_doc.get("options") or {}) + merged.update(our_doc.get("options") or {}) + our_doc["options"] = merged + return yaml.safe_dump(our_doc, sort_keys=False) + + +globals().update(DOCUMENTATION=_extend_with_ssh_options(DOCUMENTATION)) + + +MAX_JAIL_NAME_LENGTH = 255 +JAIL_NAME_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9._-]*$") +PRIVESC_CHOICES = ("doas", "sudo", "none") +# /tmp is on the remote jail host, not the Ansible controller. File names are +# randomized via ``os.urandom`` in ``put_file``, which defeats predictable-name +# attacks. Bandit's B108 check is about local-tmp usage and does not apply. +STAGING_DIR = "/tmp" # nosec B108 +STAGING_PREFIX = "ansible-jailexec-" + + +def validate_jail_name(name): + """Reject empty, overlong, or shell-unsafe jail names.""" + if not name or not str(name).strip(): + raise AnsibleConnectionFailure("Jail name cannot be empty") + name = str(name).strip() + if len(name) > MAX_JAIL_NAME_LENGTH: + raise AnsibleConnectionFailure( + f"Jail name too long (max {MAX_JAIL_NAME_LENGTH}): {name!r}" + ) + if not JAIL_NAME_RE.match(name): + raise AnsibleConnectionFailure( + f"Invalid jail name {name!r}: must start with a letter, digit or " + "underscore and contain only letters, digits, dots, underscores " + "or hyphens." + ) + return name + + +def ensure_no_traversal(path): + """Reject paths containing a ``..`` component (path traversal).""" + if path and ".." in path.replace("\\", "/").split("/"): + raise AnsibleError(f"Path contains '..' traversal: {path}") + + +def validate_jail_root(path): + """Normalize and validate a user-provided jail-root override. + + Must be a non-empty absolute POSIX path without any ``..`` components. + """ + path = (path or "").strip() + if not path: + raise AnsibleConnectionFailure("ansible_jail_root cannot be empty") + if not path.startswith("/"): + raise AnsibleConnectionFailure( + f"ansible_jail_root must be an absolute path, got {path!r}" + ) + ensure_no_traversal(path) + return posixpath.normpath(path) + + +def _decode(data): + """Return ``data`` as a str. Bytes are decoded leniently; None becomes ''.""" + if data is None: + return "" + if isinstance(data, bytes): + return data.decode("utf-8", "replace") + return data + + +def _shelljoin(*argv): + """Shell-join a command + args safely for transport over SSH.""" + return " ".join(shlex.quote(str(a)) for a in argv) + + +class Connection(SSHConnection): + """SSH to a jail host, run commands inside the jail via jexec.""" + + transport = "jailexec" + has_pipelining = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._jail_root = None + + # ---- options --------------------------------------------------------- + + @property + def jail_name(self): + name = self.get_option("jail_name") or self._play_context.remote_addr + return validate_jail_name(name) + + @property + def jail_user(self): + # Normalize None / blank / whitespace-only to "root". + return (self.get_option("jail_user") or "").strip() or "root" + + @property + def privesc(self): + # ansible-core >= 2.20 rejects off-``choices`` values at ``set_option`` + # time; older releases defer the check, so validate here too. + value = self.get_option("privilege_escalation") + if value not in PRIVESC_CHOICES: + raise AnsibleConnectionFailure( + f"Invalid privilege_escalation {value!r}: " + f"must be one of {', '.join(PRIVESC_CHOICES)}" + ) + if value == "none": + return "" + return value + + # ---- connect / lifecycle -------------------------------------------- + + def _connect(self): + if self._connected: + return self + + jail_host = (self.get_option("jail_host") or "").strip() + if not jail_host: + raise AnsibleConnectionFailure( + f"ansible_jail_host is not set for jail {self.jail_name!r}" + ) + # Redirect the inherited SSH plugin at the jail *host* instead of the + # jail (inventory) name. This is the one hook we need -- everything + # else comes from the SSH base class. + self.set_option("host", jail_host) + super()._connect() + # SSH's _connect is a no-op on _connected, but ConnectionBase's + # exec_command/put_file/fetch_file are wrapped with @ensure_connect, + # which re-enters self._connect() whenever _connected is False. We + # flip it here so the jail-root probe issued on first file op (via + # super().exec_command) doesn't recurse into us. + self._connected = True + return self + + def close(self): + self._jail_root = None + super().close() + + # ---- jail metadata --------------------------------------------------- + + def _resolve_jail_root(self): + """Look up and cache the on-host filesystem path of the jail. + + If ``ansible_jail_root`` is set, that value is used verbatim and no + SSH probe happens. Otherwise the path is resolved via + ``jls -j <name> path`` on the first file operation, then cached. + """ + if self._jail_root: + return self._jail_root + + override = self.get_option("jail_root") + if override: + self._jail_root = validate_jail_root(override) + display.vvv( + f"jailexec: jail {self.jail_name!r} root is {self._jail_root} " + "(from ansible_jail_root)", + host=self.jail_name, + ) + return self._jail_root + + name = self.jail_name + rc, stdout, stderr = super().exec_command( + _shelljoin(*(([self.privesc] if self.privesc else []) + ["jls", "-j", name, "path"])) + ) + if rc != 0: + msg = _decode(stderr).strip() or "jail not found or inaccessible" + raise AnsibleConnectionFailure(f"Cannot access jail {name!r}: {msg}") + lines = _decode(stdout).strip().splitlines() + root = lines[0].strip() if lines else "" + if not root: + raise AnsibleConnectionFailure( + f"Jail {name!r} returned no filesystem root (is it running?)" + ) + self._jail_root = root + display.vvv(f"jailexec: jail {name!r} root is {root}", host=name) + return root + + def _jail_path(self, path): + """Map a path inside the jail to its absolute path on the host.""" + ensure_no_traversal(path) + root = self._resolve_jail_root() + return posixpath.normpath(posixpath.join(root, path.lstrip("/"))) + + # ---- exec / transfer ------------------------------------------------- + + def exec_command(self, cmd, in_data=None, sudoable=True): + if not cmd or not str(cmd).strip(): + raise AnsibleError("Command cannot be empty") + + argv = [self.privesc, "jexec"] if self.privesc else ["jexec"] + if self.jail_user != "root": + argv += ["-u", self.jail_user] + argv += [self.jail_name, "/bin/sh", "-c", cmd] + wrapped = _shelljoin(*argv) + + display.vvv(f"jailexec: exec [{self.jail_name}]: {cmd}", host=self.jail_name) + return super().exec_command(wrapped, in_data=in_data, sudoable=sudoable) + + def put_file(self, in_path, out_path): + dest = self._jail_path(out_path) + dest_dir = posixpath.dirname(dest) + staged = posixpath.join(STAGING_DIR, f"{STAGING_PREFIX}{os.urandom(12).hex()}") + + display.vvv( + f"jailexec: put_file {in_path} -> jail:{out_path}", host=self.jail_name + ) + super().put_file(in_path, staged) + # Single round-trip: mkdir + move. Both go through privilege + # escalation because the destination lives inside the jail root, + # which is typically only writable by root on the host. + pe = shlex.quote(self.privesc) + " " if self.privesc else "" + move = ( + f"{pe}mkdir -p {shlex.quote(dest_dir)} && " + f"{pe}mv {shlex.quote(staged)} {shlex.quote(dest)}" + ) + rc, _, stderr = super().exec_command(move) + if rc != 0: + # Best-effort cleanup of the orphan staged file; ignore failures. + super().exec_command(f"rm -f {shlex.quote(staged)}") + raise AnsibleError( + f"put_file to jail:{out_path} failed: " + f"{_decode(stderr).strip() or 'unknown error'}" + ) + + def fetch_file(self, in_path, out_path): + src = self._jail_path(in_path) + display.vvv( + f"jailexec: fetch_file jail:{in_path} -> {out_path}", host=self.jail_name + ) + super().fetch_file(src, out_path) diff --git a/filter_plugins/ipv4.py b/filter_plugins/ipv4.py new file mode 100644 index 0000000..1c42e82 --- /dev/null +++ b/filter_plugins/ipv4.py @@ -0,0 +1,51 @@ +"""Custom Jinja2 filters for IPv4 address math.""" +import struct +import socket + + +def _ip_to_int(ip): + return struct.unpack("!I", socket.inet_aton(ip))[0] + + +def _int_to_ip(n): + return socket.inet_ntoa(struct.pack("!I", n)) + + +def ipv4_network(cidr): + """Return the network address of a CIDR. '10.0.20.2/24' -> '10.0.20.0'""" + ip, prefix = cidr.split("/") + mask = (0xFFFFFFFF << (32 - int(prefix))) & 0xFFFFFFFF + return _int_to_ip(_ip_to_int(ip) & mask) + + +def ipv4_nth(cidr, n): + """Return the nth host address in a network. '10.0.20.0/24' | ipv4_nth(101) -> '10.0.20.101'""" + network = ipv4_network(cidr) + return _int_to_ip(_ip_to_int(network) + int(n)) + + +def ipv4_nth_cidr(cidr, n): + """Return the nth host address with the original prefix. '10.0.20.0/24' | ipv4_nth_cidr(101) -> '10.0.20.101/24'""" + _, prefix = cidr.split("/") + return ipv4_nth(cidr, n) + "/" + prefix + + +def ipv4_host(cidr): + """Return just the host part of a CIDR. '10.0.20.2/24' -> '10.0.20.2'""" + return cidr.split("/")[0] + + +def ipv4_prefixlen(cidr): + """Return just the prefix length. '10.0.20.2/24' -> '24'""" + return cidr.split("/")[1] + + +class FilterModule(object): + def filters(self): + return { + "ipv4_network": ipv4_network, + "ipv4_nth": ipv4_nth, + "ipv4_nth_cidr": ipv4_nth_cidr, + "ipv4_host": ipv4_host, + "ipv4_prefixlen": ipv4_prefixlen, + } diff --git a/group_vars/jails.yml b/group_vars/jails.yml new file mode 100644 index 0000000..990469c --- /dev/null +++ b/group_vars/jails.yml @@ -0,0 +1,5 @@ +ansible_connection: jailexec +ansible_jail_name: "{{ inventory_hostname }}" +ansible_python_interpreter: /usr/local/bin/python3 +ansible_shell_executable: /bin/sh +ansible_jail_privilege_escalation: none diff --git a/host_vars/pursotin.yml b/host_vars/pursotin.yml deleted file mode 100644 index 0f7e173..0000000 --- a/host_vars/pursotin.yml +++ /dev/null @@ -1,7 +0,0 @@ -nic_lan: igc0 -nic_wan: igc1 -arch: amd64/amd64 -is_test_vm: false -jail_lan_prefix: "192.168.2" -jail_lan_prefixlen: "16" -jail_ip_offset: 0 diff --git a/host_vars/test_vm.yml b/host_vars/test_vm.yml deleted file mode 100644 index 821f137..0000000 --- a/host_vars/test_vm.yml +++ /dev/null @@ -1,12 +0,0 @@ -ansible_host: 10.0.20.2 -ansible_user: root -ansible_ssh_private_key_file: vm/id_ed25519 -ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -nic_lan: vtnet0 -nic_wan: vtnet1 -arch: arm64/aarch64 -is_test_vm: true -dns_nameserver: "10.0.20.1" -jail_lan_prefix: "10.0.20" -jail_lan_prefixlen: "24" -jail_ip_offset: 100 diff --git a/inventory b/inventory deleted file mode 100644 index 05bdd3a..0000000 --- a/inventory +++ /dev/null @@ -1,3 +0,0 @@ -[hosts] -pursotin -test_vm diff --git a/inventory/jails.py b/inventory/jails.py new file mode 100755 index 0000000..0458dc0 --- /dev/null +++ b/inventory/jails.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Dynamic inventory script that discovers jails from roles/jails/.""" +import json +import os +import re + +def main(): + # Resolve to project root (one level up from inventory/) + script_dir = os.path.dirname(os.path.realpath(__file__)) + project_root = os.path.dirname(script_dir) + jails_dir = os.path.join(project_root, "roles", "jails") + + hosts = [] + if os.path.isdir(jails_dir): + for entry in sorted(os.listdir(jails_dir)): + path = os.path.join(jails_dir, entry) + if os.path.isdir(path) and re.match(r"^\d+_", entry): + name = re.sub(r"^\d+_", "", entry) + hosts.append(name) + + inventory = { + "jails": { + "hosts": hosts, + }, + "_meta": { + "hostvars": {}, + }, + } + + print(json.dumps(inventory)) + +if __name__ == "__main__": + main() diff --git a/inventory/prod/hosts.yml b/inventory/prod/hosts.yml new file mode 100644 index 0000000..eff5ecb --- /dev/null +++ b/inventory/prod/hosts.yml @@ -0,0 +1,22 @@ +all: + children: + servers: + hosts: + pursotin: + host_roles: + - host + - host_prod + nic_lan: igc0 + nic_wan: igc1 + arch: amd64/amd64 + lan_ipv4_gateway: "192.168.0.1" + lan_ipv4_cidr: "192.168.0.10/16" + is_prod: true + jails: + vars: + ansible_jail_host: pursotin + jail_delegate_host: pursotin + jail_lan_cidr: "192.168.2.0/16" + jail_lan_offset: 0 + ssl_enabled: true + is_prod: true diff --git a/inventory/prod/jails.py b/inventory/prod/jails.py new file mode 120000 index 0000000..d036389 --- /dev/null +++ b/inventory/prod/jails.py @@ -0,0 +1 @@ +../jails.py
\ No newline at end of file diff --git a/inventory/test/hosts.yml b/inventory/test/hosts.yml new file mode 100644 index 0000000..fdf982a --- /dev/null +++ b/inventory/test/hosts.yml @@ -0,0 +1,34 @@ +all: + children: + servers: + hosts: + test_vm: + ansible_host: 10.0.20.2 + ansible_user: root + ansible_ssh_private_key_file: vm/id_ed25519 + ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityAgent=none" + host_roles: + - host + - host_test + nic_lan: vtnet0 + nic_wan: vtnet0 + arch: arm64/aarch64 + dns_nameserver: "10.0.20.1" + lan_ipv4_gateway: "10.0.20.1" + lan_ipv4_cidr: "10.0.20.2/24" + is_prod: false + jails: + vars: + ansible_host: 10.0.20.2 + ansible_jail_host: 10.0.20.2 + ansible_user: root + ansible_ssh_private_key_file: vm/id_ed25519 + ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityAgent=none" + jail_delegate_host: test_vm + jail_lan_cidr: "10.0.20.0/24" + jail_lan_offset: 100 + ssl_enabled: false + is_prod: false + ingress_wan_static: "10.0.20.3/24" + dns_nameserver: "10.0.20.1" + lan_ipv4_gateway: "10.0.20.1" diff --git a/inventory/test/jails.py b/inventory/test/jails.py new file mode 120000 index 0000000..d036389 --- /dev/null +++ b/inventory/test/jails.py @@ -0,0 +1 @@ +../jails.py
\ No newline at end of file diff --git a/playbook.yml b/playbook.yml deleted file mode 100644 index 9269a44..0000000 --- a/playbook.yml +++ /dev/null @@ -1,109 +0,0 @@ ---- -- name: Configure my FreeBSD home server - hosts: hosts - - pre_tasks: - - name: Fail if secrets.yml wasn't loaded and smtp_user is missing - assert: - that: - - smtp_user != '' - fail_msg: > - Required variables are missing. Did you forget to create a secrets.yml file? - vars: - ansible_assert_no_log: true - - vars_files: - - secrets.yml - vars: - lan_ipv4_cidr: 192.168.0.10/16 - lan_ipv4_network: 192.168.0.0/16 - lan_ipv4_gateway: 192.168.0.1 - lan_search_domain: local.jan.systems - jail_userland_14_3: 14.3-RELEASE - jail_userland_15_0: 15.0-RELEASE - jails: - - { userland: "{{ jail_userland_14_3 }}", num: 1, name: ingress } - - { userland: "{{ jail_userland_14_3 }}", num: 2, name: postgres } - - { userland: "{{ jail_userland_14_3 }}", num: 3, name: irc_thelounge } - - { userland: "{{ jail_userland_14_3 }}", num: 4, name: taulubot } - - { userland: "{{ jail_userland_14_3 }}", num: 5, name: homepage } - - { userland: "{{ jail_userland_14_3 }}", num: 6, name: hommabot } - - { userland: "{{ jail_userland_14_3 }}", num: 7, name: aggro } - - { userland: "{{ jail_userland_14_3 }}", num: 8, name: diddle } - - { userland: "{{ jail_userland_15_0 }}", num: 9, name: redis } - - { userland: "{{ jail_userland_15_0 }}", num: 10, name: samba } - - { userland: "{{ jail_userland_14_3 }}", num: 11, name: spliit } - - { userland: "{{ jail_userland_15_0 }}", num: 12, name: goaccess } - - { userland: "{{ jail_userland_15_0 }}", num: 13, name: plex } - - { userland: "{{ jail_userland_14_3 }}", num: 14, name: freshrss } - - { userland: "{{ jail_userland_14_3 }}", num: 15, name: paste } - - { userland: "{{ jail_userland_15_0 }}", num: 16, name: dl } - - { userland: "{{ jail_userland_15_0 }}", num: 17, name: syncthing } - - { userland: "{{ jail_userland_15_0 }}", num: 18, name: komga } - - { userland: "{{ jail_userland_15_0 }}", num: 19, name: leolalla_fi } - ingress_ip: "{{ jail_lan_prefix }}.{{ 1 + jail_ip_offset }}" - ingress_routes: - - { host: jan.systems, jail: homepage } - - { host: jantuomi.fi, redirect: jan.systems } - - { host: aggro.jan.systems, jail: aggro } - - { host: diddle.jan.systems, jail: diddle } - - { host: spliit.jan.systems, jail: spliit } - - { host: freshrss.jan.systems, jail: freshrss } - - { host: irc.jan.systems, jail: irc_thelounge } - - { host: paste.jan.systems, jail: paste } - - { host: leolalla.fi, jail: leolalla_fi } - - { host: immich.jan.systems, ip: 192.168.3.3, port: 2283 } # vms aren't configured in ansible - - { host: plex.jan.systems, jail: plex, port: 32400 } - - { host: komga.jan.systems, jail: komga, port: 25600 } - cert_domains: - - "jan.systems" - - "*.jan.systems" - - "jantuomi.fi" - - "*.jantuomi.fi" - - "leolalla.fi" - - "*.leolalla.fi" - cert_name: "{{ cert_domains[0] }}" - contact_email: jan@jantuomi.fi - - tasks: - - name: Run general tasks - tags: [general] - import_tasks: tasks/general.yml - - - name: Run network tasks - tags: [network] - import_tasks: tasks/network.yml - - - name: Run email tasks - tags: [email] - import_tasks: tasks/email.yml - when: not is_test_vm - - - name: Run ZFS tasks - tags: [zfs] - import_tasks: tasks/zfs.yml - when: not is_test_vm - - - name: Run common jails tasks - tags: [jails] - import_tasks: tasks/jails.yml - - - name: Run ingress jail tasks - tags: [jail_ingress] - import_tasks: tasks/jail_ingress.yml - - - name: Run postgres jail tasks - tags: [jail_postgres] - import_tasks: tasks/jail_postgres.yml - - - name: Run diddle jail tasks - tags: [jail_diddle] - import_tasks: tasks/jail_diddle.yml - - - name: Run hommabot jail tasks - tags: [jail_hommabot] - import_tasks: tasks/jail_hommabot.yml - - - name: Run homepage jail tasks - tags: [jail_homepage] - import_tasks: tasks/jail_homepage.yml diff --git a/principles.md b/principles.md deleted file mode 100644 index 245846c..0000000 --- a/principles.md +++ /dev/null @@ -1,7 +0,0 @@ -# Migration principles - -1. Jail definitions should be localized, e.g. jails/ingress.yml. A jail definition should define zfs mounts, nullfs mounts, files to install, networking scripts (pre_start etc) to insert into jail.conf. -2. Jails should be defined as hosts in the inventory file. To reach them, ansible should use a ssh conn to the host and then `jexec $jailname`. -3. Each structure in the jail definition yml (e.g. `pkg: ["nginx"]`) should be handled by an ansible role that activates when a relevant structure is defined in the jail definition. -4. Both the host (pursotin) and the jails inside it should be defined in this project, so ansible should be configured to use correct hosts and correct connection methods depending on host. -5. The main playbooks should be very light and just dispatch. diff --git a/reset-init-up.sh b/reset-init-up.sh new file mode 100755 index 0000000..d16485c --- /dev/null +++ b/reset-init-up.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -euxo pipefail + +./vm.sh reset +./vm.sh init +./vm.sh up + diff --git a/templates/root_bashrc b/roles/host/files/root_bashrc index f580db6..f580db6 100644 --- a/templates/root_bashrc +++ b/roles/host/files/root_bashrc diff --git a/roles/host/handlers/main.yml b/roles/host/handlers/main.yml new file mode 100644 index 0000000..5d0a990 --- /dev/null +++ b/roles/host/handlers/main.yml @@ -0,0 +1,9 @@ +- name: Restart sshd + service: + name: sshd + state: restarted + +- name: Restart cron + service: + name: cron + state: restarted diff --git a/roles/host/tasks/main.yml b/roles/host/tasks/main.yml new file mode 100644 index 0000000..ed9b476 --- /dev/null +++ b/roles/host/tasks/main.yml @@ -0,0 +1,217 @@ +- name: Disable resolvconf + copy: + content: "resolvconf=NO\n" + dest: /etc/resolvconf.conf + owner: root + group: wheel + mode: "0644" + +- name: Set up resolv.conf + template: + src: etc_resolv.conf.j2 + dest: /etc/resolv.conf + owner: root + group: wheel + mode: "0644" + +- name: Install packages + package: + name: "{{ item }}" + state: present + loop: + - rsync + - dma + - jq + - curl + - bash + - python + - py311-pip + - fastfetch + +- name: Set up sshd + template: + src: etc_ssh_sshd_config.j2 + dest: /etc/ssh/sshd_config + owner: root + group: wheel + mode: "0644" + notify: Restart sshd + +- name: Start sshd + service: + name: sshd + state: started + +- name: Deploy logto script + template: + src: usr_local_bin_logto.sh.j2 + dest: /usr/local/bin/logto + owner: root + group: wheel + mode: "0755" + +- name: Check if pylogsentinel is installed + shell: pip show pylogsentinel + register: pylogsentinel_check + failed_when: false + changed_when: false + +- name: Install pylogsentinel + shell: pip install pylogsentinel==0.3.0 --force --no-input + when: pylogsentinel_check.rc != 0 + +- name: Deploy pylogsentinel.conf + template: + src: usr_local_etc_pylogsentinel.conf.j2 + dest: /usr/local/etc/pylogsentinel.conf + owner: root + group: wheel + mode: "0644" + +- name: Deploy pylogsentinel-batch-email.sh + template: + src: usr_local_bin_pylogsentinel-batch-email.sh.j2 + dest: /usr/local/bin/pylogsentinel-batch-email.sh + owner: root + group: wheel + mode: "0755" + +- name: Start syslogd + service: + name: syslogd + state: started + +- name: Start auditd + service: + name: auditd + state: started + +- name: Set up periodic.conf + template: + src: etc_periodic.conf.j2 + dest: /etc/periodic.conf + owner: root + group: wheel + mode: "0644" + +- name: Set up crontab + template: + src: etc_crontab.j2 + dest: /etc/crontab + owner: root + group: wheel + mode: "0644" + notify: Restart cron + +- name: Install .bashrc + copy: + src: root_bashrc + dest: /root/.bashrc + owner: root + group: wheel + mode: "0644" + +# Jail infrastructure +- name: Discover jail directories + find: + paths: "{{ playbook_dir }}/roles/jails" + patterns: "main.yml" + recurse: true + delegate_to: localhost + register: _jail_specs + +- name: Load jail definitions + set_fact: + jail_defs: "{{ jail_defs | default([]) + [_content | combine({'num': _num, 'name': _name})] }}" + vars: + _content: "{{ lookup('file', item.path) | from_yaml }}" + _num: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') | split('_') | first | int }}" + _name: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') | regex_replace('^[0-9]+_', '') }}" + loop: "{{ _jail_specs.files | sort(attribute='path') }}" + loop_control: + label: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') }}" + when: "'/defaults/' in item.path" + +- name: Collect unique userlands + set_fact: + jail_userlands: "{{ jail_defs | map(attribute='userland') | unique | list }}" + +- name: Create base ZFS datasets + community.general.zfs: + name: "{{ item.name }}" + state: present + extra_zfs_properties: + mountpoint: "{{ item.mountpoint | default(omit) }}" + loop: + - { name: "zroot/jails", mountpoint: "/usr/local/jails" } + - { name: "zroot/jails/media" } + - { name: "zroot/jails/templates" } + - { name: "zroot/jails/containers" } + - { name: "zroot/jails/volumes", mountpoint: "none" } + - { name: "zroot/jails/volumes/goaccess_www", mountpoint: "/usr/local/jails/volumes/goaccess_www" } + - { name: "zroot/jails/volumes/postgres_data", mountpoint: "/usr/local/jails/containers/postgres/var/db/postgres" } + - { name: "zroot/jails/volumes/irc_thelounge_logs", mountpoint: "/usr/local/jails/containers/irc_thelounge/root/.thelounge/logs" } + - { name: "zroot/jails/volumes/irc_thelounge_uploads", mountpoint: "/usr/local/jails/containers/irc_thelounge/root/.thelounge/uploads" } + - { name: "zroot/jails/volumes/komga_data", mountpoint: "/usr/local/jails/containers/komga/root/.komga" } + - { name: "zroot/storage", mountpoint: "/usr/local/jails/volumes/storage" } + loop_control: + label: "{{ item.name }}" + +- name: Create storage group + group: + name: storage + gid: 1001 + +- name: Create storage user + user: + name: storage + uid: 1001 + group: storage + home: /nonexistent + shell: /usr/sbin/nologin + create_home: false + +- name: Set storage volume permissions + file: + path: /usr/local/jails/volumes/storage + state: directory + owner: "1001" + group: "1001" + mode: "0777" + +- name: Create storage directories + file: + path: "/usr/local/jails/volumes/storage/{{ item.name }}" + state: directory + owner: "1001" + group: "1001" + mode: "{{ item.mode }}" + loop: + - { name: media, mode: "0777" } + - { name: docs, mode: "0775" } + - { name: downloads, mode: "0777" } + - { name: projects-ableton, mode: "0755" } + - { name: vault, mode: "0755" } + - { name: jan-systems-2025-content, mode: "0755" } + +- name: Set up userland templates + include_tasks: userland.yml + loop: "{{ jail_userlands }}" + loop_control: + loop_var: userland + +- name: Deploy /etc/jail.conf + template: + src: jail.conf.j2 + dest: /etc/jail.conf + owner: root + group: wheel + mode: "0644" + +- name: Deploy devfs.rules + template: + src: devfs.rules.j2 + dest: /etc/devfs.rules + owner: root + group: wheel + mode: "0644" diff --git a/roles/host/tasks/userland.yml b/roles/host/tasks/userland.yml new file mode 100644 index 0000000..59de0dd --- /dev/null +++ b/roles/host/tasks/userland.yml @@ -0,0 +1,59 @@ +- name: "Create template dataset for {{ userland }}" + community.general.zfs: + name: "zroot/jails/templates/{{ userland }}" + state: present + +- name: "Check if {{ userland }} snapshot exists" + shell: zfs list -t snapshot -o name | grep -Fxq "zroot/jails/templates/{{ userland }}@base" + failed_when: false + changed_when: false + register: userland_snap + +- name: "Set up {{ userland }} template" + when: userland_snap.rc != 0 + block: + - name: Download userland + get_url: + url: "https://download.freebsd.org/ftp/releases/{{ arch }}/{{ userland }}/base.txz" + dest: "/usr/local/jails/media/{{ userland }}-base.txz" + owner: root + group: wheel + mode: "0644" + + - name: Unarchive userland + shell: "chflags -R noschg /usr/local/jails/templates/{{ userland }} 2>/dev/null; tar -xzf /usr/local/jails/media/{{ userland }}-base.txz -C /usr/local/jails/templates/{{ userland }}" + + - name: Copy localtime + copy: + remote_src: true + src: /etc/localtime + dest: "/usr/local/jails/templates/{{ userland }}/etc/localtime" + + - name: Deploy resolv.conf + template: + src: etc_resolv.conf.j2 + dest: "/usr/local/jails/templates/{{ userland }}/etc/resolv.conf" + owner: root + group: wheel + mode: "0644" + + - name: Disable resolvconf + lineinfile: + path: "/usr/local/jails/templates/{{ userland }}/etc/resolvconf.conf" + line: "resolvconf=NO" + create: true + + - name: Update userland + shell: "freebsd-update -b /usr/local/jails/templates/{{ userland }}/ fetch install" + + - name: Disable syslogd + lineinfile: + path: "/usr/local/jails/templates/{{ userland }}/etc/rc.conf" + line: 'syslogd_enable="NO"' + create: true + + - name: Install packages in template + shell: "ASSUME_ALWAYS_YES=yes pkg -c /usr/local/jails/templates/{{ userland }} install -y python3 bash" + + - name: Create snapshot + shell: "zfs snapshot zroot/jails/templates/{{ userland }}@base" diff --git a/templates/etc_devfs.rules.j2 b/roles/host/templates/devfs.rules.j2 index 531b767..9fc0bd1 100644 --- a/templates/etc_devfs.rules.j2 +++ b/roles/host/templates/devfs.rules.j2 @@ -1,19 +1,6 @@ -# "as-is" to the devfs(8) command with the exception that -# any references to other rulesets will be expanded first. These -# references must include a dollar sign '$' in front of the -# name to be expanded properly. -# -# - -# Very basic and secure ruleset: Hide everything. -# Used as a basis for other rules. -# [devfsrules_hide_all=1] add hide -# Basic devices typically necessary. -# Requires: devfsrules_hide_all -# [devfsrules_unhide_basic=2] add path null unhide add path zero unhide @@ -21,9 +8,6 @@ add path crypto unhide add path random unhide add path urandom unhide -# Devices typically needed to support logged-in users. -# Requires: devfsrules_hide_all -# [devfsrules_unhide_login=3] add path 'ptyp*' unhide add path 'ptyq*' unhide @@ -76,16 +60,10 @@ add include $devfsrules_unhide_basic add include $devfsrules_unhide_login [devfsrules_jail_postgres=5] -add include $devfsrules_hide_all -add include $devfsrules_unhide_basic -add include $devfsrules_unhide_login add include $devfsrules_jail add path 'bpf*' unhide -[devfsrules_jail_ingress=5] -add include $devfsrules_hide_all -add include $devfsrules_unhide_basic -add include $devfsrules_unhide_login +[devfsrules_jail_ingress=6] add include $devfsrules_jail add path 'bpf*' unhide add path 'pf*' unhide diff --git a/templates/etc_crontab.j2 b/roles/host/templates/etc_crontab.j2 index de1f3c6..de1f3c6 100644 --- a/templates/etc_crontab.j2 +++ b/roles/host/templates/etc_crontab.j2 diff --git a/templates/etc_periodic.conf.j2 b/roles/host/templates/etc_periodic.conf.j2 index 176b55d..176b55d 100644 --- a/templates/etc_periodic.conf.j2 +++ b/roles/host/templates/etc_periodic.conf.j2 diff --git a/templates/etc_rc.conf.j2 b/roles/host/templates/etc_rc.conf.j2 index 5380056..7cdf65e 100644 --- a/templates/etc_rc.conf.j2 +++ b/roles/host/templates/etc_rc.conf.j2 @@ -5,28 +5,28 @@ ntpd_enable="YES" ntpd_sync_on_start="YES" powerd_enable="YES" moused_nondefault_enable="NO" -# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable dumpdev="AUTO" zfs_enable="YES" -{% if is_test_vm %} -defaultrouter="10.0.20.1" -{% else %} defaultrouter="{{ lan_ipv4_gateway }}" -{% endif %} cloned_interfaces="bridge0 bridge1" +{% if is_prod %} ifconfig_{{ nic_lan }}_name="lan0" ifconfig_{{ nic_wan }}_name="wan0" ifconfig_bridge0_name="brlan0" ifconfig_bridge1_name="brwan0" -{% if is_test_vm %} -ifconfig_lan0="inet 10.0.20.2/24" -{% else %} ifconfig_lan0="inet {{ lan_ipv4_cidr }}" -{% endif %} ifconfig_lan0_ipv6="inet6 accept_rtadv" ifconfig_wan0="up" ifconfig_brlan0="addm lan0 up" ifconfig_brwan0="addm wan0 up" +{% else %} +ifconfig_bridge0_name="brlan0" +ifconfig_bridge1_name="brwan0" +ifconfig_vtnet0="inet {{ lan_ipv4_cidr }}" +ifconfig_vtnet0_ipv6="inet6 accept_rtadv" +ifconfig_brlan0="addm vtnet0 up" +ifconfig_brwan0="up" +{% endif %} zpool_gpt_labels_enable="YES" smartd_enable="YES" pf_enable="YES" @@ -39,10 +39,12 @@ clear_tmp_enable="YES" cleanvar_enable="YES" auditd_enable="YES" devd_enable="YES" -{% if not is_test_vm %} +{% if is_prod %} vm_enable="YES" vm_dir="zfs:zroot/vm" vm_list="alpine0" vm_delay="5" -kld_list="if_wg i915kms" +kld_list="pf if_wg i915kms" +{% else %} +kld_list="pf" {% endif %} diff --git a/templates/etc_resolv.conf.j2 b/roles/host/templates/etc_resolv.conf.j2 index b382ecf..b382ecf 100644 --- a/templates/etc_resolv.conf.j2 +++ b/roles/host/templates/etc_resolv.conf.j2 diff --git a/templates/etc_ssh_sshd_config.j2 b/roles/host/templates/etc_ssh_sshd_config.j2 index 06179ce..06179ce 100644 --- a/templates/etc_ssh_sshd_config.j2 +++ b/roles/host/templates/etc_ssh_sshd_config.j2 diff --git a/roles/host/templates/jail.conf.j2 b/roles/host/templates/jail.conf.j2 new file mode 100644 index 0000000..81bfbad --- /dev/null +++ b/roles/host/templates/jail.conf.j2 @@ -0,0 +1 @@ +.include "/etc/jail.conf.d/*.conf"; diff --git a/templates/usr_local_bin_logto.sh.j2 b/roles/host/templates/usr_local_bin_logto.sh.j2 index 9bb5aa1..9bb5aa1 100644 --- a/templates/usr_local_bin_logto.sh.j2 +++ b/roles/host/templates/usr_local_bin_logto.sh.j2 diff --git a/templates/usr_local_bin_pylogsentinel-batch-email.sh.j2 b/roles/host/templates/usr_local_bin_pylogsentinel-batch-email.sh.j2 index d28efd9..d28efd9 100644 --- a/templates/usr_local_bin_pylogsentinel-batch-email.sh.j2 +++ b/roles/host/templates/usr_local_bin_pylogsentinel-batch-email.sh.j2 diff --git a/templates/usr_local_etc_pylogsentinel.conf.j2 b/roles/host/templates/usr_local_etc_pylogsentinel.conf.j2 index cb80742..cb80742 100644 --- a/templates/usr_local_etc_pylogsentinel.conf.j2 +++ b/roles/host/templates/usr_local_etc_pylogsentinel.conf.j2 diff --git a/templates/usr_local_etc_rc.d_zpool_gpt_labels.j2 b/roles/host/templates/usr_local_etc_rc.d_zpool_gpt_labels.j2 index 36bc715..36bc715 100644 --- a/templates/usr_local_etc_rc.d_zpool_gpt_labels.j2 +++ b/roles/host/templates/usr_local_etc_rc.d_zpool_gpt_labels.j2 diff --git a/templates/usr_local_etc_smartd.conf.j2 b/roles/host/templates/usr_local_etc_smartd.conf.j2 index bdcbfd0..bdcbfd0 100644 --- a/templates/usr_local_etc_smartd.conf.j2 +++ b/roles/host/templates/usr_local_etc_smartd.conf.j2 diff --git a/roles/host_prod/handlers/main.yml b/roles/host_prod/handlers/main.yml new file mode 100644 index 0000000..76ec23e --- /dev/null +++ b/roles/host_prod/handlers/main.yml @@ -0,0 +1,13 @@ +- name: Apply rc.conf + shell: service kld start; service netif restart && service routing restart + +- name: Mount filesystems + shell: mount -a + +- name: Restart smartd + service: + name: smartd + state: restarted + +- name: Run newaliases + shell: newaliases diff --git a/roles/host_prod/tasks/main.yml b/roles/host_prod/tasks/main.yml new file mode 100644 index 0000000..ec635ce --- /dev/null +++ b/roles/host_prod/tasks/main.yml @@ -0,0 +1,153 @@ +- name: Set up /etc/rc.conf + template: + src: "{{ playbook_dir }}/roles/host/templates/etc_rc.conf.j2" + dest: /etc/rc.conf + owner: root + group: wheel + mode: "0644" + vars: + is_prod: true + notify: Apply rc.conf + +- name: Flush handlers + meta: flush_handlers + +- name: Install hardware packages + package: + name: "{{ item }}" + state: present + loop: + - vm-bhyve + - drm-kmod + +- name: Set up fstab + template: + src: etc_fstab.j2 + dest: /etc/fstab + owner: root + group: wheel + mode: "0644" + notify: Mount filesystems + +- name: Set up bhyve + block: + - name: Create vm dataset + community.general.zfs: + name: zroot/vm + state: present + + - name: Check if vm-bhyve is initialized + stat: + path: /zroot/vm/.config + register: vm_init_check + + - name: Run vm init + shell: vm init + when: not vm_init_check.stat.exists + +- name: Copy backup SSH private key + copy: + src: "{{ backup_ssh_privkey_file }}" + dest: /root/.ssh/backup + owner: root + group: wheel + mode: "0600" + +- name: Copy backup SSH public key + copy: + src: "{{ backup_ssh_pubkey_file }}" + dest: /root/.ssh/backup.pub + owner: root + group: wheel + mode: "0644" + +- name: Copy SSH config + template: + src: root_ssh_config.j2 + dest: /root/.ssh/config + owner: root + group: wheel + mode: "0644" + +- name: Copy backup script + template: + src: usr_local_bin_backup.sh.j2 + dest: /usr/local/bin/backup + owner: root + group: wheel + mode: "0755" + +- name: Create dma config directory + file: + path: /etc/dma + state: directory + owner: root + group: wheel + mode: "0755" + +- name: Configure dma.conf + template: + src: etc_dma_dma.conf.j2 + dest: /etc/dma/dma.conf + owner: root + group: wheel + mode: "0644" + +- name: Configure dma auth.conf + template: + src: etc_dma_auth.conf.j2 + dest: /etc/dma/auth.conf + owner: root + group: mail + mode: "0640" + +- name: Configure mail aliases + template: + src: etc_aliases.j2 + dest: /etc/aliases + owner: root + group: wheel + mode: "0644" + notify: Run newaliases + +- name: Install smartmontools + package: + name: smartmontools + state: present + +- name: Deploy smartd.conf + template: + src: "{{ playbook_dir }}/roles/host/templates/usr_local_etc_smartd.conf.j2" + dest: /usr/local/etc/smartd.conf + owner: root + group: wheel + mode: "0644" + notify: Restart smartd + +- name: Start smartd + service: + name: smartd + state: started + +- name: Deploy zpool_gpt_labels rc.d script + template: + src: "{{ playbook_dir }}/roles/host/templates/usr_local_etc_rc.d_zpool_gpt_labels.j2" + dest: /usr/local/etc/rc.d/zpool_gpt_labels + owner: root + group: wheel + mode: "0755" + register: zpool_gpt_labels_script + +- name: Run zpool_gpt_labels + service: + name: zpool_gpt_labels + state: started + when: zpool_gpt_labels_script is changed + +- name: Deploy safepf script + copy: + src: "{{ playbook_dir }}/roles/host_prod/templates/usr_local_bin_safepf.sh" + dest: /usr/local/bin/safepf + owner: root + group: wheel + mode: "0755" diff --git a/templates/etc_aliases.j2 b/roles/host_prod/templates/etc_aliases.j2 index 4fe3e7f..4fe3e7f 100644 --- a/templates/etc_aliases.j2 +++ b/roles/host_prod/templates/etc_aliases.j2 diff --git a/templates/etc_dma_auth.conf.j2 b/roles/host_prod/templates/etc_dma_auth.conf.j2 index a1000ea..a1000ea 100644 --- a/templates/etc_dma_auth.conf.j2 +++ b/roles/host_prod/templates/etc_dma_auth.conf.j2 diff --git a/templates/etc_dma_dma.conf.j2 b/roles/host_prod/templates/etc_dma_dma.conf.j2 index dfaa04f..dfaa04f 100644 --- a/templates/etc_dma_dma.conf.j2 +++ b/roles/host_prod/templates/etc_dma_dma.conf.j2 diff --git a/templates/etc_fstab.j2 b/roles/host_prod/templates/etc_fstab.j2 index 95d3fa0..95d3fa0 100644 --- a/templates/etc_fstab.j2 +++ b/roles/host_prod/templates/etc_fstab.j2 diff --git a/templates/root_ssh_config.j2 b/roles/host_prod/templates/root_ssh_config.j2 index 96f78f2..96f78f2 100644 --- a/templates/root_ssh_config.j2 +++ b/roles/host_prod/templates/root_ssh_config.j2 diff --git a/templates/usr_local_bin_backup.sh.j2 b/roles/host_prod/templates/usr_local_bin_backup.sh.j2 index f6a0e2c..f6a0e2c 100644 --- a/templates/usr_local_bin_backup.sh.j2 +++ b/roles/host_prod/templates/usr_local_bin_backup.sh.j2 diff --git a/templates/usr_local_bin_safepf.sh b/roles/host_prod/templates/usr_local_bin_safepf.sh index 1b6efee..1b6efee 100644 --- a/templates/usr_local_bin_safepf.sh +++ b/roles/host_prod/templates/usr_local_bin_safepf.sh diff --git a/roles/host_test/handlers/main.yml b/roles/host_test/handlers/main.yml new file mode 100644 index 0000000..46fcbde --- /dev/null +++ b/roles/host_test/handlers/main.yml @@ -0,0 +1,2 @@ +- name: Apply rc.conf + shell: service kld start; service netif restart && service routing restart diff --git a/roles/host_test/tasks/main.yml b/roles/host_test/tasks/main.yml new file mode 100644 index 0000000..1246073 --- /dev/null +++ b/roles/host_test/tasks/main.yml @@ -0,0 +1,13 @@ +- name: Set up /etc/rc.conf + template: + src: "{{ playbook_dir }}/roles/host/templates/etc_rc.conf.j2" + dest: /etc/rc.conf + owner: root + group: wheel + mode: "0644" + vars: + is_prod: false + notify: Apply rc.conf + +- name: Flush handlers + meta: flush_handlers diff --git a/roles/jail/handlers/main.yml b/roles/jail/handlers/main.yml new file mode 100644 index 0000000..5375cb8 --- /dev/null +++ b/roles/jail/handlers/main.yml @@ -0,0 +1,5 @@ +- name: Restart jail services + service: + name: "{{ item }}" + state: restarted + loop: "{{ services | default([]) }}" diff --git a/roles/jail/tasks/main.yml b/roles/jail/tasks/main.yml new file mode 100644 index 0000000..be72e3b --- /dev/null +++ b/roles/jail/tasks/main.yml @@ -0,0 +1,175 @@ +# Host-side setup (runs on the jail host via SSH) +- name: "Check if {{ jail_name }} container exists" + shell: "zfs list -o name | grep -Fxq 'zroot/jails/containers/{{ jail_name }}'" + failed_when: false + changed_when: false + register: jail_exists + delegate_to: "{{ jail_delegate_host }}" + +- name: "Check {{ jail_name }} userland version" + shell: "zfs get -H -o value origin zroot/jails/containers/{{ jail_name }}" + register: jail_origin + changed_when: false + when: jail_exists.rc == 0 + delegate_to: "{{ jail_delegate_host }}" + +- name: "*** MIGRATION REQUIRED: {{ jail_name }} ***" + pause: + prompt: | + + ════════════════════════════════════════════════════════════════ + JAIL USERLAND MIGRATION: {{ jail_name }} + ════════════════════════════════════════════════════════════════ + Current: {{ jail_origin.stdout | trim }} + Target: zroot/jails/templates/{{ userland }}@base + + This will: + 1. Stop the jail + 2. Rename existing dataset to *.old.<timestamp> + 3. Clone fresh from {{ userland }} + ════════════════════════════════════════════════════════════════ + + Press Enter to continue or Ctrl+C to abort + when: + - jail_exists.rc == 0 + - "userland + '@base' not in jail_origin.stdout" + +- name: "Migrate {{ jail_name }} to {{ userland }}" + shell: | + service jail stop {{ jail_name }} || true + zfs rename zroot/jails/containers/{{ jail_name }} zroot/jails/containers/{{ jail_name }}.old.$(date +%s) + when: + - jail_exists.rc == 0 + - "userland + '@base' not in jail_origin.stdout" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Clone {{ jail_name }} from template" + shell: "zfs clone zroot/jails/templates/{{ userland }}@base zroot/jails/containers/{{ jail_name }}" + when: jail_exists.rc != 0 or (jail_origin.stdout is defined and userland + '@base' not in jail_origin.stdout) + delegate_to: "{{ jail_delegate_host }}" + +- name: "Create directories for {{ jail_name }}" + file: + path: "/usr/local/jails/containers/{{ jail_name }}{{ item }}" + state: directory + owner: root + group: wheel + mode: "0755" + loop: "{{ dirs | default([]) }}" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Create mount point sources for {{ jail_name }}" + file: + path: "{{ item.src }}" + state: directory + owner: root + group: wheel + mode: "0755" + loop: "{{ nullfs | default([]) }}" + loop_control: + label: "{{ item.src }}" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Create mount point destinations for {{ jail_name }}" + file: + path: "/usr/local/jails/containers/{{ jail_name }}{{ item.dst }}" + state: directory + owner: root + group: wheel + mode: "0755" + loop: "{{ nullfs | default([]) }}" + loop_control: + label: "{{ item.dst }}" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Deploy jail.conf.d/{{ jail_name }}.conf" + template: + src: jail_conf.j2 + dest: "/etc/jail.conf.d/{{ jail_name }}.conf" + owner: root + group: wheel + mode: "0644" + vars: + 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([]) }}" + delegate_to: "{{ jail_delegate_host }}" + +- name: "Start {{ jail_name }} jail" + shell: "service jail start {{ jail_name }}" + register: jail_start + failed_when: "jail_start.rc != 0 and 'already exists' not in jail_start.stdout" + changed_when: "'already exists' not in jail_start.stdout" + delegate_to: "{{ jail_delegate_host }}" + +# In-jail provisioning (runs inside the jail via jailexec) +- name: Install packages + shell: "pkg install -y {{ pkg | join(' ') }}" + environment: + ASSUME_ALWAYS_YES: "yes" + when: pkg is defined and pkg | length > 0 + register: pkg_result + changed_when: "'Number of packages to be installed' in pkg_result.stdout" + +- name: Create parent directories for files + file: + path: "{{ item.dest | dirname }}" + state: directory + owner: root + group: wheel + mode: "0755" + loop: "{{ files | default([]) }}" + loop_control: + label: "{{ item.dest | dirname }}" + when: files is defined + +- name: Deploy files + template: + src: "{{ jail_role_dir }}/templates/{{ item.src }}" + dest: "{{ item.dest }}" + owner: "{{ item.owner | default('root') }}" + group: "{{ item.group | default('wheel') }}" + mode: "{{ item.mode | default('0644') }}" + loop: "{{ files | default([]) }}" + loop_control: + label: "{{ item.dest }}" + when: files is defined + notify: Restart jail services + +- name: Enable services + community.general.sysrc: + name: "{{ item }}_enable" + value: "YES" + loop: "{{ services | default([]) }}" + +- name: Start services + service: + name: "{{ item }}" + state: started + loop: "{{ services | default([]) }}" + +- name: Set sysctl values + sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + state: present + loop: "{{ sysctl | default([]) }}" + when: sysctl is defined + +- name: Set sysrc values + community.general.sysrc: + name: "{{ item.name }}" + value: "{{ item.value }}" + loop: "{{ sysrc | default([]) }}" + when: sysrc is defined diff --git a/roles/jail/templates/jail_conf.j2 b/roles/jail/templates/jail_conf.j2 new file mode 100644 index 0000000..2a0ea27 --- /dev/null +++ b/roles/jail/templates/jail_conf.j2 @@ -0,0 +1,56 @@ +{{ jail.name }} { + vnet; + persist; + exec.clean; + allow.raw_sockets; + mount.devfs; +{% for opt in jail.options %} + {{ opt }}; +{% endfor %} + + devfs_ruleset = {{ jail.devfs_ruleset }}; + host.hostname = "{{ jail.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"; +{% for cmd in jail.exec_prestart %} + exec.prestart += "{{ cmd }}"; +{% endfor %} +{% for cmd in jail.exec_start %} + exec.start += "{{ cmd }}"; +{% endfor %} +{% for cmd in jail.exec_poststart %} + exec.poststart += "{{ cmd }}"; +{% endfor %} +{% for cmd in jail.exec_prestop %} + exec.prestop += "{{ cmd }}"; +{% endfor %} +{% for cmd in jail.exec_stop %} + exec.stop += "{{ cmd }}"; +{% endfor %} +{% 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"; +{% endfor %} +} diff --git a/roles/jails/01_ingress/defaults/main.yml b/roles/jails/01_ingress/defaults/main.yml new file mode 100644 index 0000000..f6d5aa4 --- /dev/null +++ b/roles/jails/01_ingress/defaults/main.yml @@ -0,0 +1,92 @@ +userland: "15.0-RELEASE" +devfs_ruleset: 6 +no_default_route: true + +ingress_routes: + - { host: jan.systems, jail: homepage } + - { host: jantuomi.fi, redirect: jan.systems } + - { host: aggro.jan.systems, jail: aggro } + - { host: diddle.jan.systems, jail: diddle } + - { host: spliit.jan.systems, jail: spliit } + - { host: freshrss.jan.systems, jail: freshrss } + - { host: irc.jan.systems, jail: irc_thelounge, presets: [websocket] } + - { host: paste.jan.systems, jail: paste } + - { host: leolalla.fi, jail: leolalla_fi } + - { host: immich.jan.systems, ip: 192.168.3.3, port: 2283 } + - { host: plex.jan.systems, jail: plex, port: 32400, presets: [streaming] } + - { host: komga.jan.systems, jail: komga, port: 25600 } + +cert_domains: + - "jan.systems" + - "*.jan.systems" + - "jantuomi.fi" + - "*.jantuomi.fi" + - "leolalla.fi" + - "*.leolalla.fi" + +cert_name: "{{ cert_domains[0] }}" +contact_email: jan@jantuomi.fi + +jail_conf_options: + - "allow.raw_sockets" + +nullfs: + - src: /usr/local/jails/volumes/goaccess_www + dst: /mnt/www_goaccess + +pkg: + - nginx + - py311-certbot + - py311-certbot-nginx + - goaccess + +files: + - src: etc_pf.conf.j2 + dest: /etc/pf.conf + - src: usr_local_etc_nginx_nginx.conf.j2 + dest: /usr/local/etc/nginx/nginx.conf + - src: acme-dns-auth.py + dest: /usr/local/bin/acme-dns-auth.py + mode: "0755" + - src: usr_local_bin_hetzner_ddns.sh.j2 + dest: /usr/local/bin/hetzner_ddns.sh + mode: "0755" + - src: usr_local_etc_hetzner_auth.j2 + dest: /usr/local/etc/hetzner_auth + mode: "0600" + - src: usr_local_bin_gen_goaccess.sh.j2 + dest: /usr/local/bin/gen_goaccess.sh + mode: "0755" + - src: etc_crontab.j2 + dest: /etc/crontab + +services: + - nginx + - pf + +sysctl: + - name: net.inet.ip.forwarding + value: "1" + +sysrc: + - name: gateway_enable + value: "YES" + +nginx_presets: + websocket: + - "proxy_http_version 1.1" + - 'proxy_set_header Connection "Upgrade"' + - "proxy_set_header Upgrade $http_upgrade" + - "proxy_read_timeout 1d" + - "proxy_send_timeout 1d" + - "proxy_buffering off" + - "proxy_request_buffering off" + - "client_max_body_size 100M" + streaming: + - "proxy_http_version 1.1" + - 'proxy_set_header Connection "Upgrade"' + - "proxy_set_header Upgrade $http_upgrade" + - "proxy_redirect off" + - "proxy_buffering off" + - "proxy_read_timeout 3600s" + - "proxy_send_timeout 3600s" diff --git a/roles/jails/01_ingress/tasks/main.yml b/roles/jails/01_ingress/tasks/main.yml new file mode 100644 index 0000000..5b56286 --- /dev/null +++ b/roles/jails/01_ingress/tasks/main.yml @@ -0,0 +1,49 @@ +- name: Set WAN hooks (test) + set_fact: + exec_prestart: + - "ifconfig epw{{ jail_num }}a destroy 2>/dev/null || true" + - "ifconfig epair{{ jail_num }}001 create" + - "ifconfig epair{{ jail_num }}001a name epw{{ jail_num }}a" + - "ifconfig epair{{ jail_num }}001b name epw{{ jail_num }}b" + - "ifconfig brlan0 addm epw{{ jail_num }}a" + exec_poststart: + - "ifconfig epw{{ jail_num }}b vnet {{ jail_name }}" + - "ifconfig epw{{ jail_num }}a up" + - "jexec {{ jail_name }} ifconfig epw{{ jail_num }}b up" + - "jexec {{ jail_name }} ifconfig epw{{ jail_num }}b inet {{ ingress_wan_static }}" + - "jexec {{ jail_name }} route add default {{ lan_ipv4_gateway }}" + exec_poststop: + - "ifconfig epw{{ jail_num }}a destroy 2>/dev/null || true" + when: not is_prod + +- name: Set WAN hooks (prod) + set_fact: + exec_prestart: + - "ifconfig epw{{ jail_num }}a destroy 2>/dev/null || true" + - "ifconfig epair{{ jail_num }}001 create" + - "ifconfig epair{{ jail_num }}001a name epw{{ jail_num }}a" + - "ifconfig epair{{ jail_num }}001b name epw{{ jail_num }}b" + - "ifconfig brwan0 addm epw{{ jail_num }}a" + exec_poststart: + - "ifconfig epw{{ jail_num }}b vnet {{ jail_name }}" + - "ifconfig epw{{ jail_num }}a up" + - "jexec {{ jail_name }} ifconfig epw{{ jail_num }}b up" + - "jexec {{ jail_name }} service dhclient restart epw{{ jail_num }}b" + - "jexec {{ jail_name }} route add 10.6.210.0/24 {{ lan_ipv4_gateway }} || true" + exec_poststop: + - "ifconfig epw{{ jail_num }}a destroy 2>/dev/null || true" + when: is_prod + +- import_role: + name: jail + +- name: Check if TLS certs exist + stat: + path: "/usr/local/etc/letsencrypt/live/{{ cert_name }}" + register: _certbot_certs + when: is_prod + +- name: Pause for manual certbot setup + pause: + prompt: "Run certbot manually in the ingress jail to obtain certs, then press Enter." + when: is_prod and not (_certbot_certs.stat.exists | default(true)) diff --git a/templates/ingress/acme-dns-auth.py b/roles/jails/01_ingress/templates/acme-dns-auth.py index 77928e6..77928e6 100755 --- a/templates/ingress/acme-dns-auth.py +++ b/roles/jails/01_ingress/templates/acme-dns-auth.py diff --git a/templates/ingress/etc_crontab.j2 b/roles/jails/01_ingress/templates/etc_crontab.j2 index 6879766..6879766 100644 --- a/templates/ingress/etc_crontab.j2 +++ b/roles/jails/01_ingress/templates/etc_crontab.j2 diff --git a/templates/ingress/etc_pf.conf.j2 b/roles/jails/01_ingress/templates/etc_pf.conf.j2 index c0528e1..c0528e1 100644 --- a/templates/ingress/etc_pf.conf.j2 +++ b/roles/jails/01_ingress/templates/etc_pf.conf.j2 diff --git a/templates/ingress/usr_local_bin_gen_goaccess.sh.j2 b/roles/jails/01_ingress/templates/usr_local_bin_gen_goaccess.sh.j2 index 2cfc93a..2cfc93a 100644 --- a/templates/ingress/usr_local_bin_gen_goaccess.sh.j2 +++ b/roles/jails/01_ingress/templates/usr_local_bin_gen_goaccess.sh.j2 diff --git a/templates/ingress/usr_local_bin_hetzner_ddns.sh.j2 b/roles/jails/01_ingress/templates/usr_local_bin_hetzner_ddns.sh.j2 index a2f4430..a2f4430 100644 --- a/templates/ingress/usr_local_bin_hetzner_ddns.sh.j2 +++ b/roles/jails/01_ingress/templates/usr_local_bin_hetzner_ddns.sh.j2 diff --git a/templates/ingress/usr_local_etc_hetzner_auth.j2 b/roles/jails/01_ingress/templates/usr_local_etc_hetzner_auth.j2 index 129dccf..129dccf 100644 --- a/templates/ingress/usr_local_etc_hetzner_auth.j2 +++ b/roles/jails/01_ingress/templates/usr_local_etc_hetzner_auth.j2 diff --git a/templates/ingress/usr_local_etc_nginx_nginx.conf.j2 b/roles/jails/01_ingress/templates/usr_local_etc_nginx_nginx.conf.j2 index 16299ef..3f853c5 100644 --- a/templates/ingress/usr_local_etc_nginx_nginx.conf.j2 +++ b/roles/jails/01_ingress/templates/usr_local_etc_nginx_nginx.conf.j2 @@ -33,8 +33,6 @@ http { listen 80 default_server; server_name _; - include /usr/local/etc/nginx/snippets/ban.inc; - location / { return 404; } @@ -46,8 +44,7 @@ http { listen [::]:80; server_name {{ route.host }}; - include /usr/local/etc/nginx/snippets/ban.inc; - +{% if ssl_enabled %} return 307 https://$host$request_uri; } @@ -55,8 +52,6 @@ http { server_name {{ route.host }}; http2 on; - include /usr/local/etc/nginx/snippets/ban.inc; - listen 443 ssl; listen [::]:443 ssl; @@ -76,39 +71,26 @@ http { ssl_certificate_key /usr/local/etc/letsencrypt/live/{{ cert_name }}/privkey.pem; include /usr/local/etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem; +{% endif %} {% if route.jail is defined -%} {% for jail in jails if jail.name == route.jail -%} location / { - proxy_pass http://192.168.2.{{ jail.num }}{% if route.port is defined %}:{{ route.port }}{% endif %}; + proxy_pass http://{{ jail_lan_cidr | ipv4_nth(jail.num + jail_lan_offset | int) }}{% if route.port is defined %}:{{ route.port }}{% endif %}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - {% if jail.name == 'irc_thelounge' -%} - proxy_http_version 1.1; - proxy_set_header Connection "Upgrade"; - proxy_set_header Upgrade $http_upgrade; - - # by default nginx times out connections in one minute - proxy_read_timeout 1d; - proxy_send_timeout 1d; - proxy_buffering off; - proxy_request_buffering off; - - client_max_body_size 100M; - {% elif jail.name == 'plex' -%} - proxy_http_version 1.1; - proxy_set_header Connection "Upgrade"; - proxy_set_header Upgrade $http_upgrade; - - # Streaming-friendly behavior - proxy_redirect off; - proxy_buffering off; - - # Long streams / slow clients - proxy_read_timeout 3600s; - proxy_send_timeout 3600s; + {% if route.presets is defined -%} + {% set directives = [] -%} + {% for p in route.presets -%} + {% for d in nginx_presets[p] -%} + {% if d not in directives -%}{% set _ = directives.append(d) -%}{% endif -%} + {% endfor -%} + {% endfor -%} + {% for directive in directives -%} + {{ directive }}; + {% endfor -%} {% endif %} } {% endfor %} diff --git a/roles/jails/02_postgres/defaults/main.yml b/roles/jails/02_postgres/defaults/main.yml new file mode 100644 index 0000000..045ba4d --- /dev/null +++ b/roles/jails/02_postgres/defaults/main.yml @@ -0,0 +1,9 @@ +userland: "14.3-RELEASE" +devfs_ruleset: 5 + +jail_conf_options: + - "allow.raw_sockets" + - "allow.sysvipc" + +pkg: + - postgresql18-server diff --git a/roles/jails/02_postgres/tasks/main.yml b/roles/jails/02_postgres/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/02_postgres/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/03_irc_thelounge/defaults/main.yml b/roles/jails/03_irc_thelounge/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/03_irc_thelounge/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/03_irc_thelounge/tasks/main.yml b/roles/jails/03_irc_thelounge/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/03_irc_thelounge/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/04_taulubot/defaults/main.yml b/roles/jails/04_taulubot/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/04_taulubot/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/04_taulubot/tasks/main.yml b/roles/jails/04_taulubot/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/04_taulubot/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/05_homepage/defaults/main.yml b/roles/jails/05_homepage/defaults/main.yml new file mode 100644 index 0000000..d48c9fa --- /dev/null +++ b/roles/jails/05_homepage/defaults/main.yml @@ -0,0 +1,18 @@ +userland: "14.3-RELEASE" + +pkg: + - nginx + - rsync + - bash + +files: + - src: usr_local_etc_nginx_nginx.conf.j2 + dest: /usr/local/etc/nginx/nginx.conf + - src: etc_crontab.j2 + dest: /etc/crontab + +dirs: + - /var/www + +services: + - nginx diff --git a/roles/jails/05_homepage/tasks/main.yml b/roles/jails/05_homepage/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/05_homepage/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/templates/homepage/etc_crontab.j2 b/roles/jails/05_homepage/templates/etc_crontab.j2 index 8541f57..8541f57 100644 --- a/templates/homepage/etc_crontab.j2 +++ b/roles/jails/05_homepage/templates/etc_crontab.j2 diff --git a/templates/homepage/usr_local_etc_nginx_nginx.conf.j2 b/roles/jails/05_homepage/templates/usr_local_etc_nginx_nginx.conf.j2 index ee45405..ee45405 100644 --- a/templates/homepage/usr_local_etc_nginx_nginx.conf.j2 +++ b/roles/jails/05_homepage/templates/usr_local_etc_nginx_nginx.conf.j2 diff --git a/roles/jails/06_hommabot/defaults/main.yml b/roles/jails/06_hommabot/defaults/main.yml new file mode 100644 index 0000000..d12d40f --- /dev/null +++ b/roles/jails/06_hommabot/defaults/main.yml @@ -0,0 +1,14 @@ +userland: "14.3-RELEASE" + +pkg: + - npm + +files: + - src: root_hommabot_env.j2 + dest: /root/hommabot/.env + mode: "0600" + - src: root_hommabot_deps.sh + dest: /root/hommabot/deps.sh + mode: "0755" + - src: etc_crontab.j2 + dest: /etc/crontab diff --git a/roles/jails/06_hommabot/tasks/main.yml b/roles/jails/06_hommabot/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/06_hommabot/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/templates/hommabot/etc_crontab.j2 b/roles/jails/06_hommabot/templates/etc_crontab.j2 index 556d12a..556d12a 100644 --- a/templates/hommabot/etc_crontab.j2 +++ b/roles/jails/06_hommabot/templates/etc_crontab.j2 diff --git a/templates/hommabot/root_hommabot_deps.sh b/roles/jails/06_hommabot/templates/root_hommabot_deps.sh index 25c0e56..25c0e56 100644 --- a/templates/hommabot/root_hommabot_deps.sh +++ b/roles/jails/06_hommabot/templates/root_hommabot_deps.sh diff --git a/templates/hommabot/root_hommabot_env.j2 b/roles/jails/06_hommabot/templates/root_hommabot_env.j2 index c66c666..c66c666 100644 --- a/templates/hommabot/root_hommabot_env.j2 +++ b/roles/jails/06_hommabot/templates/root_hommabot_env.j2 diff --git a/roles/jails/07_aggro/defaults/main.yml b/roles/jails/07_aggro/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/07_aggro/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/07_aggro/tasks/main.yml b/roles/jails/07_aggro/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/07_aggro/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/08_diddle/defaults/main.yml b/roles/jails/08_diddle/defaults/main.yml new file mode 100644 index 0000000..9423bd6 --- /dev/null +++ b/roles/jails/08_diddle/defaults/main.yml @@ -0,0 +1,23 @@ +userland: "14.3-RELEASE" + +pkg: + - python311 + - py311-sqlite3 + - git + +files: + - src: root_clone.sh + dest: /root/clone.sh + mode: "0755" + - src: root_diddle_env.j2 + dest: /root/diddle/.env + mode: "0600" + - src: usr_local_bin_diddle + dest: /usr/local/bin/diddle + mode: "0755" + - src: usr_local_etc_rc.d_diddle + dest: /usr/local/etc/rc.d/diddle + mode: "0755" + +services: + - diddle diff --git a/roles/jails/08_diddle/tasks/main.yml b/roles/jails/08_diddle/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/08_diddle/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/templates/root_clone.sh b/roles/jails/08_diddle/templates/root_clone.sh index 7f92c5f..7f92c5f 100644 --- a/templates/root_clone.sh +++ b/roles/jails/08_diddle/templates/root_clone.sh diff --git a/templates/diddle/root_diddle_env.j2 b/roles/jails/08_diddle/templates/root_diddle_env.j2 index ca7db78..ca7db78 100644 --- a/templates/diddle/root_diddle_env.j2 +++ b/roles/jails/08_diddle/templates/root_diddle_env.j2 diff --git a/templates/diddle/usr_local_bin_diddle b/roles/jails/08_diddle/templates/usr_local_bin_diddle index d07cd5e..d07cd5e 100644 --- a/templates/diddle/usr_local_bin_diddle +++ b/roles/jails/08_diddle/templates/usr_local_bin_diddle diff --git a/templates/diddle/usr_local_etc_rc.d_diddle b/roles/jails/08_diddle/templates/usr_local_etc_rc.d_diddle index 01deaad..01deaad 100644 --- a/templates/diddle/usr_local_etc_rc.d_diddle +++ b/roles/jails/08_diddle/templates/usr_local_etc_rc.d_diddle diff --git a/roles/jails/09_redis/defaults/main.yml b/roles/jails/09_redis/defaults/main.yml new file mode 100644 index 0000000..5416267 --- /dev/null +++ b/roles/jails/09_redis/defaults/main.yml @@ -0,0 +1 @@ +userland: "15.0-RELEASE" diff --git a/roles/jails/09_redis/tasks/main.yml b/roles/jails/09_redis/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/09_redis/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/10_samba/defaults/main.yml b/roles/jails/10_samba/defaults/main.yml new file mode 100644 index 0000000..99c53e1 --- /dev/null +++ b/roles/jails/10_samba/defaults/main.yml @@ -0,0 +1,5 @@ +userland: "15.0-RELEASE" + +nullfs: + - src: /usr/local/jails/volumes/storage + dst: /mnt/storage diff --git a/roles/jails/10_samba/tasks/main.yml b/roles/jails/10_samba/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/10_samba/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/11_spliit/defaults/main.yml b/roles/jails/11_spliit/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/11_spliit/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/11_spliit/tasks/main.yml b/roles/jails/11_spliit/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/11_spliit/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/12_goaccess/defaults/main.yml b/roles/jails/12_goaccess/defaults/main.yml new file mode 100644 index 0000000..0fa6078 --- /dev/null +++ b/roles/jails/12_goaccess/defaults/main.yml @@ -0,0 +1,5 @@ +userland: "15.0-RELEASE" + +nullfs: + - src: /usr/local/jails/volumes/goaccess_www + dst: /var/www/goaccess diff --git a/roles/jails/12_goaccess/tasks/main.yml b/roles/jails/12_goaccess/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/12_goaccess/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/13_plex/defaults/main.yml b/roles/jails/13_plex/defaults/main.yml new file mode 100644 index 0000000..91ea619 --- /dev/null +++ b/roles/jails/13_plex/defaults/main.yml @@ -0,0 +1,5 @@ +userland: "15.0-RELEASE" + +nullfs: + - src: /usr/local/jails/volumes/storage/media + dst: /mnt/media diff --git a/roles/jails/13_plex/tasks/main.yml b/roles/jails/13_plex/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/13_plex/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/14_freshrss/defaults/main.yml b/roles/jails/14_freshrss/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/14_freshrss/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/14_freshrss/tasks/main.yml b/roles/jails/14_freshrss/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/14_freshrss/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/15_paste/defaults/main.yml b/roles/jails/15_paste/defaults/main.yml new file mode 100644 index 0000000..697ea79 --- /dev/null +++ b/roles/jails/15_paste/defaults/main.yml @@ -0,0 +1 @@ +userland: "14.3-RELEASE" diff --git a/roles/jails/15_paste/tasks/main.yml b/roles/jails/15_paste/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/15_paste/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/16_dl/defaults/main.yml b/roles/jails/16_dl/defaults/main.yml new file mode 100644 index 0000000..c296953 --- /dev/null +++ b/roles/jails/16_dl/defaults/main.yml @@ -0,0 +1,11 @@ +userland: "15.0-RELEASE" +devfs_ruleset: 4 +no_default_route: true + +jail_conf_options: + - "allow.raw_sockets" + - "allow.mlock" + +nullfs: + - src: /usr/local/jails/volumes/storage + dst: /mnt/storage diff --git a/roles/jails/16_dl/tasks/main.yml b/roles/jails/16_dl/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/16_dl/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/17_syncthing/defaults/main.yml b/roles/jails/17_syncthing/defaults/main.yml new file mode 100644 index 0000000..a2d7d8f --- /dev/null +++ b/roles/jails/17_syncthing/defaults/main.yml @@ -0,0 +1,11 @@ +userland: "15.0-RELEASE" + +nullfs: + - src: /usr/local/jails/volumes/storage/docs + dst: /mnt/docs + - src: /usr/local/jails/volumes/storage/vault + dst: /mnt/vault + - src: /usr/local/jails/volumes/storage/jan-systems-2025-content + dst: /mnt/jan-systems-2025-content + - src: /usr/local/jails/volumes/storage/projects-ableton + dst: /mnt/projects-ableton diff --git a/roles/jails/17_syncthing/tasks/main.yml b/roles/jails/17_syncthing/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/17_syncthing/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/18_komga/defaults/main.yml b/roles/jails/18_komga/defaults/main.yml new file mode 100644 index 0000000..d69a731 --- /dev/null +++ b/roles/jails/18_komga/defaults/main.yml @@ -0,0 +1,5 @@ +userland: "15.0-RELEASE" + +nullfs: + - src: /usr/local/jails/volumes/storage/media/manga + dst: /mnt/manga diff --git a/roles/jails/18_komga/tasks/main.yml b/roles/jails/18_komga/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/18_komga/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/roles/jails/19_leolalla_fi/defaults/main.yml b/roles/jails/19_leolalla_fi/defaults/main.yml new file mode 100644 index 0000000..5416267 --- /dev/null +++ b/roles/jails/19_leolalla_fi/defaults/main.yml @@ -0,0 +1 @@ +userland: "15.0-RELEASE" diff --git a/roles/jails/19_leolalla_fi/tasks/main.yml b/roles/jails/19_leolalla_fi/tasks/main.yml new file mode 100644 index 0000000..2abdaff --- /dev/null +++ b/roles/jails/19_leolalla_fi/tasks/main.yml @@ -0,0 +1,2 @@ +- import_role: + name: jail diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..f55eef1 --- /dev/null +++ b/site.yml @@ -0,0 +1,74 @@ +--- +# Site playbook +# +# Usage: +# ansible-playbook -i inventory/test site.yml +# ansible-playbook -i inventory/test site.yml --limit test_vm +# ansible-playbook -i inventory/test site.yml --limit ingress +# ansible-playbook -i inventory/prod site.yml + +- name: Configure host + hosts: servers + tags: [host] + vars_files: + - secrets.yml + vars: + lan_ipv4_network: 192.168.0.0/16 + lan_search_domain: local.jan.systems + ingress_ip: "{{ jail_lan_cidr | ipv4_nth(1 + jail_lan_offset | int) }}" + pre_tasks: + - name: Verify secrets are loaded + assert: + that: smtp_user != '' + fail_msg: "Required variables missing. Did you forget to create secrets.yml?" + no_log: true + + tasks: + - name: Apply host roles + include_role: + name: "{{ item }}" + loop: "{{ host_roles }}" + +- name: Provision jails + hosts: jails + serial: 1 + gather_facts: false + vars_files: + - secrets.yml + vars: + lan_ipv4_network: 192.168.0.0/16 + lan_search_domain: local.jan.systems + ingress_ip: "{{ jail_lan_cidr | ipv4_nth(1 + jail_lan_offset | int) }}" + pre_tasks: + - name: Resolve jail role directory + set_fact: + jail_name: "{{ ansible_jail_name }}" + jail_num: "{{ _jail_dir | basename | split('_') | first | int }}" + jail_role_dir: "{{ _jail_dir }}" + vars: + _jail_dir: '{{ lookup(''pipe'', ''find '' + playbook_dir + ''/roles/jails -maxdepth 1 -type d -name "*_'' + ansible_jail_name + ''"'') }}' + + - name: Discover jail directories + find: + paths: "{{ playbook_dir }}/roles/jails" + patterns: "main.yml" + file_type: file + recurse: true + delegate_to: localhost + register: _jail_specs + + - name: Build jails list + set_fact: + jails: "{{ jails | default([]) + [{'num': _num, 'name': _name}] }}" + vars: + _num: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') | split('_') | first | int }}" + _name: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') | regex_replace('^[0-9]+_', '') }}" + loop: "{{ _jail_specs.files | sort(attribute='path') }}" + loop_control: + label: "{{ item.path | regex_replace('.*/jails/([^/]+)/.*', '\\1') }}" + when: "'/defaults/' in item.path" + + tasks: + - name: Apply jail role + include_role: + name: "{{ jail_role_dir }}" diff --git a/tasks/email.yml b/tasks/email.yml deleted file mode 100644 index 3585c3f..0000000 --- a/tasks/email.yml +++ /dev/null @@ -1,50 +0,0 @@ -- name: Create dma config directory - file: - path: /etc/dma - state: directory - owner: root - group: wheel - mode: "0755" - -- name: Configure dma.conf - template: - src: etc_dma_dma.conf.j2 - dest: /etc/dma/dma.conf - owner: root - group: wheel - mode: "0644" - register: etc_dma_dma_conf - -- name: Configure auth.conf - template: - src: etc_dma_auth.conf.j2 - dest: /etc/dma/auth.conf - owner: root - group: mail - mode: "0640" - register: etc_dma_auth_conf - -- name: Configure aliases - template: - src: etc_aliases.j2 - dest: /etc/aliases - owner: root - group: wheel - mode: "0644" - register: etc_aliases_conf - -- name: Run newaliases - shell: newaliases - when: etc_aliases_conf.changed - -- name: Test mail delivery - when: etc_dma_dma_conf.changed or etc_dma_auth_conf.changed or etc_aliases_conf.changed - block: - - name: Send email - shell: echo "DMA test message from Ansible" | mail -s "Test DMA" root - register: mail_result - changed_when: false - - - name: Show mail result - debug: - var: mail_result diff --git a/tasks/general.yml b/tasks/general.yml deleted file mode 100644 index 0ec9219..0000000 --- a/tasks/general.yml +++ /dev/null @@ -1,167 +0,0 @@ -- name: Install packages - package: - name: "{{ item }}" - state: present - loop: - - rsync - - dma - - jq - - curl - - bash - - python - - py311-pip - - fastfetch - -- name: Install amd64-specific packages - package: - name: "{{ item }}" - state: present - loop: - - vm-bhyve - - drm-kmod - when: not is_test_vm - -- name: Set up periodic.conf - template: - src: etc_periodic.conf.j2 - dest: /etc/periodic.conf - owner: root - group: wheel - mode: "0644" - -- name: Set up /etc/rc.conf - template: - src: etc_rc.conf.j2 - dest: /etc/rc.conf - owner: root - group: wheel - mode: "0644" - register: rc_conf - -- name: Set up fstab - template: - src: etc_fstab.j2 - dest: /etc/fstab - owner: root - group: wheel - mode: "0644" - register: fstab - when: not is_test_vm - -- name: Run mount -a - shell: mount -a - when: not is_test_vm and fstab.changed - -- name: Start auditd - service: - name: auditd - state: started - -- name: Check if pylogsentinel is installed - shell: pip show pylogsentinel - register: pylogsentinel_check - failed_when: false - changed_when: false - -- name: Install pylogsentinel - shell: pip install pylogsentinel==0.3.0 --force --no-input - when: pylogsentinel_check.rc != 0 - -- name: Install pylogsentinel.conf - template: - src: usr_local_etc_pylogsentinel.conf.j2 - dest: /usr/local/etc/pylogsentinel.conf - owner: root - group: wheel - mode: "0644" - -- name: Install pylogsentinel-batch-email.sh - template: - src: usr_local_bin_pylogsentinel-batch-email.sh.j2 - dest: /usr/local/bin/pylogsentinel-batch-email.sh - owner: root - group: wheel - mode: "0755" - -- name: Set up crontab - template: - src: etc_crontab.j2 - dest: /etc/crontab - owner: root - group: wheel - mode: "0644" - register: etc_crontab - -- name: Restart cron - service: - name: cron - state: restarted - when: etc_crontab.changed - -- name: Install logto - template: - src: usr_local_bin_logto.sh.j2 - dest: /usr/local/bin/logto - owner: root - group: wheel - mode: "0755" - -- name: Copy backup SSH private key - copy: - src: "{{ backup_ssh_privkey_file }}" - dest: /root/.ssh/backup - owner: root - group: wheel - mode: "0600" - when: not is_test_vm - -- name: Copy backup SSH public key - copy: - src: "{{ backup_ssh_pubkey_file }}" - dest: /root/.ssh/backup.pub - owner: root - group: wheel - mode: "0644" - when: not is_test_vm - -- name: Copy SSH config - template: - src: root_ssh_config.j2 - dest: /root/.ssh/config - owner: root - group: wheel - mode: "0644" - when: not is_test_vm - -- name: Copy backup script - template: - src: usr_local_bin_backup.sh.j2 - dest: /usr/local/bin/backup - owner: root - group: wheel - mode: "0755" - when: not is_test_vm - -- name: Check if zroot/vm exists - shell: zfs list zroot/vm - register: zroot_vm_check - failed_when: false - changed_when: false - when: not is_test_vm - -- name: Add vm dataset for bhyve and init - when: not is_test_vm and zroot_vm_check.rc != 0 - block: - - name: Create vm dataset - shell: zfs create zroot/vm - - - name: Run vm init - shell: vm init - -- name: Install .bashrc - copy: - src: templates/root_bashrc - dest: /root/.bashrc - owner: root - group: wheel - mode: "0644" diff --git a/tasks/jail_diddle.yml b/tasks/jail_diddle.yml deleted file mode 100644 index 73424f5..0000000 --- a/tasks/jail_diddle.yml +++ /dev/null @@ -1,63 +0,0 @@ -- name: Install packages inside jail - loop: - - { jail: diddle, package: python311 } - - { jail: diddle, package: py311-sqlite3 } - - { jail: diddle, package: git } - include_tasks: pkg_jail_install.yml - -- name: Install clone.sh - template: - src: root_clone.sh - dest: /usr/local/jails/containers/diddle/root/clone.sh - owner: root - group: wheel - mode: "0755" - -- name: Clone & update diddle - shell: jexec diddle sh /root/clone.sh https://github.com/jantuomi/diddle.git /root/diddle - register: diddle_git - changed_when: diddle_git.rc == 100 - failed_when: diddle_git.rc != 0 and diddle_git.rc != 100 - -- name: Install diddle env file - template: - src: diddle/root_diddle_env.j2 - dest: /usr/local/jails/containers/diddle/root/diddle/.env - owner: root - group: wheel - mode: "0600" - register: diddle_env - -- name: Install diddle startup script - template: - src: diddle/usr_local_bin_diddle - dest: /usr/local/jails/containers/diddle/usr/local/bin/diddle - owner: root - group: wheel - mode: "0755" - register: diddle_bin - -- name: Install diddle service - template: - src: diddle/usr_local_etc_rc.d_diddle - dest: /usr/local/jails/containers/diddle/usr/local/etc/rc.d/diddle - owner: root - group: wheel - mode: "0755" - register: diddle_rc - -- name: Check if diddle service is running - command: service -j diddle diddle status - changed_when: false - failed_when: false - register: diddle_status - -- name: Ensure diddle service is enabled - shell: service -j diddle diddle enable - changed_when: false - failed_when: false - -- name: (Re)start diddle service - shell: service -j diddle diddle restart - when: diddle_git.changed or diddle_bin.changed - or diddle_rc.changed or diddle_env.changed or diddle_status.rc != 0 diff --git a/tasks/jail_homepage.yml b/tasks/jail_homepage.yml deleted file mode 100644 index 6751ad5..0000000 --- a/tasks/jail_homepage.yml +++ /dev/null @@ -1,52 +0,0 @@ -- name: Install packages inside jail - loop: - - { jail: homepage, package: nginx } - - { jail: homepage, package: rsync } - - { jail: homepage, package: bash } - include_tasks: pkg_jail_install.yml - -- name: Create /var/www - file: - path: /usr/local/jails/containers/homepage/var/www - state: directory - owner: www - group: www - mode: "0755" - -# It's important to run this after generating certs, because nginx.conf refers to files -# generated by certbot. Certbot will fail validation if nginx.conf is configured too early. -- name: Configure nginx.conf - template: - src: homepage/usr_local_etc_nginx_nginx.conf.j2 - dest: /usr/local/jails/containers/homepage/usr/local/etc/nginx/nginx.conf - owner: root - group: wheel - mode: "0644" - register: homepage_nginx_conf - -- name: Check if nginx is enabled - shell: service -j homepage nginx status - changed_when: false - failed_when: false - register: homepage_nginx_enabled - -- name: Enable and start nginx - shell: | - service -j ingress nginx enable - service -j ingress nginx restart - when: homepage_nginx_enabled.rc != 0 or homepage_nginx_conf.changed - -- name: Set up crontab - template: - src: homepage/etc_crontab.j2 - dest: /usr/local/jails/containers/homepage/etc/crontab - owner: root - group: wheel - mode: "0644" - register: jail_homepage_etc_crontab - -- name: Restart cron - service: - name: cron - state: restarted - when: jail_homepage_etc_crontab.changed diff --git a/tasks/jail_hommabot.yml b/tasks/jail_hommabot.yml deleted file mode 100644 index b86d5d6..0000000 --- a/tasks/jail_hommabot.yml +++ /dev/null @@ -1,62 +0,0 @@ -- name: Install packages inside jail - loop: - - { jail: hommabot, package: npm } - include_tasks: pkg_jail_install.yml - -- name: Build hommabot on this machine - delegate_to: localhost - shell: | - cd ../hommabot2 - npm run build - -- name: Create hommabot directory - file: - path: /usr/local/jails/containers/hommabot/root/hommabot - state: directory - owner: root - group: wheel - mode: "0755" - -- name: Install index.js - copy: - src: "{{ item }}" - dest: /usr/local/jails/containers/hommabot/root/hommabot/ - owner: root - group: wheel - mode: "0644" - loop: - - "../hommabot2/build/index.js" - - "../hommabot2/package.json" - - "../hommabot2/package-lock.json" - -- name: Install hommabot env file - template: - src: hommabot/root_hommabot_env.j2 - dest: /usr/local/jails/containers/hommabot/root/hommabot/.env - owner: root - group: wheel - mode: "0600" - -- name: Install deps.sh - copy: - src: templates/hommabot/root_hommabot_deps.sh - dest: /usr/local/jails/containers/hommabot/root/hommabot/deps.sh - owner: root - group: wheel - mode: "0755" - -- name: Install dependencies - shell: jexec hommabot /root/hommabot/deps.sh - -- name: Set up crontab - template: - src: hommabot/etc_crontab.j2 - dest: /usr/local/jails/containers/hommabot/etc/crontab - owner: root - group: wheel - mode: "0644" - register: jail_hommabot_etc_crontab - -- name: Restart cron - shell: jexec hommabot service cron restart - when: jail_hommabot_etc_crontab.changed diff --git a/tasks/jail_ingress.yml b/tasks/jail_ingress.yml deleted file mode 100644 index 5945e42..0000000 --- a/tasks/jail_ingress.yml +++ /dev/null @@ -1,211 +0,0 @@ -- name: Ensure ingress goaccess mount points exist - file: - path: "{{ item }}" - state: directory - owner: root - group: wheel - mode: "0755" - loop: - - /usr/local/jails/containers/goaccess/var/www/goaccess - - /usr/local/jails/containers/ingress/mnt/www_goaccess - -- name: Start ingress jail - shell: service jail start ingress - register: ingress_jail_start - failed_when: false - changed_when: "'already running' not in ingress_jail_start.stderr" - -- name: Install packages inside jail - loop: - - { jail: ingress, package: nginx } - - { jail: ingress, package: py311-certbot } - - { jail: ingress, package: py311-certbot-nginx } - - { jail: ingress, package: goaccess } - include_tasks: pkg_jail_install.yml - -- name: Configure pf.conf - template: - src: ingress/etc_pf.conf.j2 - dest: /usr/local/jails/containers/ingress/etc/pf.conf - owner: root - group: wheel - mode: "0644" - register: ingress_pf_conf - -- name: Reload pf.conf - shell: jexec ingress pfctl -f /etc/pf.conf - when: ingress_pf_conf.changed - -- name: Check if gateway mode is enabled - shell: jexec ingress sysrc gateway_enable | grep -q "YES" - register: ingress_gateway_enabled - failed_when: false - changed_when: false - -- name: Enable gateway mode - shell: jexec ingress sysrc gateway_enable=YES - when: ingress_gateway_enabled.rc != 0 - -- name: Check if IP forwarding is enabled - shell: jexec ingress sysctl net.inet.ip.forwarding | grep -q "1" - register: ingress_ip_forwarding_enabled - failed_when: false - changed_when: false - -- name: Enable IP forwarding - shell: jexec ingress sysctl net.inet.ip.forwarding=1 - when: ingress_ip_forwarding_enabled.rc != 0 - -- name: Install pf-ban-socket.py - copy: - src: templates/ingress/pf-ban-socket.py - dest: /usr/local/jails/containers/ingress/usr/local/bin/pf-ban-socket.py - owner: root - group: wheel - mode: "0755" - register: ingress_pf_ban_socket_py - -- name: Install pf-ban-socket service - copy: - src: templates/ingress/usr_local_etc_rc.d_pf_ban_socket - dest: /usr/local/jails/containers/ingress/usr/local/etc/rc.d/pf_ban_socket - owner: root - group: wheel - mode: "0755" - register: ingress_pf_ban_socket_service - -- name: Check if pf enabled - shell: jexec ingress sysrc pf_enable | grep -q "YES" - register: ingress_pf_enabled - failed_when: false - changed_when: false - -- name: Enable pf - shell: jexec ingress sysrc pf_enable=YES - when: ingress_pf_enabled.rc != 0 - -- name: Check if pf is running - shell: service -j ingress pf status - register: ingress_pf_status - changed_when: ingress_pf_status.rc != 0 - -- name: Start pf - shell: service -j ingress pf start - when: ingress_pf_status.rc != 0 - -- name: Enable pf-ban-socket service - shell: | - service -j ingress pf_ban_socket enable - service -j ingress pf_ban_socket restart - when: ingress_pf_ban_socket_service.changed or ingress_pf_ban_socket_py.changed - -- name: Copy acme-dns-auth.py - copy: - src: templates/ingress/acme-dns-auth.py - dest: /usr/local/jails/containers/ingress/usr/local/bin/acme-dns-auth.py - owner: root - group: wheel - mode: "0755" - -- name: Check if LetsEncrypt certs are generated - shell: ls /usr/local/jails/containers/ingress/usr/local/etc/letsencrypt/live/{{ cert_name }} - register: cert_exists - failed_when: false - changed_when: false - -- name: Manually get certs with certbot and DNS challenge - pause: - prompt: | - /usr/local/etc/letsencrypt/live/{{ cert_name }} does not exist. This means that this is the first run of this playbook. - The {{ cert_name }} cert contains all of the @ and * certs for all domains. - Check the ingress crontab template and run the certbot command manually in the ingress jail. Remove the "-n" flag. - Add the requested DNS records manually into Hetzner DNS. Continue after this is done. - when: cert_exists.rc != 0 - -- name: Configure nginx.conf - template: - src: ingress/usr_local_etc_nginx_nginx.conf.j2 - dest: /usr/local/jails/containers/ingress/usr/local/etc/nginx/nginx.conf - owner: root - group: wheel - mode: "0644" - register: nginx_conf - -- name: Create nginx snippets directory - file: - path: "/usr/local/jails/containers/ingress/usr/local/etc/nginx/snippets" - state: directory - owner: root - group: wheel - mode: "0755" - -- name: Include ban.inc - template: - src: ingress/nginx_snippet_ban.inc - dest: /usr/local/jails/containers/ingress/usr/local/etc/nginx/snippets/ban.inc - owner: root - group: wheel - mode: "0644" - -- name: Create static directories - loop: "{{ ingress_routes | selectattr('static', 'defined') | map(attribute='static') | unique | list }}" - file: - path: "/usr/local/jails/containers/ingress{{ item }}" - state: directory - recurse: yes - owner: 80 - group: 80 - mode: "0755" - -- name: Check if nginx is enabled - shell: service -j ingress nginx status - changed_when: false - failed_when: false - register: ingress_nginx_enabled - -- name: Enable and start nginx - shell: | - service -j ingress nginx enable - service -j ingress nginx restart - when: ingress_nginx_enabled.rc != 0 or nginx_conf.changed - -- name: Install hetzner_ddns.sh - template: - src: ingress/usr_local_bin_hetzner_ddns.sh.j2 - dest: /usr/local/jails/containers/ingress/usr/local/bin/hetzner_ddns.sh - owner: root - group: wheel - mode: "0755" - -- name: Set up hetzner_auth - template: - src: ingress/usr_local_etc_hetzner_auth.j2 - dest: /usr/local/jails/containers/ingress/usr/local/etc/hetzner_auth - owner: root - group: wheel - mode: "0600" - -- name: Set up gen_goaccess.sh - template: - src: ingress/usr_local_bin_gen_goaccess.sh.j2 - dest: /usr/local/jails/containers/ingress/usr/local/bin/gen_goaccess.sh - owner: root - group: wheel - mode: "0755" - -- name: Set up crontab - template: - src: ingress/etc_crontab.j2 - dest: /usr/local/jails/containers/ingress/etc/crontab - owner: root - group: wheel - mode: "0644" - vars: - tls_hosts: "{{ ingress_routes | map(attribute='host') | unique }}" - register: jail_ingress_etc_crontab - -- name: Restart cron - service: - name: cron - state: restarted - when: jail_ingress_etc_crontab.changed diff --git a/tasks/jail_postgres.yml b/tasks/jail_postgres.yml deleted file mode 100644 index 42cdacf..0000000 --- a/tasks/jail_postgres.yml +++ /dev/null @@ -1,10 +0,0 @@ -- name: Install packages inside jail - loop: - - { jail: postgres, package: postgresql18-server } - include_tasks: pkg_jail_install.yml - -- name: Enable postgresql service inside jail - shell: | - service -j postgres postgresql enable - changed_when: false -# Run initdb and start manually, not managed by Ansible diff --git a/tasks/jails.yml b/tasks/jails.yml deleted file mode 100644 index 58d22d8..0000000 --- a/tasks/jails.yml +++ /dev/null @@ -1,49 +0,0 @@ -- name: Ensure jails directory exists - file: - path: /usr/local/jails - state: directory - owner: root - group: wheel - mode: "0755" - -- name: Create ZFS datasets - loop: - - { name: "zroot/jails", mountpoint: "/usr/local/jails" } - - { name: "zroot/jails/media" } - - { name: "zroot/jails/templates" } - - { - name: "zroot/jails/templates/{{ jail_userland_14_3 }}", - userland: "{{ jail_userland_14_3 }}", - } - - { - name: "zroot/jails/templates/{{ jail_userland_15_0 }}", - userland: "{{ jail_userland_15_0 }}", - } - - { name: "zroot/jails/containers" } - loop_control: - loop_var: dataset - include_tasks: jails_dataset.yml - -- name: Configure jails - block: - - name: Configure jail.conf - template: - src: etc_jail.conf.j2 - dest: /etc/jail.conf - owner: root - group: wheel - mode: "0644" - - - name: Configure devfs.rules - template: - src: etc_devfs.rules.j2 - dest: /etc/devfs.rules - owner: root - group: wheel - mode: "0644" - - - name: Configure individual jails - loop: "{{ jails }}" - loop_control: - loop_var: jail - include_tasks: jails_single.yml diff --git a/tasks/jails_dataset.yml b/tasks/jails_dataset.yml deleted file mode 100644 index 9e2748c..0000000 --- a/tasks/jails_dataset.yml +++ /dev/null @@ -1,61 +0,0 @@ -- name: "Check if dataset {{ dataset.name }} exists" - shell: zfs list -o name | grep -Fxq "{{ dataset.name }}" - changed_when: false - failed_when: false - register: check_dataset_exists - -- name: "Create ZFS dataset {{ dataset.name }}" - shell: | - {% if dataset.mountpoint is defined %} - zfs create -o "mountpoint={{ dataset.mountpoint }}" -p "{{ dataset.name }}" - {% else %} - zfs create -p "{{ dataset.name }}" - {% endif %} - when: check_dataset_exists.rc != 0 - -- name: Check if userland snapshot already exists - shell: zfs list -t snapshot -o name | grep -Fxq "{{ dataset.name }}@base" - failed_when: false - changed_when: false - register: zfs_userland_check - when: dataset.userland is defined - -- name: Set up userland - when: dataset.userland is defined and zfs_userland_check.rc != 0 - block: - - name: Download userland - get_url: - url: https://download.freebsd.org/ftp/releases/{{ arch }}/{{ dataset.userland }}/base.txz - dest: /usr/local/jails/media/{{ dataset.userland }}-base.txz - owner: root - group: wheel - mode: "0644" - - - name: Unarchive userland - shell: tar -xzf /usr/local/jails/media/{{ dataset.userland }}-base.txz -C /usr/local/jails/templates/{{ dataset.userland }} - - - name: Copy localtime to jail userland - copy: - remote_src: true - src: /etc/localtime - dest: /usr/local/jails/templates/{{ dataset.userland }}/etc/localtime - - - name: Copy resolv.conf to jail userland - template: - src: etc_resolv.conf.j2 - dest: /usr/local/jails/templates/{{ dataset.userland }}/etc/resolv.conf - owner: root - group: wheel - mode: "0644" - - - name: Disable resolvconf in the template - shell: echo 'resolvconf=NO' >> /usr/local/jails/templates/{{ dataset.userland }}/etc/resolvconf.conf - - - name: Update userland to latest patch level - shell: freebsd-update -b /usr/local/jails/templates/{{ dataset.userland }}/ fetch install - - - name: Disable syslogd in the template - shell: echo 'syslogd_enable="NO"' >> /usr/local/jails/templates/{{ dataset.userland }}/etc/rc.conf - - - name: Create userland ZFS snapshot - shell: zfs snapshot zroot/jails/templates/{{ dataset.userland }}@base diff --git a/tasks/jails_single.yml b/tasks/jails_single.yml deleted file mode 100644 index 7c5dd74..0000000 --- a/tasks/jails_single.yml +++ /dev/null @@ -1,27 +0,0 @@ -- name: "Check if jail directory for {{ jail.name }} exists" - shell: zfs list -o name | grep -Fxq "zroot/jails/containers/{{ jail.name }}" - failed_when: false - changed_when: false - register: check_jail_directory - -- name: "ZFS clone snapshot to jail directory for {{ jail.name }}" - shell: zfs clone zroot/jails/templates/{{ jail.userland }}@base "zroot/jails/containers/{{ jail.name }}" - when: check_jail_directory.rc != 0 - -- name: Configure jail.conf.d/{{ jail.name }}.conf - template: - src: etc_jail.conf.d_[jailname].conf.j2 - dest: /etc/jail.conf.d/{{ jail.name }}.conf - owner: root - group: wheel - mode: "0644" -# -#- name: "Check if jail {{ jail.name }} is running" -# shell: jls -j "{{ jail.name }}" -# failed_when: false -# changed_when: false -# register: check_jail_active -# -#- name: "(Re)start jail {{ jail.name }}" -# shell: service jail restart "{{ jail.name }}" -# when: check_jail_active.rc != 0 diff --git a/tasks/network.yml b/tasks/network.yml deleted file mode 100644 index 86cbf46..0000000 --- a/tasks/network.yml +++ /dev/null @@ -1,48 +0,0 @@ -- name: Set up resolv.conf - template: - src: etc_resolv.conf.j2 - dest: /etc/resolv.conf - owner: root - group: wheel - mode: "0644" - -# TODO: this doesn't work without rc_conf having been run -#- name: Restart networking if interface configuration changed -# shell: service netif restart && service routing restart -# when: rc_conf.changed or resolv_conf.changed - -- name: Apply network configuration - shell: service netif restart && service routing restart - when: rc_conf.changed - -- name: Set up sshd - template: - src: etc_ssh_sshd_config.j2 - dest: /etc/ssh/sshd_config - owner: root - group: wheel - mode: "0644" - register: etc_sshd_config - -- name: Start sshd - service: - name: sshd - state: started - register: started_sshd - -- name: Restart sshd - service: - name: sshd - state: restarted - when: not started_sshd.changed and etc_sshd_config.changed - -- name: Start syslogd - service: - name: syslogd - state: started - register: started_syslogd -#- name: Restart syslogd -# service: -# name: syslogd -# state: restarted -# when: not started_syslogd.changed and rc_conf.changed diff --git a/tasks/pkg_jail_install.yml b/tasks/pkg_jail_install.yml deleted file mode 100644 index 0315a03..0000000 --- a/tasks/pkg_jail_install.yml +++ /dev/null @@ -1,9 +0,0 @@ -- name: Check if {{ item.package }} is installed - shell: pkg -j {{ item.jail }} info {{ item.package }} - register: pkg_jail_installed - changed_when: false - failed_when: false - -- name: Install {{ item.package }} - shell: pkg -j {{ item.jail }} install -y {{ item.package }} - when: pkg_jail_installed.rc != 0 diff --git a/tasks/zfs.yml b/tasks/zfs.yml deleted file mode 100644 index 59495dc..0000000 --- a/tasks/zfs.yml +++ /dev/null @@ -1,48 +0,0 @@ -- name: Ensure /usr/local/etc/rc.d/ exists - file: - path: /usr/local/etc/rc.d/ - state: directory - owner: root - group: wheel - mode: "0755" - -- name: Add zpool_gpt_labels rc.d service - template: - src: usr_local_etc_rc.d_zpool_gpt_labels.j2 - dest: /usr/local/etc/rc.d/zpool_gpt_labels - owner: root - group: wheel - mode: "0755" - register: rc_zpool_gpt_labels - -- name: Run zpool_gpt_labels - service: - name: zpool_gpt_labels - state: started - when: rc_zpool_gpt_labels.changed - -- name: Install smartd - package: - name: smartmontools - state: present - -- name: Configure smartd.conf - template: - src: usr_local_etc_smartd.conf.j2 - dest: /usr/local/etc/smartd.conf - owner: root - group: wheel - mode: "0644" - register: smartd_conf - -- name: Start smartd - service: - name: smartd - state: started - register: started_smartd - -- name: Restart smartd - service: - name: smartd - state: restarted - when: not started_smartd.changed and smartd_conf.changed diff --git a/templates/etc_jail.conf.d_[jailname].conf.j2 b/templates/etc_jail.conf.d_[jailname].conf.j2 deleted file mode 100644 index df10f47..0000000 --- a/templates/etc_jail.conf.d_[jailname].conf.j2 +++ /dev/null @@ -1,43 +0,0 @@ -# eplXa is host end (local network bridge), eplXb is jail end. -# The corresponding pubnet interface is epwX, but that's not created for all jails. -{{ jail.name }} { - # STARTUP/LOGGING/VNET - vnet; - persist; - exec.clean; - - exec.prestart = ""; - exec.start = "/bin/sh /etc/rc"; - exec.poststart = ""; - exec.prestop = ""; - exec.stop = "/bin/sh /etc/rc.shutdown"; - exec.poststop = ""; - - exec.consolelog = "/var/log/jail_console_${name}.log"; - - # PERMISSIONS - allow.raw_sockets; - exec.clean; - mount.devfs; - - # HOSTNAME/PATH - host.hostname = "${name}"; - path = "/usr/local/jails/containers/${name}"; - - # JAIL-SPECIFIC CONFIGURATION -{% set t = lookup( - 'ansible.builtin.first_found', - { - 'files': [ - 'jail_confs/' ~ jail.name ~ '.j2', - 'jail_confs/_default.j2', - ], - 'paths': [ playbook_dir ~ '/templates' ] - }, - errors='ignore' -) %} - -{% if t %} -{{ lookup('ansible.builtin.template', t) | indent(2, true) }} -{% endif %} -} diff --git a/templates/etc_jail.conf.j2 b/templates/etc_jail.conf.j2 deleted file mode 100644 index 7190816..0000000 --- a/templates/etc_jail.conf.j2 +++ /dev/null @@ -1,6 +0,0 @@ -# Include configurations from standard locations. -.include "/etc/jail.conf.d/*.conf"; -.include "/etc/jail.*.conf"; -.include "/usr/local/etc/jail[.]conf"; -.include "/usr/local/etc/jail.conf.d/*.conf"; -.include "/usr/local/etc/jail.*.conf"; diff --git a/templates/ingress/nginx_snippet_ban.inc b/templates/ingress/nginx_snippet_ban.inc deleted file mode 100644 index 029b965..0000000 --- a/templates/ingress/nginx_snippet_ban.inc +++ /dev/null @@ -1,17 +0,0 @@ -# Trap route -location ^~ /wp-admin/ { - proxy_set_header X-IP $remote_addr; - proxy_method POST; - proxy_pass http://unix:/var/run/pfban/ban.sock:/ban; - - proxy_connect_timeout 50ms; - proxy_send_timeout 50ms; - proxy_read_timeout 50ms; - - # If the socket isn't up yet, still return something - error_page 500 502 503 504 = @ban_fallback; -} - -location @ban_fallback { - return 204; -} diff --git a/templates/ingress/pf-ban-socket.py b/templates/ingress/pf-ban-socket.py deleted file mode 100644 index cbcd80e..0000000 --- a/templates/ingress/pf-ban-socket.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -import http.server -import os -import re -import socketserver -import subprocess - -SOCK_PATH = "/var/run/pfban/ban.sock" -PF_TABLE = "blocked" - -# Simple, conservative filter to avoid junk / injection -IP_RE = re.compile(r"^[0-9A-Fa-f:.]{3,}$") - - -def ensure_socket_dir(path: str) -> None: - os.makedirs(os.path.dirname(path), mode=0o755, exist_ok=True) - - -class BanHandler(http.server.BaseHTTPRequestHandler): - # Silence default logging - def log_message(self, format, *args): - return - - def do_POST(self): - if self.path != "/ban": - self.send_response(404) - self.end_headers() - return - - ip = self.headers.get("X-IP", "").strip() - - if ip and IP_RE.match(ip): - subprocess.run( - ["/sbin/pfctl", "-t", PF_TABLE, "-T", "add", ip], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - check=False, - ) - - self.send_response(204) - self.end_headers() - - def do_GET(self): - self.send_response(405) - self.end_headers() - - -class ThreadingUnixHTTPServer( - socketserver.ThreadingMixIn, - socketserver.UnixStreamServer, -): - daemon_threads = True - - -def main() -> None: - ensure_socket_dir(SOCK_PATH) - - # Remove stale socket if present - try: - os.unlink(SOCK_PATH) - except FileNotFoundError: - pass - - with ThreadingUnixHTTPServer(SOCK_PATH, BanHandler) as httpd: - # Allow nginx workers (www) to connect - os.chmod(SOCK_PATH, 0o660) - - httpd.serve_forever() - - -if __name__ == "__main__": - main() diff --git a/templates/ingress/usr_local_etc_rc.d_pf_ban_socket b/templates/ingress/usr_local_etc_rc.d_pf_ban_socket deleted file mode 100644 index d21bc2d..0000000 --- a/templates/ingress/usr_local_etc_rc.d_pf_ban_socket +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -# -# PROVIDE: pf_ban_socket -# REQUIRE: NETWORKING pf -# KEYWORD: shutdown -# -# Enable in /etc/rc.conf: -# pf_ban_socket_enable="YES" -# -. /etc/rc.subr - -name="pf_ban_socket" -rcvar=pf_ban_socket_enable - -load_rc_config $name - -: ${pf_ban_socket_command:=/usr/local/bin/pf-ban-socket.py} -: ${pf_ban_socket_log:=/var/log/${name}.log} - -start_cmd="${name}_start" -stop_cmd="${name}_stop" -status_cmd="${name}_status" - -extra_commands="status" - -pf_ban_socket_start() { - /usr/local/bin/logto ${pf_ban_socket_log} ${pf_ban_socket_command} & -} - -pf_ban_socket_status() { - if pgrep -f "pf-ban-socket.py"; then - echo "pf_ban_socket is running" - else - echo "pf_ban_socket is not running" - exit 1 - fi -} - -pf_ban_socket_stop() { - pkill "pf-ban-socket.py" -} - -run_rc_command "$1" diff --git a/templates/jail_confs/_default.j2 b/templates/jail_confs/_default.j2 deleted file mode 100644 index 3a4e492..0000000 --- a/templates/jail_confs/_default.j2 +++ /dev/null @@ -1,5 +0,0 @@ -devfs_ruleset = 4; - -{% include 'jail_confs/_epl_network.j2' %} - -exec.poststart += "jexec ${name} route add default {{ ingress_ip }} || echo 'Failed to add default route'"; diff --git a/templates/jail_confs/_epl_network.j2 b/templates/jail_confs/_epl_network.j2 deleted file mode 100644 index fa3744a..0000000 --- a/templates/jail_confs/_epl_network.j2 +++ /dev/null @@ -1,18 +0,0 @@ -## _epl_network begin -exec.prestart += "ifconfig epair{{ jail.num }}000 create || echo 'Failed to create epair{{ jail.num }}000'"; -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 += "jexec ${name} ifconfig epl{{ jail.num }}b up"; -exec.poststart += "ifconfig epl{{ jail.num }}a up"; -exec.poststart += "jexec ${name} ifconfig epl{{ jail.num }}b {{ jail_lan_prefix }}.{{ jail.num + jail_ip_offset }}/{{ jail_lan_prefixlen }}"; -exec.poststart += "jexec ${name} route delete default || echo 'No default route to delete'"; - -{% if not is_test_vm %} -exec.poststart += "jexec ${name} route add 10.6.210.0/24 {{ lan_ipv4_gateway }} || echo 'Failed to add Wireguard return route'"; -{% endif %} -exec.poststop += "ifconfig epl{{ jail.num }}a destroy"; -## _epl_network end diff --git a/templates/jail_confs/dl.j2 b/templates/jail_confs/dl.j2 deleted file mode 100644 index 8d98ef4..0000000 --- a/templates/jail_confs/dl.j2 +++ /dev/null @@ -1,6 +0,0 @@ -{% include 'jail_confs/_epl_network.j2' %} -devfs_ruleset = 4; -allow.mlock; - -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage /usr/local/jails/containers/dl/mnt/storage || echo 'Failed to mount'"; -exec.poststop += "umount /usr/local/jails/containers/dl/mnt/storage || echo 'Failed to umount'"; diff --git a/templates/jail_confs/ingress.j2 b/templates/jail_confs/ingress.j2 deleted file mode 100644 index 5964d06..0000000 --- a/templates/jail_confs/ingress.j2 +++ /dev/null @@ -1,22 +0,0 @@ -devfs_ruleset = 6; - -{% include 'jail_confs/_epl_network.j2' %} - -exec.prestart += "ifconfig epair{{ jail.num }}001 create || echo 'Failed to create epair{{ jail.num }}001'"; -exec.prestart += "ifconfig epair{{ jail.num }}001a name epw1a"; -exec.prestart += "ifconfig epair{{ jail.num }}001b name epw1b"; -exec.prestart += "ifconfig brwan0 addm epw{{ jail.num }}a"; - -exec.poststart += "ifconfig epw{{ jail.num }}b vnet ${name}"; -exec.poststart += "jexec ${name} ifconfig epw{{ jail.num }}b up"; -exec.poststart += "ifconfig epw{{ jail.num }}a up"; -{% if is_test_vm %} -exec.poststart += "jexec ${name} ifconfig epw{{ jail.num }}b inet 10.0.20.3/24"; -exec.poststart += "jexec ${name} route add default 10.0.20.1"; -{% else %} -exec.poststart += "jexec ${name} service dhclient restart epw{{ jail.num }}b"; -{% endif %} - -exec.prestart += "mount -t nullfs /usr/local/jails/containers/goaccess/var/www/goaccess /usr/local/jails/containers/ingress/mnt/www_goaccess || echo 'Failed to mount'"; -exec.poststop += "umount /usr/local/jails/containers/ingress/mnt/www_goaccess || echo 'Failed to umount'"; -exec.poststop += "ifconfig epw{{ jail.num }}a destroy"; diff --git a/templates/jail_confs/komga.j2 b/templates/jail_confs/komga.j2 deleted file mode 100644 index 2567fa2..0000000 --- a/templates/jail_confs/komga.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{% include 'jail_confs/_default.j2' %} - -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/media/manga /usr/local/jails/containers/komga/mnt/manga || echo 'Failed to mount'"; -exec.poststop += "umount /usr/local/jails/containers/komga/mnt/manga || echo 'Failed to umount'"; diff --git a/templates/jail_confs/plex.j2 b/templates/jail_confs/plex.j2 deleted file mode 100644 index 2446a14..0000000 --- a/templates/jail_confs/plex.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{% include 'jail_confs/_default.j2' %} - -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/media /usr/local/jails/containers/plex/mnt/media || echo 'Failed to mount'"; -exec.poststop += "umount /usr/local/jails/containers/plex/mnt/media || echo 'Failed to umount'"; diff --git a/templates/jail_confs/postgres.j2 b/templates/jail_confs/postgres.j2 deleted file mode 100644 index bb3d1f1..0000000 --- a/templates/jail_confs/postgres.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{% include 'jail_confs/_epl_network.j2' %} -devfs_ruleset = 5; - -allow.sysvipc; diff --git a/templates/jail_confs/samba.j2 b/templates/jail_confs/samba.j2 deleted file mode 100644 index 24835fa..0000000 --- a/templates/jail_confs/samba.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{% include 'jail_confs/_default.j2' %} - -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage /usr/local/jails/containers/samba/mnt/storage || echo 'Failed to mount'"; -exec.poststop += "umount /usr/local/jails/containers/samba/mnt/storage || echo 'Failed to umount'"; diff --git a/templates/jail_confs/syncthing.j2 b/templates/jail_confs/syncthing.j2 deleted file mode 100644 index 980d47c..0000000 --- a/templates/jail_confs/syncthing.j2 +++ /dev/null @@ -1,11 +0,0 @@ -{% include 'jail_confs/_default.j2' %} - -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/docs /usr/local/jails/containers/syncthing/mnt/docs || echo 'Failed to mount'"; -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/vault /usr/local/jails/containers/syncthing/mnt/vault || echo 'Failed to mount'"; -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/jan-systems-2025-content /usr/local/jails/containers/syncthing/mnt/jan-systems-2025-content || echo 'Failed to mount'"; -exec.prestart += "mount -t nullfs /usr/local/jails/volumes/storage/projects-ableton /usr/local/jails/containers/syncthing/mnt/projects-ableton || echo 'Failed to mount'"; - -exec.poststop += "umount /usr/local/jails/containers/syncthing/mnt/projects-ableton || echo 'Failed to umount'"; -exec.poststop += "umount /usr/local/jails/containers/syncthing/mnt/jan-systems-2025-content || echo 'Failed to umount'"; -exec.poststop += "umount /usr/local/jails/containers/syncthing/mnt/vault || echo 'Failed to umount'"; -exec.poststop += "umount /usr/local/jails/containers/syncthing/mnt/docs || echo 'Failed to umount'"; @@ -43,7 +43,7 @@ SEED_ISO="${VM_DIR}/seed.iso" SSH_KEY="${VM_DIR}/id_ed25519" SSH_PORT_LAN=22 -VM_RAM=2048 +VM_RAM=4096 VM_CPUS=2 DISK_SIZE=20G @@ -100,6 +100,7 @@ runcmd: - cp /usr/share/zoneinfo/Europe/Helsinki /etc/localtime - sysrc ifconfig_vtnet0="inet 10.0.20.2/24" - sysrc defaultrouter="10.0.20.1" + - echo 'vfs.zfs.arc.max=1073741824' >> /boot/loader.conf - ifconfig vtnet0 inet 10.0.20.2/24 - route add default 10.0.20.1 - sed -i '' 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config @@ -151,8 +152,6 @@ cmd_up() { -cdrom "$SEED_ISO" \ -netdev vmnet-shared,id=lan,start-address=10.0.20.1,end-address=10.0.20.254,subnet-mask=255.255.255.0 \ -device virtio-net-pci,netdev=lan \ - -netdev vmnet-shared,id=wan,start-address=10.0.20.1,end-address=10.0.20.254,subnet-mask=255.255.255.0 \ - -device virtio-net-pci,netdev=wan \ -serial unix:"$SERIAL_SOCK",server,nowait \ -monitor unix:"$MONITOR_SOCK",server,nowait \ -pidfile "$PID_FILE" \ @@ -207,7 +206,7 @@ cmd_reset() { } cmd_ssh() { - exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + exec env SSH_AUTH_SOCK= ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityAgent=none \ -i "$SSH_KEY" root@10.0.20.2 "$@" } |
