diff --git a/.ansible-lint b/.ansible-lint index cf3d01dfe..b6984c592 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -2,3 +2,7 @@ exclude_paths: - .ansible/ - .github/ + +# Enable custom rules for volume permission validation +rulesdir: + - .ansible-lint-rules diff --git a/.ansible-lint-rules/VolumePermissionsRule.py b/.ansible-lint-rules/VolumePermissionsRule.py new file mode 100644 index 000000000..2ec243011 --- /dev/null +++ b/.ansible-lint-rules/VolumePermissionsRule.py @@ -0,0 +1,53 @@ +"""Custom ansible-lint rule for validating volume mount permissions.""" +import re +from typing import Any, Dict, List + +from ansiblelint.rules import AnsibleLintRule + + +class VolumePermissionsRule(AnsibleLintRule): + """Volume mounts must have explicit read-write or read-only permissions.""" + + id = "volume-permissions" + description = "Volume mounts must specify explicit :rw or :ro permissions" + version_changed = "1.0.0" + + # Pattern for valid volume mount: source:destination:options + # Options are comma-separated, e.g. rw,Z or ro,z + volume_pattern = re.compile(r'^[^:]+:[^:]+:(rw|ro)(?:,[zZ])?$') + + def matchtask(self, task: Dict[str, Any], file=None) -> List[Dict[str, Any]]: + """Check if task contains volume mounts without explicit permissions.""" + results = [] + + if "containers.podman.podman_container" in task: + container_config = task["containers.podman.podman_container"] + + for volume_key in ["volume", "volumes"]: + if volume_key in container_config: + volume_data = container_config[volume_key] + + volume_specs = [] + if isinstance(volume_data, str): + volume_specs = [volume_data] + elif isinstance(volume_data, list): + volume_specs = [spec for spec in volume_data if isinstance(spec, str)] + + for volume_spec in volume_specs: + if not self._is_valid_volume(volume_spec): + results.append({ + "message": f"Volume mount '{volume_spec}' missing explicit permission (:rw or :ro)", + "filename": str(file) if file else "unknown", + "linenumber": task.get("__line__", 1), + }) + + return results + + def _is_valid_volume(self, volume_spec: str) -> bool: + """Check if volume specification has valid permissions.""" + if "{{" in volume_spec and "}}" in volume_spec: + return True + + volume_spec = volume_spec.strip().strip('"\'') + + return bool(self.volume_pattern.match(volume_spec)) diff --git a/development/requirements.txt b/development/requirements.txt index 48384b1a1..63de69df2 100644 --- a/development/requirements.txt +++ b/development/requirements.txt @@ -7,3 +7,4 @@ pytest-testinfra pytest-durations python-dateutil pyyaml +ansible-lint diff --git a/src/roles/candlepin/tasks/main.yml b/src/roles/candlepin/tasks/main.yml index 4b2628138..9c7d8280f 100644 --- a/src/roles/candlepin/tasks/main.yml +++ b/src/roles/candlepin/tasks/main.yml @@ -88,8 +88,8 @@ - 'candlepin-artemis-jaas-conf,target=/etc/tomcat/conf.d/jaas.conf,mode=440,type=mount' - 'candlepin-db-ca,target={{ candlepin_database_ssl_ca_path }},mode=0440,type=mount' volumes: - - /var/log/candlepin:/var/log/candlepin:Z - - /var/log/tomcat:/var/log/tomcat:Z + - /var/log/candlepin:/var/log/candlepin:rw,Z + - /var/log/tomcat:/var/log/tomcat:rw,Z quadlet_options: - | [Install] diff --git a/src/roles/foreman/tasks/main.yaml b/src/roles/foreman/tasks/main.yaml index f08ac17f9..90d6435d7 100644 --- a/src/roles/foreman/tasks/main.yaml +++ b/src/roles/foreman/tasks/main.yaml @@ -104,7 +104,7 @@ network: host hostname: "{{ ansible_facts['hostname'] }}.local" volume: - - 'foreman-data-run:/var/run/foreman:z' + - 'foreman-data-run:/var/run/foreman:rw,z' secrets: - 'foreman-database-url,type=env,target=DATABASE_URL' - 'foreman-seed-admin-user,type=env,target=SEED_ADMIN_USER' @@ -142,7 +142,7 @@ network: host hostname: "{{ ansible_facts['hostname'] }}.local" volume: - - 'foreman-data-run:/var/run/foreman:z' + - 'foreman-data-run:/var/run/foreman:rw,z' secrets: - 'foreman-database-url,type=env,target=DATABASE_URL' - 'foreman-settings-yaml,type=mount,target=/etc/foreman/settings.yaml' @@ -197,7 +197,7 @@ hostname: "{{ ansible_facts['hostname'] }}.local" command: "foreman-rake {{ item.rake }}" volume: - - 'foreman-data-run:/var/run/foreman:z' + - 'foreman-data-run:/var/run/foreman:rw,z' secrets: - 'foreman-database-url,type=env,target=DATABASE_URL' - 'foreman-seed-admin-user,type=env,target=SEED_ADMIN_USER' diff --git a/src/roles/postgresql/tasks/main.yml b/src/roles/postgresql/tasks/main.yml index fe13649ed..360ff2539 100644 --- a/src/roles/postgresql/tasks/main.yml +++ b/src/roles/postgresql/tasks/main.yml @@ -30,7 +30,7 @@ sdnotify: healthy network: host volumes: - - "{{ postgresql_data_dir }}:/var/lib/pgsql/data:Z" + - "{{ postgresql_data_dir }}:/var/lib/pgsql/data:rw,Z" secrets: - 'postgresql-admin-password,target=POSTGRESQL_ADMIN_PASSWORD,type=env' env: diff --git a/src/roles/pulp/defaults/main.yaml b/src/roles/pulp/defaults/main.yaml index a4b9fa44a..7e731abe3 100644 --- a/src/roles/pulp/defaults/main.yaml +++ b/src/roles/pulp/defaults/main.yaml @@ -11,7 +11,7 @@ pulp_content_service_worker_count: "{{ (2 * ([8, ansible_facts['processor_nproc' pulp_api_service_worker_count: "{{ ([4, ansible_facts['processor_nproc']] | min) + 1 }}" pulp_volumes: - - /var/lib/pulp:/var/lib/pulp + - /var/lib/pulp:/var/lib/pulp:rw pulp_api_container_name: pulp-api pulp_content_container_name: pulp-content diff --git a/src/roles/redis/tasks/main.yaml b/src/roles/redis/tasks/main.yaml index 93837c90c..36029eb7d 100644 --- a/src/roles/redis/tasks/main.yaml +++ b/src/roles/redis/tasks/main.yaml @@ -23,7 +23,7 @@ sdnotify: true command: ["run-redis", "--supervised", "systemd"] volumes: - - /var/lib/redis:/data:Z + - /var/lib/redis:/data:rw,Z quadlet_options: - | [Install]