From 06cb21ed8e9e5c8f5833b19a8274a9a16ac0e3a3 Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Fri, 12 Jul 2019 22:51:43 +0200 Subject: [PATCH] Better integration of ACR and AKS (#3) --- R/az_container_registry.R | 16 ++++++++++++++++ README.md | 6 +----- man/acr.Rd | 5 +++++ tests/testthat/test03_aks.R | 7 +------ vignettes/vig03_rbac.Rmd | 29 ++++++++++++++++++++++++----- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/R/az_container_registry.R b/R/az_container_registry.R index b01e31f..615d1c2 100644 --- a/R/az_container_registry.R +++ b/R/az_container_registry.R @@ -6,6 +6,7 @@ #' @section Methods: #' The following methods are available, in addition to those provided by the [AzureRMR::az_resource] class: #' - `new(...)`: Initialize a new ACR object. See 'Details'. +#' - `add_role_assignment(principal, role, scope=NULL, ...)`: Adds a role for the specified principal. This is an override mainly to handle AKS objects, so that the Kubernetes cluster can be granted access to the registry. You can use the `...` arguments to supply authentication details for AzureGraph, which is used to retrieve the cluster service principal. #' - `list_credentials`: Return the username and passwords for this registry. Only valid if the Admin user for the registry has been enabled. #' - `list_policies`: Return the policies for this registry. #' - `list_usages`: Return the usage for this registry. @@ -46,6 +47,10 @@ #' # see who has push and pull access #' myacr$list_role_assignments() #' +#' # grant a Kubernetes cluster pull access +#' myaks <- rg$get_aks("myaks") +#' myacr$add_role_assignment(myaks, "Acrpull") +#' #' # get the registry endpoint (for interactive use) #' myacr$get_docker_registry() #' @@ -59,6 +64,17 @@ acr <- R6::R6Class("az_container_registry", inherit=AzureRMR::az_resource, public=list( + add_role_assignment=function(principal, role, scope=NULL, ...) + { + if(is_aks(principal)) + { + tenant <- self$token$tenant + gr <- graph_login(tenant, ...) + principal <- gr$get_app(principal$properties$servicePrincipalProfile$clientId) + } + super$add_role_assignment(principal, role, scope) + }, + list_credentials=function() { creds <- private$res_op("listCredentials", http_verb="POST") diff --git a/README.md b/README.md index 9943afb..adcb581 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,7 @@ aks <- resgroup$create_aks("myakscluster", agent_pools=aks_pools("pool1", 2, "Standard_DS2_v2", "Linux")) # give the cluster pull access to the registry -aks_app <- aks$properties$servicePrincipalProfile$clientId -acr$add_role_assignment( - principal=AzureGraph::get_graph_login()$get_app(aks_app), - role="Acrpull" -) +acr$add_role_assignment(aks, "Acrpull") # get cluster endpoint, deploy from ACR to AKS with predefined yaml definition file clus <- aks$get_cluster() diff --git a/man/acr.Rd b/man/acr.Rd index d771666..21ed920 100644 --- a/man/acr.Rd +++ b/man/acr.Rd @@ -17,6 +17,7 @@ Class representing an Azure Container Registry (ACR) resource. For working with The following methods are available, in addition to those provided by the \link[AzureRMR:az_resource]{AzureRMR::az_resource} class: \itemize{ \item \code{new(...)}: Initialize a new ACR object. See 'Details'. +\item \code{add_role_assignment(principal, role, scope=NULL, ...)}: Adds a role for the specified principal. This is an override mainly to handle AKS objects, so that the Kubernetes cluster can be granted access to the registry. You can use the \code{...} arguments to supply authentication details for AzureGraph, which is used to retrieve the cluster service principal. \item \code{list_credentials}: Return the username and passwords for this registry. Only valid if the Admin user for the registry has been enabled. \item \code{list_policies}: Return the policies for this registry. \item \code{list_usages}: Return the usage for this registry. @@ -54,6 +55,10 @@ myacr$list_policies() # see who has push and pull access myacr$list_role_assignments() +# grant a Kubernetes cluster pull access +myaks <- rg$get_aks("myaks") +myacr$add_role_assignment(myaks, "Acrpull") + # get the registry endpoint (for interactive use) myacr$get_docker_registry() diff --git a/tests/testthat/test03_aks.R b/tests/testthat/test03_aks.R index 5089d35..57651bf 100644 --- a/tests/testthat/test03_aks.R +++ b/tests/testthat/test03_aks.R @@ -55,12 +55,7 @@ test_that("AKS works with RBAC", aks <- rg$create_aks(aksname, agent_pools=aks_pools("pool1", 2)) expect_true(is_aks(aks)) - aks_app <- aks$properties$servicePrincipalProfile$clientId - - acr$add_role_assignment( - principal=AzureGraph::get_graph_login(tenant)$get_app(aks_app), - role="Acrpull" - ) + acr$add_role_assignment(aks, "Acrpull") clus <- aks$get_cluster() expect_true(is_kubernetes_cluster(clus)) diff --git a/vignettes/vig03_rbac.Rmd b/vignettes/vig03_rbac.Rmd index 136f272..6741b18 100644 --- a/vignettes/vig03_rbac.Rmd +++ b/vignettes/vig03_rbac.Rmd @@ -45,21 +45,40 @@ rg$create_aci("myinstance", image="myacr.azurecr.io/myimage", ## Authenticating with a service principal from AKS to ACR -This is the corresponding scenario for a Kubernetes cluster. We'll reuse the registry from the above example. +The corresponding scenario for a Kubernetes cluster is much simpler: we simply call the `add_role_assignment` method for the ACR object, passing it the AKS object. We'll reuse the registry from the above example. ```r # create the AKS resource aks <- rg$create_aks("myaks", agent_pools=aks_pools("pool1", 2), enable_rbac=TRUE) -# get the app ID for the cluster service principal -aks_app_id <- aks$properties$servicePrincipalProfile$clientID - # give the app pull access to the registry -reg$add_role_assignment(gr$get_app(aks_app_id), "Acrpull") +reg$add_role_assignment(aks, "Acrpull") ``` After giving the cluster service principal the necessary permissions, you can then deploy images from the registry as normal. +## Creating an AKS resource and reusing an existing service principal + +This scenario is most relevant when creating an AKS resource in an automated environment, ie without a logged-in user's credentials. Currently, creating an AKS resource also involves creating an associated service principal, for the cluster to manage its sub-resources. In turn, creating this service principal will attempt to get the credentials for the logged-in user, which fails if there is no user present. + +To avoid this, you can create an app ahead of time and pass it to `create_aks`: + +```r +# login to ARM and MS Graph with client credentials flow +# your app must have the right permissions to work with ARM and Graph +az <- AzureRMR::create_azure_login("mytenant", app="app_id", password="clientsecret") +gr <- AzureGraph::create_graph_login("mytenant", app="app_id", password="clientsecret") + +app <- gr$create_app("myaksapp") + +az$ + get_subscription("sub_id")$ + get_resource_group("rgname")$ + create_aks("myaks", + cluster_service_principal=app, + agent_pools=aks_pools("pool1", 2, "Standard_DS2_v2", "Linux")) +``` + ## Integrating AKS with Azure Active Directory Integrating AKS and AAD requires creating two registered apps, the client and server, and giving them permissions to talk to each other. Most of the work here is actually done using the AzureGraph package; once the apps are correctly configured, we then pass them to the `create_aks` method. You'll need to be an administrator for your AAD tenant to carry out these steps.