]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | =========== |
2 | STS in Ceph | |
3 | =========== | |
4 | ||
5 | Secure Token Service is a web service in AWS that returns a set of temporary security credentials for authenticating federated users. | |
6 | The link to official AWS documentation can be found here: https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html. | |
7 | ||
8 | Ceph Object Gateway implements a subset of STS APIs that provide temporary credentials for identity and access management. | |
9 | These temporary credentials can be used to make subsequent S3 calls which will be authenticated by the STS engine in Ceph Object Gateway. | |
10 | Permissions of the temporary credentials can be further restricted via an IAM policy passed as a parameter to the STS APIs. | |
11 | ||
12 | STS REST APIs | |
13 | ============= | |
14 | ||
15 | The following STS REST APIs have been implemented in Ceph Object Gateway: | |
16 | ||
17 | 1. AssumeRole: Returns a set of temporary credentials that can be used for | |
18 | cross-account access. The temporary credentials will have permissions that are | |
19 | allowed by both - permission policies attached with the Role and policy attached | |
20 | with the AssumeRole API. | |
21 | ||
22 | Parameters: | |
23 | **RoleArn** (String/ Required): ARN of the Role to Assume. | |
24 | ||
25 | **RoleSessionName** (String/ Required): An Identifier for the assumed role | |
26 | session. | |
27 | ||
28 | **Policy** (String/ Optional): An IAM Policy in JSON format. | |
29 | ||
30 | **DurationSeconds** (Integer/ Optional): The duration in seconds of the session. | |
31 | Its default value is 3600. | |
32 | ||
33 | **ExternalId** (String/ Optional): A unique Id that might be used when a role is | |
34 | assumed in another account. | |
35 | ||
36 | **SerialNumber** (String/ Optional): The Id number of the MFA device associated | |
37 | with the user making the AssumeRole call. | |
38 | ||
39 | **TokenCode** (String/ Optional): The value provided by the MFA device, if the | |
40 | trust policy of the role being assumed requires MFA. | |
41 | ||
42 | 2. AssumeRoleWithWebIdentity: Returns a set of temporary credentials for users that | |
43 | have been authenticated by a web/mobile app by an OpenID Connect /OAuth2.0 Identity Provider. | |
44 | Currently Keycloak has been tested and integrated with RGW. | |
45 | ||
46 | Parameters: | |
47 | **RoleArn** (String/ Required): ARN of the Role to Assume. | |
48 | ||
49 | **RoleSessionName** (String/ Required): An Identifier for the assumed role | |
50 | session. | |
51 | ||
52 | **Policy** (String/ Optional): An IAM Policy in JSON format. | |
53 | ||
54 | **DurationSeconds** (Integer/ Optional): The duration in seconds of the session. | |
55 | Its default value is 3600. | |
56 | ||
57 | **ProviderId** (String/ Optional): Fully qualified host component of the domain name | |
58 | of the IDP. Valid only for OAuth2.0 tokens (not for OpenID Connect tokens). | |
59 | ||
60 | **WebIdentityToken** (String/ Required): The OpenID Connect/ OAuth2.0 token, which the | |
61 | application gets in return after authenticating its user with an IDP. | |
62 | ||
f91f0fd5 TL |
63 | Before invoking AssumeRoleWithWebIdentity, an OpenID Connect Provider entity (which the web application |
64 | authenticates with), needs to be created in RGW. | |
65 | ||
20effc67 TL |
66 | The trust between the IDP and the role is created by adding a condition to the role's trust policy, which |
67 | allows access only to applications which satisfy the given condition. | |
68 | All claims of the JWT are supported in the condition of the role's trust policy. | |
69 | An example of a policy that uses the 'aud' claim in the condition is of the form:: | |
f91f0fd5 | 70 | |
20effc67 | 71 | '''{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":["arn:aws:iam:::oidc-provider/<URL of IDP>"]},"Action":["sts:AssumeRoleWithWebIdentity"],"Condition":{"StringEquals":{"<URL of IDP> :app_id":"<aud>"}}}]}''' |
f91f0fd5 | 72 | |
20effc67 TL |
73 | The app_id in the condition above must match the 'aud' claim of the incoming token. |
74 | ||
75 | An example of a policy that uses the 'sub' claim in the condition is of the form:: | |
76 | ||
77 | "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":[\"arn:aws:iam:::oidc-provider/<URL of IDP>\"]},\"Action\":[\"sts:AssumeRoleWithWebIdentity\"],\"Condition\":{\"StringEquals\":{\"<URL of IDP> :sub\":\"<sub>\"\}\}\}\]\}" | |
78 | ||
79 | Similarly, an example of a policy that uses 'azp' claim in the condition is of the form:: | |
80 | ||
81 | "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":[\"arn:aws:iam:::oidc-provider/<URL of IDP>\"]},\"Action\":[\"sts:AssumeRoleWithWebIdentity\"],\"Condition\":{\"StringEquals\":{\"<URL of IDP> :azp\":\"<azp>\"\}\}\}\]\}" | |
f91f0fd5 | 82 | |
f67539c2 TL |
83 | A shadow user is created corresponding to every federated user. The user id is derived from the 'sub' field of the incoming web token. |
84 | The user is created in a separate namespace - 'oidc' such that the user id doesn't clash with any other user ids in rgw. The format of the user id | |
85 | is - <tenant>$<user-namespace>$<sub> where user-namespace is 'oidc' for users that authenticate with oidc providers. | |
86 | ||
20effc67 TL |
87 | RGW now supports Session tags that can be passed in the web token to AssumeRoleWithWebIdentity call. More information related to Session Tags can be found here |
88 | :doc:`session-tags`. | |
89 | ||
9f95a23c TL |
90 | STS Configuration |
91 | ================= | |
92 | ||
93 | The following configurable options have to be added for STS integration:: | |
94 | ||
95 | [client.radosgw.gateway] | |
96 | rgw sts key = {sts key for encrypting the session token} | |
97 | rgw s3 auth use sts = true | |
98 | ||
9f95a23c TL |
99 | Note: By default, STS and S3 APIs co-exist in the same namespace, and both S3 |
100 | and STS APIs can be accessed via the same endpoint in Ceph Object Gateway. | |
101 | ||
102 | Examples | |
103 | ======== | |
104 | ||
105 | 1. The following is an example of AssumeRole API call, which shows steps to create a role, assign a policy to it | |
106 | (that allows access to S3 resources), assuming a role to get temporary credentials and accessing s3 resources using | |
107 | those credentials. In this example, TESTER1 assumes a role created by TESTER, to access S3 resources owned by TESTER, | |
108 | according to the permission policy attached to the role. | |
109 | ||
2a845540 TL |
110 | .. code-block:: console |
111 | ||
112 | radosgw-admin caps add --uid="TESTER" --caps="roles=*" | |
113 | ||
114 | 2. The following is an example of the AssumeRole API call, which shows steps to create a role, assign a policy to it | |
115 | (that allows access to S3 resources), assuming a role to get temporary credentials and accessing S3 resources using | |
116 | those credentials. In this example, TESTER1 assumes a role created by TESTER, to access S3 resources owned by TESTER, | |
117 | according to the permission policy attached to the role. | |
118 | ||
9f95a23c TL |
119 | .. code-block:: python |
120 | ||
121 | import boto3 | |
122 | ||
123 | iam_client = boto3.client('iam', | |
124 | aws_access_key_id=<access_key of TESTER>, | |
125 | aws_secret_access_key=<secret_key of TESTER>, | |
126 | endpoint_url=<IAM URL>, | |
127 | region_name='' | |
128 | ) | |
129 | ||
20effc67 | 130 | policy_document = '''{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["arn:aws:iam:::user/TESTER1"]},"Action":["sts:AssumeRole"]}]}''' |
9f95a23c TL |
131 | |
132 | role_response = iam_client.create_role( | |
133 | AssumeRolePolicyDocument=policy_document, | |
134 | Path='/', | |
135 | RoleName='S3Access', | |
136 | ) | |
137 | ||
20effc67 | 138 | role_policy = '''{"Version":"2012-10-17","Statement":{"Effect":"Allow","Action":"s3:*","Resource":"arn:aws:s3:::*"}}''' |
9f95a23c TL |
139 | |
140 | response = iam_client.put_role_policy( | |
141 | RoleName='S3Access', | |
142 | PolicyName='Policy1', | |
143 | PolicyDocument=role_policy | |
144 | ) | |
145 | ||
146 | sts_client = boto3.client('sts', | |
147 | aws_access_key_id=<access_key of TESTER1>, | |
148 | aws_secret_access_key=<secret_key of TESTER1>, | |
149 | endpoint_url=<STS URL>, | |
150 | region_name='', | |
151 | ) | |
152 | ||
153 | response = sts_client.assume_role( | |
154 | RoleArn=role_response['Role']['Arn'], | |
155 | RoleSessionName='Bob', | |
156 | DurationSeconds=3600 | |
157 | ) | |
158 | ||
159 | s3client = boto3.client('s3', | |
160 | aws_access_key_id = response['Credentials']['AccessKeyId'], | |
161 | aws_secret_access_key = response['Credentials']['SecretAccessKey'], | |
162 | aws_session_token = response['Credentials']['SessionToken'], | |
163 | endpoint_url=<S3 URL>, | |
164 | region_name='',) | |
165 | ||
166 | bucket_name = 'my-bucket' | |
167 | s3bucket = s3client.create_bucket(Bucket=bucket_name) | |
168 | resp = s3client.list_buckets() | |
169 | ||
170 | 2. The following is an example of AssumeRoleWithWebIdentity API call, where an external app that has users authenticated with | |
171 | an OpenID Connect/ OAuth2 IDP (Keycloak in this example), assumes a role to get back temporary credentials and access S3 resources | |
172 | according to permission policy of the role. | |
173 | ||
174 | .. code-block:: python | |
175 | ||
176 | import boto3 | |
177 | ||
178 | iam_client = boto3.client('iam', | |
179 | aws_access_key_id=<access_key of TESTER>, | |
180 | aws_secret_access_key=<secret_key of TESTER>, | |
181 | endpoint_url=<IAM URL>, | |
182 | region_name='' | |
183 | ) | |
184 | ||
f91f0fd5 TL |
185 | oidc_response = iam_client.create_open_id_connect_provider( |
186 | Url=<URL of the OpenID Connect Provider, | |
187 | ClientIDList=[ | |
188 | <Client id registered with the IDP> | |
189 | ], | |
190 | ThumbprintList=[ | |
191 | <Thumbprint of the IDP> | |
192 | ] | |
193 | ) | |
194 | ||
20effc67 | 195 | policy_document = '''{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":["arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/demo"]},"Action":["sts:AssumeRoleWithWebIdentity"],"Condition":{"StringEquals":{"localhost:8080/auth/realms/demo:app_id":"customer-portal"}}}]}''' |
9f95a23c TL |
196 | role_response = iam_client.create_role( |
197 | AssumeRolePolicyDocument=policy_document, | |
198 | Path='/', | |
199 | RoleName='S3Access', | |
200 | ) | |
201 | ||
20effc67 | 202 | role_policy = '''{"Version":"2012-10-17","Statement":{"Effect":"Allow","Action":"s3:*","Resource":"arn:aws:s3:::*"}}''' |
9f95a23c TL |
203 | |
204 | response = iam_client.put_role_policy( | |
205 | RoleName='S3Access', | |
206 | PolicyName='Policy1', | |
207 | PolicyDocument=role_policy | |
208 | ) | |
209 | ||
210 | sts_client = boto3.client('sts', | |
211 | aws_access_key_id=<access_key of TESTER1>, | |
212 | aws_secret_access_key=<secret_key of TESTER1>, | |
213 | endpoint_url=<STS URL>, | |
214 | region_name='', | |
215 | ) | |
216 | ||
217 | response = client.assume_role_with_web_identity( | |
218 | RoleArn=role_response['Role']['Arn'], | |
219 | RoleSessionName='Bob', | |
220 | DurationSeconds=3600, | |
221 | WebIdentityToken=<Web Token> | |
222 | ) | |
223 | ||
224 | s3client = boto3.client('s3', | |
225 | aws_access_key_id = response['Credentials']['AccessKeyId'], | |
226 | aws_secret_access_key = response['Credentials']['SecretAccessKey'], | |
227 | aws_session_token = response['Credentials']['SessionToken'], | |
228 | endpoint_url=<S3 URL>, | |
229 | region_name='',) | |
230 | ||
231 | bucket_name = 'my-bucket' | |
232 | s3bucket = s3client.create_bucket(Bucket=bucket_name) | |
233 | resp = s3client.list_buckets() | |
234 | ||
f91f0fd5 TL |
235 | How to obtain thumbprint of an OpenID Connect Provider IDP |
236 | ========================================================== | |
237 | 1. Take the OpenID connect provider's URL and add /.well-known/openid-configuration | |
238 | to it to get the URL to get the IDP's configuration document. For example, if the URL | |
239 | of the IDP is http://localhost:8000/auth/realms/quickstart, then the URL to get the | |
240 | document from is http://localhost:8000/auth/realms/quickstart/.well-known/openid-configuration | |
241 | ||
242 | 2. Use the following curl command to get the configuration document from the URL described | |
243 | in step 1:: | |
244 | ||
245 | curl -k -v \ | |
246 | -X GET \ | |
247 | -H "Content-Type: application/x-www-form-urlencoded" \ | |
248 | "http://localhost:8000/auth/realms/quickstart/.well-known/openid-configuration" \ | |
249 | | jq . | |
250 | ||
251 | 3. From the response of step 2, use the value of "jwks_uri" to get the certificate of the IDP, | |
252 | using the following code:: | |
253 | curl -k -v \ | |
254 | -X GET \ | |
255 | -H "Content-Type: application/x-www-form-urlencoded" \ | |
256 | "http://$KC_SERVER/$KC_CONTEXT/realms/$KC_REALM/protocol/openid-connect/certs" \ | |
257 | | jq . | |
258 | ||
259 | 3. Copy the result of "x5c" in the response above, in a file certificate.crt, and add | |
260 | '-----BEGIN CERTIFICATE-----' at the beginning and "-----END CERTIFICATE-----" | |
261 | at the end. | |
262 | ||
263 | 4. Use the following OpenSSL command to get the certificate thumbprint:: | |
264 | ||
265 | openssl x509 -in certificate.crt -fingerprint -noout | |
266 | ||
267 | 5. The result of the above command in step 4, will be a SHA1 fingerprint, like the following:: | |
268 | ||
269 | SHA1 Fingerprint=F7:D7:B3:51:5D:D0:D3:19:DD:21:9A:43:A9:EA:72:7A:D6:06:52:87 | |
270 | ||
271 | 6. Remove the colons from the result above to get the final thumbprint which can be as input | |
272 | while creating the OpenID Connect Provider entity in IAM:: | |
273 | ||
274 | F7D7B3515DD0D319DD219A43A9EA727AD6065287 | |
275 | ||
9f95a23c TL |
276 | Roles in RGW |
277 | ============ | |
278 | ||
279 | More information for role manipulation can be found here | |
280 | :doc:`role`. | |
281 | ||
f91f0fd5 TL |
282 | OpenID Connect Provider in RGW |
283 | ============================== | |
284 | ||
285 | More information for OpenID Connect Provider entity manipulation | |
286 | can be found here | |
287 | :doc:`oidc`. | |
288 | ||
9f95a23c TL |
289 | Keycloak integration with Radosgw |
290 | ================================= | |
291 | ||
292 | Steps for integrating Radosgw with Keycloak can be found here | |
293 | :doc:`keycloak`. | |
294 | ||
295 | STSLite | |
296 | ======= | |
297 | STSLite has been built on STS, and documentation for the same can be found here | |
2a845540 | 298 | :doc:`STSLite`. |