Amazon Web Services ブログ
Java ベースの Kubernetes オペレーターを使用した Amazon EKS での Kubernetes RBAC と IAM の統合
はじめに
Kubernetes ネイティブアプリケーションは、Kubernetes クラスターにデプロイされ、Kubernetes API と kubectl などのクライアント側ツールの両方を使用して管理されるアプリケーションです。Kubernetes オペレーターは、etcd データベースクラスターや Prometheus モニタリング/アラートシステムなど、重要な Kubernetes アプリケーションをデプロイするための抽象概念です。このようなアプリケーションに必要なドメイン固有の知識を持つカスタムリソースとコントローラを使用して Kubernetes 機能を拡張するメカニズムを提供します。
カスタムコントローラを開発するには、Kubernetes コントローラランタイムと対話するために低レベルの API が必要です。GoLang 開発者コミュニティは、豊富なフレームワークと一連の安定した SDK から得た利点を長い間使用してきました。Java は世界で最も人気のあるプログラミング言語の 1 つですが、そのようなライブラリリソースは Java 開発者コミュニティでは利用できませんでした。最近、公式の Kubernetes Java SDK プロジェクトは、カスタムコントローラの開発に役立つコントローラビルダー Java SDK を作成するために、GoLang SDK の主要機能の多くが Java に移植されたと発表しました。
このブログ記事では、Kubernetes Java SDK の新機能を活用して Kubernetes のロールベースのアクセスコントロール (RBAC) と、Amazon Elastic Kubernetes Service (Amazon EKS) を使用してプロビジョニングされた Kubernetes クラスター内の AWS Identity and Access Management (IAM) との統合を管理するのに役立つ Kubernetes オペレーターの実装について詳しく説明します。
Amazon EKS での認証と承認
Amazon EKS は IAM を使用して、Kubernetes クラスターに認証を提供します。kubectl を使用して内部で Amazon EKS とやり取りする場合、AWS CLI のバージョン 1.18.49 以降で利用可能な aws eks get-token コマンド、または Kubernetes 向け AWS IAM Authenticator を使用して認証トークンをフェッチします。これは、Kubernetes API サーバーに送信される HTTP リクエストの認証ヘッダーで渡されます。デフォルトでは、aws sts get-caller-identity コマンドで返される IAM 認証情報を使用してこのトークンを生成します。Amazon EKS は、token authentication webhook を使用してリクエストを認証しますが、認証はネイティブの Kubernetes RBAC に依存しています。認証された IAM プリンシパルに関連付けられた IAM ポリシーによって付与される一連のアクセス許可は、Amazon EKS クラスターでクライアントが実行できる操作や実行できない操作には何の影響も及ぼしません。IAM と RBAC の間の統合で核心となるのは、IAM プリンシパル (ロール/ユーザー) と Kubernetes サブジェクト (ユーザー/グループ) 間のマッピングを提供する Amazon EKS クラスターに適用される aws-auth ConfigMap です。後者は、クライアントに付与されるアクセスをコントロールする Kubernetes Roles/ClusterRoles および RoleBindings/ClusterRoleBindings に関連付けられます。
AWS アカウント管理者が 1 つ以上の IAM グループにユーザーを追加または削除して IAM ユーザーの属性を変更するとき、それらのアクションが Amazon EKS クラスター内の aws-auth ConfigMap に対応する更新を自動的にトリガーし、Amazon EKS クラスターでそのユーザーに付与されるアクセスレベルをコントロールできる状態が望ましいでしょう。Kubernetes オペレーターは、このような自動化を設計するための理想的なパターンです。
アーキテクチャ
Kubernetes RBAC と IAM 間の自動化された統合を実装するために使用されるアーキテクチャは、次の主な要素で構成されています。
- Kubernetes Java SDK を使用して実装されたオペレーター。このオペレーターは、デプロイメントとして実装されたカスタムコントローラである CustomResourceDefinition が定義した IamUserGroup という名前のカスタムリソースをパッケージ化します。aws-auth ConfigMap を変更できるようにする IamUserGroup カスタムリソース、Role/RoleBinding リソースに対する追加/更新/削除アクションに関連する Kubernetes クラスターのイベントに対して、カスタムコントローラが応答します。
- IAM ユーザーが IAM グループに追加、または IAM グループから削除されるたびに実行がトリガーされる AWS Lambda 関数として実装された Kubernetes Java クライアント。これは、IAM サービスからリアルタイムデータのストリームを配信し、そのデータを AWS Lambda などのターゲットにルーティングするのを容易にするサーバーレスイベントバスサービスである Amazon EventBridge を使用して実現されます。
- IAM グループがマッピングされている Kubernetes サブジェクトに付与されるアクセスをコントロールする Role/RoleBinding リソース。
カスタムコントローラの実装
Java SDK を使用してカスタムコントローラを作成するには、io.kubernetes.client.informer.SharedInformer インターフェイスの実装を提供する必要があります。SharedInformer は、コントローラなどのクライアントに、Kubernetes API サーバーによって実行されたアクション (クラスターリソースの作成/更新/削除などのアクション) を通知する責任を負います。SharedInformer の個別インスタンスは、カスタムコントローラに監視させたいライフサイクルイベントを持つポッド、ConfigMap などのクラスターリソースごとに必要になります。各カスタムコントローラは、io.kubernetes.client.extended.controller.Controller インターフェイスの実装によって表されます。SDK は、デフォルトの実装クラスである DefaultController を提供します。これは、ほとんどのユースケースに対して十分であり、クラスターイベントに応答するために必要な配管を含んでいます。次に、io.kubernetes.client.extended.controller.reconciler.Reconciler インターフェイスの実装が各カスタムコントローラに関連付けられます。リコンサイラサーバーは、ユーザーがオブジェクトで指定した状態を実際のクラスター状態と比較してから、実際のクラスター状態にユーザーが指定した状態を反映させる操作を実行します。最後に、io.kubernetes.client.extended.controller.builder.ControllerWatch インターフェイスの実装が各カスタムコントローラに必要です。これは、Informer フレームワークから送信された Kubernetes リソースに関連するイベント通知のフィルタリングと、カスタムコントローラ応答の定義を担当します。これらのクラスは、Java SDK のカスタムコントローラビルダーフレームワークのコアを構成します。
Spring Framework を使用して Kubernetes アプリケーションを開発する場合、SDK の Spring 統合コンポーネントを利用し、宣言型セマンティクスと Spring アノテーションを使用して前述のクラスを結び付けることができます。以下のコードは、@KubernetesInformer アノテーションを使用して Spring Bean として宣言されている SharedInformerFactory クラスを示しています。SDK の Spring BeanDefinitionRegistryPostProcessor は、そのような Bean を処理し、@KubernetesInformer アノテーションごとに SharedInformer をインスタンス化して、Spring Bean として登録します。
@KubernetesInformers({
@KubernetesInformer(
apiTypeClass = IamUserGroupCustomObject.class,
apiListTypeClass = IamUserGroupCustomObjectList.class,
groupVersionResource = @GroupVersionResource(
apiGroup = "octank.com",
apiVersion = "v1",
resourcePlural = "iamusergroups"),
resyncPeriodMillis = 60 * 1000L)
})
public class SpringSharedInformerFactory extends SharedInformerFactory {
public SpringSharedInformerFactory (ApiClient apiClient) {
super (apiClient);
}
}
次に、リコンサイラサーバーの実装を提供し、@KubernetesReconciler アノテーションを使用してそれを Spring Bean として宣言します。SDK の Spring BeanFactoryPostProcessor はこれらの Bean を処理し、各リコンサイラサーバーのコントローラを作成します。各リコンサイラは、@AddWatchEventFilter、@UpdateWatchEventFilter、および @DeleteWatchEventFilter で注釈が付けられた一連のメソッドを提供する必要があります。これらは、コントローラがカスタムリソースの各ライフサイクルイベントを調整する必要があるかどうかに応じて true/false を返します。@KubernetesReconcilerWatch アノテーションごとに、ControllerWatch インスタンスが作成され、Kubernetes リソースに関連する追加/更新/削除イベント通知のフィルタリングに役立つハンドラーとして、前述のメソッドが割り当てられます。以下のコードは、Reconciler Bean のスケルトンを示しています (詳細については、Git リポジトリから完全なソースコードをダウンロードしてください) 。
@KubernetesReconciler(
value = "iamUserGroupController",
workerCount = 2,
watches = @KubernetesReconcilerWatches({
@KubernetesReconcilerWatch(
apiTypeClass = IamUserGroupCustomObject.class,
resyncPeriodMillis = 60*1000L)
}))
public class IamUserGroupReconciler implements Reconciler {
private GenericKubernetesApi<V1ConfigMap, V1ConfigMapList> apiConfigMap;
private SharedInformer<IamUserGroupCustomObject> iamUserGroupInformer;
public IamUserGroupReconciler(ApiClient apiClient, SharedInformer<IamUserGroupCustomObject> iamGroupInformer) {
this.iamUserGroupInformer = iamGroupInformer;
this.apiConfigMap = new GenericKubernetesApi<V1ConfigMap, V1ConfigMapList>(
V1ConfigMap.class,
V1ConfigMapList.class,
"",
"v1",
"configmaps",
apiClient);
}
@AddWatchEventFilter(apiTypeClass = IamUserGroupCustomObject.class)
public boolean onAddFilter(IamUserGroupCustomObject iamUserGroup) {
}
@UpdateWatchEventFilter(apiTypeClass = IamUserGroupCustomObject.class)
public boolean onUpdateFilter(IamUserGroupCustomObject oldIamUserGroup, IamUserGroupCustomObject newIamUserGroup) {
}
@DeleteWatchEventFilter(apiTypeClass = IamUserGroupCustomObject.class)
public boolean onDeleteFilter(IamUserGroupCustomObject iamUserGroup, boolean deletedFinalStateUnknown) {
}
@Override
public Result reconcile(Request request) {
}
}
次に、カスタムリソースの実装が必要です。以下に示す YAML マニフェストを使用して CustomResourceDefinition を定義することから始めます。CRD は IamUserGroup という名前のカスタムリソースのスキーマを定義します。
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: iamusergroups.octank.com
spec:
group: octank.com
version: v1
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
kind: IamUserGroup
plural: iamusergroups
singular: iamusergroup
shortNames:
- ig
preserveUnknownFields: false
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
iamUser:
type: string
iamGroups:
type: array
items:
type: string
username:
type: string
IamUserGroup カスタムリソースのインスタンスをデプロイするには、以下に示すような YAML マニフェストを使用します。マニフェストは、IAM プリンシパルのユーザー名、Amazon リソースネーム (ARN) 間の関連付け、および IAM グループのリストとしての関連付けを提供します。
---
apiVersion: octank.com/v1
kind: IamUserGroup
metadata:
namespace: kube-system
name: viji-developers
spec:
iamUser: 'arn:aws:iam::937351234567:user/viji'
iamGroups:
- developers
username: viji
カスタムコントローラは、IamUserGroupCustomObject、IamUserGroupCustomObjectList、および IamUserGroupCustomObjectSpec の一連の Java クラスを使用して IamUserGroup カスタムリソースを管理します。IamUserGroupCustomObject は、マニフェスト全体のオブジェクト表現です。IamUserGroupCustomObjectSpec はマニフェストの仕様セクションに対応し、IamUserGroupCustomObjectList は IamUserGroup リソースのコレクションを表します。Reconcilerの reconcile メソッドは、これらのクラスを使用して IamUserGroup リソースの iamUser、iamGroups、および username 属性を取得し、当該 IamUserGroup リソースが追加、更新、または削除されたかどうかに応じて、aws-auth ConfigMap に関連する変更を加えます。
オペレーターパッケージにあるアーティファクトの最後のセットは、以下に示す Kubernetes ServiceAccount、ClusterRole、および ClusterRoleBinding の定義になります。カスタムコントローラは、iamusergroup という名前のサービスアカウント ID で実行されます。これには、IamUserGroup および ConfigMap リソースに対するすべてのアクションを実行する権限が付与されます。
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: iamusergroup
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: iamusergroup-role
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: iamusergroup-operator
rules:
- apiGroups:
- octank.com
resources:
- iamusergroups
verbs:
- '*'
- apiGroups:
- ""
resources:
- configmaps
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: iamusergroup-rolebinding
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: iamusergroup-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: iamusergroup-role
subjects:
- kind: ServiceAccount
name: iamusergroup
namespace: kube-system
認証済みユーザーに付与されるアクセスをコントロールするには、別の一連の Role/RoleBinding 定義が必要です。これらの RBAC リソース自体はオペレーターパッケージ per se の一部ではありません。これらの定義は、Kubernetes サブジェクトを管理する方法と、ターゲット環境でのアクセスに依存します。実演を目的とするこの実装で、IamUserGroup カスタムリソースに挙げられている IAM グループは、Kubernetes グループと 1 対 1 で関連付けられていると見なされます。したがって、以下に示す YAML ファイルでは、IAM グループ開発者は Kubernetes グループ開発者に関連付けられ、後者は developer-role という名前のロールにバインドされ、dev 名前空間のすべての Kubernetes リソースへの完全なアクセス権を付与します。
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: developers-role
namespace: dev
rules:
- apiGroups:
- ""
- apps
- batch
- extensions
- rbac.authorization.k8s.io
resources: ["*"]
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: developers-role-binding
namespace: dev
roleRef:
kind: Role
name: developers-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
AWS Lambda を使用する Kubernetes クライアント
Lambda 関数は、IAM ユーザーが IAM グループに追加または削除されたときに、IAM サービスから送信されたイベント通知を処理するように設計されています。これらの通知は、以下に示す JSON データの形式で送信されます。
{
"version":"0",
"id":"5628e467-803a-8ffb-7194-e2b6f9831549",
"detail-type":"AWS API Call via CloudTrail",
"source":"aws.iam",
"account":"937351234567",
"time":"2020-05-15T15:47:12Z",
"region":"us-east-1",
"resources":[
],
"detail":{
"eventVersion":"1.05",
"userIdentity":{
},
"eventTime":"2020-05-15T15:47:12Z",
"eventSource":"iam.amazonaws.com",
"eventName":"AddUserToGroup",
"awsRegion":"us-east-1",
"sourceIPAddress":"72.21.196.64",
"userAgent":"console.amazonaws.com",
"requestParameters":{
"groupName":"developers",
"userName":"viji"
},
"responseElements":null,
"requestID":"02814799-fb64-4f01-96f4-a674ebdc9119",
"eventID":"22648995-6e51-4edc-acc7-2d719df264d8",
"eventType":"AwsApiCall"
}
}
データの eventSource フィールドは iam.amazonaws.com に設定されます。eventName フィールドは、実行されたアクションを示します。AddUserToGroup と RemoveUserFromGroup は、当社が関心を寄せている 2 つのイベントです。requestParameters フィールドには、アクションに関連付けられた IAM ユーザーとグループの名前が含まれています。このデータが与えられると、Lambda 関数は IamGroupCustomObject クラスのインスタンスを作成し、Kubernetes API を呼び出して IamUserGroup カスタムリソースのインスタンスを作成または削除します。
Lambda 関数ランタイム環境は、aws eks get-token または aws-iam-authenticator トークンコマンドを実行して、Kubernetes API サーバーとの通信に必要な認証トークンを取得できません。したがって、このトークンはプログラムで構築する必要があります。Kubernetes AWS IAM Authenticator のドキュメントでは、このトークンがどのように構築されるかについて、クラスターの外部からの API 承認というセクションで詳しく説明しています。トークンは、Java を使用した署名計算の例で提供されているヘルパークラスを使用して、AWS 署名バージョン 4 アルゴリズムで生成されます。認証トークンは、次に示すように構築された HTTP URL の base64 エンコードバージョンです。
https://sts.us-east-1.amazonaws.com/?
Action=GetCallerIdentity&
Version=2011-06-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=ASIA12345NGDGUPABCDEF/20200517/us-east-1/sts/aws4_request&
X-Amz-Date=20200517T131023Z&
X-Amz-Expires=60&
X-Amz-Security-Token=IQoJb3JpZ2luX2VjEHYaCXVzLWVhc3QtMSJHMEUCIQCSdHOdiAh4c1+FiOM/diA8NNkFSMnwORgyfO68rdxdgAIgAXEVzjsu9H5WDvbqkihQ94Ugw5MzczNTE5MzA5NzUiDGon8DFp20Zw0pgiayqzASBP4b/bAa0gZtoP8U4bM+gFiH5xpVtj18HyVEy5uGm1xgCpb3P/z0/lFmWm3bQbqwOeA809NyDWfaYD79FgqydT4lD3H0AQg4IsvLx/qQSSK0pXwoMYe82xhbG8yQQzW7x5flYqiP80xe1R1HPrlmAVfhCnCyKifghmQujucJoRfW4wcC0wGwetSXDpu1fCsCfZkMO2CD2dgDdnliz07kFig0A9EVMawCzoOnQP9jYRhfxWML7vhPYFOuMBlpzWN219LlZo982Q/4o/lSE5USnCCbAsKd59JkzjrH2G5HEp1EGs4rlRrZNYHwFMVeW3FbED8+crQcOz2F95AN9D8gmaFHn4my68Vz/b8gW7F2C11q7wNk18at0/EGrKl4ty18vMjmB/qxUk2xvO5YDLxcnwThXS3AL5HL1eT15xg0wLIz1OzZ0n4Cd94iLR/U29IgY5VlV3G03RjdP8+uvv+OrHheL96CsCDY4MQ+55J7wbNWIPLWjqFtGFZgNUIslifZv4bAX5F8dj2cQAidMxyK2EXi+aAgmP/B4WtSM38OQ=&
X-Amz-SignedHeaders=host;x-k8s-aws-id&
X-Amz-Signature=fd5b5f2f8a3d0c7b128ca785d9fb71f3b583741c8648f036e20d80d2c0450503
Lambda 関数で IamUserGroup カスタムリソースを作成または削除できるようにするには、次の構成設定が必要です。
- K8s-Lambda-Client-Role などの IAM ロールの Amazon リソースネーム (ARN) は、Amazon EKS クラスターの aws-auth ConfigMap の mapRoles セクションにある、lambda-clients などの Kubernetes グループにマッピングされています。この IAM ロールの一時的な認証情報は、認証トークンを生成するために使用されます。
- IAM ユーザーの認証情報 (アクセスキー、シークレットアクセスキー) は、最小権限のセキュリティガイドラインに準拠しており、IAM ロールを引き受ける権限のみが付与されています。
- IamUserGroup カスタムリソースを追加/更新/削除するための lambda-clients Kubernetes グループアクセス許可を付与する Amazon EKS クラスターの Role および RoleBinding 定義。
- Amazon EKS クラスターの API サーバーエンドポイント URL と認証局データは、どちらも aws eks update-kubeconfig コマンドを使用して取得されます。
以下のコードは、Lambda 関数の Java ハンドラーのスケルトンを示しています (詳細については、Git リポジトリから完全なソースコードをダウンロードしてください)。
public class IAMEventHandler implements RequestStreamHandler {
private GenericKubernetesApi<IamUserGroupCustomObject, IamUserGroupCustomObjectList> apiIamGroupClient = // Initialize Kubernetes API client
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
String inputString = // Retrieve from input stream
JsonObject inputObject = new JsonObject (inputString);
String account = inputObject.getString("account");
String eventName = inputObject.getJsonObject("detail").getString("eventName");
String eventSource = inputObject.getJsonObject("detail").getString("eventSource");
String groupName = inputObject.getJsonObject("detail").getJsonObject("requestParameters").getString("groupName");
String userName = inputObject.getJsonObject("detail").getJsonObject("requestParameters").getString("userName");
String userArn = String.format("arn:aws:iam::%s:user/%s", account, userName);
String objName = userName.concat("-").concat(groupName).toLowerCase();
String objNamespace = "kube-system";
IamUserGroupCustomObject iamUserGroup =
new IamUserGroupCustomObject()
.apiVersion("octank.com/v1")
.kind("IamUserGroup")
.metadata(new V1ObjectMeta()
.name(objName)
.namespace(objNamespace))
.spec(new IamUserGroupCustomObjectSpec()
.iamUser(userArn)
.username(userName)
.group(groupName));
if (eventName.equals(ADD_USER_TO_GROUP)) {
KubernetesApiResponse<IamUserGroupCustomObject> createResponse = apiIamGroupClient.create(iamUserGroup);
}
else if (eventName.equals(REMOVE_USER_FROM_GROUP)) {
KubernetesApiResponse<IamUserGroupCustomObject> createResponse = apiIamGroupClient.delete(objNamespace, objName);
}
}
}
Amazon EKS クラスターでのオペレーターのテスト
次に、この Kubernetes オペレーターを Amazon EKS クラスターで試してみましょう。operator.yaml という名前の次の YAML マニフェストは、このオペレーターを Kubernetes クラスターにデプロイするために必要なすべてのアーティファクトを定義します。
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: iamusergroups.octank.com
spec:
group: octank.com
version: v1
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
kind: IamUserGroup
plural: iamusergroups
singular: iamusergroup
shortNames:
- ig
preserveUnknownFields: false
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
iamUser:
type: string
iamGroups:
type: array
items:
type: string
username:
type: string
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: iamusergroup
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: iamusergroup-role
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: iamusergroup-operator
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- '*'
- apiGroups:
- octank.com
resources:
- iamusergroups
verbs:
- '*'
- apiGroups:
- ""
resources:
- configmaps
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: iamusergroup-rolebinding
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: iamusergroup-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: iamusergroup-role
subjects:
- kind: ServiceAccount
name: iamusergroup
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: iamusergroup
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: iamusergroup
role: operator
template:
metadata:
labels:
app: iamusergroup
role: operator
annotations:
prometheus.io/scrape: 'false'
spec:
serviceAccountName: iamusergroup
containers:
- name: java
image: eksworkshop/k8s-iam-operator:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
protocol: TCP
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1000Mi"
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 15
timeoutSeconds: 1
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 15
timeoutSeconds: 1
periodSeconds: 10
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: iamusergroup-svc
namespace: kube-system
spec:
sessionAffinity: None
type: ClusterIP
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
app: iamusergroup
role: operator
kubectl apply -f operator.yaml を使用して、Amazon EKS クラスターにデプロイします。以下は、このコマンドの出力です。
customresourcedefinition.apiextensions.k8s.io/iamusergroups.octank.com created
serviceaccount/iamusergroup created
clusterrole.rbac.authorization.k8s.io/iamusergroup-role created
clusterrolebinding.rbac.authorization.k8s.io/iamusergroup-rolebinding created
deployment.apps/iamusergroup created service/iamusergroup-svc created
クラスター内の aws-auth ConfigMap の初期状態には、ワーカーノードが Amazon EKS クラスターに参加できるようにするマッピングが含まれています。以下に示す YAML マニフェストの変更を適用して、この ConfigMap を変更します。これにより、ロール K8s-Lambda-Client-Role に属する一時的な認証情報を使用してクライアントが認証された場合、クライアントを lambda-clients Kubernetes グループに関連付けるマッピングが作成されます。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::937351234567:role/eks-worker-stack-WorkerNodeInstanceRole-1FET9MJ4MU56
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
- rolearn: arn:aws:iam::937351234567:role/K8s-Lambda-Client-Role
username: lambda-client
groups:
- lambda-clients
Kubernetes グループ lambda-clients には、以下の YAML マニフェストに示されている Role/RoleBinding 定義を作成することにより、kube-system 名前空間内の IamUserGroup カスタムリソースへのフルアクセスのみが付与されます。したがって、Amazon KKS クラスターに対して Lambda クライアントを認証するためにロール K8s-Lambda-Client-Role を使用することで、クラスター内のアクションのスコープを制限します。
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: lambda-clients-role
namespace: kube-system
rules:
- apiGroups:
- octank.com
resources:
- iamusergroups
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: lambda-clients-rolebinding
namespace: kube-system
roleRef:
kind: Role
name: lambda-clients-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: lambda-clients
apiGroup: rbac.authorization.k8s.io
Amazon EKS の Lambda クライアントは、コマンド aws lambda create-function を使用して、次の JSON ファイルを入力としてデプロイされます。
{
"FunctionName": "K8sClientForIAMEvents",
"Runtime": "java8",
"Role": "arn:aws:iam::937351234567:role/Lambda-Execution-Role",
"Handler": "com.octank.IAMEventHandler::handleRequest",
"Description": "K8s Client to Process IAM Notifications",
"Timeout": 30,
"MemorySize": 512,
"Code": {
"S3Bucket": "sarathy-lambda-handlers",
"S3Key": "eksLambda.jar"
},
"Environment": {
"Variables": {
"REGION": "us-east-1",
"STS_ENDPOINT": "sts.us-east-1.amazonaws.com",
"ACCESS_KEY_ID": "AKIATRU5TN0127H5VTXM",
"SECRET_ACCESS_KEY": "sIbm2UbhXhusmk8sDjy-+Gens85n6ym///rQySTMb7Aw",
"ASSUMED_ROLE": "arn:aws:iam::937351234567:role/K8s-Lambda-Client-Role",
"CLUSTER_NAME": "k8s-sarathy-cluster",
"API_SERVER": "https://9E700EEF26B4378A9109685E2C99D393.gr7.us-east-1.eks.amazonaws.com",
"CERT_AUTHORITY": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01EVXhPREF4TURNd05sb1hEVE13TURVeE5qQXhNRE13Tmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBT2IwCkQrT3ZESDRRYy9wcXRVR0FjRlVnMWUrNFhta0lBY0VUNzZXaEVWYVFRWDNLMzkwNUZ1Y1g4U2ppY3hQd3hUSEYKczBhR0puWEl0WDh3V2hrQU53Q0VkUnM5bGdaNTQxcEdKQ1JTdFVYbTR5UWNoVUg1Uk0xR2Fpelk0OVAyQ0RlVApuMzh2TXhDb2JSSWdacW5IaFFCeWNQY21hT3p6dnNjQXFlRCtveGYzLzFSVWhRdEdvZE5iS284KzdnbHhiVVhsCmFTS2VWamhBcWZMMzJTK3plUFlncFUzN0pjSlo5cEY0VTNoYUlDN0ZPWUhvNlVWU1VUN1o2SjlvcFVnT1AyemgKRzBhVUVCaUpaYkdIRlluOVJ5aUJxc1lHRk9VZ1J2OEYwdWRsUnNXUWFkVVJrOVRmQitBMW15YnZVUzRTK09xbAo4cjlBcHhQZSthVUpTZSszL0RrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBVVhLeDFENkdHcnc4SGhrZ3VzNmFTTmlzcDgKQSt1TkhSS2J3aTRxTjR1SVBHR05rOTdXNjJwZkJvc09lMElYdjN1dlBXZDl4M2kwVG9qWVphZ2xTOVRUOWNycwpod0xhNlpNWmRpMkZFeThYRC8vdVpoRUszditMNlY0cXY1b2E1SWw3a3IrVDZpYUVOMzhZMllSaGJ0MDNVUUJVClV2ZXlUVDVKdENuZkFMaUJWRzBJQk0xUUkxRkpiRVJqOWpTVXFiWHpWUnNBUHJYTktSSkJxdktjRG5DK09OWkoKVkpCWTN2RDJVVGI2N1VtVXhaS2tKRHpiK1N5SEFkV3lLTzYvWCtyNEZWRHl2NjRyME1Kd0RQLzc3OGd1OGlrbgo0MzhDUVFSQmIyMUN6ZFhkOEw0MDBpbzNYVUNJQTNvajZ2aWViRXR0VmVYZUFSTTV5aG1yQTBOejl3dz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
}
}
}
次に、EventBridge ルールが作成され、次の一連のコマンドを使用して Lambda 関数がターゲットとして割り当てられます。
EVENT_RULE_ARN=$(aws events put-rule --name IAMUserGroupRule --event-pattern "{\"source\":[\"aws.iam\"]}" --query RuleArn --output text)
aws lambda add-permission \
--function-name K8sClientForIAMEvents \
--statement-id 'd6f44629-efc0-4f38-96db-d75ba7d06579' \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn $EVENT_RULE_ARN
aws events put-targets --rule IAMUserGroupRule --targets file://lambdaTarget.json
次に、IAM ダッシュボードに移動して、viji という名前のユーザーを編集し、そのユーザーを developer および testers という名前のグループに追加します。IAM AddUserToGroup API は一度に 1 つのグループのみをユーザーに追加でき、UI では複数を選択できるため、CloudWatch Logs からわかるように、これにより Lambda 関数への 2 つの個別のイベント通知がトリガーされます。
kubectl get configmap aws-auth -n kube-system -o yaml コマンドを実行すると、IAM ダッシュボードで実行されたアクションと一致する aws-auth ConfigMap に加えられた変更が確認されます。
apiVersion: v1
data:
mapRoles: |
- rolearn: arn:aws:iam::937351234567:role/eks-worker-stack-WorkerNodeInstanceRole-134DR0KSSN2K
username: system:node:{{EC2PrivateDNSName}}
groups: ['system:bootstrappers', 'system:nodes']
- rolearn: arn:aws:iam::937351234567:role/K8s-Lambda-Client-Role
username: lambda-client
groups: [lambda-clients]
mapUsers: |
- userarn: arn:aws:iam::937351234567:user/viji
groups: [testers, developers]
username: viji
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
次に、aws iam remove-user-from-group –group-name developers –user-name viji コマンドを使用して、ユーザー viji を開発者グループから削除します。 kubectl get configmap aws-auth -n kube-system -o yaml コマンドからの出力は次のようになります。
apiVersion: v1
data:
mapRoles: |
- rolearn: arn:aws:iam::937351234567:role/eks-worker-stack-WorkerNodeInstanceRole-134DR0KSSN2K
username: system:node:{{EC2PrivateDNSName}}
groups: ['system:bootstrappers', 'system:nodes']
- rolearn: arn:aws:iam::937351234567:role/K8s-Lambda-Client-Role
username: lambda-client
groups: [lambda-clients]
mapUsers: |
- userarn: arn:aws:iam::937351234567:user/viji
groups: [testers]
username: viji
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
IAM ユーザーを IAM グループに追加、または IAM グループから削除するには、kubectl コマンドを使用して、IAmUserGroup カスタムリソースを定義する適切な YAML マニフェストを作成することもできます。これらのイベントは、カスタムコントローラによってまったく同じ方法で処理されます。
ソースコード
カスタムコントローラと Lambda 関数の完全なソースコードは、次のリンクからダウンロードできます。
https://github.com/aws-samples/k8s-rbac-iam-java-operator/tree/master/java-operator
https://github.com/aws-samples/k8s-rbac-iam-java-operator/tree/master/lambda-client
最後に
オペレーターは、ドメイン固有のロジックをカプセル化する Kubernetes 上に重要なアプリケーションを構築する優れた方法です。Kubernetes コントローラビルダー Java SDK を使用すれば、開発者はカスタムコントローラを作成して、関連するコンポーネントを Kubernetes コントローラランタイムに簡単に接続できます。コンテナワークロードのオーケストレーションに Kubernetes を採用していて、Java に関する社内専門知識が豊富な組織は、Kubernetes Java SDK を利用して、Kubernetes API 経由で Java でカスタムツールを構築できます。