From a2fad7d4c038362e607ab78b858094672bc8124f Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Wed, 6 May 2026 17:02:49 -0400 Subject: [PATCH 1/3] Consolidate certificate vars into a single file The default and custom_server certificate vars files defined identical paths since custom certificates are normalized into the same directory structure during deployment. Remove the vars file indirection and use a single certificates.yml for all certificate sources. Co-Authored-By: Claude Opus 4.6 --- development/playbooks/deploy-dev/deploy-dev.yaml | 2 +- src/playbooks/deploy/deploy.yaml | 2 +- .../{default_certificates.yml => certificates.yml} | 0 src/vars/custom_server_certificates.yml | 14 -------------- tests/conftest.py | 4 ++-- 5 files changed, 4 insertions(+), 18 deletions(-) rename src/vars/{default_certificates.yml => certificates.yml} (100%) delete mode 100644 src/vars/custom_server_certificates.yml diff --git a/development/playbooks/deploy-dev/deploy-dev.yaml b/development/playbooks/deploy-dev/deploy-dev.yaml index caf89c1d9..803b3816c 100644 --- a/development/playbooks/deploy-dev/deploy-dev.yaml +++ b/development/playbooks/deploy-dev/deploy-dev.yaml @@ -5,7 +5,7 @@ vars_files: - "../../../src/vars/defaults.yml" - "../../../src/vars/flavors/{{ flavor }}.yml" - - "../../../src/vars/{{ certificates_source }}_certificates.yml" + - "../../../src/vars/certificates.yml" - "../../../src/vars/images.yml" - "../../../src/vars/database.yml" - "../../../src/vars/foreman.yml" diff --git a/src/playbooks/deploy/deploy.yaml b/src/playbooks/deploy/deploy.yaml index 837d36c98..8fb18f0ff 100644 --- a/src/playbooks/deploy/deploy.yaml +++ b/src/playbooks/deploy/deploy.yaml @@ -6,7 +6,7 @@ vars_files: - "../../vars/defaults.yml" - "../../vars/flavors/{{ flavor }}.yml" - - "../../vars/{{ certificates_source }}_certificates.yml" + - "../../vars/certificates.yml" - "../../vars/images.yml" - "../../vars/tuning/{{ tuning }}.yml" - "../../vars/database.yml" diff --git a/src/vars/default_certificates.yml b/src/vars/certificates.yml similarity index 100% rename from src/vars/default_certificates.yml rename to src/vars/certificates.yml diff --git a/src/vars/custom_server_certificates.yml b/src/vars/custom_server_certificates.yml deleted file mode 100644 index a70f33583..000000000 --- a/src/vars/custom_server_certificates.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -certificates_ca_directory: /root/certificates -ca_key_password: "{{ certificates_ca_directory }}/private/ca.pwd" -ca_certificate: "{{ certificates_ca_directory }}/certs/ca.crt" -ca_key: "{{ certificates_ca_directory }}/private/ca.key" -server_certificate: "{{ certificates_ca_directory }}/certs/{{ ansible_facts['fqdn'] }}.crt" -server_key: "{{ certificates_ca_directory }}/private/{{ ansible_facts['fqdn'] }}.key" -server_ca_certificate: "{{ certificates_ca_directory }}/certs/server-ca.crt" -ca_bundle: "{{ certificates_ca_directory }}/certs/ca-bundle.crt" -client_certificate: "{{ certificates_ca_directory }}/certs/{{ ansible_facts['fqdn'] }}-client.crt" -client_key: "{{ certificates_ca_directory }}/private/{{ ansible_facts['fqdn'] }}-client.key" -client_ca_certificate: "{{ certificates_ca_directory }}/certs/ca.crt" -localhost_key: "{{ certificates_ca_directory }}/private/localhost.key" -localhost_certificate: "{{ certificates_ca_directory }}/certs/localhost.crt" diff --git a/tests/conftest.py b/tests/conftest.py index 656632c90..5a850a530 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,9 +45,9 @@ def client_fqdn(client_hostname): @pytest.fixture(scope="module") -def certificates(certificate_source, server_fqdn): +def certificates(server_fqdn): env = Environment(loader=FileSystemLoader("."), autoescape=select_autoescape()) - template = env.get_template(f"./src/vars/{certificate_source}_certificates.yml") + template = env.get_template("./src/vars/certificates.yml") context = {'ansible_facts': {'fqdn': server_fqdn}} # we have vars that refer to other vars, so load them once and then re-render the template context.update(yaml.safe_load(template.render(context))) From eda771068446ceca2cb71a7a77449b1d7182262c Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Wed, 6 May 2026 16:24:09 -0400 Subject: [PATCH 2/3] Add migration CI job to test the migrate command Add a dedicated CI job that exercises the foremanctl migrate workflow: install foreman-installer to create a realistic environment with an answer file and certificates, run foremanctl migrate to convert the answer file to foremanctl parameters, then deploy and test the result. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/test.yml | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10c21d23c..f06101056 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -278,6 +278,68 @@ jobs: ## If no one connects after 5 minutes, shut down server. wait-timeout-minutes: 5 + migration: + strategy: + fail-fast: false + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + - name: Setup libvirt for Vagrant + uses: voxpupuli/setup-vagrant@v0 + - name: Install Ansible + run: pip install --upgrade ansible-core + - name: Setup environment + run: ./setup-environment + - name: Start VMs + run: | + ./forge vms start --vms "quadlet client" + - name: Configure repositories + run: | + ./forge setup-repositories + - name: Create installer certificates + run: | + ./forge installer-certs + - name: Run image pull + run: | + ./foremanctl pull-images + - name: Run migration + run: | + ./foremanctl migrate --output /var/lib/foremanctl/parameters.yaml + - name: Run deployment + run: | + ./foremanctl deploy --certificate-source=installer --foreman-initial-admin-password=changeme --initial-organization "Foreman CI" --initial-location "Internet" --tuning development + - name: Run tests + run: | + ./forge test --pytest-args="--certificate-source=installer" + - name: Run smoker + run: | + ./forge smoker + - name: Archive smoker report + if: ${{ always() }} + uses: actions/upload-artifact@v7 + with: + name: smoker-migration + path: "/home/runner/smoker/report/" + - name: Generate sos reports + if: ${{ always() }} + run: ./forge sos + - name: Archive sos reports + if: ${{ always() }} + uses: actions/upload-artifact@v7 + with: + name: sosreport-migration + path: sos/ + - name: Setup upterm session + if: ${{ failure() }} + uses: owenthereal/action-upterm@v1 + with: + limit-access-to-actor: true + wait-timeout-minutes: 5 + # A dummy job that you can mark as a required check instead of each individual test test-suite: if: always() @@ -285,6 +347,7 @@ jobs: - tests - devel-tests - upgrade + - migration - ansible-lint runs-on: ubuntu-latest name: Test suite From b14f1a8c347c5e2194ec7f8605c9130e39a92a40 Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Wed, 6 May 2026 16:26:07 -0400 Subject: [PATCH 3/3] Auto-detect and normalize installer certificates Move foreman-installer certificate normalization into the migrate subcommand so it runs once during migration rather than on every deploy. The migrate_certificates role copies certs from /root/ssl-build/ into /root/certificates/, persists the CA passphrase into parameters.yaml, and backs up the original directory. Detect custom server certificates by comparing the internal CA with the server CA. When they differ, persist certificates_source: custom_server to prevent subsequent deploys from overwriting the custom server cert. Remove the installer certificate source since migrated certs use the default source paths after normalization. Mark certificate path parameters as IGNORE in the answer file migration since the role handles cert files directly. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/test.yml | 12 +- docs/user/certificates.md | 38 ++--- .../_certificate_source/metadata.obsah.yaml | 3 +- src/playbooks/deploy/metadata.obsah.yaml | 3 - src/playbooks/migrate/migrate.yaml | 7 + src/plugins/modules/migrate_answers.py | 8 +- .../defaults/main.yml | 2 + .../migrate_foreman_installer/tasks/main.yml | 152 ++++++++++++++++++ src/vars/installer_certificates.yml | 24 --- tests/certificates_test.py | 2 +- tests/conftest.py | 2 +- tests/migration_test.py | 47 ++++++ tests/unit/migrate_test.py | 19 +++ 13 files changed, 249 insertions(+), 70 deletions(-) create mode 100644 src/roles/migrate_foreman_installer/defaults/main.yml create mode 100644 src/roles/migrate_foreman_installer/tasks/main.yml delete mode 100644 src/vars/installer_certificates.yml create mode 100644 tests/migration_test.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f06101056..d01e1df3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,6 @@ jobs: matrix: certificate_source: - default - - installer security: - none database: @@ -51,9 +50,6 @@ jobs: - centos/stream10 iop: - enabled - exclude: - - certificate_source: installer - box: centos/stream10 include: - certificate_source: default security: fapolicyd @@ -97,10 +93,6 @@ jobs: - name: Configure repositories run: | ./forge setup-repositories - - name: Create installer certificates - if: contains(matrix.certificate_source, 'installer') - run: | - ./forge installer-certs - name: Create custom certificates if: matrix.certificate_source == 'custom_server' run: | @@ -311,10 +303,10 @@ jobs: ./foremanctl migrate --output /var/lib/foremanctl/parameters.yaml - name: Run deployment run: | - ./foremanctl deploy --certificate-source=installer --foreman-initial-admin-password=changeme --initial-organization "Foreman CI" --initial-location "Internet" --tuning development + ./foremanctl deploy --foreman-initial-admin-password=changeme --initial-organization "Foreman CI" --initial-location "Internet" --tuning development - name: Run tests run: | - ./forge test --pytest-args="--certificate-source=installer" + ./forge test --pytest-args="--certificate-source=default" - name: Run smoker run: | ./forge smoker diff --git a/docs/user/certificates.md b/docs/user/certificates.md index 310419375..875f20316 100644 --- a/docs/user/certificates.md +++ b/docs/user/certificates.md @@ -6,7 +6,7 @@ This document describes how certificate generation and management works in forem ### Certificate Sources -foremanctl supports three certificate sources that determine how certificates are obtained: +foremanctl supports two certificate sources that determine how certificates are obtained: **Default Source (`certificate_source: default`)** - Automatically generates self-signed certificates during deployment @@ -19,11 +19,6 @@ foremanctl supports three certificate sources that determine how certificates ar - Server certificate, key, and CA bundle are copied to `/root/certificates/` - Certificate source persists across deployments; original files only needed on first deploy or when updating certificates -**Installer Source (`certificate_source: installer`)** -- Uses existing certificates from a previous `foreman-installer` deployment -- Useful for migration scenarios where certificates already exist -- Certificate files must be present at expected foreman-installer paths - ### Usage #### Using Auto-Generated Certificates (Default) @@ -59,13 +54,17 @@ foremanctl deploy \ foremanctl deploy --certificate-source=default ``` -#### Using Existing Installer Certificates +#### Migrating from foreman-installer + +When migrating from a `foreman-installer` deployment, use the `migrate` command to normalize existing certificates into foremanctl's canonical structure: ```bash -# Use certificates from previous foreman-installer -foremanctl deploy --certificate-source=installer +foremanctl migrate --output /var/lib/foremanctl/parameters.yaml +foremanctl deploy ``` +The `migrate` command copies certificates from `/root/ssl-build/` into `/root/certificates/`, persists the CA passphrase so foremanctl can issue new certificates, and backs up the original directory to `/root/ssl-build.bak/`. See the [migration guide](../migration-guide.md) for full details. + ### Certificate Locations After deployment, certificates are available at: @@ -81,10 +80,9 @@ After deployment, certificates are available at: - Server CA Certificate: `/root/certificates/certs/server-ca.crt` (custom CA that signed server cert) - Client Certificate: `/root/certificates/certs/-client.crt` (generated by internal CA) -**Installer Source:** -- CA Certificate: `/root/ssl-build/katello-default-ca.crt` -- Server Certificate: `/root/ssl-build//-apache.crt` -- Client Certificate: `/root/ssl-build//-foreman-client.crt` +**After Migration:** +- Certificates from `foreman-installer` are normalized into the same paths as the Default Source above +- Original directory is backed up to `/root/ssl-build.bak/` ### CNAME Support @@ -131,7 +129,6 @@ The `--certificate-renew` flag is **not persisted** in foremanctl’s answers fi - Uses the same lifetime for both client and server certificates - Limited certificate customization options -- Custom server certificates cannot be combined with `certificate_source: installer` - CNAMEs are only applied to certificates generated by the internal CA ## Internal Design @@ -174,10 +171,9 @@ For `certificate_source: custom_server`: 2. **Custom Server Certificates**: Copy the custom server cert, key, and CA bundle from user-provided paths to `/root/certificates/` (only when certificate paths are provided) 3. **Host Certificate Issuance**: Generate client certificate and localhost certificate signed by the internal CA (server cert for FQDN is skipped) -For `certificate_source: installer`: +#### Migration from foreman-installer -- Uses existing certificates from `/root/ssl-build/` generated by foreman-installer -- No certificate generation performed; files must already exist +The `foremanctl migrate` command includes a `migrate_foreman_installer` role that normalizes `foreman-installer` certificates into the canonical `/root/certificates/` structure. It also reads the CA passphrase from the installer's password file and persists it into foremanctl's configuration so that subsequent deploys can issue new certificates using the original CA. #### Variable System @@ -197,14 +193,6 @@ client_certificate: "{{ certificates_ca_directory }}/certs/{{ ansible_facts['fqd - The `server_ca_certificate` points to the custom CA that signed the server certificate - The `ca_bundle` contains both the internal CA and custom server CA -**Installer Source (`src/vars/installer_certificates.yml`):** -```yaml -ca_certificate: "/root/ssl-build/katello-default-ca.crt" -server_certificate: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-apache.crt" -server_ca_certificate: "/root/ssl-build/katello-server-ca.crt" -client_certificate: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-foreman-client.crt" -``` - #### Integration with Deployment In `src/playbooks/deploy/deploy.yaml`: diff --git a/src/playbooks/_certificate_source/metadata.obsah.yaml b/src/playbooks/_certificate_source/metadata.obsah.yaml index 03eb3a847..2be245b6a 100644 --- a/src/playbooks/_certificate_source/metadata.obsah.yaml +++ b/src/playbooks/_certificate_source/metadata.obsah.yaml @@ -1,9 +1,8 @@ --- variables: certificates_source: - help: Where certificates are coming from. Currently default Ansible role, the foreman-installer, or custom server certificates. + help: Where certificates are coming from. Currently default Ansible role or custom server certificates. parameter: --certificate-source choices: - default - - installer - custom_server diff --git a/src/playbooks/deploy/metadata.obsah.yaml b/src/playbooks/deploy/metadata.obsah.yaml index 0f75a7922..7a68eec1d 100644 --- a/src/playbooks/deploy/metadata.obsah.yaml +++ b/src/playbooks/deploy/metadata.obsah.yaml @@ -48,9 +48,6 @@ variables: constraints: required_together: - [certificates_custom_server_certificate, certificates_custom_server_key, certificates_custom_server_ca_certificate] - forbidden_if: - - [certificates_source, installer, [certificates_custom_server_certificate, certificates_custom_server_key, certificates_custom_server_ca_certificate]] - include: - _certificate_source - _certificate_validity diff --git a/src/playbooks/migrate/migrate.yaml b/src/playbooks/migrate/migrate.yaml index 6fa2ec577..70415cbef 100644 --- a/src/playbooks/migrate/migrate.yaml +++ b/src/playbooks/migrate/migrate.yaml @@ -27,3 +27,10 @@ vars: _unmappable_warning: "Warning: {{ migration_result.unmappable | length }} parameter(s) could not be mapped - see warnings above" _output_file_msg: "Output written to: {{ migration_result.output_file }}" + +- name: Migrate foreman-installer certificates + hosts: + - quadlet + become: true + roles: + - role: migrate_foreman_installer diff --git a/src/plugins/modules/migrate_answers.py b/src/plugins/modules/migrate_answers.py index 6eee062e8..cd2bb6cb1 100755 --- a/src/plugins/modules/migrate_answers.py +++ b/src/plugins/modules/migrate_answers.py @@ -27,10 +27,10 @@ def cast_database_mode(value): ('foreman', 'initial_admin_username'): 'foreman_initial_admin_username', ('foreman', 'initial_admin_password'): 'foreman_initial_admin_password', - # Certificate configuration - ('foreman', 'server_ssl_cert'): 'server_certificate', - ('foreman', 'server_ssl_key'): 'server_key', - ('foreman', 'server_ssl_ca'): 'ca_certificate', + # Certificate paths are handled by the migrate_foreman_installer role + ('foreman', 'server_ssl_cert'): 'IGNORE', + ('foreman', 'server_ssl_key'): 'IGNORE', + ('foreman', 'server_ssl_ca'): 'IGNORE', # TODO: Add more mappings as discovered } diff --git a/src/roles/migrate_foreman_installer/defaults/main.yml b/src/roles/migrate_foreman_installer/defaults/main.yml new file mode 100644 index 000000000..ee4b0ad9f --- /dev/null +++ b/src/roles/migrate_foreman_installer/defaults/main.yml @@ -0,0 +1,2 @@ +--- +migrate_foreman_installer_ca_directory: /root/certificates diff --git a/src/roles/migrate_foreman_installer/tasks/main.yml b/src/roles/migrate_foreman_installer/tasks/main.yml new file mode 100644 index 000000000..060e2cf9c --- /dev/null +++ b/src/roles/migrate_foreman_installer/tasks/main.yml @@ -0,0 +1,152 @@ +--- +- name: Check if installer certificates exist + ansible.builtin.stat: + path: /root/ssl-build/katello-default-ca.crt + register: migrate_foreman_installer_installer_ca + +- name: Normalize installer certificates + when: migrate_foreman_installer_installer_ca.stat.exists + block: + - name: Install crypto dependencies + ansible.builtin.package: + name: + - python3-cryptography + state: present + + - name: Create certificate directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - "{{ migrate_foreman_installer_ca_directory }}/certs" + - "{{ migrate_foreman_installer_ca_directory }}/private" + - "{{ migrate_foreman_installer_ca_directory }}/requests" + + - name: Copy CA certificate from installer + ansible.builtin.copy: + src: /root/ssl-build/katello-default-ca.crt + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/ca.crt" + remote_src: true + mode: '0444' + + - name: Copy server CA certificate from installer + ansible.builtin.copy: + src: /root/ssl-build/katello-server-ca.crt + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/server-ca.crt" + remote_src: true + mode: '0444' + + - name: Detect custom server certificates + ansible.builtin.stat: + path: "{{ item }}" + checksum_algorithm: sha256 + loop: + - /root/ssl-build/katello-default-ca.crt + - /root/ssl-build/katello-server-ca.crt + register: migrate_foreman_installer_ca_checksums + + - name: Set custom server certificate flag + ansible.builtin.set_fact: + migrate_foreman_installer_custom_server_certs: >- + {{ migrate_foreman_installer_ca_checksums.results[0].stat.checksum + != migrate_foreman_installer_ca_checksums.results[1].stat.checksum }} + + - name: Persist certificates_source for custom server certificates + ansible.builtin.lineinfile: + path: /var/lib/foremanctl/parameters.yaml + regexp: '^certificates_source:' + line: "certificates_source: custom_server" + create: true + mode: '0600' + when: migrate_foreman_installer_custom_server_certs + + - name: Create CA bundle from installer certificates + ansible.builtin.assemble: + src: "{{ migrate_foreman_installer_ca_directory }}/certs" + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/ca-bundle.crt" + regexp: '(ca|server-ca)\.crt$' + mode: '0444' + + - name: Copy CA key from installer + ansible.builtin.copy: + src: /root/ssl-build/katello-default-ca.key + dest: "{{ migrate_foreman_installer_ca_directory }}/private/ca.key" + remote_src: true + mode: '0440' + + - name: Copy CA password from installer + ansible.builtin.copy: + src: /root/ssl-build/katello-default-ca.pwd + dest: "{{ migrate_foreman_installer_ca_directory }}/private/ca.pwd" + remote_src: true + mode: '0600' + + - name: Read CA password from installer + ansible.builtin.slurp: + src: /root/ssl-build/katello-default-ca.pwd + register: migrate_foreman_installer_installer_ca_password + no_log: true + + - name: Persist CA password to foremanctl configuration + ansible.builtin.lineinfile: + path: /var/lib/foremanctl/parameters.yaml + regexp: '^certificates_ca_password:' + line: "certificates_ca_password: \"{{ migrate_foreman_installer_installer_ca_password.content | b64decode | trim }}\"" + create: true + mode: '0600' + no_log: true + + - name: Copy server certificate from installer + ansible.builtin.copy: + src: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-apache.crt" + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/{{ ansible_facts['fqdn'] }}.crt" + remote_src: true + mode: '0444' + + - name: Copy server key from installer + ansible.builtin.copy: + src: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-apache.key" + dest: "{{ migrate_foreman_installer_ca_directory }}/private/{{ ansible_facts['fqdn'] }}.key" + remote_src: true + mode: '0440' + + - name: Copy client certificate from installer + ansible.builtin.copy: + src: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-foreman-client.crt" + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/{{ ansible_facts['fqdn'] }}-client.crt" + remote_src: true + mode: '0444' + + - name: Copy client key from installer + ansible.builtin.copy: + src: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-foreman-client.key" + dest: "{{ migrate_foreman_installer_ca_directory }}/private/{{ ansible_facts['fqdn'] }}-client.key" + remote_src: true + mode: '0440' + + - name: Copy localhost certificate from installer + ansible.builtin.copy: + src: /root/ssl-build/localhost/localhost-tomcat.crt + dest: "{{ migrate_foreman_installer_ca_directory }}/certs/localhost.crt" + remote_src: true + mode: '0444' + + - name: Copy localhost key from installer + ansible.builtin.copy: + src: /root/ssl-build/localhost/localhost-tomcat.key + dest: "{{ migrate_foreman_installer_ca_directory }}/private/localhost.key" + remote_src: true + mode: '0440' + + - name: Backup installer certificate directory + ansible.builtin.copy: + src: /root/ssl-build/ + dest: /root/ssl-build.bak/ + remote_src: true + mode: preserve + + - name: Remove original installer certificate directory + ansible.builtin.file: + path: /root/ssl-build + state: absent diff --git a/src/vars/installer_certificates.yml b/src/vars/installer_certificates.yml deleted file mode 100644 index 6939e6310..000000000 --- a/src/vars/installer_certificates.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -ca_key_password: "/root/ssl-build/katello-default-ca.pwd" -ca_certificate: "/root/ssl-build/katello-default-ca.crt" -ca_key: "/root/ssl-build/katello-default-ca.key" -server_certificate: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-apache.crt" -server_key: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-apache.key" -server_ca_certificate: "/root/ssl-build/katello-server-ca.crt" -ca_bundle: "/root/ssl-build/ca-bundle.crt" -client_certificate: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-foreman-client.crt" -client_key: "/root/ssl-build/{{ ansible_facts['fqdn'] }}/{{ ansible_facts['fqdn'] }}-foreman-client.key" -client_ca_certificate: "{{ ca_certificate }}" -localhost_key: "/root/ssl-build/localhost/localhost-tomcat.key" -localhost_certificate: "/root/ssl-build/localhost/localhost-tomcat.crt" - -iop_gateway_server_certificate: "/root/ssl-build/localhost/localhost-iop-core-gateway-server.crt" -iop_gateway_server_key: "/root/ssl-build/localhost/localhost-iop-core-gateway-server.key" -iop_gateway_server_ca_certificate: "/root/ssl-build/katello-default-ca.crt" -iop_gateway_client_certificate: "/root/ssl-build/localhost/localhost-iop-core-gateway-client.crt" -iop_gateway_client_key: "/root/ssl-build/localhost/localhost-iop-core-gateway-client.key" -iop_gateway_client_ca_certificate: "/root/ssl-build/katello-server-ca.crt" -iop_vmaas_client_ca_certificate: "/root/ssl-build/katello-server-ca.crt" -iop_cvemap_downloader_client_cert: "{{ client_certificate }}" -iop_cvemap_downloader_client_key: "{{ client_key }}" -iop_cvemap_downloader_client_ca: "{{ client_ca_certificate }}" diff --git a/tests/certificates_test.py b/tests/certificates_test.py index 1eb67a1f3..c923c6416 100644 --- a/tests/certificates_test.py +++ b/tests/certificates_test.py @@ -17,7 +17,7 @@ def test_default_server_ca_matches_internal_ca(server, certificates, default_cer ca_info = certificate_info(server, certificates['ca_certificate']) server_ca_info = certificate_info(server, certificates['server_ca_certificate']) assert ca_info['subject'] == server_ca_info['subject'], \ - "Default/installer server CA should match the internal CA" + "Default server CA should match the internal CA" def test_custom_server_ca_differs_from_internal_ca(server, certificates, custom_certificates): ca_info = certificate_info(server, certificates['ca_certificate']) diff --git a/tests/conftest.py b/tests/conftest.py index 5a850a530..ef20c2c89 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,7 @@ def pytest_addoption(parser): - parser.addoption("--certificate-source", action="store", default="default", choices=('default', 'installer', 'custom_server'), help="Certificate source used during deployment") + parser.addoption("--certificate-source", action="store", default="default", choices=('default', 'custom_server'), help="Certificate source used during deployment") parser.addoption("--database-mode", action="store", default="internal", choices=('internal', 'external'), help="Whether the database is internal or external") diff --git a/tests/migration_test.py b/tests/migration_test.py new file mode 100644 index 000000000..955d23ce8 --- /dev/null +++ b/tests/migration_test.py @@ -0,0 +1,47 @@ +import pytest +import yaml + + +@pytest.fixture(scope="module") +def migrated_environment(server): + if not server.file("/root/ssl-build.bak").exists: + pytest.skip("Not a migrated environment") + + +def test_installer_directory_removed(server, migrated_environment): + assert not server.file("/root/ssl-build").exists + + +def test_installer_backup_exists(server, migrated_environment): + backup = server.file("/root/ssl-build.bak") + assert backup.exists + assert backup.is_directory + + +@pytest.mark.parametrize("subdir", ["certs", "private", "requests"]) +def test_certificate_directories(server, migrated_environment, subdir): + d = server.file(f"/root/certificates/{subdir}") + assert d.exists + assert d.is_directory + assert d.mode == 0o755 + + +def test_ca_password_file(server, migrated_environment): + f = server.file("/root/certificates/private/ca.pwd") + assert f.exists + assert f.mode == 0o600 + + +def test_ca_password_persisted(server, migrated_environment): + f = server.file("/var/lib/foremanctl/parameters.yaml") + assert f.exists + params = yaml.safe_load(f.content_string) + assert "certificates_ca_password" in params + assert len(params["certificates_ca_password"]) > 0 + + +def test_default_certs_no_custom_source(server, migrated_environment): + f = server.file("/var/lib/foremanctl/parameters.yaml") + assert f.exists + params = yaml.safe_load(f.content_string) + assert "certificates_source" not in params diff --git a/tests/unit/migrate_test.py b/tests/unit/migrate_test.py index cf8a3a5f4..21e9ea857 100644 --- a/tests/unit/migrate_test.py +++ b/tests/unit/migrate_test.py @@ -58,6 +58,25 @@ def test_ignore_parameters(self): assert 'db_manage_rake' not in str(result['unmappable']) assert result['mapped']['database_host'] == 'localhost' + def test_certificate_parameters_ignored(self): + """Test that certificate path parameters are ignored (handled by migration role)""" + old_config = { + 'foreman': { + 'server_ssl_cert': '/etc/pki/katello/certs/server.crt', + 'server_ssl_key': '/etc/pki/katello/private/server.key', + 'server_ssl_ca': '/etc/pki/katello/certs/ca.crt', + 'db_host': 'localhost' + } + } + + result = migrate_answers.apply_mappings(old_config) + + assert 'server_certificate' not in result['mapped'] + assert 'server_key' not in result['mapped'] + assert 'ca_certificate' not in result['mapped'] + assert not any('ssl' in p for p in result['unmappable']) + assert result['mapped']['database_host'] == 'localhost' + def test_unmappable_parameters(self): """Test that unmappable parameters are reported""" old_config = {