From 4c908893f0d4ffaf5e0531b6efcfd3c427872435 Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Thu, 29 Oct 2026 16:59:10 -0600 Subject: [PATCH 1/3] Fix issues with ownership when relocating platform directories with custom user/group --- roles/platform/CLAUDE.md | 7 +- roles/platform/tasks/certify-platform.yml | 72 +++++++++---------- roles/platform/tasks/create-itential-user.yml | 3 - roles/platform/tasks/main.yml | 37 ++-------- roles/platform/tasks/set-file-ownership.yml | 28 ++++---- roles/platform/tasks/validate-vars.yml | 18 +++++ .../platform-validation-report.md.j2 | 2 +- 7 files changed, 81 insertions(+), 86 deletions(-) diff --git a/roles/platform/CLAUDE.md b/roles/platform/CLAUDE.md index 4f585f37..936bee9c 100644 --- a/roles/platform/CLAUDE.md +++ b/roles/platform/CLAUDE.md @@ -9,14 +9,12 @@ Installs and configures Itential Platform (IAP). Handles OS user/directory setup 1. `validate-vars.yml` (always tagged) — validates required variables and loads release-specific vars 2. Configure OS: a. `create-itential-user.yml` — create `itential` user and group - b. Create directories: `platform_server_dir`, `platform_config_dir`, `platform_log_dir` - c. Add sudoers entry for `chroot` (required by TemplateBuilder) - d. `configure-firewalld.yml` — open HTTP and HTTPS ports + b. `configure-firewalld.yml` — open HTTP and HTTPS ports 3. Install Dependencies, Platform, Adapters, App Artifacts (notifies `Update Itential release file`): a. `install-platform-dependency-packages.yml` — install OS packages b. `install-nodejs.yml` — install NodeJS c. `install-python.yml` — install Python; installs pip dependencies as `platform_user` (`itential`) via `pip install --user` rather than system-wide as root, then validates each application dependency actually imports (see `roles/python/CLAUDE.md`). No `/usr/local/bin` python3/pip3 symlinks are created — the P6 Platform RPM already creates its own `python3` symlink under `/home/itential/bin`. - d. `install-platform.yml` — install IAP RPM packages. The itential-platform RPM declares a hardcoded `itential:itential` owner for `platform_server_dir`/`platform_config_dir`/`platform_log_dir` in its own package metadata, so installing/upgrading it silently resets ownership on these directories. Immediately after this step, `platform_user`/`platform_group` ownership is re-applied to all three (same `ansible.builtin.file` call as step 2b) so a non-default `platform_user` can actually start the service afterward. + d. `install-platform.yml` — install IAP RPM packages. The itential-platform RPM declares a hardcoded `itential:itential` owner for the paths it ships (`platform_root_dir`, `platform_services_dir`, `platform_server_dir`, `platform_config_dir`, `platform_log_dir`, and `platform_itential_home_dir`), so installing/upgrading it silently resets ownership on them. Immediately after this step, when `platform_user`/`platform_group` is not `itential`, `set-file-ownership.yml` runs `chown -hR platform_user:platform_group` on `platform_root_dir` (which contains the server and services dirs) and `platform_itential_home_dir`, and a non-recursive `chown -h` on `platform_config_dir` and `platform_log_dir` (their contents are created after the install). It uses `chown` rather than the `file` module because a recursive `file` run over ~250k files takes minutes. This lets a non-default `platform_user` start the service afterward. e. `install-adapters.yml` — install configured adapters f. `install-app-artifacts.yml` — when `platform_app_artifacts_enabled: true` 4. `configure-selinux.yml` — apply SELinux configuration @@ -31,6 +29,7 @@ Installs and configures Itential Platform (IAP). Handles OS user/directory setup - Asserts repo credentials when packages are downloaded from HTTP URLs - Asserts `platform_release` is defined OR all individual vars (`platform_nodejs_package`, `platform_python_version`, `platform_python_packages`, `platform_python_app_dependencies`) are defined - Asserts `platform_encryption_key` is a 64-character hex string +- Asserts `platform_root_dir`, `platform_config_dir`, `platform_log_dir` and `platform_itential_home_dir` are not top-level system directories (`/`, `/etc`, `/home`, `/var`, `/var/log`, etc.), since ownership of these is changed to `platform_user:platform_group` - Validates Vault variables when `platform_configure_vault: true` - Validates PKI source directories when copy-certs flags are enabled - Loads `vars/platform-release-.yml` and sets release-specific defaults for NodeJS/Python when not overridden in inventory diff --git a/roles/platform/tasks/certify-platform.yml b/roles/platform/tasks/certify-platform.yml index 2b2de734..d35102a0 100644 --- a/roles/platform/tasks/certify-platform.yml +++ b/roles/platform/tasks/certify-platform.yml @@ -39,11 +39,11 @@ - name: Check Itential config file exists ansible.builtin.stat: - path: /etc/itential/platform.properties + path: "{{ platform_config_dir }}/platform.properties" register: platform_conf_file - name: Get Itential config file permissions - ansible.builtin.command: ls -la /etc/itential/platform.properties + ansible.builtin.command: ls -la {{ platform_config_dir }}/platform.properties register: platform_conf_permissions ignore_errors: true changed_when: false @@ -56,7 +56,7 @@ - name: Parse Itential config for server_id ansible.builtin.shell: cmd: > - grep '^server_id' /etc/itential/platform.properties | + grep '^server_id' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_server_id @@ -67,7 +67,7 @@ - name: Parse Itential config for mongo_auth_enabled ansible.builtin.shell: cmd: > - grep '^mongo_auth_enabled' /etc/itential/platform.properties + grep '^mongo_auth_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_auth_enabled @@ -78,7 +78,7 @@ - name: Parse Itential config for mongo_user ansible.builtin.shell: cmd: > - grep '^mongo_user' /etc/itential/platform.properties + grep '^mongo_user' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_user @@ -89,7 +89,7 @@ - name: Parse Itential config for mongo_auth_db ansible.builtin.shell: cmd: > - grep '^mongo_auth_db' /etc/itential/platform.properties | + grep '^mongo_auth_db' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_auth_db @@ -100,7 +100,7 @@ - name: Parse Itential config for mongo_db_name ansible.builtin.shell: cmd: > - grep '^mongo_db_name' /etc/itential/platform.properties | + grep '^mongo_db_name' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_db_name @@ -111,7 +111,7 @@ - name: Parse Itential config for mongo_url ansible.builtin.shell: cmd: > - grep '^mongo_url' /etc/itential/platform.properties | + grep '^mongo_url' {{ platform_config_dir }}/platform.properties | sed 's/^mongo_url[[:space:]]*=[[:space:]]*//' | xargs register: platform_mongo_url @@ -132,7 +132,7 @@ - name: Parse Itential config for mongo_tls_enabled ansible.builtin.shell: cmd: > - grep '^mongo_tls_enabled' /etc/itential/platform.properties | + grep '^mongo_tls_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_tls_enabled @@ -143,7 +143,7 @@ - name: Parse Itential config for mongo_read_concern ansible.builtin.shell: cmd: > - grep '^mongo_read_concern' /etc/itential/platform.properties | + grep '^mongo_read_concern' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_read_concern @@ -154,7 +154,7 @@ - name: Parse Itential config for mongo_write_concern ansible.builtin.shell: cmd: > - grep '^mongo_write_concern' /etc/itential/platform.properties | + grep '^mongo_write_concern' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_mongo_write_concern @@ -165,7 +165,7 @@ - name: Parse Itential config for redis_username ansible.builtin.shell: cmd: > - grep '^redis_username' /etc/itential/platform.properties | + grep '^redis_username' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_username @@ -176,7 +176,7 @@ - name: Parse Itential config for redis_host ansible.builtin.shell: cmd: > - grep '^redis_host' /etc/itential/platform.properties | + grep '^redis_host' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_host @@ -187,7 +187,7 @@ - name: Parse Itential config for redis_sentinel_username ansible.builtin.shell: cmd: > - grep '^redis_sentinel_username' /etc/itential/platform.properties | + grep '^redis_sentinel_username' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_sentinel_username @@ -198,7 +198,7 @@ - name: Parse Itential config for redis_sentinels ansible.builtin.shell: cmd: > - grep '^redis_sentinels' /etc/itential/platform.properties | + grep '^redis_sentinels' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_sentinels @@ -209,7 +209,7 @@ - name: Parse Itential config for redis_name ansible.builtin.shell: cmd: > - grep '^redis_name' /etc/itential/platform.properties | + grep '^redis_name' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_name @@ -220,7 +220,7 @@ - name: Parse Itential config for redis_tls ansible.builtin.shell: cmd: > - grep '^redis_tls' /etc/itential/platform.properties + grep '^redis_tls' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_redis_tls @@ -231,7 +231,7 @@ - name: Parse Itential config for task_worker_enabled ansible.builtin.shell: cmd: > - grep '^task_worker_enabled' /etc/itential/platform.properties | + grep '^task_worker_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_task_worker_enabled @@ -242,7 +242,7 @@ - name: Parse Itential config for job_worker_enabled ansible.builtin.shell: cmd: > - grep '^job_worker_enabled' /etc/itential/platform.properties | + grep '^job_worker_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_job_worker_enabled @@ -253,7 +253,7 @@ - name: Parse Itential config for default_user_username ansible.builtin.shell: cmd: > - grep '^default_user_username' /etc/itential/platform.properties | + grep '^default_user_username' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_default_user_username @@ -264,7 +264,7 @@ - name: Parse Itential config for webserver_http_enabled ansible.builtin.shell: cmd: > - grep '^webserver_http_enabled' /etc/itential/platform.properties | + grep '^webserver_http_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_http_enabled @@ -275,7 +275,7 @@ - name: Parse Itential config for webserver_https_enabled ansible.builtin.shell: cmd: > - grep '^webserver_https_enabled' /etc/itential/platform.properties | + grep '^webserver_https_enabled' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_https_enabled @@ -286,7 +286,7 @@ - name: Parse Itential config for webserver_http_port ansible.builtin.shell: cmd: > - grep '^webserver_http_port' /etc/itential/platform.properties | + grep '^webserver_http_port' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_http_port @@ -297,7 +297,7 @@ - name: Parse Itential config for webserver_https_port ansible.builtin.shell: cmd: > - grep '^webserver_https_port' /etc/itential/platform.properties | + grep '^webserver_https_port' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_https_port @@ -308,7 +308,7 @@ - name: Parse Itential config for webserver_https_key ansible.builtin.shell: cmd: > - grep '^webserver_https_key' /etc/itential/platform.properties | + grep '^webserver_https_key' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_https_key @@ -319,7 +319,7 @@ - name: Parse Itential config for webserver_https_cert ansible.builtin.shell: cmd: > - grep '^webserver_https_cert' /etc/itential/platform.properties | + grep '^webserver_https_cert' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_https_cert @@ -330,7 +330,7 @@ - name: Parse Itential config for log_directory ansible.builtin.shell: cmd: > - grep '^log_directory' /etc/itential/platform.properties | + grep '^log_directory' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_log_directory @@ -341,7 +341,7 @@ - name: Parse Itential config for log_file ansible.builtin.shell: cmd: > - grep '^log_file' /etc/itential/platform.properties | + grep '^log_file' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_log_file @@ -352,7 +352,7 @@ - name: Parse Itential config for log_level ansible.builtin.shell: cmd: > - grep '^log_level[[:space:]]*=' /etc/itential/platform.properties | + grep '^log_level[[:space:]]*=' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_log_level @@ -363,7 +363,7 @@ - name: Parse Itential config for webserver_log_directory ansible.builtin.shell: cmd: > - grep '^webserver_log_directory' /etc/itential/platform.properties | + grep '^webserver_log_directory' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_log_directory @@ -374,7 +374,7 @@ - name: Parse Itential config for webserver_log_filename ansible.builtin.shell: cmd: > - grep '^webserver_log_filename' /etc/itential/platform.properties | + grep '^webserver_log_filename' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_webserver_log_filename @@ -385,7 +385,7 @@ - name: Parse Itential config for services_directory ansible.builtin.shell: cmd: > - grep '^service_directory' /etc/itential/platform.properties | + grep '^service_directory' {{ platform_config_dir }}/platform.properties | awk -F'=' '{print $2}' | xargs register: platform_service_directory @@ -628,7 +628,7 @@ changed_when: false failed_when: false become: true - become_user: "itential" + become_user: "{{ platform_user }}" become_flags: '-i' # become_method: ansible.builtin.su @@ -639,7 +639,7 @@ changed_when: false failed_when: false become: true - become_user: "itential" + become_user: "{{ platform_user }}" become_flags: '-i' - name: Get list of installed Python modules @@ -649,7 +649,7 @@ changed_when: false failed_when: false become: true - become_user: "itential" + become_user: "{{ platform_user }}" become_flags: '-i' - name: Get pip version @@ -659,7 +659,7 @@ changed_when: false failed_when: false become: true - become_user: "itential" + become_user: "{{ platform_user }}" become_flags: '-i' # ============================================================================ diff --git a/roles/platform/tasks/create-itential-user.yml b/roles/platform/tasks/create-itential-user.yml index 367dd020..10e1e5cc 100644 --- a/roles/platform/tasks/create-itential-user.yml +++ b/roles/platform/tasks/create-itential-user.yml @@ -1,9 +1,6 @@ # Copyright (c) 2024, Itential, Inc # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- - -# TODO: Remove this file once the RPM is creating the directories correctly - - name: Create itential group ansible.builtin.group: name: "{{ platform_group }}" diff --git a/roles/platform/tasks/main.yml b/roles/platform/tasks/main.yml index 93e54422..6f26d23f 100644 --- a/roles/platform/tasks/main.yml +++ b/roles/platform/tasks/main.yml @@ -12,24 +12,10 @@ - name: Configure OS tags: configure_os block: - # TODO: Remove once the Itential RPM is creating the directories correctly - name: Create itential user ansible.builtin.include_tasks: file: create-itential-user.yml - # TODO: Remove once the Itential RPM is creating the directories correctly - - name: Create itential directories - ansible.builtin.file: - name: "{{ item }}" - state: directory - owner: "{{ platform_user }}" - group: "{{ platform_group }}" - mode: "0775" - with_items: - - "{{ platform_server_dir }}" - - "{{ platform_config_dir }}" - - "{{ platform_log_dir }}" - - name: Configure firewalld ansible.builtin.include_tasks: file: configure-firewalld.yml @@ -65,22 +51,13 @@ ansible.builtin.include_tasks: file: install-platform.yml - # The itential-platform RPM declares a hardcoded itential:itential - # owner for these directories in its own package metadata, so - # installing/upgrading it resets any ownership set beforehand. - # Re-apply platform_user/platform_group here so a non-default - # platform_user can actually read/write them. - - name: Re-apply itential directory ownership after RPM install - ansible.builtin.file: - name: "{{ item }}" - state: directory - owner: "{{ platform_user }}" - group: "{{ platform_group }}" - mode: "0775" - with_items: - - "{{ platform_server_dir }}" - - "{{ platform_config_dir }}" - - "{{ platform_log_dir }}" + # The itential-platform RPM hardcodes itential:itential ownership on + # every path it ships (root, home, config and log dirs), on both + # install and upgrade. Re-own them to platform_user/platform_group + # so a non-default user owns everything. + - name: Set ownership of Itential Platform files after RPM install + ansible.builtin.include_tasks: + file: set-file-ownership.yml when: platform_user != 'itential' or platform_group != 'itential' - name: Install Adapters diff --git a/roles/platform/tasks/set-file-ownership.yml b/roles/platform/tasks/set-file-ownership.yml index 6ea7ba6c..6313e3e0 100644 --- a/roles/platform/tasks/set-file-ownership.yml +++ b/roles/platform/tasks/set-file-ownership.yml @@ -2,17 +2,21 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- -# TODO: Remove this file once the RPM is creating the directories correctly - -# Using chown and chmod is a faster way to enforce the file ownership and -# permissions. The file module in ansible checks each and every file/dir -# in the tree, 'chown -R' does not, it just sets it. +# The itential-platform RPM declares itential:itential ownership on every path it +# ships, so installing it resets any ownership set beforehand. platform_server_dir +# and platform_services_dir live under platform_root_dir, so they are covered by it. +# The root and home trees are shipped by the RPM and are re-owned recursively. The +# config and log dirs only hold files created after the install, so only the +# directories themselves are re-owned. +# Using chown is much faster than the file module with recurse (seconds vs. minutes +# for ~250k files). -h re-owns symlinks (e.g. /bin/python3) rather than their targets. - name: Set appropriate ownership on all itential files - ansible.builtin.command: - cmd: "chown -R {{ platform_user }}:{{ platform_group }} {{ platform_server_dir }}" - changed_when: true - -- name: Set appropriate permissions on all itential files - ansible.builtin.command: - cmd: "chmod -R 775 {{ platform_server_dir }}" + ansible.builtin.shell: + cmd: >- + chown -hR {{ platform_user }}:{{ platform_group }} + {{ platform_root_dir | quote }} + {{ platform_itential_home_dir | quote }} && + chown -h {{ platform_user }}:{{ platform_group }} + {{ platform_config_dir | quote }} + {{ platform_log_dir | quote }} changed_when: true diff --git a/roles/platform/tasks/validate-vars.yml b/roles/platform/tasks/validate-vars.yml index 7f92a2ba..2e688fe8 100644 --- a/roles/platform/tasks/validate-vars.yml +++ b/roles/platform/tasks/validate-vars.yml @@ -70,6 +70,24 @@ fail_msg: platform_encryption_key must be a 64 character hexadecimal string when: validate_platform_encryption_key | default(true) + - name: Validate Itential Platform directories are not system directories + ansible.builtin.assert: + that: >- + (item | regex_replace('(?<=.)/+$', '')) not in + ['/', '/bin', '/boot', '/dev', '/etc', '/home', '/lib', '/lib64', '/media', '/mnt', + '/opt', '/proc', '/root', '/run', '/sbin', '/srv', '/sys', '/tmp', '/usr', '/var', + '/var/log'] + fail_msg: >- + {{ item }} is a system directory. Itential Platform changes ownership of + platform_root_dir, platform_config_dir, platform_log_dir and + platform_itential_home_dir to platform_user:platform_group. Set them to + dedicated directories. + loop: + - "{{ platform_root_dir }}" + - "{{ platform_config_dir }}" + - "{{ platform_log_dir }}" + - "{{ platform_itential_home_dir }}" + - name: Validate that platform_otel_webserver_metrics_enabled is a boolean when set ansible.builtin.assert: that: platform_otel_webserver_metrics_enabled is none or platform_otel_webserver_metrics_enabled is boolean diff --git a/roles/platform/templates/platform-validation-report.md.j2 b/roles/platform/templates/platform-validation-report.md.j2 index 869ebeea..2e2a83e5 100644 --- a/roles/platform/templates/platform-validation-report.md.j2 +++ b/roles/platform/templates/platform-validation-report.md.j2 @@ -82,7 +82,7 @@ Host details not available ## Configuration Files - **Config File Exists:** {{ 'YES ✓' if (platform_conf_file is defined and platform_conf_file.stat is defined and platform_conf_file.stat.exists) else 'NO ✗' }} {% if platform_conf_file is defined and platform_conf_file.stat is defined and platform_conf_file.stat.exists %} -- **Config File Path:** `/etc/itential/platform.properties` +- **Config File Path:** `{{ platform_config_dir }}/platform.properties` - **Permissions:** {{ platform_conf_permissions.stdout | default('Unknown') if platform_conf_permissions is defined else 'Unknown' }} {% endif %} From 4f638809f6d2d752878ac97f1ed2702156c6e442 Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Fri, 30 Oct 2026 07:43:21 -0600 Subject: [PATCH 2/3] Consolidate platform file ownership task --- roles/platform/CLAUDE.md | 2 +- roles/platform/tasks/install-platform.yml | 21 ++++++++++++++++++++ roles/platform/tasks/main.yml | 9 --------- roles/platform/tasks/set-file-ownership.yml | 22 --------------------- 4 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 roles/platform/tasks/set-file-ownership.yml diff --git a/roles/platform/CLAUDE.md b/roles/platform/CLAUDE.md index 936bee9c..6671043e 100644 --- a/roles/platform/CLAUDE.md +++ b/roles/platform/CLAUDE.md @@ -14,7 +14,7 @@ Installs and configures Itential Platform (IAP). Handles OS user/directory setup a. `install-platform-dependency-packages.yml` — install OS packages b. `install-nodejs.yml` — install NodeJS c. `install-python.yml` — install Python; installs pip dependencies as `platform_user` (`itential`) via `pip install --user` rather than system-wide as root, then validates each application dependency actually imports (see `roles/python/CLAUDE.md`). No `/usr/local/bin` python3/pip3 symlinks are created — the P6 Platform RPM already creates its own `python3` symlink under `/home/itential/bin`. - d. `install-platform.yml` — install IAP RPM packages. The itential-platform RPM declares a hardcoded `itential:itential` owner for the paths it ships (`platform_root_dir`, `platform_services_dir`, `platform_server_dir`, `platform_config_dir`, `platform_log_dir`, and `platform_itential_home_dir`), so installing/upgrading it silently resets ownership on them. Immediately after this step, when `platform_user`/`platform_group` is not `itential`, `set-file-ownership.yml` runs `chown -hR platform_user:platform_group` on `platform_root_dir` (which contains the server and services dirs) and `platform_itential_home_dir`, and a non-recursive `chown -h` on `platform_config_dir` and `platform_log_dir` (their contents are created after the install). It uses `chown` rather than the `file` module because a recursive `file` run over ~250k files takes minutes. This lets a non-default `platform_user` start the service afterward. + d. `install-platform.yml` — install IAP RPM packages. The itential-platform RPM declares a hardcoded `itential:itential` owner for the paths it ships (`platform_root_dir`, `platform_services_dir`, `platform_server_dir`, `platform_config_dir`, `platform_log_dir`, and `platform_itential_home_dir`), so installing/upgrading it silently resets ownership on them. At the end of `install-platform.yml`, when `platform_user`/`platform_group` is not `itential`, a task runs `chown -hR platform_user:platform_group` on `platform_root_dir` (which contains the server and services dirs) and `platform_itential_home_dir`, and a non-recursive `chown -h` on `platform_config_dir` and `platform_log_dir` (their contents are created after the install). It uses `chown` rather than the `file` module because a recursive `file` run over ~250k files takes minutes. This lets a non-default `platform_user` start the service afterward. e. `install-adapters.yml` — install configured adapters f. `install-app-artifacts.yml` — when `platform_app_artifacts_enabled: true` 4. `configure-selinux.yml` — apply SELinux configuration diff --git a/roles/platform/tasks/install-platform.yml b/roles/platform/tasks/install-platform.yml index 5f2f4946..3dd82f66 100644 --- a/roles/platform/tasks/install-platform.yml +++ b/roles/platform/tasks/install-platform.yml @@ -87,3 +87,24 @@ vars: offline_rpms_path: "{{ platform_offline_control_node_rpms_dir }}/platform" when: offline_install_enabled + +# The itential-platform RPM hardcodes itential:itential ownership on every path it +# ships (root, home, config and log dirs), on both install and upgrade. Re-own them +# to platform_user/platform_group so a non-default user owns everything. +# platform_server_dir and platform_services_dir live under platform_root_dir, so they +# are covered by it. The root and home trees are re-owned recursively. The config and +# log dirs only hold files created after the install, so only the dirs are re-owned. +# chown is used instead of the file module with recurse because it takes seconds vs. +# minutes for ~250k files. -h re-owns symlinks (e.g. /bin/python3) rather than +# their targets. +- name: Set ownership of Itential Platform files after RPM install + ansible.builtin.shell: + cmd: >- + chown -hR {{ platform_user }}:{{ platform_group }} + {{ platform_root_dir | quote }} + {{ platform_itential_home_dir | quote }} && + chown -h {{ platform_user }}:{{ platform_group }} + {{ platform_config_dir | quote }} + {{ platform_log_dir | quote }} + changed_when: true + when: platform_user != 'itential' or platform_group != 'itential' diff --git a/roles/platform/tasks/main.yml b/roles/platform/tasks/main.yml index 6f26d23f..08416163 100644 --- a/roles/platform/tasks/main.yml +++ b/roles/platform/tasks/main.yml @@ -51,15 +51,6 @@ ansible.builtin.include_tasks: file: install-platform.yml - # The itential-platform RPM hardcodes itential:itential ownership on - # every path it ships (root, home, config and log dirs), on both - # install and upgrade. Re-own them to platform_user/platform_group - # so a non-default user owns everything. - - name: Set ownership of Itential Platform files after RPM install - ansible.builtin.include_tasks: - file: set-file-ownership.yml - when: platform_user != 'itential' or platform_group != 'itential' - - name: Install Adapters tags: install_adapters block: diff --git a/roles/platform/tasks/set-file-ownership.yml b/roles/platform/tasks/set-file-ownership.yml deleted file mode 100644 index 6313e3e0..00000000 --- a/roles/platform/tasks/set-file-ownership.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2024, Itential, Inc -# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) ---- - -# The itential-platform RPM declares itential:itential ownership on every path it -# ships, so installing it resets any ownership set beforehand. platform_server_dir -# and platform_services_dir live under platform_root_dir, so they are covered by it. -# The root and home trees are shipped by the RPM and are re-owned recursively. The -# config and log dirs only hold files created after the install, so only the -# directories themselves are re-owned. -# Using chown is much faster than the file module with recurse (seconds vs. minutes -# for ~250k files). -h re-owns symlinks (e.g. /bin/python3) rather than their targets. -- name: Set appropriate ownership on all itential files - ansible.builtin.shell: - cmd: >- - chown -hR {{ platform_user }}:{{ platform_group }} - {{ platform_root_dir | quote }} - {{ platform_itential_home_dir | quote }} && - chown -h {{ platform_user }}:{{ platform_group }} - {{ platform_config_dir | quote }} - {{ platform_log_dir | quote }} - changed_when: true From 54edaaaca8401bbf6ebff96fc6842848baf04cbc Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Fri, 30 Oct 2026 09:32:15 -0600 Subject: [PATCH 3/3] Fix issue with platform log directory relocate --- roles/platform/CLAUDE.md | 1 + .../tasks/install-platform-packages-relocate.yml | 2 +- roles/platform/tasks/validate-vars.yml | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/roles/platform/CLAUDE.md b/roles/platform/CLAUDE.md index 6671043e..8880fde2 100644 --- a/roles/platform/CLAUDE.md +++ b/roles/platform/CLAUDE.md @@ -30,6 +30,7 @@ Installs and configures Itential Platform (IAP). Handles OS user/directory setup - Asserts `platform_release` is defined OR all individual vars (`platform_nodejs_package`, `platform_python_version`, `platform_python_packages`, `platform_python_app_dependencies`) are defined - Asserts `platform_encryption_key` is a 64-character hex string - Asserts `platform_root_dir`, `platform_config_dir`, `platform_log_dir` and `platform_itential_home_dir` are not top-level system directories (`/`, `/etc`, `/home`, `/var`, `/var/log`, etc.), since ownership of these is changed to `platform_user:platform_group` +- Asserts `platform_log_dir` ends in `/platform`. The RPM's relocatable prefix is `/var/log/itential` and it always creates `platform` beneath it, so `install-platform-packages-relocate.yml` relocates the parent of `platform_log_dir` - Validates Vault variables when `platform_configure_vault: true` - Validates PKI source directories when copy-certs flags are enabled - Loads `vars/platform-release-.yml` and sets release-specific defaults for NodeJS/Python when not overridden in inventory diff --git a/roles/platform/tasks/install-platform-packages-relocate.yml b/roles/platform/tasks/install-platform-packages-relocate.yml index 808dee6b..6950d565 100644 --- a/roles/platform/tasks/install-platform-packages-relocate.yml +++ b/roles/platform/tasks/install-platform-packages-relocate.yml @@ -27,7 +27,7 @@ vars: relocations: - { old: "{{ platform_root_dir_default }}", new: "{{ platform_root_dir }}" } - - { old: "{{ platform_log_dir_default }}", new: "{{ platform_log_dir }}" } + - { old: "{{ platform_log_dir_default | dirname }}", new: "{{ platform_log_dir | dirname }}" } - { old: "{{ platform_config_dir_default }}", new: "{{ platform_config_dir }}" } - { old: "{{ platform_itential_home_dir_default }}", new: "{{ platform_itential_home_dir }}" } loop: "{{ platform_packages_find_result.files | map(attribute='path') }}" diff --git a/roles/platform/tasks/validate-vars.yml b/roles/platform/tasks/validate-vars.yml index 2e688fe8..8547db4d 100644 --- a/roles/platform/tasks/validate-vars.yml +++ b/roles/platform/tasks/validate-vars.yml @@ -88,6 +88,15 @@ - "{{ platform_log_dir }}" - "{{ platform_itential_home_dir }}" + - name: Validate platform_log_dir can be relocated + ansible.builtin.assert: + that: platform_log_dir | basename == platform_log_dir_default | basename + fail_msg: >- + platform_log_dir must end in /{{ platform_log_dir_default | basename }}. The + itential-platform RPM only relocates the parent directory + ({{ platform_log_dir_default | dirname }}) and always creates + {{ platform_log_dir_default | basename }} beneath it. + - name: Validate that platform_otel_webserver_metrics_enabled is a boolean when set ansible.builtin.assert: that: platform_otel_webserver_metrics_enabled is none or platform_otel_webserver_metrics_enabled is boolean