diff --git a/.ostree/README.md b/.ostree/README.md new file mode 100644 index 0000000..530d20b --- /dev/null +++ b/.ostree/README.md @@ -0,0 +1,5 @@ +*NOTE*: The `*.txt` files are used by `get_ostree_data.sh` to create the lists +of packages, and to find other system roles used by this role. DO NOT use them +directly. + +The script `meta/make_ostree_packages_files` is used to generate these files. diff --git a/.ostree/get_ostree_data.sh b/.ostree/get_ostree_data.sh new file mode 100755 index 0000000..cec08b0 --- /dev/null +++ b/.ostree/get_ostree_data.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +set -euo pipefail + +ostree_dir="${OSTREE_DIR:-"$(dirname "$(realpath "$0")")"}" + +if [ -z "${4:-}" ] || [ "${1:-}" = help ] || [ "${1:-}" = -h ]; then + cat <&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH + exit 2 +} + +get_packages() { + local ostree_dir pkgtype pkgfile rolefile + ostree_dir="$1" + for pkgtype in "${pkgtypes[@]}"; do + for suff in "" "-$distro" "-${distro}-${major_ver}" "-${distro}-${ver}"; do + pkgfile="$ostree_dir/packages-${pkgtype}${suff}.txt" + if [ -f "$pkgfile" ]; then + cat "$pkgfile" + fi + done + rolefile="$ostree_dir/roles-${pkgtype}.txt" + if [ -f "$rolefile" ]; then + local roles role rolepath + roles="$(cat "$rolefile")" + for role in $roles; do + rolepath="$(get_rolepath "$ostree_dir" "$role")" + if [ -z "$rolepath" ]; then + 1>&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH + exit 2 + fi + get_packages "$rolepath" + done + fi + done | sort -u +} + +format_packages_json() { + local comma pkgs pkg + comma="" + pkgs="[" + while read -r pkg; do + pkgs="${pkgs}${comma}\"${pkg}\"" + comma=, + done + pkgs="${pkgs}]" + echo "$pkgs" +} + +format_packages_raw() { + cat +} + +format_packages_yaml() { + while read -r pkg; do + echo "- $pkg" + done +} + +format_packages_toml() { + while read -r pkg; do + echo "[[packages]]" + echo "name = \"$pkg\"" + echo "version = \"*\"" + done +} + +distro="${distro_ver%%-*}" +ver="${distro_ver##*-}" +if [[ "$ver" =~ ^([0-9]*) ]]; then + major_ver="${BASH_REMATCH[1]}" +else + echo ERROR: cannot parse major version number from version "$ver" + exit 1 +fi + +"get_$category" "$ostree_dir" | "format_${category}_$format" diff --git a/.ostree/packages-runtime-CentOS-6.txt b/.ostree/packages-runtime-CentOS-6.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-CentOS-6.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-CentOS-7.txt b/.ostree/packages-runtime-CentOS-7.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-CentOS-7.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-CentOS-8.txt b/.ostree/packages-runtime-CentOS-8.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-CentOS-8.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-CentOS-9.txt b/.ostree/packages-runtime-CentOS-9.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-CentOS-9.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-Fedora.txt b/.ostree/packages-runtime-Fedora.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-Fedora.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-RedHat-6.txt b/.ostree/packages-runtime-RedHat-6.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-RedHat-6.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-RedHat-7.txt b/.ostree/packages-runtime-RedHat-7.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-RedHat-7.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-RedHat-8.txt b/.ostree/packages-runtime-RedHat-8.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-RedHat-8.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-runtime-RedHat-9.txt b/.ostree/packages-runtime-RedHat-9.txt new file mode 100644 index 0000000..a725f65 --- /dev/null +++ b/.ostree/packages-runtime-RedHat-9.txt @@ -0,0 +1,2 @@ +openssh +openssh-server diff --git a/.ostree/packages-testing-CentOS.txt b/.ostree/packages-testing-CentOS.txt new file mode 100644 index 0000000..35562c2 --- /dev/null +++ b/.ostree/packages-testing-CentOS.txt @@ -0,0 +1 @@ +man-db diff --git a/.ostree/packages-testing-Fedora.txt b/.ostree/packages-testing-Fedora.txt new file mode 100644 index 0000000..35562c2 --- /dev/null +++ b/.ostree/packages-testing-Fedora.txt @@ -0,0 +1 @@ +man-db diff --git a/.ostree/packages-testing-RedHat.txt b/.ostree/packages-testing-RedHat.txt new file mode 100644 index 0000000..35562c2 --- /dev/null +++ b/.ostree/packages-testing-RedHat.txt @@ -0,0 +1 @@ +man-db diff --git a/.ostree/packages-testing.txt b/.ostree/packages-testing.txt new file mode 100644 index 0000000..71e9adc --- /dev/null +++ b/.ostree/packages-testing.txt @@ -0,0 +1,2 @@ +bash +openssh-server diff --git a/.ostree/roles-runtime.txt b/.ostree/roles-runtime.txt new file mode 100644 index 0000000..a41c34a --- /dev/null +++ b/.ostree/roles-runtime.txt @@ -0,0 +1,2 @@ +firewall +selinux diff --git a/README-ostree.md b/README-ostree.md new file mode 100644 index 0000000..a9f0185 --- /dev/null +++ b/README-ostree.md @@ -0,0 +1,66 @@ +# rpm-ostree + +The role supports running on [rpm-ostree](https://coreos.github.io/rpm-ostree/) +systems. The primary issue is that the `/usr` filesystem is read-only, and the +role cannot install packages. Instead, it will just verify that the necessary +packages and any other `/usr` files are pre-installed. The role will change the +package manager to one that is compatible with `rpm-ostree` systems. + +## Building + +To build an ostree image for a particular operating system distribution and +version, use the script `.ostree/get_ostree_data.sh` to get the list of +packages. If the role uses other system roles, then the script will include the +packages for the other roles in the list it outputs. The list of packages will +be sorted in alphanumeric order. + +Usage: + +```bash +.ostree/get_ostree_data.sh packages runtime DISTRO-VERSION FORMAT +``` + +`DISTRO-VERSION` is in the format that Ansible uses for `ansible_distribution` +and `ansible_distribution_version` - for example, `Fedora-38`, `CentOS-8`, +`RedHat-9.4` + +`FORMAT` is one of `toml`, `json`, `yaml`, `raw` + +* `toml` - each package in a TOML `[[packages]]` element + +```toml +[[packages]] +name = "package-a" +version = "*" +[[packages]] +name = "package-b" +version = "*" +... +``` + +* `yaml` - a YAML list of packages + +```yaml +- package-a +- package-b +... +``` + +* `json` - a JSON list of packages + +```json +["package-a","package-b",...] +``` + +* `raw` - a plain text list of packages, one per line + +```bash +package-a +package-b +... +``` + +What format you choose depends on which image builder you are using. For +example, if you are using something based on +[osbuild-composer](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/composing_installing_and_managing_rhel_for_edge_images/index#creating-an-image-builder-blueprint-for-a-rhel-for-edge-image-using-the-command-line-interface_composing-a-rhel-for-edge-image-using-image-builder-command-line), +you will probably want to use the `toml` output format. diff --git a/README.md b/README.md index d8d1748..d423bce 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ If you want to use advanced functionality of this role that can configure firewall and selinux for you, which is mostly useful when custom port is used, the role requires additional collections which are specified in `meta/collection-requirements.yml`. These are not automatically installed. +If you want to manage `rpm-ostree` systems, additional collections are required. You must install them like this: ```bash @@ -58,7 +59,8 @@ ansible-galaxy install -vv -r meta/collection-requirements.yml ``` For more information, see `sshd_manage_firewall` and `sshd_manage_selinux` -options below. These roles are supported only on Red Hat based Linux. +options below, and the `rpm-ostree` section. This additional functionality is +supported only on Red Hat based Linux. ## Role variables @@ -455,6 +457,10 @@ to the `options_body` and/or `options_match`. To regenerate the templates, from within the `meta/` directory run: `./make_option_lists` +## rpm-ostree + +See README-ostree.md + ## License LGPLv3 diff --git a/meta/collection-requirements.yml b/meta/collection-requirements.yml index a9c13c4..d767353 100644 --- a/meta/collection-requirements.yml +++ b/meta/collection-requirements.yml @@ -1,3 +1,4 @@ --- collections: + - name: ansible.posix - name: fedora.linux_system_roles diff --git a/meta/make_ostree_packages_files b/meta/make_ostree_packages_files new file mode 100755 index 0000000..9451346 --- /dev/null +++ b/meta/make_ostree_packages_files @@ -0,0 +1,48 @@ +#!/bin/bash + +set -euo pipefail + +declare -A os_family=( [RedHat]=RedHat [CentOS]=RedHat [Fedora]=RedHat + [AlmaLinux]=RedHat [Rocky]=RedHat [OracleLinux]=RedHat ) + +for file in vars/*.yml; do + if [ "$file" = vars/main.yml ]; then + continue + fi + # get platform and optional version + if [[ "$file" =~ vars/([^_]+)_([0-9]+).yml$ ]]; then + platform="${BASH_REMATCH[1]}" + version="${BASH_REMATCH[2]}" + packages_file=".ostree/packages-runtime-${platform}-${version}.txt" + elif [[ "$file" =~ vars/([^_.]+).yml$ ]]; then + platform="${BASH_REMATCH[1]}" + packages_file=".ostree/packages-runtime-${platform}.txt" + else + echo ERROR: cannot parse "$file" + exit 1 + fi + # only os_family == RedHat is supported + if [ "${os_family[$platform]:-}" = RedHat ]; then + : # proceed - fall through + else + echo platform "$platform" not supported for ostree + continue + fi + # parse packages from file + printit=0 + while read -r item pkg; do + if [[ "$item" =~ ^__sshd_packages: ]]; then + printit=1 + elif [ "$printit" = 1 ]; then + if [ "$item" = "-" ] && [ -n "$pkg" ]; then + echo "$pkg" + else + break + fi + fi + done < "$file" | sort > "$packages_file" + # remove empty files + if [ ! -s "$packages_file" ]; then + rm -f "$packages_file" + fi +done diff --git a/tasks/install.yml b/tasks/install.yml index 3230371..228af84 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -8,6 +8,8 @@ ansible.builtin.package: name: "{{ sshd_packages }}" state: present + use: "{{ (__sshd_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" - name: Sysconfig configuration ansible.builtin.template: diff --git a/tasks/variables.yml b/tasks/variables.yml index 3c263aa..aef0478 100644 --- a/tasks/variables.yml +++ b/tasks/variables.yml @@ -5,6 +5,18 @@ when: not ansible_facts.keys() | list | intersect(__sshd_required_facts) == __sshd_required_facts +- name: Determine if system is ostree and set flag + when: not __sshd_is_ostree is defined + block: + - name: Check if system is ostree + ansible.builtin.stat: + path: /run/ostree-booted + register: __ostree_booted_stat + + - name: Set flag to indicate system is ostree + ansible.builtin.set_fact: + __sshd_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" + - name: Set OS dependent variables ansible.builtin.include_vars: "{{ lookup('first_found', params) }}" vars: diff --git a/tests/tasks/setup.yml b/tests/tasks/setup.yml index db14ee4..d5fbd43 100644 --- a/tests/tasks/setup.yml +++ b/tests/tasks/setup.yml @@ -5,10 +5,35 @@ when: - ansible_facts['distribution'] == 'Debian' +- name: Determine if system is ostree and set flag + when: not __sshd_is_ostree is defined + block: + - name: Check if system is ostree + ansible.builtin.stat: + path: /run/ostree-booted + register: __ostree_booted_stat + + - name: Set flag to indicate system is ostree + ansible.builtin.set_fact: + __sshd_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" + +- name: Ensure test users exist on ostree systems + ansible.builtin.shell: | + if ! grep -q ^nobody /etc/passwd && grep -q ^nobody /usr/lib/passwd; then + grep ^nobody /usr/lib/passwd >> /etc/passwd + fi + if ! grep -q ^nobody /etc/group && grep -q ^nobody /usr/lib/group; then + grep ^nobody /usr/lib/group >> /etc/group + fi + when: __sshd_is_ostree | d(false) + changed_when: true + - name: Make sure openssh is installed before creating backup ansible.builtin.package: name: openssh-server state: present + use: "{{ (__sshd_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" - name: Make sure openssh has runtime directory on debian ansible.builtin.file: diff --git a/tests/tests_all_options.yml b/tests/tests_all_options.yml index ff66e99..5641c24 100644 --- a/tests/tests_all_options.yml +++ b/tests/tests_all_options.yml @@ -28,11 +28,13 @@ state: absent when: - ansible_facts['os_family'] == "RedHat" + - not __sshd_is_ostree | d(false) - name: Reinstall manual pages for openssh-server on RHEL ansible.builtin.command: "{{ pkg_mgr | quote }} reinstall -y openssh-server" when: - ansible_facts['os_family'] == "RedHat" + - not __sshd_is_ostree | d(false) changed_when: true - name: Unminimize image on Debian. It looks like there is no simpler way to get manual pages @@ -49,17 +51,35 @@ - openssh-doc - bash state: present + use: "{{ (__sshd_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" when: - ansible_facts['distribution'] == "Alpine" + - name: Make sure manual pages and bash are installed on RedHat 7+ + ansible.builtin.package: + name: + - man-db + - bash + state: present + use: "{{ (__sshd_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + when: + - ansible_facts['os_family'] == "RedHat" + - ansible_facts['distribution_major_version'] | int > 6 + - name: Make sure manual pages and bash are installed elsewhere ansible.builtin.package: name: - man - bash state: present + use: "{{ (__sshd_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" when: - ansible_facts['distribution'] != "Alpine" + - ansible_facts['os_family'] != "RedHat" or + ansible_facts['distribution_major_version'] | int == 6 - name: Get list of options from manual page ansible.builtin.shell: >-