Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support podman_credential_files #142

Merged
merged 1 commit into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,62 @@ must also set `podman_registry_username`. You can override this on a per-spec
basis with `registry_password`. The use of `container_image_password` was
unsupported and is deprecated.

### podman_credential_files

This is a `list`. Each element of the list is a `dict` describing a podman
credential file used to authenticate to registries. See `man
containers-auth.json` and `man containers-registries.conf`:`credential-helpers`
for more information about the format of these files, and the default directory
search path.
NOTE: These files contain authentication credentials. Please be careful with
them. You are strongly encouraged to use Ansible Vault to encrypt them.
The keys of each `dict` are as follows:

* `state` - default is `present`. Use `absent` to remove files.
* `file_src` - This is the name of a file on the controller node which will be
copied to `file` on the managed node. Do not specify this if you specify
`file_content` or `template_src`, which will take precedence over `file_src`.
* `template_src` - This is the name of a file on the controller node which will
be templated using the `template` module and copied to `file` on the managed
node. Do not specify this if you specify `file_content` or `file_src`.
* `file_content` - This is a string in `containers-auth.json` format. It will be
used as the contents of `file` on the managed node. Do not specify this if
you specify `file_src` or `template_src`.
* `file` - This is the name of a file on the managed node that will contain the
`auth.json` contents. The default value will be
`$HOME/.config/containers/auth.json`. If you specify a relative path, it will
be relative to `$HOME/.config/containers`. If you specify something other
than the defaults mentioned in `man containers-auth.json`, you will also need
to configure `credential-helpers` in `containers-registries.conf` using
`podman_registries_conf`. Any missing parent directories will be created.
* `run_as_user` - Use this to specify a per-credential file owner. If you do
not specify this, then the global default `podman_run_as_user` value will be
used. Otherwise, `root` will be used. NOTE: The user must already exist - the
role will not create one. The user must be present in `/etc/subuid`. NOTE:
This is used as the user for the `$HOME` directory if `file` is not specified,
and as the owner of the file. If you want the owner of the file to be
different than the user used for `$HOME`, specify `file` as an absolute path.
* `run_as_group` - Use this to specify a per-credential file group. If you do
not specify this, then the global default `podman_run_as_group` value will be
used. Otherwise, `root` will be used. NOTE: The group must already exist -
the role will not create one. The group must be present in `/etc/subgid`.
* `mode` - The mode of the file - default is `"0600"`.

For example, if you have

```yaml
podman_credential_files:
- file_src: auth.json
run_as_user: my_user
```

The local file `auth.json` will be looked up in the usual Ansible `file` search
paths and will be copied to the file
`/home/my_user/.config/containers/auth.json` on the managed node. The file
owner will be `my_user` and the mode will be `"0600"`. The directories
`/home/my_user/.config` and `/home/my_user/.config/containers` will be created
if they do not exist.

## Variables Exported by the Role

### podman_version
Expand Down
7 changes: 7 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ podman_registry_username: "{{ container_image_user | d('') }}"
# password to use to authenticate to the registry
# override by specifying registry_password on a per-spec basis
podman_registry_password: "{{ container_image_password | d('') }}"

# List of files containing registry credentials
# If not using the default containers-auth.json, you may need to
# also provide an entry for "credential-helpers" in the registries.conf
# For more information, see "man containers-auth.json" and
# "man containers-registries.conf"
podman_credential_files: []
96 changes: 96 additions & 0 deletions tasks/handle_credential_files.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SPDX-License-Identifier: MIT
---
- name: Set user and group
set_fact:
__podman_credential_user: "{{ __podman_credential_item['run_as_user']
if 'run_as_user' in __podman_credential_item else podman_run_as_user }}"
__podman_credential_group: "{{ __podman_credential_item['run_as_group']
if 'run_as_group' in __podman_credential_item else podman_run_as_group }}"

# NOTE: Sets __podman_group that we use below
- name: Check user and group information
include_tasks: handle_user_group.yml
vars:
__podman_user: "{{ __podman_credential_user }}"
__podman_spec_item: "{{ __podman_credential_item }}"

- name: Set credential variables
set_fact:
__podman_credential_str: "{{ __podman_credential_item['file_content']
if 'file_content' in __podman_credential_item
else lookup('template', __podman_credential_item['template_src'])
if 'template_src' in __podman_credential_item
else none }}"
__podman_credential_file_src: "{{ __podman_credential_item['file_src']
if 'file_src' in __podman_credential_item
else none }}"
__podman_credential_file: "{{ __authdir ~ 'auth.json'
if not 'file' in __podman_credential_item
else __authdir ~ __podman_credential_item['file']
if not __podman_credential_item['file'] is abs
else __podman_credential_item['file'] }}"
__podman_credential_mode: "{{ __podman_credential_item['mode']
if 'mode' in __podman_credential_item else '0600' }}"
__podman_credential_state: "{{ __podman_credential_item['state']
if 'state' in __podman_credential_item else 'present' }}"
vars:
__authdir: "{{
ansible_facts['getent_passwd'][__podman_credential_user][4] ~
'/.config/containers/' }}"
no_log: true

- name: Handle state present
when: __podman_credential_state == "present"
block:
- name: Ensure the credentials directory is present
file:
path: "{{ __podman_credential_file | dirname }}"
state: directory
owner: "{{ __podman_credential_user }}"
group: "{{ __podman_group }}"
mode: "0700"

- name: Ensure credential file is copied
copy:
src: "{{ __podman_credential_file_src }}"
dest: "{{ __podman_credential_file }}"
owner: "{{ __podman_credential_user }}"
group: "{{ __podman_group }}"
mode: "{{ __podman_credential_mode }}"
when: __podman_credential_file_src | length > 0
no_log: true

- name: Ensure credential file content is present
copy:
content: "{{ __podman_credential_str }}"
dest: "{{ __podman_credential_file }}"
owner: "{{ __podman_credential_user }}"
group: "{{ __podman_group }}"
mode: "{{ __podman_credential_mode }}"
when:
- __podman_credential_str | length > 0
- not __podman_credential_file_src
no_log: true

- name: Handle state absent
when: __podman_credential_state == "absent"
block:
- name: Ensure credential file is absent
file:
path: "{{ __podman_credential_file }}"
state: absent
no_log: true

- name: Find files in credentials directory
find:
path: "{{ __podman_credential_file | dirname }}"
file_type: any
hidden: true
register: __credential_dir_files
no_log: true

- name: Ensure the credentials directory is absent if empty
file:
path: "{{ __podman_credential_file | dirname }}"
state: absent
when: __credential_dir_files.matched == 0
5 changes: 3 additions & 2 deletions tasks/handle_user_group.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
- name: Set group for podman user
set_fact:
__podman_group: |-
{%- if "run_as_group" in __podman_spec_item -%}
{{ __podman_spec_item["run_as_group"] }}
{% set item_group = __podman_spec_item.get("run_as_group") %}
{%- if item_group and item_group | length > 0 -%}
{{ item_group }}
{%- elif podman_run_as_group is not none -%}
{{ podman_run_as_group }}
{%- else -%}
Expand Down
7 changes: 7 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@
loop_var: __podman_secret_item
no_log: true

- name: Handle credential files
include_tasks: handle_credential_files.yml
loop: "{{ podman_credential_files }}"
loop_control:
loop_var: __podman_credential_item
no_log: true

- name: Handle Kubernetes specifications
include_tasks: handle_kube_spec.yml
loop: "{{ podman_kube_specs }}"
Expand Down
89 changes: 85 additions & 4 deletions tests/tests_auth_and_security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
ContainerName: auth_test_1_quadlet
podman_kube_spec_base:
state: started
registry_username: "{{ __podman_test_username }}"
registry_password: "{{ __podman_test_password }}"
kube_file_content:
apiVersion: v1
kind: Pod
Expand Down Expand Up @@ -68,11 +66,23 @@
rescue:
- name: Check error
fail:
msg: Role did not fail as expected
when: "'UNREACHABLE' in ansible_failed_result.msg"
msg: >-
Role did not fail with the expected message {{ expected_msg }} but
instead failed with {{ ansible_failed_result | to_nice_json }}
when: not expected_msg in ansible_failed_result.results[0].msg | d("")
vars:
expected_msg: Failed to pull image

- name: Run remaining tasks in block with cleanup
block:
- name: Create a local tmpdir
tempfile:
prefix: lsr_
suffix: _podman
state: directory
register: __local_tmpdir
delegate_to: localhost

- name: Run the role with credentials in spec
include_role:
name: linux-system-roles.podman
Expand All @@ -95,6 +105,77 @@
podman_kube_specs:
- "{{ podman_kube_spec_base }}"

- name: Get authfile locally
fetch:
src: "{{ __podman_test_authfile }}"
dest: "{{ __local_tmpdir.path ~ '/' }}"
flat: true

- name: Provide a credentials file - root
include_role:
name: linux-system-roles.podman
vars:
podman_credential_files:
- file_src: "{{ __local_tmpdir.path ~ '/auth.json' }}"
file: /root/.config/containers/auth.json
podman_quadlet_specs:
- "{{ podman_quadlet_spec_base }}"
podman_kube_specs:
- "{{ podman_kube_spec_base }}"

- name: Create a user for rootless
user:
name: auth_test_user1
uid: 2001

- name: Provide a credentials file - rootless
include_role:
name: linux-system-roles.podman
vars:
podman_credential_files:
- template_src: "{{ __local_tmpdir.path ~ '/auth.json' }}"
run_as_user: auth_test_user1
__run_as_user:
run_as_user: auth_test_user1
podman_quadlet_specs:
- "{{ podman_quadlet_spec_base | combine(__run_as_user) }}"
podman_kube_specs:
- "{{ podman_kube_spec_base | combine(__run_as_user) }}"

always:
- name: Remove all container resources - root
include_role:
name: linux-system-roles.podman
vars:
podman_kube_specs:
- "{{ podman_kube_spec_base | combine({'state': 'absent'}) }}"
podman_quadlet_specs:
- "{{ podman_quadlet_spec_base | combine({'state': 'absent'}) }}"
podman_credential_files:
- state: absent

- name: Remove pods and units - rootless
include_role:
name: linux-system-roles.podman
vars:
podman_run_as_user: auth_test_user1
podman_kube_specs:
- "{{ podman_kube_spec_base | combine({'state': 'absent'}) }}"
podman_quadlet_specs:
- "{{ podman_quadlet_spec_base | combine({'state': 'absent'}) }}"
podman_credential_files:
- state: absent

- name: Remove user
user:
name: auth_test_user1
state: absent

- name: Remove local tmpdir
file:
path: "{{ __local_tmpdir.path }}"
state: absent
delegate_to: localhost

- name: Clean up registry
include_tasks: tasks/cleanup_registry.yml
Loading