diff --git a/.ansible-lint b/.ansible-lint index 5e30b9bf6..f7c8dbe54 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,4 +1,7 @@ --- +use_default_rules: true +rulesdir: + - .ansible-lint-rules/ enable_list: - var-naming[no-role-prefix] exclude_paths: diff --git a/.ansible-lint-rules/__init__.py b/.ansible-lint-rules/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/.ansible-lint-rules/empty_defaults.py b/.ansible-lint-rules/empty_defaults.py new file mode 100644 index 000000000..7b95ae4c4 --- /dev/null +++ b/.ansible-lint-rules/empty_defaults.py @@ -0,0 +1,52 @@ +"""Implementation of var-defaults rule.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ansiblelint.rules import AnsibleLintRule +from ansiblelint.utils import parse_yaml_from_file + +if TYPE_CHECKING: + from ansiblelint.errors import MatchError + from ansiblelint.file_utils import Lintable + + +class EmptyDefaultsRule(AnsibleLintRule): + """Role default variables should not have empty values.""" + + id = "var-defaults" + severity = "HIGH" + tags = ["idiom"] + version_added = "custom" + + _ids = { + "var-defaults[no-empty]": "Role default variables must not be null or empty strings.", + } + + def matchyaml(self, file: Lintable) -> list[MatchError]: + """Return matches for empty defaults in role defaults files.""" + results: list[MatchError] = [] + + if str(file.kind) != "vars" or not file.data: + return results + + if not file.role or "defaults" not in file.path.parts: + return results + + meta_data = parse_yaml_from_file(str(file.path)) + if not isinstance(meta_data, dict): + return results + + for key, value in meta_data.items(): + if value is None or value == "": + results.append( + self.create_matcherror( + message=f"Role default variable '{key}' has an empty value. Use `undef(hint='…')` to indicate defaults that need to be overriden.", + filename=file, + tag="var-defaults[no-empty]", + data=key, + ), + ) + + return results diff --git a/.ansible-lint-rules/no_static_secrets.py b/.ansible-lint-rules/no_static_secrets.py new file mode 100644 index 000000000..80701376c --- /dev/null +++ b/.ansible-lint-rules/no_static_secrets.py @@ -0,0 +1,63 @@ +"""Implementation of var-secrets rule.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ansiblelint.rules import AnsibleLintRule +from ansiblelint.text import has_jinja +from ansiblelint.utils import parse_yaml_from_file + +if TYPE_CHECKING: + from ansiblelint.errors import MatchError + from ansiblelint.file_utils import Lintable + +SECRET_SUFFIXES = ( + "_password", + "_passwd", + "_secret", + "_token", +) + + +class NoStaticSecretsRule(AnsibleLintRule): + """Variables that look like secrets must not have static default values.""" + + id = "var-secrets" + severity = "HIGH" + tags = ["security"] + version_added = "custom" + + _ids = { + "var-secrets[no-static]": "Secret variables must use Jinja expressions, not static strings.", + } + + @staticmethod + def _looks_like_secret(name: str) -> bool: + return any(name.endswith(suffix) for suffix in SECRET_SUFFIXES) + + def matchyaml(self, file: Lintable) -> list[MatchError]: + """Flag secret-looking variables with static string values.""" + results: list[MatchError] = [] + + if str(file.kind) != "vars" or not file.data: + return results + + meta_data = parse_yaml_from_file(str(file.path)) + if not isinstance(meta_data, dict): + return results + + for key, value in meta_data.items(): + if not self._looks_like_secret(str(key)): + continue + if isinstance(value, str) and not has_jinja(value): + results.append( + self.create_matcherror( + message=f"Secret variable '{key}' has a static value. Use a Jinja expression instead.", + filename=file, + tag="var-secrets[no-static]", + data=key, + ), + ) + + return results diff --git a/development/roles/foreman_development/defaults/main.yaml b/development/roles/foreman_development/defaults/main.yaml index 6977b125b..53539bfae 100644 --- a/development/roles/foreman_development/defaults/main.yaml +++ b/development/roles/foreman_development/defaults/main.yaml @@ -12,13 +12,13 @@ foreman_development_client_certificate: "{{ foreman_client_certificate }}" foreman_development_client_key: "{{ foreman_client_key }}" foreman_development_admin_user: "admin" -foreman_development_admin_password: "changeme" +foreman_development_admin_password: "changeme" # noqa: var-secrets[no-static] foreman_development_candlepin_url: "https://localhost:23443/candlepin" foreman_development_git_repo: "https://github.com/theforeman/foreman.git" foreman_development_git_revision: "develop" -foreman_development_github_username: "" +foreman_development_github_username: "" # noqa: var-defaults[no-empty] foreman_development_hammer_git_repo: "https://github.com/theforeman/hammer-cli.git" @@ -33,7 +33,7 @@ foreman_development_database_host: "localhost" foreman_development_database_port: 5432 foreman_development_database_name: "foreman_development" foreman_development_database_user: "foreman" -foreman_development_database_password: "foreman" +foreman_development_database_password: "foreman" # noqa: var-secrets[no-static] foreman_development_nodejs_stream: "22" diff --git a/docs/developer/playbooks-and-roles.md b/docs/developer/playbooks-and-roles.md index aa2a1a932..082a434e8 100644 --- a/docs/developer/playbooks-and-roles.md +++ b/docs/developer/playbooks-and-roles.md @@ -172,6 +172,45 @@ include: - _flavor_features ``` +## Secret Management + +Secrets (passwords, tokens, OAuth secrets) must never be hardcoded. Use the `ansible.builtin.password` lookup to auto-generate secrets and persist them to files under `obsah_state_path`. + +### Pattern + +Every secret needs two variables: a `_file` variable pointing to the state file, and the secret variable itself using a `lookup` against that file. + +```yaml +example_database_password_file: "{{ obsah_state_path }}/example-db-password" +example_database_password: "{{ lookup('ansible.builtin.password', example_database_password_file, chars=['ascii_letters', 'digits']) }}" +``` + +The `lookup` generates a random password on first run and writes it to the file. On subsequent runs, it reads the existing value, ensuring the secret is stable across deploys. + +### Parameters + +| Parameter | Usage | +|-----------|-------| +| `chars` | Character set for generation. Use `['ascii_letters', 'digits']` for passwords safe in URLs and connection strings. | +| `length` | Password length. Defaults to 20 if omitted. Use `length=32` for high-entropy secrets like OAuth tokens. | + +### Where to define secrets + +Define secrets in `src/vars/` files, not in role `defaults/`. Vars files have higher Ansible precedence and are the effective source of truth at deploy time. + +| Secret type | Define in | +|-------------|-----------| +| Database passwords | `src/vars/database.yml` | +| IOP database passwords | `src/vars/database_iop.yml` | +| general passwords/secrets | `src/vars/base.yaml` | +| Foreman-specific passwords/secrets | `src/vars/foreman.yml` | + +### Naming conventions + +- State file variable: `__password_file` (or `_secret_file` for non-password secrets) +- State file path: `{{ obsah_state_path }}/--password` (use hyphens, no underscores) +- Secret variable: `__password` + ## How to Add a New Command 1. Create a directory under `src/playbooks//` (or `development/playbooks/` for dev tools). diff --git a/src/roles/candlepin/defaults/main.yml b/src/roles/candlepin/defaults/main.yml index a0a8b15b4..acac7570f 100644 --- a/src/roles/candlepin/defaults/main.yml +++ b/src/roles/candlepin/defaults/main.yml @@ -20,7 +20,5 @@ candlepin_database_host: localhost candlepin_database_port: 5432 candlepin_database_ssl: false candlepin_database_ssl_mode: disable -candlepin_database_ssl_ca: +candlepin_database_ssl_ca: # noqa: var-defaults[no-empty] candlepin_database_ssl_ca_path: /etc/candlepin/certs/db-ca.crt -candlepin_database_ssl_cert: -candlepin_database_ssl_key: diff --git a/src/roles/foreman/defaults/main.yaml b/src/roles/foreman/defaults/main.yaml index fad6b1161..fc39c789d 100644 --- a/src/roles/foreman/defaults/main.yaml +++ b/src/roles/foreman/defaults/main.yaml @@ -9,7 +9,7 @@ foreman_database_host: localhost foreman_database_port: 5432 foreman_database_pool: 9 foreman_database_ssl_mode: disable -foreman_database_ssl_ca: +foreman_database_ssl_ca: # noqa: var-defaults[no-empty] foreman_database_ssl_ca_path: /etc/foreman/db-ca.crt foreman_name: "{{ ansible_facts['fqdn'] }}" diff --git a/src/roles/hammer/defaults/main.yml b/src/roles/hammer/defaults/main.yml index 3a089cf91..0949b20fd 100644 --- a/src/roles/hammer/defaults/main.yml +++ b/src/roles/hammer/defaults/main.yml @@ -1,6 +1,6 @@ --- hammer_foreman_server_url: "https://{{ ansible_facts['fqdn'] }}" -hammer_ca_certificate: "" +hammer_ca_certificate: "" # noqa: var-defaults[no-empty] hammer_default_plugins: - foreman hammer_plugins: [] diff --git a/src/roles/iop_advisor/defaults/main.yaml b/src/roles/iop_advisor/defaults/main.yaml index 52645e1d1..a5722ba2c 100644 --- a/src/roles/iop_advisor/defaults/main.yaml +++ b/src/roles/iop_advisor/defaults/main.yaml @@ -5,6 +5,6 @@ iop_advisor_registry_auth_file: /etc/foreman/registry-auth.json iop_advisor_database_name: advisor_db iop_advisor_database_user: advisor_user -iop_advisor_database_password: CHANGEME +iop_advisor_database_password: "{{ undef(hint='Set a secure database password') }}" iop_advisor_database_host: host.containers.internal iop_advisor_database_port: 5432 diff --git a/src/roles/iop_inventory/defaults/main.yaml b/src/roles/iop_inventory/defaults/main.yaml index b287bbf78..66ce97c79 100644 --- a/src/roles/iop_inventory/defaults/main.yaml +++ b/src/roles/iop_inventory/defaults/main.yaml @@ -5,6 +5,6 @@ iop_inventory_registry_auth_file: /etc/foreman/registry-auth.json iop_inventory_database_name: inventory_db iop_inventory_database_user: inventory_admin -iop_inventory_database_password: CHANGEME +iop_inventory_database_password: "{{ undef(hint='Set a secure database password') }}" iop_inventory_database_host: host.containers.internal iop_inventory_database_port: 5432 diff --git a/src/roles/iop_remediation/defaults/main.yaml b/src/roles/iop_remediation/defaults/main.yaml index 99bceb8e9..987b83430 100644 --- a/src/roles/iop_remediation/defaults/main.yaml +++ b/src/roles/iop_remediation/defaults/main.yaml @@ -5,6 +5,6 @@ iop_remediation_registry_auth_file: /etc/foreman/registry-auth.json iop_remediation_database_name: remediations_db iop_remediation_database_user: remediations_user -iop_remediation_database_password: CHANGEME +iop_remediation_database_password: "{{ undef(hint='Set a secure database password') }}" iop_remediation_database_host: "host.containers.internal" iop_remediation_database_port: "5432" diff --git a/src/roles/iop_vmaas/defaults/main.yaml b/src/roles/iop_vmaas/defaults/main.yaml index 2d5f0511f..9e681d43a 100644 --- a/src/roles/iop_vmaas/defaults/main.yaml +++ b/src/roles/iop_vmaas/defaults/main.yaml @@ -5,7 +5,7 @@ iop_vmaas_registry_auth_file: /etc/foreman/registry-auth.json iop_vmaas_database_name: vmaas_db iop_vmaas_database_user: vmaas_admin -iop_vmaas_database_password: CHANGEME +iop_vmaas_database_password: "{{ undef(hint='Set a secure database password') }}" iop_vmaas_database_host: "host.containers.internal" iop_vmaas_database_port: "5432" diff --git a/src/roles/iop_vulnerability/defaults/main.yaml b/src/roles/iop_vulnerability/defaults/main.yaml index 4811acf93..42c1b12f4 100644 --- a/src/roles/iop_vulnerability/defaults/main.yaml +++ b/src/roles/iop_vulnerability/defaults/main.yaml @@ -5,7 +5,7 @@ iop_vulnerability_registry_auth_file: /etc/foreman/registry-auth.json iop_vulnerability_database_name: vulnerability_db iop_vulnerability_database_user: vulnerability_admin -iop_vulnerability_database_password: CHANGEME +iop_vulnerability_database_password: "{{ undef(hint='Set a secure database password') }}" iop_vulnerability_database_host: "host.containers.internal" iop_vulnerability_database_port: "5432" diff --git a/src/roles/postgresql/defaults/main.yml b/src/roles/postgresql/defaults/main.yml index 7c80c3a68..82d1eb667 100644 --- a/src/roles/postgresql/defaults/main.yml +++ b/src/roles/postgresql/defaults/main.yml @@ -8,7 +8,7 @@ postgresql_restart_policy: always postgresql_data_dir: /var/lib/pgsql/data -postgresql_admin_password: "CHANGEME" +postgresql_admin_password: "{{ undef(hint='Set a secure database password') }}" postgresql_max_connections: 500 postgresql_shared_buffers: 512MB diff --git a/src/roles/pulp/defaults/main.yaml b/src/roles/pulp/defaults/main.yaml index 2d33986b7..fa6a81a11 100644 --- a/src/roles/pulp/defaults/main.yaml +++ b/src/roles/pulp/defaults/main.yaml @@ -45,7 +45,7 @@ pulp_database_user: pulp pulp_database_host: localhost pulp_database_port: 5432 pulp_database_ssl_mode: disabled -pulp_database_ssl_ca: +pulp_database_ssl_ca: # noqa: var-defaults[no-empty] pulp_database_ssl_ca_path: /etc/pulp/certs/db-ca.crt pulp_settings_database_env: diff --git a/src/vars/base.yaml b/src/vars/base.yaml index 1926ebc6f..b85b9d02f 100644 --- a/src/vars/base.yaml +++ b/src/vars/base.yaml @@ -3,9 +3,12 @@ certificates_hostnames: - "{{ ansible_facts['fqdn'] }}" - localhost -certificates_ca_password: "CHANGEME" -candlepin_keystore_password: "CHANGEME" -candlepin_oauth_secret: "CHANGEME" +certificates_ca_password_file: "{{ obsah_state_path }}/certificates-ca-password" +certificates_ca_password: "{{ lookup('ansible.builtin.password', certificates_ca_password_file, chars=['ascii_letters', 'digits']) }}" +candlepin_keystore_password_file: "{{ obsah_state_path }}/candlepin-keystore-password" +candlepin_keystore_password: "{{ lookup('ansible.builtin.password', candlepin_keystore_password_file, chars=['ascii_letters', 'digits']) }}" +candlepin_oauth_secret_file: "{{ obsah_state_path }}/candlepin-oauth-secret" +candlepin_oauth_secret: "{{ lookup('ansible.builtin.password', candlepin_oauth_secret_file, chars=['ascii_letters', 'digits'], length=32) }}" candlepin_ca_key_password: "{{ ca_key_password }}" candlepin_ca_key: "{{ ca_key }}" diff --git a/src/vars/database.yml b/src/vars/database.yml index 2a89bed01..236e62421 100644 --- a/src/vars/database.yml +++ b/src/vars/database.yml @@ -6,13 +6,19 @@ database_ssl_ca: foreman_database_name: foreman foreman_database_user: foreman -foreman_database_password: CHANGEME +foreman_database_password_file: "{{ obsah_state_path }}/foreman-db-password" +foreman_database_password: "{{ lookup('ansible.builtin.password', foreman_database_password_file, chars=['ascii_letters', 'digits']) }}" candlepin_database_name: candlepin candlepin_database_user: candlepin -candlepin_database_password: CHANGEME +candlepin_database_password_file: "{{ obsah_state_path }}/candlepin-db-password" +candlepin_database_password: "{{ lookup('ansible.builtin.password', candlepin_database_password_file, chars=['ascii_letters', 'digits']) }}" pulp_database_name: pulp pulp_database_user: pulp -pulp_database_password: CHANGEME +pulp_database_password_file: "{{ obsah_state_path }}/pulp-db-password" +pulp_database_password: "{{ lookup('ansible.builtin.password', pulp_database_password_file, chars=['ascii_letters', 'digits']) }}" + +postgresql_admin_password_file: "{{ obsah_state_path }}/postgresql-admin-password" +postgresql_admin_password: "{{ lookup('ansible.builtin.password', postgresql_admin_password_file, chars=['ascii_letters', 'digits']) }}" candlepin_database_host: "{{ database_host }}" candlepin_database_port: "{{ database_port }}" diff --git a/src/vars/database_iop.yml b/src/vars/database_iop.yml index 792333b20..9166b923a 100644 --- a/src/vars/database_iop.yml +++ b/src/vars/database_iop.yml @@ -6,31 +6,36 @@ iop_inventory_database_host: "{{ iop_database_host }}" iop_inventory_database_port: "{{ iop_database_port }}" iop_inventory_database_name: inventory_db iop_inventory_database_user: inventory_admin -iop_inventory_database_password: CHANGEME +iop_inventory_database_password_file: "{{ obsah_state_path }}/iop-inventory-db-password" +iop_inventory_database_password: "{{ lookup('ansible.builtin.password', iop_inventory_database_password_file, chars=['ascii_letters', 'digits']) }}" iop_advisor_database_host: "{{ iop_database_host }}" iop_advisor_database_port: "{{ iop_database_port }}" iop_advisor_database_name: advisor_db iop_advisor_database_user: advisor_user -iop_advisor_database_password: CHANGEME +iop_advisor_database_password_file: "{{ obsah_state_path }}/iop-advisor-db-password" +iop_advisor_database_password: "{{ lookup('ansible.builtin.password', iop_advisor_database_password_file, chars=['ascii_letters', 'digits']) }}" iop_remediation_database_host: "{{ iop_database_host }}" iop_remediation_database_port: "{{ iop_database_port }}" iop_remediation_database_name: remediations_db iop_remediation_database_user: remediations_user -iop_remediation_database_password: CHANGEME +iop_remediation_database_password_file: "{{ obsah_state_path }}/iop-remediation-db-password" +iop_remediation_database_password: "{{ lookup('ansible.builtin.password', iop_remediation_database_password_file, chars=['ascii_letters', 'digits']) }}" iop_vmaas_database_host: "{{ iop_database_host }}" iop_vmaas_database_port: "{{ iop_database_port }}" iop_vmaas_database_name: vmaas_db iop_vmaas_database_user: vmaas_admin -iop_vmaas_database_password: CHANGEME +iop_vmaas_database_password_file: "{{ obsah_state_path }}/iop-vmaas-db-password" +iop_vmaas_database_password: "{{ lookup('ansible.builtin.password', iop_vmaas_database_password_file, chars=['ascii_letters', 'digits']) }}" iop_vulnerability_database_host: "{{ iop_database_host }}" iop_vulnerability_database_port: "{{ iop_database_port }}" iop_vulnerability_database_name: vulnerability_db iop_vulnerability_database_user: vulnerability_admin -iop_vulnerability_database_password: CHANGEME +iop_vulnerability_database_password_file: "{{ obsah_state_path }}/iop-vulnerability-db-password" +iop_vulnerability_database_password: "{{ lookup('ansible.builtin.password', iop_vulnerability_database_password_file, chars=['ascii_letters', 'digits']) }}" iop_postgresql_databases: - name: "{{ iop_inventory_database_name }}" diff --git a/src/vars/installer_certificates.yml b/src/vars/installer_certificates.yml index 6939e6310..f94ca3cae 100644 --- a/src/vars/installer_certificates.yml +++ b/src/vars/installer_certificates.yml @@ -1,5 +1,5 @@ --- -ca_key_password: "/root/ssl-build/katello-default-ca.pwd" +ca_key_password: "/root/ssl-build/katello-default-ca.pwd" # noqa: var-secrets[no-static] 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" diff --git a/tests/ansible_lint/conftest.py b/tests/ansible_lint/conftest.py new file mode 100644 index 000000000..fa605ad46 --- /dev/null +++ b/tests/ansible_lint/conftest.py @@ -0,0 +1,29 @@ +"""Makes ansible-lint pytest fixtures available for lint rule tests.""" + +import pytest +from ansiblelint.file_utils import Lintable +from ansiblelint.rules import RulesCollection +from ansiblelint.runner import Runner +from ansiblelint.testing.fixtures import * # noqa: F403 + +CUSTOM_RULESDIR = ".ansible-lint-rules" + + +@pytest.fixture +def custom_rules(config_options, app): # noqa: F811 + """Return a RulesCollection loaded from .ansible-lint-rules/.""" + from ansiblelint.rules import RulesCollection + + return RulesCollection( + app=app, + rulesdirs=[CUSTOM_RULESDIR], + options=config_options, + ) + + +@pytest.fixture +def ansible_lint_runner(request, custom_rules: RulesCollection) -> list: + path = request.param[0] + rule_id = request.param[1] + results = Runner(Lintable(path), rules=custom_rules).run() + return [r for r in results if r.rule.id == rule_id] diff --git a/tests/ansible_lint/test_empty_defaults.py b/tests/ansible_lint/test_empty_defaults.py new file mode 100644 index 000000000..c3bca0401 --- /dev/null +++ b/tests/ansible_lint/test_empty_defaults.py @@ -0,0 +1,19 @@ +"""Tests for var-defaults[no-empty] rule.""" + +import pytest + +RULE_ID = "var-defaults" + + +@pytest.mark.parametrize("ansible_lint_runner", [("tests/fixtures/ansible-lint/roles/test_empty_defaults", RULE_ID)], indirect=True) +def test_empty_defaults_are_flagged(ansible_lint_runner) -> None: + """Null and empty string defaults produce match errors.""" + assert len(ansible_lint_runner) == 4 + for result in ansible_lint_runner: + assert result.tag == "var-defaults[no-empty]" + + +@pytest.mark.parametrize("ansible_lint_runner", [("tests/fixtures/ansible-lint/roles/test_empty_defaults/vars/main.yml", RULE_ID)], indirect=True) +def test_vars_file_not_checked(ansible_lint_runner) -> None: + """Vars files are not checked, only defaults.""" + assert len(ansible_lint_runner) == 0 diff --git a/tests/ansible_lint/test_no_static_secrets.py b/tests/ansible_lint/test_no_static_secrets.py new file mode 100644 index 000000000..df64a3452 --- /dev/null +++ b/tests/ansible_lint/test_no_static_secrets.py @@ -0,0 +1,25 @@ +"""Tests for var-secrets[no-static] rule.""" + +import pytest + +RULE_ID = "var-secrets" + + +@pytest.mark.parametrize("ansible_lint_runner", [("tests/fixtures/ansible-lint/roles/test_static_secrets/defaults/main.yml", RULE_ID)], indirect=True) +def test_static_secrets_flagged(ansible_lint_runner) -> None: + """Static secret values produce match errors.""" + assert len(ansible_lint_runner) == 2 + for result in ansible_lint_runner: + assert result.tag == "var-secrets[no-static]" + + +@pytest.mark.parametrize("ansible_lint_runner", [("tests/fixtures/ansible-lint/roles/test_static_secrets/vars/main.yml", RULE_ID)], indirect=True) +def test_jinja_secrets_pass(ansible_lint_runner) -> None: + """Jinja expression secrets are not flagged.""" + assert len(ansible_lint_runner) == 0 + + +@pytest.mark.parametrize("ansible_lint_runner", [("tests/fixtures/ansible-lint/vars/secrets.yml", RULE_ID)], indirect=True) +def test_non_role_vars_checked(ansible_lint_runner) -> None: + """Non-role vars files are also checked.""" + assert len(ansible_lint_runner) == 1 diff --git a/tests/fixtures/ansible-lint/roles/test_empty_defaults/defaults/main.yml b/tests/fixtures/ansible-lint/roles/test_empty_defaults/defaults/main.yml new file mode 100644 index 000000000..8dd6aaf82 --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_empty_defaults/defaults/main.yml @@ -0,0 +1,12 @@ +--- +test_empty_defaults_null_var: +test_empty_defaults_another_null: +test_empty_defaults_empty_string: "" +test_empty_defaults_another_empty_string: '' +test_empty_defaults_name: "some_value" +test_empty_defaults_port: 8080 +test_empty_defaults_enabled: false +test_empty_defaults_items: + - one + - two +test_empty_defaults_plugins: [] diff --git a/tests/fixtures/ansible-lint/roles/test_empty_defaults/tasks/main.yml b/tests/fixtures/ansible-lint/roles/test_empty_defaults/tasks/main.yml new file mode 100644 index 000000000..9a8e9facf --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_empty_defaults/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder task + ansible.builtin.debug: + msg: "test" diff --git a/tests/fixtures/ansible-lint/roles/test_empty_defaults/vars/main.yml b/tests/fixtures/ansible-lint/roles/test_empty_defaults/vars/main.yml new file mode 100644 index 000000000..75ac4445f --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_empty_defaults/vars/main.yml @@ -0,0 +1,3 @@ +--- +test_empty_defaults_internal: +test_empty_defaults_internal_empty: "" diff --git a/tests/fixtures/ansible-lint/roles/test_static_secrets/defaults/main.yml b/tests/fixtures/ansible-lint/roles/test_static_secrets/defaults/main.yml new file mode 100644 index 000000000..70c034b22 --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_static_secrets/defaults/main.yml @@ -0,0 +1,10 @@ +--- +# Should be flagged (static secrets) +test_static_secrets_database_password: CHANGEME +test_static_secrets_oauth_secret: "my-secret" +# Should NOT be flagged (Jinja expression) +test_static_secrets_admin_password: "{{ lookup('ansible.builtin.password', '/tmp/passwd') }}" +test_static_secrets_api_token: "{{ some_other_token }}" +# Should NOT be flagged (not a secret name) +test_static_secrets_hostname: "example.com" +test_static_secrets_port: 8080 diff --git a/tests/fixtures/ansible-lint/roles/test_static_secrets/tasks/main.yml b/tests/fixtures/ansible-lint/roles/test_static_secrets/tasks/main.yml new file mode 100644 index 000000000..9a8e9facf --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_static_secrets/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder task + ansible.builtin.debug: + msg: "test" diff --git a/tests/fixtures/ansible-lint/roles/test_static_secrets/vars/main.yml b/tests/fixtures/ansible-lint/roles/test_static_secrets/vars/main.yml new file mode 100644 index 000000000..4d0ad8f40 --- /dev/null +++ b/tests/fixtures/ansible-lint/roles/test_static_secrets/vars/main.yml @@ -0,0 +1,4 @@ +--- +# All Jinja - should NOT be flagged +test_static_secrets_ca_password: "{{ ca_key_password }}" +test_static_secrets_consumer_secret: "{{ lookup('ansible.builtin.password', '/tmp/secret') }}" diff --git a/tests/fixtures/ansible-lint/vars/secrets.yml b/tests/fixtures/ansible-lint/vars/secrets.yml new file mode 100644 index 000000000..3d43b3374 --- /dev/null +++ b/tests/fixtures/ansible-lint/vars/secrets.yml @@ -0,0 +1,7 @@ +--- +# Should be flagged (static secret in non-role vars file) +some_database_password: CHANGEME +# Should NOT be flagged (Jinja expression) +some_other_password: "{{ generated_password }}" +# Should NOT be flagged (not a secret name) +some_database_name: mydb