Skip to content

Commit f12e38d

Browse files
authored
Merge pull request #937 from stackhpc/yoga-secure-container-builds
CI: Avoid leaking credentials in container image builds
2 parents e5f9e21 + 74e87c5 commit f12e38d

File tree

13 files changed

+175
-33
lines changed

13 files changed

+175
-33
lines changed

.github/workflows/stackhpc-all-in-one.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ jobs:
8686
with:
8787
submodules: true
8888

89-
- name: Make sure dockerd is running and test Docker.
89+
- name: Make sure dockerd is running and test Docker
9090
run: |
91-
docker run --rm hello-world
91+
docker ps
9292
9393
- name: Output image tag
9494
id: image_tag

.github/workflows/stackhpc-container-image-build.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ jobs:
125125

126126
- name: Make sure dockerd is running and test Docker
127127
run: |
128-
docker run --rm hello-world
128+
docker ps
129129
130130
- name: Install Kayobe
131131
run: |
@@ -136,10 +136,11 @@ jobs:
136136
pip install -U pip &&
137137
pip install ../src/kayobe
138138
139-
# Required for Docker registry login. Normally installed during host configure.
139+
# Required for Pulp auth proxy deployment and Docker registry login.
140+
# Normally installed during host configure.
140141
- name: Install Docker Python SDK
141142
run: |
142-
pip install --user docker
143+
sudo pip install docker
143144
144145
- name: Configure localhost as a seed
145146
run: |
@@ -150,11 +151,23 @@ jobs:
150151
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3
151152
EOF
152153
154+
# See etc/kayobe/ansible/roles/pulp_auth_proxy/README.md for details.
155+
# NOTE: We override pulp_auth_proxy_conf_path to a path shared by the
156+
# runner and dind containers.
157+
- name: Deploy an authenticating package repository mirror proxy
158+
run: |
159+
source venvs/kayobe/bin/activate &&
160+
source src/kayobe-config/kayobe-env --environment ci-builder &&
161+
kayobe playbook run $KAYOBE_CONFIG_PATH/ansible/pulp-auth-proxy.yml -e pulp_auth_proxy_conf_path=/home/runner/_work/pulp_proxy
162+
env:
163+
KAYOBE_VAULT_PASSWORD: ${{ secrets.KAYOBE_VAULT_PASSWORD }}
164+
153165
- name: Build and push kolla overcloud images
154166
run: |
155167
args="${{ github.event.inputs.regexes }}"
156168
args="$args -e kolla_base_distro=${{ matrix.distro }}"
157169
args="$args -e kolla_tag=${{ needs.generate-tag.outputs.kolla_tag }}"
170+
args="$args -e stackhpc_repo_mirror_auth_proxy_enabled=true"
158171
if ${{ inputs.push }} == 'true'; then
159172
args="$args --push"
160173
fi
@@ -169,6 +182,7 @@ jobs:
169182
run: |
170183
args="-e kolla_base_distro=${{ matrix.distro }}"
171184
args="$args -e kolla_tag=${{ needs.generate-tag.outputs.kolla_tag }}"
185+
args="$args -e stackhpc_repo_mirror_auth_proxy_enabled=true"
172186
if ${{ inputs.push }} == 'true'; then
173187
args="$args --push"
174188
fi

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,7 @@ etc/kayobe/environments/aufn-ceph/kolla/config/nova/ceph.client.glance.keyring
5858

5959
# Tempest logs
6060
tempest-artifacts
61+
62+
# Ansible Galaxy roles & collections
63+
etc/kayobe/ansible/roles/*\.*/
64+
etc/kayobe/ansible/collections/

doc/source/contributor/environments/ci-builder.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ Next, configure the host OS & services.
9595
9696
kayobe seed host configure
9797
98+
.. _authenticating-pulp-proxy:
99+
100+
Authenticating Pulp proxy
101+
-------------------------
102+
103+
If you are building against authenticated package repositories such as those in
104+
`Ark <https://ark.stackhpc.com>`_, you will need to provide secure access to
105+
the repositories without leaking credentials into the built images or their
106+
metadata. This is typically not the case for a client-local Pulp, which
107+
provides unauthenticated read-only access to the repositories on a trusted
108+
network.
109+
110+
Docker provides `build
111+
secrets <https://docs.docker.com/build/building/secrets/>`_, but these must be
112+
explicitly requested for each RUN statement, making them challenging to use in
113+
Kolla.
114+
115+
StackHPC Kayobe Configuration provides support for deploying an authenticating
116+
Pulp proxy that injects an HTTP basic auth header into requests that it
117+
proxies. Because this proxy bypasses Pulp's authentication, it must not be
118+
exposed to any untrusted environment.
119+
120+
To deploy the proxy:
121+
122+
.. parsed-literal::
123+
124+
kayobe playbook run $KAYOBE_CONFIG_PATH/ansible/pulp-auth-proxy.yml
125+
98126
Building images
99127
===============
100128

@@ -105,6 +133,9 @@ At this point you are ready to build and push some container images.
105133
kayobe seed container image build --push
106134
kayobe overcloud container image build --push
107135
136+
If using an :ref:`authenticating Pulp proxy <authenticating-pulp-proxy>`,
137+
append ``-e stackhpc_repo_mirror_auth_proxy_enabled=true`` to these commands.
138+
108139
The container images are tagged as |current_release|-<datetime>.
109140

110141
To use the new images, edit
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
# See roles/pulp_auth_proxy/README.md for details.
3+
4+
- name: Deploy Pulp auth proxy
5+
hosts: container-image-builders
6+
gather_facts: false
7+
tasks:
8+
- import_role:
9+
name: pulp_auth_proxy
10+
vars:
11+
pulp_auth_proxy_url: "{{ stackhpc_repo_mirror_url }}"
12+
pulp_auth_proxy_username: "{{ stackhpc_repo_mirror_username }}"
13+
pulp_auth_proxy_password: "{{ stackhpc_repo_mirror_password }}"
14+
pulp_auth_proxy_conf_path: "{{ base_path }}/containers/pulp_proxy"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Pulp Auth Proxy
2+
3+
There is currently no practical, secure way to provide credentials for
4+
accessing Ark's authenticated package repositories from within a Kolla build.
5+
Docker provides [build
6+
secrets](https://docs.docker.com/build/building/secrets/), but these must be
7+
explicitly requested for each RUN statement, making them challenging to use in
8+
Kolla.
9+
10+
This role deploys an Nginx container that runs as a reverse proxy, injecting an
11+
HTTP basic authentication header into requests.
12+
13+
Because this proxy bypasses Pulp's authentication, it must not be exposed to
14+
any untrusted environment.
15+
16+
## Role variables
17+
18+
* `pulp_auth_proxy_pulp_url`: URL of the Pulp server to proxy requests to.
19+
* `pulp_auth_proxy_username`: Username of the Pulp server to proxy requests to.
20+
* `pulp_auth_proxy_password`: Password of the Pulp server to proxy requests to.
21+
* `pulp_auth_proxy_conf_path`: Path to a directory in which to write Nginx
22+
configuration.
23+
* `pulp_auth_proxy_listen_ip`: IP address on the Docker host on which to
24+
listen. Default is `127.0.0.1`.
25+
* `pulp_auth_proxy_listen_port`: Port on the Docker host on which to listen.
26+
Default is 80.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
pulp_auth_proxy_url:
3+
pulp_auth_proxy_username:
4+
pulp_auth_proxy_password:
5+
pulp_auth_proxy_conf_path:
6+
pulp_auth_proxy_listen_ip: 127.0.0.1
7+
pulp_auth_proxy_listen_port: 80
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
- name: "Ensure {{ pulp_auth_proxy_conf_path }} exists"
3+
ansible.builtin.file:
4+
path: "{{ pulp_auth_proxy_conf_path }}"
5+
state: directory
6+
mode: 0700
7+
become: true
8+
9+
- name: Ensure pulp_proxy.conf is templated
10+
ansible.builtin.template:
11+
src: pulp_proxy.conf.j2
12+
dest: "{{ pulp_auth_proxy_conf_path }}/pulp_proxy.conf"
13+
mode: 0600
14+
become: true
15+
register: pulp_proxy_conf
16+
17+
- name: Ensure pulp_proxy container is running
18+
community.docker.docker_container:
19+
name: pulp_proxy
20+
image: nginx:stable-alpine
21+
ports:
22+
- "{{ pulp_auth_proxy_listen_ip }}:{{ pulp_auth_proxy_listen_port }}:80"
23+
restart_policy: "no"
24+
restart: "{{ pulp_proxy_conf is changed }}"
25+
volumes:
26+
- "{{ pulp_auth_proxy_conf_path }}/pulp_proxy.conf:/etc/nginx/conf.d/default.conf:ro"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
server {
2+
listen {{ pulp_auth_proxy_listen_port }};
3+
server_name pulp_proxy;
4+
location / {
5+
proxy_pass {{ pulp_auth_proxy_url }};
6+
proxy_set_header X-Real-IP $remote_addr;
7+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
8+
proxy_set_header X-Forwarded-Proto $scheme;
9+
proxy_set_header Host {{ pulp_auth_proxy_url | urlsplit('hostname') }};
10+
# The important part: add basic auth header
11+
proxy_set_header Authorization "Basic {{ (pulp_auth_proxy_username ~ ':' ~ pulp_auth_proxy_password) | b64encode }}";
12+
proxy_pass_header Authorization;
13+
# See https://stackoverflow.com/questions/25329941/nginx-caching-proxy-fails-with-ssl23-get-server-hellosslv3-alert-handshake-fail/25330027#25330027
14+
proxy_ssl_server_name on;
15+
proxy_ssl_protocols TLSv1.2;
16+
}
17+
}

etc/kayobe/environments/ci-builder/stackhpc-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ resolv_is_managed: false
4343
# Host and port of a package repository mirror.
4444
# Build against the development Pulp service repositories.
4545
# Use Ark's package repositories to install packages.
46-
stackhpc_repo_mirror_url: "{{ stackhpc_release_pulp_url }}"
46+
stackhpc_repo_mirror_url: "{{ stackhpc_repo_mirror_auth_proxy_url if stackhpc_repo_mirror_auth_proxy_enabled | bool else stackhpc_release_pulp_url }}"
4747
stackhpc_repo_mirror_username: "{{ stackhpc_docker_registry_username }}"
4848
stackhpc_repo_mirror_password: "{{ stackhpc_docker_registry_password }}"
4949

etc/kayobe/kolla.yml

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -287,16 +287,21 @@ base_centos_repo_overrides_post_yum_rocky_list: "{{ stackhpc_rocky_9_repos + sta
287287
base_centos_repo_overrides_post_yum_list: "{{ base_centos_repo_overrides_post_yum_centos_list if kolla_base_distro == 'centos' else base_centos_repo_overrides_post_yum_rocky_list }}"
288288
stackhpc_yum_repos: "{{ stackhpc_centos_stream_repos if kolla_base_distro == 'centos' else stackhpc_rocky_9_repos }}"
289289

290+
# Apt sources.list entry prefix.
291+
# If using an authenticating Pulp proxy we need to trust the repository because
292+
# the certificate provided by the upstream repo will not match the proxy's IP.
293+
stackhpc_ubuntu_repo_prefix: "deb {% if stackhpc_repo_mirror_auth_proxy_enabled | bool %}[trusted=yes] {% endif %}"
294+
290295
# List of base repositories for Ubuntu Focal.
291296
stackhpc_ubuntu_focal_base_repos:
292-
- "deb {{ stackhpc_repo_ubuntu_focal_url }} focal main universe"
293-
- "deb {{ stackhpc_repo_ubuntu_focal_url }} focal-updates main universe"
294-
- "deb {{ stackhpc_repo_ubuntu_focal_url }} focal-backports main universe"
295-
- "deb {{ stackhpc_repo_ubuntu_focal_security_url }} focal-security main universe"
297+
- "{{ stackhpc_ubuntu_repo_prefix }}{{ stackhpc_repo_ubuntu_focal_url }} focal main universe"
298+
- "{{ stackhpc_ubuntu_repo_prefix }}{{ stackhpc_repo_ubuntu_focal_url }} focal-updates main universe"
299+
- "{{ stackhpc_ubuntu_repo_prefix }}{{ stackhpc_repo_ubuntu_focal_url }} focal-backports main universe"
300+
- "{{ stackhpc_ubuntu_repo_prefix }}{{ stackhpc_repo_ubuntu_focal_security_url }} focal-security main universe"
296301

297302
# List of UCA repositories for Ubuntu Focal.
298303
stackhpc_ubuntu_focal_uca_repos:
299-
- "deb {{ stackhpc_repo_ubuntu_cloud_archive_url }} focal-updates/{{ openstack_release }} main"
304+
- "{{ stackhpc_ubuntu_repo_prefix }}{{ stackhpc_repo_ubuntu_cloud_archive_url }} focal-updates/{{ openstack_release }} main"
300305

301306
# List of repositories for Ubuntu Focal.
302307
stackhpc_ubuntu_focal_repos: "{{ stackhpc_ubuntu_focal_base_repos + stackhpc_ubuntu_focal_uca_repos }}"
@@ -317,26 +322,16 @@ kolla_build_blocks:
317322
sed -i -e '/\[{{ repo.tag }}\]/,/^\[/ s/^\(mirrorlist *=.*\)/#\1/g' \
318323
-e '/\[{{ repo.tag }}\]/,/^\[/ s/^[# ]*\(baseurl *=.*\)/#\1/g' \
319324
-e '/\[{{ repo.tag }}\]/,/^\[/ s/^[# ]*\(metalink *=.*\)/#\1/g' \
320-
{% if stackhpc_repo_mirror_username is truthy %}
321-
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\nusername={{ stackhpc_repo_mirror_username }}|' \
322-
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\npassword={{ stackhpc_repo_mirror_password }}|' \
323-
{% endif %}
324325
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\nbaseurl={{ repo.url }}|' /etc/yum.repos.d/{{ repo.file }}{% if not loop.last %} && \
325326
{% endif %}
326327
{% endfor %}
327328
{% else %}
328329
RUN \
329330
rm /etc/apt/sources.list && \
330-
rm -f /etc/apt/auth.conf && \
331-
{% if stackhpc_repo_mirror_url | urlsplit('scheme') == 'https' %}
332-
{# We lack the ca-certificates package at this stage, so don't verify the CA #}
331+
{% if stackhpc_repo_mirror_auth_proxy_enabled | bool %}
332+
{# We lack the ca-certificates package at this stage, so don't verify the CA initially #}
333333
echo 'Acquire::https::Verify-Peer "false";' > /etc/apt/apt.conf.d/90no-verify-peer && \
334334
{% endif %}
335-
{% if stackhpc_repo_mirror_username is truthy %}
336-
echo 'machine {{ stackhpc_repo_mirror_url }}' >> /etc/apt/auth.conf && \
337-
echo 'login {{ stackhpc_repo_mirror_username }}' >> /etc/apt/auth.conf && \
338-
echo 'password {{ stackhpc_repo_mirror_password }}' >> /etc/apt/auth.conf && \
339-
{% endif %}
340335
{% for repo in stackhpc_ubuntu_focal_base_repos %}
341336
echo '{{ repo }}' >> /etc/apt/sources.list {% if not loop.last %} && \
342337
{% endif %}
@@ -354,10 +349,6 @@ kolla_build_blocks:
354349
sed -i -e '/\[{{ repo.tag }}\]/,/^\[/ s/^\(mirrorlist *=.*\)/#\1/g' \
355350
-e '/\[{{ repo.tag }}\]/,/^\[/ s/^[# ]*\(baseurl *=.*\)/#\1/g' \
356351
-e '/\[{{ repo.tag }}\]/,/^\[/ s/^[# ]*\(metalink *=.*\)/#\1/g' \
357-
{% if stackhpc_repo_mirror_username is truthy %}
358-
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\nusername={{ stackhpc_repo_mirror_username }}|' \
359-
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\npassword={{ stackhpc_repo_mirror_password }}|' \
360-
{% endif %}
361352
-e '/\[{{ repo.tag }}\]/,/^\[/ s|^\(name.*\)|\1\nbaseurl={{ repo.url }}|' /etc/yum.repos.d/{{ repo.file }}{% if not loop.last %} &&{% endif %} \
362353
{% endfor %}
363354
{% endif %}
@@ -368,13 +359,7 @@ kolla_build_blocks:
368359
{% endif %}
369360
RUN \
370361
rm /etc/apt/sources.list && \
371-
rm -f /etc/apt/auth.conf && \
372362
rm -f /etc/apt/apt.conf.d/90no-verify-peer && \
373-
{% if stackhpc_repo_mirror_username is truthy %}
374-
echo 'machine {{ stackhpc_repo_mirror_url }}' >> /etc/apt/auth.conf && \
375-
echo 'login {{ stackhpc_repo_mirror_username }}' >> /etc/apt/auth.conf && \
376-
echo 'password {{ stackhpc_repo_mirror_password }}' >> /etc/apt/auth.conf && \
377-
{% endif %}
378363
{% for repo in stackhpc_ubuntu_focal_repos %}
379364
echo '{{ repo }}' >> /etc/apt/sources.list {% if not loop.last %} && \
380365
{% endif %}

etc/kayobe/stackhpc.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ stackhpc_repo_mirror_username:
88
# Password of a package repository mirror.
99
stackhpc_repo_mirror_password:
1010

11+
# Whether to use an authenticating reverse proxy to access the package
12+
# repository mirror. This may be used when building container images, to avoid
13+
# injecting package repository mirror credentials into the built images. See
14+
# ansible/roles/pulp_auth_proxy/README.md for details.
15+
stackhpc_repo_mirror_auth_proxy_enabled: false
16+
17+
# URL of an authenticating reverse proxy used to access the package repository
18+
# mirror. Used during container image builds when
19+
# stackhpc_repo_mirror_auth_proxy_enabled is true.
20+
stackhpc_repo_mirror_auth_proxy_url: "http://localhost"
21+
1122
# Distribution name. Either 'development' or 'production'.
1223
stackhpc_repo_distribution: "development"
1324

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Adds a custom playbook (``pulp-auth-proxy.yml``) for deploying an
5+
authenticating proxy for Pulp. This can be used when building container
6+
images to avoid leaking credentials for package repositories into the built
7+
images or their metadata.

0 commit comments

Comments
 (0)