Merge pull request #47 from Azure/daobando/aadouthmd

Create AAD-OAuth.md
This commit is contained in:
Sajay Antony 2017-06-21 11:35:38 -10:00 коммит произвёл GitHub
Родитель 61862bfd10 87792df785
Коммит 961b0ade77
1 изменённых файлов: 190 добавлений и 0 удалений

190
docs/AAD-OAuth.md Normal file
Просмотреть файл

@ -0,0 +1,190 @@
# Azure Container Registry integration with Azure Active Directory
The Azure Container Registry allows users to manage a private Docker registry on the cloud. Our service enables customers to store and manage container images across all types of Azure deployments, keep container images near deployments to reduce latency and costs, maintain Windows and Linux container images in a single Docker registry, use familiar, open-source Docker command line interface (CLI) tools, and simplify registry access management with Azure Active Directory.
The integration of Azure Container Registry with Azure Active Directory is crucial in order to enable transparent authentication and authorization of users and headless services using AAD credentials. In this scenario, a user will only have to use their AAD credentials to log-in to their private registry, and the Azure Container Service will take care of the authorization validation of each operation using the provided credentials.
Under the hood Azure Container Service utilizes the [oauth2](https://oauth.net/2/) authorization protocol, as described by the [Docker Registry v2 authentication via central service documentation](https://docs.docker.com/registry/spec/auth/token/) as well as the [Docker Registry v2 Bearer token specification](https://docs.docker.com/registry/spec/auth/jwt/). The JWT tokens generated by the Azure Container Registry are easy to observe in [jwt.io](https://jwt.io/).
## Authenticating to a registry with Azure CLI
The process to log in to the registry, from the user's perspective, is simple. The user will use the Microsoft Azure CLI 2.0:
```
az acr login -n contosoregistry
```
Internally, the CLI will follow these steps:
1. Calls to Azure Resource Manager to resolve the login server for the specified registry.
2. Obtains refresh credentials from the profile in use. For a headless call, this will give you the registered SPN, for a regular user this will give you a refresh token.
3. Makes an HTTPS GET call to the registry server's `/v2` endpoint, without credentials. A bearer token authentication challenge is expected, specifying realm and service values. The realm contains the authentication server's URL.
4. Makes an HTTPS POST call to the authentication server's `/oauth2/exchange` endpoint, with a body indicating the grant type, the service, the tenant, and the credentials.
5. From the server's response, we extract an Azure Container Registry refresh token.
6. Pass the refresh token as the password to the Docker CLI, using a null GUID as the username and calling `docker login`. From here on, the docker CLI takes care of the authorization cycle using oauth2.
At the end Docker will store the refresh token and go through the oauth2 flow on each operation it does against the Azure Container Registry.
## Listing a repository with Azure CLI
The Microsoft Azure CLI 2.0 allows users to also list the repositories registries, and list tags for a repository in a registry. Here's how users can achieve listing the repositories in a registry:
```
az acr repository list -n contosoregistry
```
Internally, the CLI will follow these steps:
1. Calls to Azure Resource Manager to resolve the login server for the specified registry.
2. Obtains refresh credentials from the profile in use. For a headless call, this will give you the registered SPN, for a regular user this will give you a refresh token.
3. Makes an HTTPS GET call to the registry server's `/v2` endpoint, without credentials. A bearer token authentication challenge is expected, specifying realm and service values. The realm contains the authentication server's URL.
4. Makes an HTTPS POST call to the authentication server's `/oauth2/exchange` endpoint, with a body indicating the grant type, the service, the tenant, and the credentials.
5. From the server's response we extract an Azure Container Registry refresh token.
6. Makes an HTTPS POST call to the authentication server's `/oauth2/token` endpoint, with a body indicating the grant type, the service, the scope, and the Azure Container Registry refresh token.
7. From the server's response we extract an Azure Container Registry access token.
8. Makes an HTTPS GET call to the registry server's `/v2/_catalog` endpoint using the access token as the bearer token.
9. Obtains the data from the service and displays it.
When listing the tags of a repository, every step above is the same except for the call to the endpoint that gives the tags which is `/v2/contosoregistry/tags/list` instead of `/v2/_catalog`.
# Azure Container Registry refresh tokens and access tokens
Let's follow an example call to list a repository:
```
az acr repository list -n contosoregistry
```
This will produce a JWT refresh token with the following payload:
```
{
"jti": "365e3b5b-844e-4a21-a38c-4d8aebdd6a06",
"sub": "user@contoso.com"
"nbf": 1497988712,
"exp": 1497990801,
"iat": 1497988712,
"iss": "Azure Container Registry",
"aud": "contosoregistry.azurecr.io",
"version": "1.0",
"grant_type": "access_token_refresh_token",
"tenant": "409520d4-8100-4d1d-ad47-72432ddcc120",
"credential": "AQA...iAA"
"permissions": {
"actions": [
"*"
],
"notActions": []
}
}
```
Followed by an access token with the following payload:
```
{
"jti": "ec425c1e-7eda-4f70-adb5-19f927e34a41",
"sub": "user@contoso.com"
"nbf": 1497988907,
"exp": 1497993407,
"iat": 1497988907,
"iss": "Azure Container Registry",
"aud": "contosoregistry.azurecr.io",
"access": [
{
"type": "registry",
"name": "catalog",
"actions": [
"*"
]
}
]
}
```
# Getting credentials programatically
In order to sign in to a container you'll need to exchange AAD credentials for ACR credentials. The accepted form of credential exchange are:
- AAD access token
- AAD refresh token
- AAD access token and refresh token
Ideally you'll present both the AAD access token and the AAD refresh token. The AAD access token is used to talk to the Azure Resource Manager and query for the set of permissions that the user has for the container registry resource. The AAD refresh token is used in two ways:
1. If no AAD access token was presented, the AAD refresh token is used to obtain an AAD access token.
2. The AAD refresh token is sent back to the user so they can initiate a token refresh cycle against AAD. If no AAD refresh token is sent, then the client won't have this credential at hand to initiate a credential refresh.
The cycle to get credentials looks as follows:
1. Call `/oauth2/exchange` presenting the AAD refresh token and the AAD access token. The service will return you an ACR refresh token.
2. Call `/oauth2/token` presenting the ACR refresh token. The service will return you an ACR access token which you can use to call the Azure Container Registry's APIs.
## Calling `/oauth2/exchange` to get an ACR refresh token
Assume you have the following:
1. A valid container registry, which here we'll call `contosoregistry.azurecr.io`
2. The AAD tenant identifier associated to the credentials, which here we'll take to be `409520d4-8100-4d1d-ad47-72432ddcc120`.
3. Valid AAD access token and AAD refresh token credentials with access to the aforementioned container registry.
Here's how such a call looks when done via `curl`:
```bash
export registry="contosoregistry.azurecr.io"
export tenant="409520d4-8100-4d1d-ad47-72432ddcc120"
export aad_refresh_token="AQA...iAA"
export aad_access_token="eyJ...H-g"
curl -v -X POST -H "Content-Type: application/x-www-form-urlencoded" -d \
"grant_type=access_token_refresh_token&service=$registry&tenant=$tenant&refresh_token=$aad_refresh_token&access_token=$aad_access_token" \
https://$registry/oauth2/exchange
```
The body of the POST message is a querystring-like text that specifies the following values:
- `grant_type`, which can take a value of `access_token_refresh_token`, or `access_token`, or `refresh_token`.
- `service`, which must indicate the name of your Azure container registry.
- ` tenant`, which is the AAD tenant associated to the AAD credentials.
- `refresh_token`, the AAD refresh token, mandatory when `grant_type` is `access_token_refresh_token` or `refresh_token`.
- `access_token`, the AAD access token, mandatory when `grant_type` is `access_token_refresh_token` or `access_token`.
The outcome of this operation will be a response with status 200 OK and a body with the following JSON payload:
```json
{"refresh_token":"eyJ...L7a"}
```
This response is the ACR refresh token which you can inspect with [jwt.io](https://jwt.io/). You can now use it to obtain an ACR access token programmatically or simply send it to the `docker login` command to get docker talking to the Azure Container Registry.
## Authenticating docker with an ACR refresh token
Once you have obtained an ACR refresh token, you can use the docker CLI to sign in to your registry like this:
```bash
export registry="contosoregistry.azurecr.io"
export acr_username="00000000-0000-0000-0000-000000000000"
export acr_refresh_token="eyJ...L7a"
docker login -u "$acr_username" -p "$acr_refresh_token" $registry
```
The null GUID tells the container registry that this is an ACR refresh token during the login flow.
## Calling `/oauth2/token` to get an ACR access token
Assume you have the following:
1. A valid container registry, which here we'll call `contosoregistry.azurecr.io`.
2. A valid ACR refresh token.
3. The desired scope for the operation, as specified [here](https://docs.docker.com/registry/spec/auth/scope/) and [here](https://docs.docker.com/registry/spec/auth/token/#requesting-a-token); in this example we'll ask for the `"registry:catalog:*"` scope that will allow us to call the `/v2/_catalog` API.
Here's how such a call looks when done via `curl`:
```bash
export registry="contosoregistry.azurecr.io"
export acr_refresh_token="eyJ...L7a"
export scope="registry:catalog:*"
curl -v -X POST -H "Content-Type: application/x-www-form-urlencoded" -d \
"grant_type=refresh_token&service=$registry&scope=$scope&refresh_token=$acr_refresh_token" \
https://$registry/oauth2/token
```
The body of the POST message is a querystring-like text that specifies the following values:
- `grant_type` which is expected to be `refresh_token`.
- `service`, which must indicate the name of your Azure container registry.
- `scope`, which is expected to be a valid [scope](https://docs.docker.com/registry/spec/auth/scope/), and can be specified more than once for multiple scope requests.
- `refresh_token`, which must be a valid ACR refresh token, as obtained by calling `/oauth2/exchange`.
The outcome of this operation will be a response with status 200 OK and a body with the following JSON payload:
```json
{"access_token":"eyJ...xcg"}
```
This response is the ACR access token which you can inspect with [jwt.io](https://jwt.io/). You can now use it to call APIs exposed by the Azure Container Registry
## Calling an Azure Container Registry API
Assume you have the following:
1. A valid container registry, which here we'll call `contosoregistry.azurecr.io`.
2. A correctly crafted ACR access token
Here's how a call to an Azure Container Registry API would look like when done via `curl`:
```bash
export registry="contosoregistry.azurecr.io"
export acr_access_token="eyJ...xcg"
curl -v -H "Authorization: Bearer $acr_access_token" https://$registry/v2/_catalog
```
This should result in a status 200 OK, and a body with a JSON payload listing the repositories held in this registry:
```json
{"repositories":["alpine","hello-world","contoso-marketing"]}
```