Skip to content

image-download-rhel: new image-download replacement#9071

Merged
allisonkarlitskaya merged 1 commit into
mainfrom
unicorn-barf
Jun 10, 2026
Merged

image-download-rhel: new image-download replacement#9071
allisonkarlitskaya merged 1 commit into
mainfrom
unicorn-barf

Conversation

@allisonkarlitskaya

@allisonkarlitskaya allisonkarlitskaya commented May 22, 2026

Copy link
Copy Markdown
Member

This is capable of downloading RHEL images from AWS.

We identify URLs that should be STS-authenticated by using a list (currently 1) of fnmatches on URLs. This list is only consulted if you don't have a normal static key in ~/.config/cockpit-dev/s3-keys/.

Once we've identified that we want to issue a transient key, the process goes like this:

  • you get a Kerberos ticket with kinit

  • we use gssapi to get a SPNEGO token for auth.redhat.com

  • we post a login attempt using the token and hopefully get back a SAML POST binding HTML page which we can extract the SAML assertion from

  • with the SAML assertion in hand we can use AWS STS to generate a transient token for a particular IAM Role. If you're in the it-cloud-aws-727920394381-cockpit-ci-images-download rover group this will allow you to download RHEL images from AWS

  • we use the STS token to do the S3 signing operation

This should be more or less transparent to anyone who is in the correct group and logged in with kerberos. There's no need for us to maintain a list of users and tokens anymore and human developers can delete their old s3-keys.

The new downloader is intended as an eventual replacement for image-download. It's a rewrite from the ground up and has a couple of other niceties:

  • urllib instead of curl (which was getting complicated with all the extra headers)

  • checksum verification based on filename

  • improved locking vs. the old downloader: no more poll loop or subtle race conditions with deleted lockfiles

  • 🌈 rainbows!

Some features not implemented:

  • --state and modification times (which seems to be dead code)
  • --force

With S3 keys for EU Linode mirror (light mode):

image

Without any statically-configured S3 keys (dark mode):

image

Comment thread image-download-rhel Fixed
Comment thread lib/cute.py Fixed
Comment thread lib/cute.py Fixed
Comment thread lib/cute.py Fixed
Comment thread image-download-rhel Fixed
Comment thread lib/cute.py Fixed
Comment thread lib/gssapi_saml_sts.py Fixed
Comment thread lib/locking.py Fixed
Comment thread lib/gssapi_saml_sts.py Fixed
Comment thread saml-login Fixed
@allisonkarlitskaya allisonkarlitskaya force-pushed the unicorn-barf branch 2 times, most recently from ebc362d to 700e9e4 Compare May 26, 2026 09:06
allisonkarlitskaya added a commit to cockpit-project/cockpituous that referenced this pull request May 26, 2026
gssapi is already installed in the container so this just brings a very
thin wrapper for it.

This is basically just for type checking: the gssapi import is lazy and
won't run in CI but mypy gets unhappy about it.  We could ignore it, but
it produces different errors about unused ignores (due to a workaround
for another issue).

See cockpit-project/bots#9071 and
pythongssapi/python-gssapi#338
@allisonkarlitskaya allisonkarlitskaya force-pushed the unicorn-barf branch 2 times, most recently from dc8dbdf to e54f025 Compare May 26, 2026 09:45
allisonkarlitskaya added a commit to cockpit-project/cockpituous that referenced this pull request May 26, 2026
gssapi is already installed in the container so this just brings a very
thin wrapper for it.

This is basically just for type checking: the gssapi import is lazy and
won't run in CI but mypy gets unhappy about it.  We could ignore it, but
it produces different errors about unused ignores (due to a workaround
for another issue).

See cockpit-project/bots#9071 and
pythongssapi/python-gssapi#338
allisonkarlitskaya added a commit that referenced this pull request May 26, 2026
We're slowly moving away from Linode and this is the next step: this
mirror is the one that lets anyone download non-RHEL images, but the AWS
mirror now fills that role.

The eu-central-1 Linode mirror will remain online for now because of the
large number of people who have an S3 key issued there which allows them
to download the RHEL images.  We're going to slowly migrate to #9071 for
that, but it's not there yet.

Dropping us-east-1 also lets us get rid of our special "public secret"
key.
allisonkarlitskaya added a commit that referenced this pull request May 26, 2026
We're slowly moving away from Linode and this is the next step: this
mirror is the one that lets anyone download non-RHEL images, but the AWS
mirror now fills that role.

The eu-central-1 Linode mirror will remain online for now because of the
large number of people who have an S3 key issued there which allows them
to download the RHEL images.  We're going to slowly migrate to #9071 for
that, but it's not there yet.

Dropping us-east-1 also lets us get rid of our special "public secret"
key.

@martinpitt martinpitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a quick look only -- this is rather overwhelming to review in fine detail. It feels like it ought not to be that complex, especially as your initial PoC was like 100 lines? Why did that blow up so much, apart from accidental complexity like cute and the webkit script?

Comment thread lib/gssapi_saml_sts.py
Comment on lines +207 to +209
idp_url='https://auth.redhat.com/auth/realms/EmployeeIDP/protocol/saml/clients/itaws',
role_arn='arn:aws:iam::727920394381:role/727920394381-cockpit-ci-images-download',
provider_arn='arn:aws:iam::727920394381:saml-provider/RedHatInternal',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add comments/URLs to where these magic values come from?

Comment thread lib/locking.py Outdated
Comment thread lib/progressbar.py
@@ -0,0 +1,155 @@
# Copyright (C) 2026 Red Hat, Inc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OMG, can this grow some pwd.getpwuid(os.getuid()).pw_name != 'martin'? This is really distracting..

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NO_COLOR=1? :D

no seriously though, I figured this might be controversial. See the name of the branch ;)

Comment thread lib/gssapi_saml_sts.py Outdated
Comment thread lib/gssapi_saml_sts.py Outdated
Comment thread saml-login
@@ -0,0 +1,113 @@
#!/usr/bin/env python3

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new saml-login isn't referenced anywhere yet? Do we actually need this for uploading images?

Note: we should eliminate the static AWS_KEY_LOGS secret and move to OIDC/JWT based auth with temporary tokens. For that we can hopefully re-use some machinery that this PR introduces already. But it wouldn't start with a Kerberos token. Let's chat about this today.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I already played around with OIDC a bit when trying to implement this stuff and it would fit well into this file.

That's definitely "next step" though.

Comment thread lib/gssapi_saml_sts.py
key, expiration = _parse_sts_response(xml)
path = cache_path(role_name)
path.parent.mkdir(parents=True, exist_ok=True)
with open(os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600), 'w') as fp:

@Venefilyn Venefilyn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quickly went over but feel like it needs more docs in general and a bit concerned about the storing of credentials. How long are the credentials valid for?

Comment thread lib/progressbar.py
Comment thread lib/progressbar.py
Comment thread lib/progressbar.py
Comment thread image-download-rhel Outdated
Comment thread lib/progressbar.py
Comment thread lib/gssapi_saml_sts.py Outdated
Comment thread lib/gssapi_saml_sts.py
Comment thread saml-login Outdated
@allisonkarlitskaya allisonkarlitskaya force-pushed the unicorn-barf branch 3 times, most recently from 85f64c2 to 4901e1c Compare June 8, 2026 09:47
Comment thread image-download
return False
url, key, total = result

with open(os.open(temp, os.O_CREAT | os.O_RDWR, 0o666), 'r+b') as fp:
Comment thread image-download
return False
url, key, total = result

with open(os.open(temp, os.O_CREAT | os.O_RDWR, 0o666), 'r+b') as fp:
@allisonkarlitskaya

Copy link
Copy Markdown
Member Author

/image-refresh rhel-8-10

@allisonkarlitskaya

Copy link
Copy Markdown
Member Author

/image-refresh arch

@cockpituous

Copy link
Copy Markdown
Contributor

Task scheduled: issue-9071 image-refresh/rhel-8-10

Testing Farm link: https://artifacts.osci.redhat.com/testing-farm/58f156c1-e1ae-40f7-b17b-a75bbd90c7e1

Job JSON
{
  "repo": "cockpit-project/bots",
  "sha": "4901e1caf922b24c8eaae8b1fc2c4739a58cb78a",
  "pull": 9071,
  "slug": "image-refresh-rhel-8-10-4901e1ca-20260608-110242",
  "context": "image-refresh/rhel-8-10",
  "command": [
    "./image-refresh",
    "--verbose",
    "--issue=9071",
    "rhel-8-10"
  ],
  "secrets": [
    "github-token",
    "image-upload"
  ]
}

@cockpituous

Copy link
Copy Markdown
Contributor

Task scheduled: issue-9071 image-refresh/arch

Testing Farm link: https://artifacts.osci.redhat.com/testing-farm/18db7dd5-bf04-45b7-bb4d-f0257c576174

Job JSON
{
  "repo": "cockpit-project/bots",
  "sha": "4901e1caf922b24c8eaae8b1fc2c4739a58cb78a",
  "pull": 9071,
  "slug": "image-refresh-arch-4901e1ca-20260608-110246",
  "context": "image-refresh/arch",
  "command": [
    "./image-refresh",
    "--verbose",
    "--issue=9071",
    "arch"
  ],
  "secrets": [
    "github-token",
    "image-upload"
  ]
}

@cockpituous

Copy link
Copy Markdown
Contributor

@cockpituous

Copy link
Copy Markdown
Contributor

@cockpituous

Copy link
Copy Markdown
Contributor

@cockpituous

Copy link
Copy Markdown
Contributor

allisonkarlitskaya pushed a commit that referenced this pull request Jun 8, 2026
Removed:
  yajl (2.1.0-7)

Added:
  hwloc (2.13.0-1)
  libblake3 (1.8.4-1)
  libntfs-3g (2026.2.25-1)
  libtool (2.6.1-1)
  onetbb (2023.0.0-1)

Changed:
  bash (5.3.9-1 -> 5.3.12-1)
  capstone (5.0.7-2 -> 5.0.9-1)
  cockpit (362-1 -> 363.2-1)
  crun (1.27.1-1 -> 1.28-1)
  dnsmasq (2.92.rel2-2 -> 2.93-1)
  hwdata (0.407-1 -> 0.408-1)
  iana-etc (20260511-1 -> 20260530-1)
  kbd (2.9.0-1 -> 2.10.0-1)
  libdrm (2.4.133-1 -> 2.4.134-1)
  libnet (2:1.3-1 -> 2:1.3-2)
  libnghttp3 (1.15.0-1 -> 1.16.0-1)
  libngtcp2 (1.22.1-1 -> 1.23.0-1)
  libvirt (1:12.3.0-1 -> 1:12.4.0-1)
  libvirt-python (1:12.3.0-1 -> 1:12.4.0-1)
  libxkbcommon (1.13.1-1 -> 1.13.2-1)
  linux (7.0.10.arch1-1 -> 7.0.11.arch1-1)
  linux-api-headers (6.19-1 -> 7.0-1)
  llvm-libs (22.1.5-1 -> 22.1.6-1)
  mesa (1:26.1.1-2 -> 1:26.1.2-1)
  mkinitcpio (41-3 -> 41-4)
  ntfs-3g (2022.10.3-2 -> 2026.2.25-1)
  passt (2026_05_07.1afd4ed-1 -> 2026_05_26.038c51e-1)
  pcp (7.1.4-1 -> 7.1.5-1)
  pcsclite (2.4.1-1 -> 2.5.0-1)
  perf (7.0.9-1 -> 7.0.10-1)
  protobuf (34.1-1 -> 35.0-1)
  protobuf-c (1.5.2-9 -> 1.5.2-10)
  python-debian (1.1.0-2 -> 1.1.1-1)
  python-idna (3.16-1 -> 3.18-1)
  python-protobuf (34.1-1 -> 35.0-1)
  qemu-audio-spice (11.0.0-1 -> 11.0.1-1)
  qemu-base (11.0.0-1 -> 11.0.1-1)
  qemu-block-curl (11.0.0-1 -> 11.0.1-1)
  qemu-chardev-spice (11.0.0-1 -> 11.0.1-1)
  qemu-common (11.0.0-1 -> 11.0.1-1)
  qemu-guest-agent (11.0.0-1 -> 11.0.1-1)
  qemu-hw-usb-host (11.0.0-1 -> 11.0.1-1)
  qemu-hw-usb-redirect (11.0.0-1 -> 11.0.1-1)
  qemu-img (11.0.0-1 -> 11.0.1-1)
  qemu-system-x86 (11.0.0-1 -> 11.0.1-1)
  qemu-system-x86-firmware (11.0.0-1 -> 11.0.1-1)
  qemu-tools (11.0.0-1 -> 11.0.1-1)
  qemu-ui-opengl (11.0.0-1 -> 11.0.1-1)
  qemu-ui-spice-app (11.0.0-1 -> 11.0.1-1)
  qemu-ui-spice-core (11.0.0-1 -> 11.0.1-1)
  rpcbind (1.2.8-1 -> 1.2.9-1)
  sqlite (3.53.1-1 -> 3.53.2-1)
  systemd (260.1-2 -> 260.2-2)
  systemd-libs (260.1-2 -> 260.2-2)
  systemd-sysvcompat (260.1-2 -> 260.2-2)
  vim (9.2.0511-1 -> 9.2.0600-1)
  vim-runtime (9.2.0511-1 -> 9.2.0600-1)
  which (2.23-1 -> 2.25-1)

Closes #9071
allisonkarlitskaya pushed a commit that referenced this pull request Jun 8, 2026
Removed:

Added:

Changed:

Closes #9071
Venefilyn
Venefilyn previously approved these changes Jun 8, 2026

@Venefilyn Venefilyn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM if it works.

Comment thread lib/gssapi_saml_sts.py Outdated
Comment thread lib/gssapi_saml_sts.py Outdated
Comment thread lib/gssapi_saml_sts.py
Comment thread lib/gssapi_saml_sts.py
Comment thread lib/gssapi_saml_sts.py
Comment on lines +184 to +192
# Red Hat employee IdP (SAML via Kerberos) → AWS IAM role for cockpit-ci-images
# Rover group: https://rover.redhat.com/groups/group/it-cloud-aws-727920394381-cockpit-ci-images-download
TARGETS = {
'https://cockpit-ci-images*.s3.*.amazonaws.com/rhel-*': SAMLTarget(
idp_url='https://auth.redhat.com/auth/realms/EmployeeIDP/protocol/saml/clients/itaws',
provider_arn='arn:aws:iam::727920394381:saml-provider/RedHatInternal',
role_arn='arn:aws:iam::727920394381:role/727920394381-cockpit-ci-images-download',
),
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you find the arn?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created it... this could probably use a writeup in the cockpituous repo.

Comment thread lib/ansi.py
Comment thread lib/progressbar.py
This is capable of downloading RHEL images from AWS.

We identify URLs that should be STS-authenticated by using a list
(currently 1) of fnmatches on URLs.  This list is only consulted if you
don't have a normal static key in ~/.config/cockpit-dev/s3-keys/.

Once we've identified that we want to issue a transient key, the process
goes like this:

  - you get a Kerberos ticket with kinit

  - we use gssapi to get a SPNEGO token for auth.redhat.com

  - we post a login attempt using the token and hopefully get back a SAML
    POST binding HTML page which we can extract the SAML assertion from

  - with the SAML assertion in hand we can use AWS STS to generate a
    transient token for a particular IAM Role.  If you're in the
    it-cloud-aws-727920394381-cockpit-ci-images-download rover group
    this will allow you to download RHEL images from AWS

  - we use the STS token to do the S3 signing operation

This should be more or less transparent to anyone who is in the correct
group and logged in with kerberos.  There's no need for us to maintain a
list of users and tokens anymore and human developers can delete their
old s3-keys.

The new downloader is intended as an eventual replacement for
image-download.  It's a rewrite from the ground up and has a couple of
other niceties:

  - urllib instead of curl (which was getting complicated with all the
    extra headers)

  - checksum verification based on filename

  - improved locking vs. the old downloader: no more poll loop or subtle
    race conditions with deleted lockfiles

  - 🌈 rainbows!

Some features not implemented:

  - `--state` and modification times (which seems to be dead code)
  - `--force`

For users who don't want to use Kerberos (which requires connecting to
VPN) there's also a saml-login script which will do a SAML interaction
in a simple WebKit view and save the credential to the cache (for a
validity of one hour).
@allisonkarlitskaya allisonkarlitskaya merged commit e635e8d into main Jun 10, 2026
5 of 6 checks passed
@allisonkarlitskaya allisonkarlitskaya deleted the unicorn-barf branch June 10, 2026 16:51
@allisonkarlitskaya allisonkarlitskaya mentioned this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants