Skip to content

Commit a66e831

Browse files
authored
Merge pull request #38 from XenitAB/update/cloud-resources
Refactor cloud resources page and add Azure docs
2 parents bc2e6bd + e8d9d73 commit a66e831

File tree

7 files changed

+599
-312
lines changed

7 files changed

+599
-312
lines changed

.markdown-lint.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ MD013:
77
# Number of characters
88
line_length: 200
99

10+
MD024:
11+
# Only check sibling headings
12+
allow_different_nesting: true
13+
# Only check sibling headings
14+
siblings_only: true
15+
1016
MD033:
1117
# Allowed elements
1218
allowed_elements:
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
---
2+
id: cloud-iam
3+
title: Cloud IAM
4+
---
5+
6+
Sometimes applications will need to integrate with other cloud resources as they require things like persistent data storage. When working with XKS each namespace is accompanied by a Azure resource
7+
group or a AWS account. This is where cloud resources can be created by each tenant. To keep things simple it may be a good idea to not share these resources across multiple tenants, as one of the
8+
tenants has to own the resource. Instead look at other options like exposing an API inside the cluster instead. As ony may expect the authentication methods differ when running XKS in Azure and AWS.
9+
This is because the APIs and underlying authentication methods differ greatly. It is important to take this into consideration when reading these documentation.
10+
11+
## Cloud Providers
12+
13+
### Azure
14+
15+
The reccomended way to authenticate towards Azure in XKS is to make use of [AAD Pod Identity](https://github.com/Azure/aad-pod-identity) which runs inside the cluster. AAD Pod Identity allows Pods
16+
within the cluster to use [managed identities](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) to authenticate towards Azure. This removes the need
17+
for static credentials that have to be passed to the Pods. It works by intercepting API requests before the leave the cluster and will attach the correct credential based on the Pod source of the
18+
request.
19+
20+
Each tenant namespace comes preconfigured with an [AzureIdentity](https://azure.github.io/aad-pod-identity/docs/concepts/azureidentity/) and
21+
[AzureIdentityBinding](https://azure.github.io/aad-pod-identity/docs/concepts/azureidentitybinding/). These have been setup so that the identity has access to the tenants resource group. All that has
22+
to be done to enable the managed identity is to add the label `foo` to the Pod. The preconfigured AzureIdentity has a labelselector which expects the label to have the same value as the namespace
23+
name.
24+
25+
This example will deploy a Pod with the Azure CLI so that you can test access to the Azure API.
26+
27+
```yaml
28+
apiVersion: v1
29+
kind: Pod
30+
metadata:
31+
name: msi-test
32+
namespace: tenant
33+
labels:
34+
aadpodidbinding: ${NAMESPACE_NAME}
35+
spec:
36+
containers:
37+
- name: msi-test
38+
image: mcr.microsoft.com/azure-cli
39+
tty: true
40+
volumeMounts:
41+
- name: az-cli
42+
mountPath: /root/.azure
43+
volumes:
44+
- name: az-cli
45+
emptyDir: {}
46+
47+
```
48+
49+
After the Pod has started you can execute a shell in the Pod and verify that the managed identity is working.
50+
51+
```bash
52+
kubectl -n tenant exec -it msi-test
53+
az login --identity
54+
az account show
55+
```
56+
57+
#### SDK
58+
59+
A common scenario is that an application may need API access to an Azure resources through the API. In these cases the best solution is to use the language specific SDKs which will most of the time
60+
support MSI credentials. Below are examples for how to create a client using MSI credentials that can interact with Azure storage account blobs.
61+
62+
<!-- markdownlint-disable -->
63+
** Golang **
64+
65+
```go
66+
package main
67+
68+
import (
69+
"time"
70+
71+
"github.com/Azure/go-autorest/autorest/azure/auth"
72+
blob "github.com/Azure/azure-storage-blob-go/azblob"
73+
)
74+
75+
func main() {
76+
msiConfig := auth.NewMSIConfig()
77+
78+
spt, err := msiConfig.ServicePrincipalToken()
79+
if err != nil {
80+
return nil, err
81+
}
82+
if err := spt.Refresh(); err != nil {
83+
return nil, err
84+
}
85+
86+
tc, err := blob.NewTokenCredential(spt.Token().AccessToken, func(tc blob.TokenCredential) time.Duration {
87+
err := spt.Refresh()
88+
if err != nil {
89+
return 30 * time.Second
90+
}
91+
tc.SetToken(spt.Token().AccessToken)
92+
return spt.Token().Expires().Sub(time.Now().Add(2 * time.Minute))
93+
}), nil
94+
}
95+
```
96+
97+
** C# **
98+
99+
```aspnet
100+
using Azure;
101+
using Azure.Identity;
102+
using Azure.Storage.Blobs;
103+
104+
async static Task CreateBlockBlobAsync(string accountName, string containerName, string blobName)
105+
{
106+
string containerEndpoint = string.Format("https://{0}.blob.core.windows.net/{1}",
107+
accountName,
108+
containerName);
109+
BlobContainerClient containerClient = new BlobContainerClient(new Uri(containerEndpoint),
110+
new DefaultAzureCredential());
111+
}
112+
```
113+
<!-- markdownlint-restore -->
114+
115+
#### Limiting Permissions
116+
117+
TBD
118+
119+
### AWS
120+
121+
When authenticating towards AWS in XKS we recommend using [IAM Roles for Service Accounts](https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-enable-IAM.html) (IRSA). IRSA
122+
works by intercepting AWS API calls before leaving the cluster and appending the correct authentication token to the request. This removes the need to static security credentials as it is handled
123+
outside the app. IRSA works by annotating a Service Account with a reference to a specfic AWS IAM role. When that Service Account is attacthed to a Pod, the Pod will be able to assume the IAM role.
124+
The reason IRSA works in a multi tenant cluster is because the reference is multi directional. The Service Account has to specify the full role ARN it wants to assume and the IAM role has to specify
125+
the name and namespace of the Service Account whihc is allowed to assume the role. So it is not enough to know the ARN of the role unless you have access to the correct namespace and Service Account.
126+
127+
Start by defining a variable for the OIDC URLs that need to be trusted. Currently this is a static definition that needs to be specified but work is planned to make this value discoverable in the
128+
future.
129+
130+
```hcl
131+
variable "oidc_urls" {
132+
description = "List of EKS OIDC URLs to trust."
133+
type = list(string)
134+
}
135+
```
136+
137+
A new OIDC provider has to be created for each trusted URL. The simplest way to do this is to iterate the URL list. This should only be done once per tenant account, so try to define all roles in the
138+
same Terraform state.
139+
140+
```hcl
141+
data "tls_certificate" "this" {
142+
for_each = {
143+
for v in var.oidc_urls :
144+
v => v
145+
}
146+
url = each.value
147+
}
148+
149+
resource "aws_iam_openid_connect_provider" "this" {
150+
for_each = {
151+
for v in var.oidc_urls :
152+
v => v
153+
}
154+
client_id_list = ["sts.amazonaws.com"]
155+
thumbprint_list = [data.tls_certificate.this[each.value].certificates[0].sha1_fingerprint]
156+
url = each.value
157+
}
158+
```
159+
160+
Define an AWS IAM policy document and an instance of the [IRSA Terraform module](https://github.com/XenitAB/terraform-modules/tree/main/modules/aws/irsa). The policy docuemnt describes which
161+
permissions should be granted to a Pod and the IRSA module creates the IAM policy and role for a Service Account in a specific namespace. The example below will for example only work with a Service
162+
Account called `irsa-test` in the namespace `tenant`. Keep in mind that a policy document and module instance is required for each unique permission set.
163+
164+
```hcl
165+
data "aws_iam_policy_document" "get_login_profile" {
166+
statement {
167+
effect = "Allow"
168+
actions = [
169+
"iam:GetLoginProfile",
170+
]
171+
resources = ["*"]
172+
}
173+
}
174+
175+
module "irsa_test" {
176+
source = "github.com/xenitab/terraform-modules//modules/aws/irsa?ref=2021.08.9"
177+
178+
name = "irsa-test"
179+
oidc_providers = [
180+
for v in var.oidc_urls :
181+
{
182+
url = v
183+
arn = aws_iam_openid_connect_provider.this[v].arn
184+
}
185+
]
186+
kubernetes_namespace = "tenant"
187+
kubernetes_service_account = "irsa-test"
188+
policy_json = data.aws_iam_policy_document.get_login_profile.json
189+
}
190+
```
191+
192+
It is a good idea to output the arn of the creted role, as it will be needed in the next step.
193+
194+
```hcl
195+
output "irsa_test_arn" {
196+
value = module.irsa_test.role_arn
197+
}
198+
```
199+
200+
The correct IAM roles and policies should be created after the Terraform has been applied. The next step is to create a Service Account with the same name as specied in the IRSA module and annotate it
201+
with the key `eks.amazonaws.com/role-arn` where the value should be the full ARN of the created IAM role, note that the account id is part of the ARN as the IAM role is created in a different account
202+
than the one the cluster is located in.
203+
204+
```yaml
205+
apiVersion: v1
206+
kind: ServiceAccount
207+
metadata:
208+
name: irsa-test
209+
namespace: tenant
210+
annotations:
211+
eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/irsa-test
212+
```
213+
214+
Create a Pod using the newly created Service Account to test using the IAM role.
215+
216+
```yaml
217+
apiVersion: v1
218+
kind: Pod
219+
metadata:
220+
name: irsa-test
221+
namespace: tenant
222+
spec:
223+
serviceAccountName: irsa-test
224+
containers:
225+
- name: irsa-test
226+
image: amazon/aws-cli
227+
command: ["sh"]
228+
stdin: true
229+
tty: true
230+
```
231+
232+
After the Pod has started you can execute a shell in the Pod and verify that the managed identity is working.
233+
234+
```bash
235+
kubectl -n tenant exec -it irsa-test
236+
aws sts get-caller-identity
237+
```
238+
239+
#### SDK
240+
241+
TBD

0 commit comments

Comments
 (0)