Skip to content

Commit 24a6b02

Browse files
committed
feat(VPCInstanceAuthenticator): add support for VPC authentication flow
1 parent cf0d4d2 commit 24a6b02

File tree

7 files changed

+371
-6
lines changed

7 files changed

+371
-6
lines changed

Authentication.md

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ The python-sdk-core project supports the following types of authentication:
33
- Basic Authentication
44
- Bearer Token Authentication
55
- Identity and Access Management (IAM) Authentication
6+
- VPC Instance Authentication
67
- Container Authentication
78
- Cloud Pak for Data Authentication
89
- No Authentication
@@ -13,13 +14,10 @@ so it is important for the SDK user to consult with the appropriate service docu
1314
to understand which authenticators are supported for that service.
1415

1516
The python-sdk-core allows an authenticator to be specified in one of two ways:
16-
1. programmatically - the SDK user invokes the appropriate function(s) to create an instance of the
17-
desired authenticator and then passes the authenticator instance when constructing an instance of the service client.
17+
1. programmatically - the SDK user invokes the appropriate function(s) to create an instance of the
18+
desired authenticator and then passes the authenticator instance when constructing an instance of the service.
1819
2. configuration - the SDK user provides external configuration information (in the form of environment variables
19-
or a credentials file) to indicate the type of authenticator, along with the configuration of the necessary properties
20-
for that authenticator.
21-
The SDK user then invokes the configuration-based service client constructor method
22-
to construct an instance of the authenticator and service client that reflect the external configuration information.
20+
or a credentials file) to indicate the type of authenticator, along with the configuration of the necessary properties for that authenticator. The SDK user then invokes the configuration-based service client constructor method to construct an instance of the authenticator and service client that reflect the external configuration information.
2321

2422
The sections below will provide detailed information for each authenticator
2523
which will include the following:
@@ -145,6 +143,15 @@ form:
145143

146144
- url: (optional) The URL representing the IAM token service endpoint. If not specified, a suitable
147145
default value is used.
146+
Make sure that you use an IAM token service endpoint that is appropriate for the
147+
location of the service being used by your application.
148+
For example, if you are using an instance of a service in the "production" environment
149+
(e.g. `https://resource-controller.cloud.ibm.com`),
150+
then the default "prod" IAM token service endpoint should suffice.
151+
However, if your application is using an instance of a service in the "staging" environment
152+
(e.g. `https://resource-controller.test.cloud.ibm.com`),
153+
then you would also need to configure the authenticator to use the IAM token service "staging"
154+
endpoint as well (`https://iam.test.cloud.ibm.com`).
148155

149156
- client_id/client_secret: (optional) The `client_id` and `client_secret` fields are used to form a
150157
"basic auth" Authorization header for interactions with the IAM token server. If neither field
@@ -229,6 +236,15 @@ One of `iam_profile_name` or `iam_profile_id` must be specified.
229236
- url: (optional) The base endpoint URL of the IAM token service.
230237
The default value of this property is the "prod" IAM token service endpoint
231238
(`https://iam.cloud.ibm.com`).
239+
Make sure that you use an IAM token service endpoint that is appropriate for the
240+
location of the service being used by your application.
241+
For example, if you are using an instance of a service in the "production" environment
242+
(e.g. `https://resource-controller.cloud.ibm.com`),
243+
then the default "prod" IAM token service endpoint should suffice.
244+
However, if your application is using an instance of a service in the "staging" environment
245+
(e.g. `https://resource-controller.test.cloud.ibm.com`),
246+
then you would also need to configure the authenticator to use the IAM token service "staging"
247+
endpoint as well (`https://iam.test.cloud.ibm.com`).
232248

233249
- client_id/client_secret: (optional) The `client_id` and `client_secret` fields are used to form a
234250
"basic auth" Authorization header for interactions with the IAM token server. If neither field
@@ -277,6 +293,76 @@ service = ExampleServiceV1.new_instance(service_name='example_service')
277293
```
278294

279295

296+
## VPC Instance Authentication
297+
The `VpcInstanceAuthenticator` is intended to be used by application code
298+
running inside a VPC-managed compute resource (virtual server instance) that has been configured
299+
to use the "compute resource identity" feature.
300+
The compute resource identity feature allows you to assign a trusted IAM profile to the compute resource as its "identity".
301+
This, in turn, allows applications running within the compute resource to take on this identity when interacting with
302+
IAM-secured IBM Cloud services.
303+
This results in a simplified security model that allows the application developer to:
304+
- avoid storing credentials in application code, configuraton files or a password vault
305+
- avoid managing or rotating credentials
306+
307+
The `VpcInstanceAuthenticator` will invoke the appropriate operations on the compute resource's locally-available
308+
VPC Instance Metadata Service to (1) retrieve an instance identity token
309+
and then (2) exchange that instance identity token for an IAM access token.
310+
The authenticator will repeat these steps to obtain a new IAM access token whenever the current access token expires.
311+
The IAM access token is added to each outbound request in the `Authorization` header in the form:
312+
```
313+
Authorization: Bearer <IAM-access-token>
314+
```
315+
316+
### Properties
317+
318+
- iam_profile_crn: (optional) the crn of the linked trusted IAM profile to be used when obtaining the IAM access token.
319+
320+
- iam_profile_id: (optional) the id of the linked trusted IAM profile to be used when obtaining the IAM access token.
321+
322+
- url: (optional) The VPC Instance Metadata Service's base URL.
323+
The default value of this property is `http://169.254.169.254`, and should not need to be specified in normal situations.
324+
325+
Usage Notes:
326+
1. At most one of `iam_profile_crn` or `iam_profile_id` may be specified. The specified value must map
327+
to a trusted IAM profile that has been linked to the compute resource (virtual server instance).
328+
329+
2. If both `iam_profile_crn` and `iam_profile_id` are specified, then an error occurs.
330+
331+
3. If neither `iam_profile_crn` nor `iam_profile_id` are specified, then the default trusted profile linked to the compute resource will be used to perform the IAM token exchange.
332+
If no default trusted profile is defined for the compute resource, then an error occurs.
333+
334+
335+
### Programming example
336+
```python
337+
from ibm_cloud_sdk_core.authenticators import VPCInstanceAuthenticator
338+
from <sdk-package-name>.example_service_v1 import *
339+
340+
# Create the authenticator.
341+
authenticator = VPCInstanceAuthenticator(iam_profile_crn='crn:iam-profile-123')
342+
343+
# Construct the service instance.
344+
service = ExampleServiceV1(authenticator=authenticator)
345+
346+
# 'service' can now be used to invoke operations.
347+
```
348+
349+
### Configuration example
350+
External configuration:
351+
```
352+
export EXAMPLE_SERVICE_AUTH_TYPE=vpc
353+
export EXAMPLE_SERVICE_IAM_PROFILE_CRN=crn:iam-profile-123
354+
```
355+
Application code:
356+
```python
357+
from <sdk-package-name>.example_service_v1 import *
358+
359+
# Construct the service instance.
360+
service = ExampleServiceV1.new_instance(service_name='example_service')
361+
362+
# 'service' can now be used to invoke operations.
363+
```
364+
365+
280366
## Cloud Pak for Data
281367
The `CloudPakForDataAuthenticator` will accept a user-supplied username value, along with either a
282368
password or apikey, and will

ibm_cloud_sdk_core/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from .token_managers.jwt_token_manager import JWTTokenManager
4343
from .token_managers.cp4d_token_manager import CP4DTokenManager
4444
from .token_managers.container_token_manager import ContainerTokenManager
45+
from .token_managers.vpc_instance_token_manager import VPCInstanceTokenManager
4546
from .api_exception import ApiException
4647
from .utils import datetime_to_string, string_to_datetime, read_external_sources
4748
from .utils import datetime_to_string_list, string_to_datetime_list

ibm_cloud_sdk_core/authenticators/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@
3939
from .container_authenticator import ContainerAuthenticator
4040
from .cp4d_authenticator import CloudPakForDataAuthenticator
4141
from .iam_authenticator import IAMAuthenticator
42+
from .vpc_instance_authenticator import VPCInstanceAuthenticator
4243
from .no_auth_authenticator import NoAuthAuthenticator

ibm_cloud_sdk_core/authenticators/authenticator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Authenticator(ABC):
2626
AUTHTYPE_IAM = 'iam'
2727
AUTHTYPE_CONTAINER = 'container'
2828
AUTHTYPE_CP4D = 'cp4d'
29+
AUTHTYPE_VPC = 'vpc'
2930
AUTHTYPE_NOAUTH = 'noAuth'
3031
AUTHTYPE_UNKNOWN = 'unknown'
3132

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# coding: utf-8
2+
3+
# Copyright 2021 IBM All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from typing import Optional
18+
19+
from requests import Request
20+
from requests.api import head
21+
22+
from ..token_managers.vpc_instance_token_manager import VPCInstanceTokenManager
23+
from .authenticator import Authenticator
24+
25+
26+
class VPCInstanceAuthenticator(Authenticator):
27+
"""VPCInstanceAuthenticator implements an authentication scheme in which it
28+
retrieves an "instance identity token" and exchanges that
29+
for an IAM access token using the VPC Instance Metadata Service API which is available
30+
on the local compute resource (VM).
31+
The instance identity token is similar to an IAM apikey, except that it is managed
32+
automatically by the compute resource provider (VPC).
33+
The resulting IAM access token is then added to outbound requests in an Authorization header of the form:
34+
35+
Authorization: Bearer <access-token>
36+
37+
Keyword Arguments:
38+
iam_profile_crn (str, optional):
39+
The CRN of the linked trusted IAM profile to be used as the identity of the compute resource.
40+
At most one of iamProfileCrn or iamProfileId may be specified. If neither one is specified,
41+
then the default IAM profile defined for the compute resource will be used. Defaults to None.
42+
iam_profile_id (str, optional):
43+
The ID of the linked trusted IAM profile to be used when obtaining the IAM access token.
44+
At most one of iamProfileCrn or iamProfileId may be specified. If neither one is specified,
45+
then the default IAM profile defined for the compute resource will be used. Defaults to None.
46+
url (str, optional):
47+
The VPC Instance Metadata Service's base endpoint URL. Defaults to 'http://169.254.169.254'.
48+
49+
Attributes:
50+
iam_profile_crn (str, optional): The CRN of the linked trusted IAM profile.
51+
iam_profile_id (str, optional): The ID of the linked trusted IAM profile.
52+
url (str, optional): The VPC Instance Metadata Service's base endpoint URL.
53+
"""
54+
55+
def __init__(self,
56+
iam_profile_crn: Optional[str] = None,
57+
iam_profile_id: Optional[str] = None,
58+
url: Optional[str] = 'http://169.254.169.254') -> None:
59+
60+
self.token_manager = VPCInstanceTokenManager(
61+
url=url, iam_profile_crn=iam_profile_crn, iam_profile_id=iam_profile_id)
62+
63+
self.validate()
64+
65+
def authentication_type(self) -> str:
66+
"""Returns this authenticator's type ('VPC')."""
67+
return Authenticator.AUTHTYPE_VPC
68+
69+
def validate(self) -> None:
70+
super().validate()
71+
72+
if self.token_manager.iam_profile_crn and self.token_manager.iam_profile_id:
73+
raise ValueError(
74+
'At most one of "iam_profile_id" or "iam_profile_crn" may be specified.')
75+
76+
def authenticate(self, req: Request) -> None:
77+
"""Adds IAM authentication information to the request.
78+
79+
The IAM access token will be added to the request's headers in the form:
80+
81+
Authorization: Bearer <bearer-token>
82+
83+
Args:
84+
req: The request to add IAM authentication information too. Must contain a key to a dictionary
85+
called headers.
86+
"""
87+
headers = req.get('headers')
88+
bearer_token = self.token_manager.get_token()
89+
headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
90+
91+
92+
def set_iam_profile_crn(self, iam_profile_crn: str) -> None:
93+
"""Sets CRN of the IAM profile.
94+
95+
Args:
96+
iam_profile_crn (str): the CRN of the linked trusted IAM profile to be used as
97+
the identity of the compute resource.
98+
99+
Raises:
100+
ValueError: At most one of iam_profile_crn or iam_profile_id may be specified.
101+
If neither one is specified, then the default IAM profile defined
102+
for the compute resource will be used.
103+
"""
104+
self.token_manager.set_iam_profile_crn(iam_profile_crn)
105+
self.validate()
106+
107+
def set_iam_profile_id(self, iam_profile_id: str) -> None:
108+
"""Sets the ID of the IAM profile.
109+
110+
Args:
111+
iam_profile_id (str): id of the linked trusted IAM profile to be used when obtaining
112+
the IAM access token
113+
114+
Raises:
115+
ValueError: At most one of iam_profile_crn or iam_profile_id may be specified.
116+
If neither one is specified, then the default IAM profile defined
117+
for the compute resource will be used.
118+
"""
119+
self.token_manager.set_iam_profile_id(iam_profile_id)
120+
self.validate()

ibm_cloud_sdk_core/get_authenticator.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
from ibm_cloud_sdk_core.authenticators.vpc_instance_authenticator import VPCInstanceAuthenticator
1718
from .authenticators import (Authenticator, BasicAuthenticator, BearerTokenAuthenticator, ContainerAuthenticator,
1819
CloudPakForDataAuthenticator, IAMAuthenticator, NoAuthAuthenticator)
1920
from .utils import read_external_sources
@@ -91,6 +92,11 @@ def __construct_authenticator(config: dict) -> Authenticator:
9192
disable_ssl_verification=config.get(
9293
'AUTH_DISABLE_SSL', 'false').lower() == 'true',
9394
scope=config.get('SCOPE'))
95+
elif auth_type == Authenticator.AUTHTYPE_VPC.lower():
96+
authenticator = VPCInstanceAuthenticator(
97+
iam_profile_crn=config.get('IAM_PROFILE_CRN'),
98+
iam_profile_id=config.get('IAM_PROFILE_ID'),
99+
url=config.get('AUTH_URL'))
94100
elif auth_type == Authenticator.AUTHTYPE_NOAUTH.lower():
95101
authenticator = NoAuthAuthenticator()
96102

0 commit comments

Comments
 (0)