From 9e9b17f6c92eec9a70d65e1e22f392e20bc964f6 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 15:36:25 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=83=20Document=20the=20EKS=20shell=20s?= =?UTF-8?q?cripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-eks/10_create_cluster.sh | 3 ++ prepare-eks/20_create_users.sh | 8 +++++ prepare-eks/30_create_or_update_policy.sh | 39 ++++++++++++++++++--- prepare-eks/40_attach_policy.sh | 8 ++++- prepare-eks/50_aws_auth.sh | 15 ++++++-- prepare-eks/60_setup_rbac_and_ns.sh | 42 +++++++++++++++++++---- prepare-eks/70_oidc.sh | 38 +++++++++++++++++--- prepare-eks/80_s3_bucket.sh | 11 ++++++ prepare-eks/99_cleanup_old_policy.sh | 7 ---- 9 files changed, 145 insertions(+), 26 deletions(-) delete mode 100755 prepare-eks/99_cleanup_old_policy.sh diff --git a/prepare-eks/10_create_cluster.sh b/prepare-eks/10_create_cluster.sh index 53cb81df..e7dc26de 100755 --- a/prepare-eks/10_create_cluster.sh +++ b/prepare-eks/10_create_cluster.sh @@ -1,4 +1,7 @@ #!/bin/sh +# Create an EKS cluster. +# This is not idempotent (each time you run it, it creates a new cluster). + eksctl create cluster \ --node-type=t3.large \ --nodes-max=10 \ diff --git a/prepare-eks/20_create_users.sh b/prepare-eks/20_create_users.sh index 5a2f5d80..71a470bc 100755 --- a/prepare-eks/20_create_users.sh +++ b/prepare-eks/20_create_users.sh @@ -1,4 +1,12 @@ #!/bin/sh +# For each user listed in "users.txt", create an IAM user. +# Also create AWS API access keys, and store them in "users.keys". +# This is idempotent (you can run it multiple times, it will only +# create the missing users). However, it will not remove users. +# Note that you can remove users from "users.keys" (or even wipe +# that file out entirely) and then this script will delete their +# keys and generate new keys for them (and add the new keys to +# "users.keys".) echo "Getting list of existing users ..." aws iam list-users --output json | jq -r .Users[].UserName > users.tmp diff --git a/prepare-eks/30_create_or_update_policy.sh b/prepare-eks/30_create_or_update_policy.sh index f5fc6ecd..9c7d11bc 100755 --- a/prepare-eks/30_create_or_update_policy.sh +++ b/prepare-eks/30_create_or_update_policy.sh @@ -1,6 +1,16 @@ #!/bin/sh +# Create an IAM policy to authorize users to do "aws eks update-kubeconfig". +# This is idempotent, which allows to update the policy document below if +# you want the users to do other things as well. +# Note that each time you run this script, it will actually create a new +# version of the policy, set that version as the default version, and +# remove all non-default versions. (Because you can only have up to +# 5 versions of a given policy, so you need to clean them up.) +# After running that script, you will want to attach the policy to our +# users (check the other scripts in that directory). -JSON='{ +POLICY_NAME=user.container.training +POLICY_DOC='{ "Version": "2012-10-17", "Statement": [ { @@ -15,8 +25,27 @@ JSON='{ ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) -#aws iam create-policy --policy-name user.container.training --policy-document "$JSON" -aws iam create-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --policy-document "$JSON" --set-as-default +aws iam create-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --policy-document "$POLICY_DOC" \ + --set-as-default -# Uncomment this to check which users have the policy -#aws iam list-entities-for-policy --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training +# For reference, the command below creates a policy without versioning: +#aws iam create-policy \ +#--policy-name user.container.training \ +#--policy-document "$JSON" + +for VERSION in $( + aws iam list-policy-versions \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --query 'Versions[?!IsDefaultVersion].VersionId' \ + --output text) +do + aws iam delete-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --version-id "$VERSION" +done + +# For reference, the command below shows all users using the policy: +#aws iam list-entities-for-policy \ +#--policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME diff --git a/prepare-eks/40_attach_policy.sh b/prepare-eks/40_attach_policy.sh index 569557b2..42483ecd 100755 --- a/prepare-eks/40_attach_policy.sh +++ b/prepare-eks/40_attach_policy.sh @@ -1,8 +1,14 @@ #!/bin/sh +# Attach our user policy to all the users defined in "users.txt". +# This should be idempotent, because attaching the same policy +# to the same user multiple times doesn't do anything. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) +POLICY_NAME=user.container.training for U in $(cat users.txt); do echo "Attaching policy to user $U ..." - aws iam attach-user-policy --user-name $U --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training + aws iam attach-user-policy \ + --user-name $U \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME done diff --git a/prepare-eks/50_aws_auth.sh b/prepare-eks/50_aws_auth.sh index ba140a39..f586a30e 100755 --- a/prepare-eks/50_aws_auth.sh +++ b/prepare-eks/50_aws_auth.sh @@ -1,9 +1,17 @@ #!/bin/sh +# Update the aws-auth ConfigMap to map our IAM users to Kubernetes users. +# Each user defined in "users.txt" will be mapped to a Kubernetes user +# with the same name, and put in the "container.training" group, too. +# This is idempotent. +# WARNING: this will wipe out the mapUsers component of the aws-auth +# ConfigMap, removing all users that aren't in "users.txt". +# It won't touch mapRoles, so it shouldn't break the role mappings +# put in place by EKS. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) rm -f users.map -for U in ada.lovelace also.lol; do +for U in $(cat users.txt); do echo "\ - userarn: arn:aws:iam::$ACCOUNT:user/$U username: $U @@ -11,5 +19,6 @@ echo "\ " >> users.map done -kubectl create --namespace=kube-system configmap aws-auth --dry-run=client --from-file=mapUsers=users.map -o yaml | kubectl apply -f- - +kubectl create --namespace=kube-system configmap aws-auth \ + --dry-run=client --from-file=mapUsers=users.map -o yaml \ + | kubectl apply -f- diff --git a/prepare-eks/60_setup_rbac_and_ns.sh b/prepare-eks/60_setup_rbac_and_ns.sh index 9b87a169..3d52f2c9 100755 --- a/prepare-eks/60_setup_rbac_and_ns.sh +++ b/prepare-eks/60_setup_rbac_and_ns.sh @@ -1,13 +1,43 @@ #!/bin/sh -kubectl create rolebinding --namespace default container.training --group=container.training --clusterrole=view -kubectl create clusterrole view-nodes --verb=get,list,watch --resource=node -kubectl create clusterrolebinding view-nodes --group=container.training --clusterrole=view-nodes -kubectl create clusterrole view-namespaces --verb=get,list,watch --resource=namespace -kubectl create clusterrolebinding view-namespaces --group=container.training --clusterrole=view-namespaces +# Create a shared Kubernetes Namespace ("container-training") as well as +# individual namespaces for every user in "users.txt", and set up a bunch +# of permissions. +# Specifically: +# - each user gets "view" permissions in the "default" Namespace +# - each user gets "edit" permissions in the "container-training" Namespace +# - each user gets permissions to list Nodes and Namespaces +# - each user gets "admin" permissions in their personal Namespace +# Note that since Kubernetes Namespaces can't have dots in their names, +# if a user has dots, dots will be mapped to dashes. +# So user "ada.lovelace" will get namespace "ada-lovelace". +# This is kind of idempotent (but will raise a bunch of errors for objects +# that already exist). +# TODO: if this needs to evolve, replace all the "create" operations by +# "apply" operations. But this is good enough for now. + +kubectl create rolebinding --namespace default container.training \ + --group=container.training --clusterrole=view + +kubectl create clusterrole view-nodes \ + --verb=get,list,watch --resource=node +kubectl create clusterrolebinding view-nodes \ + --group=container.training --clusterrole=view-nodes + +kubectl create clusterrole view-namespaces \ + --verb=get,list,watch --resource=namespace +kubectl create clusterrolebinding view-namespaces \ + --group=container.training --clusterrole=view-namespaces kubectl create namespace container-training -kubectl create rolebinding --namespace container-training edit --group=container.training --clusterrole=edit +kubectl create rolebinding --namespace container-training edit \ + --group=container.training --clusterrole=edit +# Note: API calls to EKS tend to be fairly slow. To optimize things a bit, +# instead of running "kubectl" N times, we generate a bunch of YAML and +# apply it. It will still generate a lot of API calls but it's much faster +# than calling "kubectl" N times. It might be possible to make this even +# faster by generating a "kind: List" (I don't know if this would issue +# a single API calls or multiple ones; TBD!) for U in $(cat users.txt); do NS=$(echo $U | tr . -) cat < /tmp/policy.json +aws iam update-assume-role-policy \ + --role-name $ROLE_NAME \ + --policy-document file:///tmp/policy.json diff --git a/prepare-eks/80_s3_bucket.sh b/prepare-eks/80_s3_bucket.sh index a67c9de6..66539250 100755 --- a/prepare-eks/80_s3_bucket.sh +++ b/prepare-eks/80_s3_bucket.sh @@ -1,4 +1,15 @@ #!/bin/sh +# Create an S3 bucket with two objects in it: +# - public.txt (world-readable) +# - private.txt (private) +# Also create an IAM policy granting read-only access to the bucket +# (and therefore, to the private object). +# Finally, attach the policy to an IAM role (for instance, the role +# created by another script in this directory). +# This isn't idempotent, but it can be made idempotent by replacing the +# "aws iam create-policy" call with "aws iam create-policy-version" and +# a bit of extra elbow grease. (See other scripts in this directory for +# an example). ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) BUCKET=container.training diff --git a/prepare-eks/99_cleanup_old_policy.sh b/prepare-eks/99_cleanup_old_policy.sh deleted file mode 100755 index cb562f65..00000000 --- a/prepare-eks/99_cleanup_old_policy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) - -for VERSION in $(aws iam list-policy-versions --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training | jq -r '.Versions[].VersionId'); do - aws iam delete-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --version-id "$VERSION" -done