Skip to content

Commit cf86978

Browse files
stevsmitSteven Smith
andauthored
Starts work for keyless auth for robot accounts (quay#1125)
Co-authored-by: Steven Smith <[email protected]>
1 parent cd4265f commit cf86978

File tree

3 files changed

+292
-2
lines changed

3 files changed

+292
-2
lines changed

manage_quay/master.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ include::modules/configuring-oidc-authentication.adoc[leveloffset=+1]
9898
include::modules/configuring-red-hat-sso.adoc[leveloffset=+2]
9999
include::modules/enabling-team-sync-oidc.adoc[leveloffset=+2]
100100

101+
//keyless auth
102+
103+
include::modules/keyless-authentication-robot-accounts.adoc[leveloffset=+1]
104+
101105
//aws sts
102106
include::modules/configuring-aws-sts-quay.adoc[leveloffset=+1]
103107
include::modules/configuring-quay-standalone-aws-sts.adoc[leveloffset=+2]
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
:_content-type: PROCEDURE
2+
[id="keyless-authentication-robot-accounts"]
3+
= Keyless authentication with robot accounts
4+
5+
In previous versions of {productname}, robot account tokens were valid for the lifetime of the token unless deleted or regenerated. Tokens that do not expire have security implications for users who do not want to store long-term passwords or manage the deletion, or regeneration, or new authentication tokens.
6+
7+
With {productname} {producty}, {productname} administrators are provided the ability to exchange external OIDC token for short-lived, or _ephemeral_ robot account tokens with either Red Hat Single Sign-On (based on the Keycloak project) or Microsoft Entra ID. This allows robot accounts to leverage tokens that last one hour, which are are refreshed regularly and can be used to authenticate individual transactions.
8+
9+
This feature greatly enhances the security of your {productname} registry by mitigating the possibility of robot token exposure by removing the tokens after one hour.
10+
11+
Configuring keyless authentication with robot accounts is a multi-step procedure that requires setting a robot federation, generating an OAuth2 token from your OIDC provider, and exchanging the OAuth2 token for a robot account access token.
12+
13+
[id="generating-oauth2-token-using-keycloak"]
14+
== Generating an OAuth2 token with Red Hat Sign Sign-On
15+
16+
The following procedure shows you how to generate an OAuth2 token using Red Hat Single Sign-On. Depending on your OIDC provider, these steps will vary.
17+
18+
.Procedure
19+
20+
. On the Red Hat Single Sign-On UI:
21+
22+
.. Click *Clients* and then the name of the application or service that can request authentication of a user.
23+
24+
.. On the *Settings* page of your client, ensure that the following options are set or enabled:
25+
+
26+
* *Client ID*
27+
* *Valid redirect URI*
28+
* *Client authentication*
29+
* *Authorization*
30+
* *Standard flow*
31+
* *Direct access grants*
32+
+
33+
[NOTE]
34+
====
35+
Settings can differ depending on your setup.
36+
====
37+
38+
.. On the *Credentials* page, store the *Client Secret* for future use.
39+
40+
.. On the *Users* page, click *Add user* and enter a username, for example, `service-account-quaydev`. Then, click *Create*.
41+
42+
.. Click the name of of the user, for example *service-account-quaydev* on the *Users* page.
43+
44+
.. Click the *Credentials* tab -> *Set password* -> and provide a password for the user. If warranted, you can make this password temporary by selecting the *Temporary* option.
45+
46+
.. Click the *Realm settings* tab -> *OpenID Endpoint Configuration*. Store the `/protocol/openid-connect/token` endpoint. For example:
47+
+
48+
[source,text]
49+
----
50+
http://localhost:8080/realms/master/protocol/openid-connect/token
51+
----
52+
53+
. On a web browser, navigate to the following URL:
54+
+
55+
[source,text]
56+
----
57+
http://<keycloak_url>/realms/<realm_name>/protocol/openid-connect/auth?response_type=code&client_id=<client_id>
58+
----
59+
60+
. When prompted, log in with the *service-account-quaydev* user and the temporary password you set. Complete the login by providing the required information and setting a permanent password if necessary.
61+
62+
. You are redirected to the URI address provided for your client. For example:
63+
+
64+
[source,text]
65+
----
66+
https://localhost:3000/cb?session_state=5c9bce22-6b85-4654-b716-e9bbb3e755bc&iss=http%3A%2F%2Flocalhost%3A8080%2Frealms%2Fmaster&code=ea5b76eb-47a5-4e5d-8f71-0892178250db.5c9bce22-6b85-4654-b716-e9bbb3e755bc.cdffafbc-20fb-42b9-b254-866017057f43
67+
----
68+
+
69+
Take note of the `code` provided in the address. For example:
70+
+
71+
[source,text]
72+
----
73+
code=ea5b76eb-47a5-4e5d-8f71-0892178250db.5c9bce22-6b85-4654-b716-e9bbb3e755bc.cdffafbc-20fb-42b9-b254-866017057f43
74+
----
75+
+
76+
[NOTE]
77+
====
78+
This is a temporary code that can only be used one time. If necessary, you can refresh the page or revisit the URL to obtain another code.
79+
====
80+
81+
. On your terminal, use the following `curl -X POST` command to generate a temporary OAuth2 access token:
82+
+
83+
[source,terminal]
84+
----
85+
$ curl -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" <1>
86+
-H "Content-Type: application/x-www-form-urlencoded" \
87+
-d "client_id=quaydev" <2>
88+
-d "client_secret=g8gPsBLxVrLo2PjmZkYBdKvcB9C7fmBz" <3>
89+
-d "grant_type=authorization_code"
90+
-d "code=ea5b76eb-47a5-4e5d-8f71-0892178250db.5c9bce22-6b85-4654-b716-e9bbb3e755bc.cdffafbc-20fb-42b9-b254-866017057f43" <4>
91+
----
92+
<1> The `protocol/openid-connect/token` endpoint found on the *Realm settings* page of the Red Hat Single Sign-On UI.
93+
<2> The Client ID used for this procedure.
94+
<3> The Client Secret for the Client ID.
95+
<4> The code returned from the redirect URI.
96+
+
97+
.Example output
98+
+
99+
[source,terminal]
100+
----
101+
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTVmExVHZ6eDd2cHVmc1dkZmc1SHdua1ZDcVlOM01DN1N5T016R0QwVGhVIn0...",
102+
"expires_in":60,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJiNTBlZTVkMS05OTc1LTQwMzUtYjNkNy1lMWQ5ZTJmMjg0MTEifQ.oBDx6B3pUkXQO8m-M3hYE7v-w25ak6y70CQd5J8f5EuldhvTwpWrC1K7yOglvs09dQxtq8ont12rKIoCIi4WXw","token_type":"Bearer","not-before-policy":0,"session_state":"5c9bce22-6b85-4654-b716-e9bbb3e755bc","scope":"profile email"}
103+
----
104+
105+
. Store the `access_token` from the previously step, as it will be exchanged for a {productname} robot account token in the following procedure.
106+
107+
[id="setting-robot-federation"]
108+
== Setting up a robot account federation by using the {productname} v2 UI
109+
110+
The following procedure shows you how to set up a robot account federation by using the {productname} v2 UI. This procedure uses Red Hat Single Sign-On, which is based on the Keycloak project. These steps, and the information used to set up a robot account federation, will vary depending on your OIDC provider.
111+
112+
.Prerequisites
113+
114+
* You have created an organization. The following example uses `fed_test`.
115+
* You have created a robot account. The following example uses `fest_test+robot1`.
116+
* You have configured a OIDC for your {productname} deployment. The following example uses Red Hat Single Sign-On.
117+
118+
.Procedure
119+
120+
. On the Red Hat Single Sign-On main page:
121+
122+
.. Select the appropriate realm that is authenticated for use with {productname}. Store the issuer URL, for example, `\https://keycloak-auth-realm.quayadmin.org/realms/quayrealm`.
123+
124+
.. Click *Users* -> the name of the user to be linked with the robot account for authentication. You must use the same user account that you used when generating the OAuth2 access token.
125+
126+
.. On the *Details* page, store the *ID* of the user, for example, `449e14f8-9eb5-4d59-a63e-b7a77c75f770`.
127+
+
128+
[NOTE]
129+
====
130+
The information collected in this step will vary depending on your OIDC provider. For example, with Red Hat Single Sign-On, the *ID* of a user is used as the *Subject* to set up the robot account federation in a subsequent step. For a different OIDC provider, like Microsoft Entra ID, this information is stored as the *Subject*.
131+
====
132+
133+
. On your {productname} registry:
134+
135+
.. Navigate to *Organizations* and click the name of your organization, for example, *fed_test*.
136+
137+
.. Click *Robot Accounts*.
138+
139+
.. Click the menu kebab -> *Set robot federation*.
140+
141+
.. Click the *+* symbol.
142+
143+
.. In the popup window, include the following information:
144+
+
145+
* *Issuer URL*: `\https://keycloak-auth-realm.quayadmin.org/realms/quayrealm`. For Red Hat Single Sign-On, this is the the URL of your Red Hat Single Sign-On realm. This might vary depending on your OIDC provider.
146+
* *Subject*: `449e14f8-9eb5-4d59-a63e-b7a77c75f770`. For Red Hat Single Sign-On, the *Subject* is the *ID* of your Red Hat Single Sign-On user. This varies depending on your OIDC provider. For example, if you are using Microsoft Entra ID, the *Subject* will be the *Subject* or your Entra ID user.
147+
148+
.. Click *Save*.
149+
150+
[id="exchanging-oauth2-robot-account-token"]
151+
== Exchanging an OAuth2 access token for a {productname} robot account token
152+
153+
The following procedure leverages the `access token` generated in the previous procedure to create a new {productname} robot account token. The new {productname} robot account token is used for authentication between your OIDC provider and {productname}.
154+
155+
[NOTE]
156+
====
157+
The following example uses a Python script to exchange the OAuth2 access token for a {productname} robot account token.
158+
====
159+
160+
.Prerequisites
161+
162+
* You have the `python3` CLI tool installed.
163+
164+
.Procedure
165+
166+
. Save the following Python script in a `.py` file, for example, `robot_fed_token_auth.py`
167+
+
168+
[source,python]
169+
----
170+
import requests
171+
import os
172+
173+
TOKEN=os.environ.get('TOKEN')
174+
robot_user = "fed-test+robot1"
175+
176+
def get_quay_robot_token(fed_token):
177+
URL = "https://<quay-server.example.com>/oauth2/federation/robot/token"
178+
response = requests.get(URL, auth=(robot_user,fed_token)) <1>
179+
print(response)
180+
print(response.text)
181+
182+
if __name__ == "__main__":
183+
get_quay_robot_token(TOKEN)
184+
----
185+
<1> If your {productname} deployment is using custom SSL/TLS certificates, the response must be `response = requests.get(URL,auth=(robot_user,fed_token),verify=False)`, which includes the `verify=False` flag.
186+
187+
. Export the OAuth2 access token as `TOKEN`. For example:
188+
+
189+
[source,terminal]
190+
----
191+
$ export TOKEN = eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTVmExVHZ6eDd2cHVmc1dkZmc1SHdua1ZDcVlOM01DN1N5T016R0QwVGhVIn0...
192+
----
193+
194+
. Run the `robot_fed_token_auth.py` script by entering the following command:
195+
+
196+
[source,terminal]
197+
----
198+
$ python3 robot_fed_token_auth.py
199+
----
200+
+
201+
.Example output
202+
+
203+
[source,terminal]
204+
----
205+
<Response [200]>
206+
{"token": "291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZ..."}
207+
----
208+
+
209+
[IMPORTANT]
210+
====
211+
This token expires after one hour. After one hour, a new token must be generated.
212+
====
213+
214+
. Export the robot account access token as `QUAY_TOKEN`. For example:
215+
+
216+
[source,terminal]
217+
----
218+
$ export QUAY_TOKEN=291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZ
219+
----
220+
221+
[id="pushing-pulling-images-robot-account"]
222+
== Pushing and pulling images
223+
224+
After you have generated a new robot account access token and exported it, you can log in and the robot account using the access token and push and pull images.
225+
226+
.Prerequisites
227+
228+
* You have exported the OAuth2 access token into a new robot account access token.
229+
230+
.Procedure
231+
232+
. Log in to your {productname} registry using the `fest_test+robot1` robot account and the `QUAY_TOKEN` access token. For example:
233+
+
234+
[source,terminal]
235+
----
236+
$ podman login <quay-server.example.com> -u fed_test+robot1 -p $QUAY_TOKEN
237+
----
238+
239+
. Pull an image from a {productname} repository for which the robot account has the proper permissions. For example:
240+
+
241+
[source,terminal]
242+
----
243+
$ podman pull <quay-server.example.com/<repository_name>/<image_name>>
244+
----
245+
+
246+
.Example output
247+
+
248+
[source,terminal]
249+
----
250+
Getting image source signatures
251+
Copying blob 900e6061671b done
252+
Copying config 8135583d97 done
253+
Writing manifest to image destination
254+
Storing signatures
255+
8135583d97feb82398909c9c97607159e6db2c4ca2c885c0b8f590ee0f9fe90d
256+
0.57user 0.11system 0:00.99elapsed 68%CPU (0avgtext+0avgdata 78716maxresident)k
257+
800inputs+15424outputs (18major+6528minor)pagefaults 0swaps
258+
----
259+
260+
. Attempt to pull an image from a {productname} repository for which the robot account does _not_ have the proper permissions. For example:
261+
+
262+
[source,terminal]
263+
----
264+
$ podman pull <quay-server.example.com/<different_repository_name>/<image_name>>
265+
----
266+
+
267+
.Example output
268+
+
269+
[source,terminal]
270+
----
271+
Error: initializing source docker://quay-server.example.com/example_repository/busybox:latest: reading manifest in quay-server.example.com/example_repository/busybox: unauthorized: access to the requested resource is not authorized
272+
----
273+
+
274+
After one hour, the credentials for this robot account are set to expire. Afterwards, you must generate a new access token for this robot account.

modules/rn_3_13_0.adoc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,27 @@ Additional auto-pruning policies can be added on the {productname} v2 UI by clic
4040
For more information about setting auto-prune policies, see link:https://docs.redhat.com/en/documentation/red_hat_quay/{producty}/html-single/manage_red_hat_quay/index#red-hat-quay-namespace-auto-pruning-overview[{productname} auto-prune overview].
4141

4242
[id="example-feature-2"]
43-
=== Example Feature 2
43+
=== Keyless authentication with robot accounts
44+
45+
In previous versions of {productname}, robot account tokens were valid for the lifetime of the token unless deleted or regenerated. Tokens that do not expire have security implications for users who do not want to store long-term passwords or manage the deletion, or regeneration, or new authentication tokens.
46+
47+
With {productname} {producty}, {productname} administrators are provided the ability to exchange {productname} robot account tokens for an external OIDC token. This allows robot accounts to leverage short-lived, or _ephemeral tokens_, that last one hour. Ephemeral tokens are refreshed regularly and can be used to authenticate individual transactions.
48+
49+
This feature greatly enhances the security of your {productname} registry by mitigating the possibility of robot token exposure by removing the tokens after one hour.
50+
51+
For more information, see. . .
4452

4553
[id="v2-ui-enhancement"]
4654
=== {productname} v2 UI enhancements
4755

4856
The following enhancements have been made to the {productname} v2 UI.
4957

5058
[id="example-v2-ui-enhancement"]
51-
==== Example v2 UI enhancement 2
59+
==== Robot federation selection
60+
61+
A new configuration page, *Set robot federation*, has been added to the {productname} v2 UI. This can be found by navigating to your organization or repository's robot account, clicking the menu kebab, and then clicking *Set robot federation*. This page is used when configuring keyless authentication with robot accounts, and allows you to add multiple OIDC providers to a single robot account.
62+
63+
For more information, see . . .
5264

5365
[id="new-quay-config-fields-313"]
5466
== New {productname} configuration fields

0 commit comments

Comments
 (0)