From bd606ada300052cb3482f7a95b98909dadd30979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Mass=C3=A9?= Date: Thu, 23 Jul 2026 09:39:41 +0000 Subject: [PATCH] add support for polkit rules, fix permissions, create users & groups --- cookbooks/base/Makefile | 10 ++++ scripts/common.mk | 82 +++++++++++++++++++++++--------- scripts/polkit.eslint.config.mjs | 21 ++++++++ 3 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 scripts/polkit.eslint.config.mjs diff --git a/cookbooks/base/Makefile b/cookbooks/base/Makefile index 371440f..4675d85 100644 --- a/cookbooks/base/Makefile +++ b/cookbooks/base/Makefile @@ -19,3 +19,13 @@ pre-requisites:: exit 1; \ fi ; \ done + +install-actions-pre:: + @set -Eeuo pipefail; \ + run() { echo $$*; "$$@"; }; \ + if ! getent group itix-svc >/dev/null; then \ + run groupadd -g 10000 itix-svc; \ + fi ; \ + if ! getent passwd itix-svc >/dev/null; then \ + run useradd -u 10000 -g 10000 -M -d /tmp -c "ITIX Misc. Services" itix-svc; \ + fi diff --git a/scripts/common.mk b/scripts/common.mk index 8bc497e..640fee3 100644 --- a/scripts/common.mk +++ b/scripts/common.mk @@ -84,31 +84,35 @@ SYSTEMD_TIMER_NAMES := $(wildcard *.timer) # The main systemd units will be enabled and started after installation. SYSTEMD_MAIN_UNIT_NAMES ?= $(wildcard *.target) -SYSTEMD_START_UNITS = $(SYSTEMD_MAIN_UNIT_NAMES) +SYSTEMD_START_UNITS ?= $(SYSTEMD_MAIN_UNIT_NAMES) # Generated systemd units (quadlets) cannot be enabled. # That's why we filter them out from the list of units to be enabled. -SYSTEMD_ENABLE_UNITS = $(filter-out $(QUADLET_UNIT_NAMES),$(SYSTEMD_MAIN_UNIT_NAMES)) +SYSTEMD_ENABLE_UNITS ?= $(filter-out $(QUADLET_UNIT_NAMES),$(SYSTEMD_MAIN_UNIT_NAMES)) # Configuration files CONFIG_FILES := $(shell find config/ -mindepth 1 \! -path "config/examples/*" \! -path "config/examples" 2>/dev/null) TMPFILESD_FILES = $(filter-out %/examples, $(wildcard tmpfiles.d/*)) SYSCTLD_FILES = $(filter-out %/examples, $(wildcard sysctl.d/*)) PROFILED_FILES = $(filter-out %/examples, $(wildcard profile.d/*)) +POLKITD_FILES = $(filter-out %/examples, $(wildcard polkit-rules.d/*)) TARGET_CONFIG_FILES = $(patsubst config/%, $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%, $(CONFIG_FILES)) TARGET_TMPFILESD_FILES = $(patsubst tmpfiles.d/%, $(TARGET_CHROOT)/etc/tmpfiles.d/%, $(TMPFILESD_FILES)) TARGET_SYSCTLD_FILES = $(patsubst sysctl.d/%, $(TARGET_CHROOT)/etc/sysctl.d/%, $(SYSCTLD_FILES)) TARGET_PROFILED_FILES = $(patsubst profile.d/%, $(TARGET_CHROOT)/etc/profile.d/%, $(PROFILED_FILES)) +TARGET_POLKITD_FILES = $(patsubst polkit-rules.d/%, $(TARGET_CHROOT)/etc/polkit-1/rules.d/60-%, $(POLKITD_FILES)) # Example configuration files EXAMPLES_CONFIG_FILES := $(shell find config/examples -mindepth 1 2>/dev/null) EXAMPLES_TMPFILESD_FILES = $(wildcard tmpfiles.d/examples/*) EXAMPLES_SYSCTLD_FILES = $(wildcard sysctl.d/examples/*) EXAMPLES_PROFILED_FILES = $(wildcard profile.d/examples/*) +EXAMPLES_POLKITD_FILES = $(wildcard polkit-rules.d/examples/*) TARGET_EXAMPLES_CONFIG_FILES = $(patsubst config/examples/%, $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%, $(EXAMPLES_CONFIG_FILES)) TARGET_EXAMPLES_TMPFILESD_FILES = $(patsubst tmpfiles.d/examples/%, $(TARGET_CHROOT)/etc/tmpfiles.d/%, $(EXAMPLES_TMPFILESD_FILES)) TARGET_EXAMPLES_SYSCTLD_FILES = $(patsubst sysctl.d/examples/%, $(TARGET_CHROOT)/etc/sysctl.d/%, $(EXAMPLES_SYSCTLD_FILES)) TARGET_EXAMPLES_PROFILED_FILES = $(patsubst profile.d/examples/%, $(TARGET_CHROOT)/etc/profile.d/%, $(EXAMPLES_PROFILED_FILES)) +TARGET_EXAMPLES_POLKITD_FILES = $(patsubst polkit-rules.d/examples/%, $(TARGET_CHROOT)/etc/polkit-1/rules.d/60-%, $(EXAMPLES_POLKITD_FILES)) # Example quadlet and systemd drop-ins files EXAMPLES_QUADLET_DROPINS_FILES := $(shell if [ -d dropins ]; then find dropins -mindepth 1 -type f | grep -E '\.(container|volume|network|pod|build|image)\.d/' 2>/dev/null; fi) @@ -119,10 +123,10 @@ TARGET_EXAMPLES_SYSTEMD_DROPINS_FILES = $(patsubst dropins/%, $(TARGET_CHROOT)/e # All configuration files to be installed TARGET_FILES += $(addprefix $(TARGET_CHROOT)/etc/containers/systemd/, $(QUADLETS_FILES)) \ $(addprefix $(TARGET_CHROOT)/etc/systemd/system/, $(SYSTEMD_FILES)) \ - $(TARGET_CONFIG_FILES) $(TARGET_TMPFILESD_FILES) $(TARGET_SYSCTLD_FILES) $(TARGET_PROFILED_FILES) + $(TARGET_CONFIG_FILES) $(TARGET_TMPFILESD_FILES) $(TARGET_SYSCTLD_FILES) $(TARGET_PROFILED_FILES) $(TARGET_POLKITD_FILES) # All example configuration files to be installed -TARGET_EXAMPLE_FILES += $(TARGET_EXAMPLES_CONFIG_FILES) $(TARGET_EXAMPLES_TMPFILESD_FILES) $(TARGET_EXAMPLES_SYSCTLD_FILES) $(TARGET_EXAMPLES_PROFILED_FILES) $(TARGET_EXAMPLES_QUADLET_DROPINS_FILES) $(TARGET_EXAMPLES_SYSTEMD_DROPINS_FILES) +TARGET_EXAMPLE_FILES += $(TARGET_EXAMPLES_CONFIG_FILES) $(TARGET_EXAMPLES_TMPFILESD_FILES) $(TARGET_EXAMPLES_SYSCTLD_FILES) $(TARGET_EXAMPLES_PROFILED_FILES) $(TARGET_EXAMPLES_POLKITD_FILES) $(TARGET_EXAMPLES_QUADLET_DROPINS_FILES) $(TARGET_EXAMPLES_SYSTEMD_DROPINS_FILES) # Dependencies on other projects # List here the names of other projects (directories at the top-level) that this project depends on. @@ -162,16 +166,27 @@ pre-requisites:: exit 1; \ fi @set -Eeuo pipefail; \ - for tool in install systemctl systemd-analyze systemd-tmpfiles sysctl virt-install virsh qemu-img journalctl coreos-installer resize butane yq podlet pip3 ncat; do \ + for tool in install systemctl systemd-analyze systemd-tmpfiles sysctl virt-install virsh qemu-img journalctl coreos-installer resize butane yq podlet pip3 ncat npx; do \ if ! which $$tool &>/dev/null ; then \ echo "$$tool is not installed. Please install it first." >&2; \ exit 1; \ fi ; \ done +# ESLint used to lint polkit rules. Pinned for reproducibility; override for a local install (e.g. ESLINT=eslint). +dryrun: ESLINT ?= npx --yes eslint@9 +# Polkit JavaScript rules shipped by this project (linted during dryrun). +dryrun: POLKIT_RULES_FILES = $(POLKITD_FILES) $(EXAMPLES_POLKITD_FILES) + # Perform a dry run of the podman systemd generator to validate the quadlet and systemd files. +# Also lint the polkit rules using ESLint. dryrun: QUADLET_UNIT_DIRS="$$PWD" /usr/lib/systemd/system-generators/podman-system-generator -dryrun > /dev/null + @run() { echo $$*; "$$@"; }; \ + set -Eeuo pipefail; \ + if [ x$(POLKIT_RULES_FILES) != x ]; then \ + run $(ESLINT) --no-config-lookup --config $(SCRIPTS_DIR)/polkit.eslint.config.mjs $(POLKIT_RULES_FILES); \ + fi # Create the base directories needed for installation. $(TARGET_CHROOT)/etc/containers/systemd $(TARGET_CHROOT)/etc/systemd/system $(TARGET_CHROOT)/etc/tmpfiles.d $(TARGET_CHROOT)/etc/sysctl.d $(TARGET_CHROOT)/etc/profile.d: @@ -189,29 +204,40 @@ $(TARGET_CHROOT)/etc/containers/systemd/%: % $(TARGET_CHROOT)/etc/containers/sys $(TARGET_CHROOT)/etc/systemd/system/%: % $(TARGET_CHROOT)/etc/systemd/system install -m 0644 -o root -g root $< $@ -# Copy configuration files, handling executable and non-executable files differently. +# Copy configuration files, handling .env files, "container" build contexts and regular files differently. $(TARGET_CONFIG_FILES): $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%: config/% $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME) $(TARGET_EXAMPLES_CONFIG_FILES): $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%: config/examples/% $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME) -$(filter-out %.env, $(TARGET_CONFIG_FILES) $(TARGET_EXAMPLES_CONFIG_FILES)): +$(TARGET_CONFIG_FILES) $(TARGET_EXAMPLES_CONFIG_FILES): @run() { echo $$*; "$$@"; }; \ set -Eeuo pipefail; \ - if [ -d $< ]; then \ - run install -d -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) $@; \ - else \ - path="$<"; \ - extension="$${path##*.}"; \ - if [ "$$extension" == "sh" ] && [ -x "$<" ]; then \ + case "$<" in \ + *.env) \ + # .env files may hold secrets: owned by root:root with restrictive 0600 permissions. \ + run install -D -m 0600 -o root -g root $< $@ ;; \ + */container|*/container/*) \ + # Build contexts (Containerfile, entrypoints, ...) are consumed by root: owned by root:root with broad permissions. \ + if [ -d "$<" ]; then \ + run install -d -m 0755 -o root -g root $@; \ + elif [ -x "$<" ]; then \ run install -m 0755 -o root -g root $< $@; \ - elif [ -x $< ]; then \ - run install -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@; \ else \ - run install -m 0644 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@; \ - fi ; \ - fi; \ - -# Handle .env files separately to set more restrictive permissions -$(filter %.env, $(TARGET_CONFIG_FILES) $(TARGET_EXAMPLES_CONFIG_FILES)): - install -m 0600 -o root -g root -D $< $@ + run install -m 0644 -o root -g root $< $@; \ + fi ;; \ + *) \ + if [ -d "$<" ]; then \ + run install -d -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) $@; \ + else \ + path="$<"; \ + extension="$${path##*.}"; \ + if [ "$$extension" == "sh" ] && [ -x "$<" ]; then \ + run install -m 0755 -o root -g root $< $@; \ + elif [ -x $< ]; then \ + run install -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@; \ + else \ + run install -m 0644 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@; \ + fi ; \ + fi ;; \ + esac; \ # Copy systemd and quadlet drop-ins files $(TARGET_EXAMPLES_QUADLET_DROPINS_FILES): $(TARGET_CHROOT)/etc/containers/systemd/%: dropins/% $(TARGET_CHROOT)/etc/containers/systemd @@ -237,6 +263,12 @@ $(TARGET_EXAMPLES_PROFILED_FILES): $(TARGET_CHROOT)/etc/profile.d/%: profile.d/e $(TARGET_PROFILED_FILES) $(TARGET_EXAMPLES_PROFILED_FILES): install -D -m 0644 -o root -g root $< $@ +# Copy polkit-rules.d files +$(TARGET_POLKITD_FILES): $(TARGET_CHROOT)/etc/polkit-1/rules.d/60-%: polkit-rules.d/% $(TARGET_CHROOT)/etc/polkit-1/rules.d +$(TARGET_EXAMPLES_POLKITD_FILES): $(TARGET_CHROOT)/etc/polkit-1/rules.d/60-%: polkit-rules.d/examples/% $(TARGET_CHROOT)/etc/polkit-1/rules.d +$(TARGET_POLKITD_FILES) $(TARGET_EXAMPLES_POLKITD_FILES): + install -D -m 0644 -o root -g root $< $@ + # Create the directory to store quadlet state and data. $(TARGET_CHROOT)/var/lib/quadlets/$(PROJECT_NAME): install -d -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) $@ @@ -269,6 +301,12 @@ install-actions: install-actions-pre systemctl daemon-reload @run() { echo $$*; "$$@"; }; \ set -Eeuo pipefail; \ + if [[ "$(PROJECT_GID)" -ne 0 ]] && ! getent group "$(PROJECT_GID)" &>/dev/null; then \ + run groupadd -g $(PROJECT_GID) $(PROJECT_NAME); \ + fi; \ + if [[ "$(PROJECT_UID)" -ne 0 ]] && ! getent passwd "$(PROJECT_UID)" >/dev/null 2>&1; then \ + run useradd -u $(PROJECT_UID) -g $(PROJECT_GID) -M -d /var/lib/quadlets/$(PROJECT_NAME) -c "$(PROJECT_NAME) quadlet" $(PROJECT_NAME); \ + fi; \ if [[ ! "$(QUADLET_UNIT_NAMES)" =~ ^[[:space:]]*$$ ]]; then \ run systemd-analyze --generators=true verify $(QUADLET_UNIT_NAMES); \ fi; \ diff --git a/scripts/polkit.eslint.config.mjs b/scripts/polkit.eslint.config.mjs new file mode 100644 index 0000000..de091cc --- /dev/null +++ b/scripts/polkit.eslint.config.mjs @@ -0,0 +1,21 @@ +// ESLint flat config for polkit JavaScript rules. +// Fedora's polkit uses the duktape engine: ES5.1 only, with a `polkit` global +// (plus Polkit/Netgroup helpers) injected at runtime. +export default [ + { + files: ["**/*.rules"], + languageOptions: { + ecmaVersion: 5, + sourceType: "script", + globals: { + polkit: "readonly", + Polkit: "readonly", + Netgroup: "readonly", + }, + }, + rules: { + "no-undef": "error", // catches typos on the polkit API / action ids stored in vars + "no-unused-vars": "warn", + }, + }, +]; \ No newline at end of file