Skip to content

Commit 9218cfd

Browse files
blink1073NoahStapp
andauthored
DRIVERS-2945 AWS EKS Pod Identity (#655)
Co-authored-by: Noah Stapp <[email protected]>
1 parent 32fd8eb commit 9218cfd

File tree

11 files changed

+281
-42
lines changed

11 files changed

+281
-42
lines changed

.evergreen/auth_aws/README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ for setting up local secrets handling are in the wiki page.
3636
The ECS test variant requires a slightly different approach, since we need to run the code in a container.
3737

3838
Set up a `run-mongodb-aws-ecs-test.sh` script that will run on the container. This script should be
39-
copied to `${DRIVERS_TOOLS}/.evergreen/auth_aws/src/.evergreen`. The driver code and test code should
40-
be compiled if necessary, and then compressed into a `src.tgz` file that will be expanded and used in
41-
the container.
39+
copied to `${DRIVERS_TOOLS}/.evergreen/auth_aws/src/.evergreen`.
4240

4341
```bash
4442
# Set up the target directory.
@@ -52,6 +50,26 @@ cp ${PROJECT_DIRECTORY}/.evergreen/run-mongodb-aws-ecs-test.sh $ECS_SRC_DIR/.eve
5250
PROJECT_DIRECTORY="$ECS_SRC_DIR" MONGODB_BINARIES="/path/to/mongodb/bin" $AUTH_AWS_DIR/aws_setup.sh ecs
5351
```
5452

53+
## EKS Test Process
54+
55+
The EKS Pod Identity test variant also requires a slightly different approach, since we need to run the code in a
56+
kubernetes pod. The MongoDB server version running in the k8s cluster can be set with `MONGODB_VERSION`.
57+
58+
Set up a `run-mongodb-aws-eks-test.sh` script that will run on the pod. This script should be
59+
copied to `${DRIVERS_TOOLS}/.evergreen/auth_aws/src/.evergreen`.
60+
61+
```bash
62+
# Set up the target directory.
63+
EKS_SRC_DIR=${DRIVERS_TOOLS}/.evergreen/auth_aws/src
64+
mkdir -p $EKS_SRC_DIR/.evergreen
65+
# Move the test script to the correct location.
66+
cp ${PROJECT_DIRECTORY}/.evergreen/run-mongodb-aws-eks-test.sh $EKS_SRC_DIR/.evergreen
67+
# Driver-specific - compile/build code if needed.
68+
# Driver-specific - move artifacts needed for test to $EKS_SRC_DIR
69+
# Run the test
70+
PROJECT_DIRECTORY="$EKS_SRC_DIR" $AUTH_AWS_DIR/aws_setup.sh eks
71+
```
72+
5573
## Deprecated Scripts
5674

5775
The top-level JavaScript files in this directory are deprecated and no longer needed when

.evergreen/auth_aws/aws_setup.sh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,29 @@ if [ ! -f "./secrets-export.sh" ]; then
2222
fi
2323

2424
# Remove any AWS creds that might be set in the parent env.
25-
unset AWS_ACCESS_KEY_ID
26-
unset AWS_SECRET_ACCESS_KEY
27-
unset AWS_SESSION_TOKEN
25+
# We will need those creds for eks to set up the cluster.
26+
if [ $1 != "eks" ]; then
27+
unset AWS_ACCESS_KEY_ID
28+
unset AWS_SECRET_ACCESS_KEY
29+
unset AWS_SESSION_TOKEN
30+
fi
2831

2932
source ./secrets-export.sh
3033

3134
if [ -f $SCRIPT_DIR/test-env.sh ]; then
3235
rm $SCRIPT_DIR/test-env.sh
3336
fi
3437

38+
export PROJECT_DIRECTORY
3539
python aws_tester.py "$@"
40+
41+
# Remove any AWS creds that might be set in the parent env.
42+
if [ $1 == "eks" ]; then
43+
unset AWS_ACCESS_KEY_ID
44+
unset AWS_SECRET_ACCESS_KEY
45+
unset AWS_SESSION_TOKEN
46+
fi
47+
3648
source $SCRIPT_DIR/test-env.sh
3749

3850
popd

.evergreen/auth_aws/aws_tester.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import json
88
import logging
99
import os
10+
import random
1011
import subprocess
1112
import sys
1213
from functools import partial
@@ -26,9 +27,6 @@ def join(*parts):
2627

2728

2829
sys.path.insert(0, str(HERE / "lib"))
29-
from aws_assign_instance_profile import _assign_instance_policy
30-
from aws_assume_role import _assume_role
31-
from aws_assume_web_role import _assume_role_with_web_identity
3230
from util import get_key as _get_key
3331

3432
ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*"
@@ -43,7 +41,7 @@ def join(*parts):
4341
CONFIG = json.load(fid)
4442
get_key = partial(_get_key, uppercase=False)
4543
except FileNotFoundError:
46-
CONFIG = os.environ
44+
CONFIG = os.environ.copy()
4745
get_key = partial(_get_key, uppercase=True)
4846

4947

@@ -72,6 +70,8 @@ def create_user(user, kwargs):
7270

7371

7472
def setup_assume_role():
73+
from aws_assume_role import _assume_role
74+
7575
# Assume the role to get temp creds.
7676
os.environ["AWS_ACCESS_KEY_ID"] = CONFIG[get_key("iam_auth_assume_aws_account")]
7777
os.environ["AWS_SECRET_ACCESS_KEY"] = CONFIG[
@@ -100,6 +100,8 @@ def setup_assume_role():
100100

101101
def setup_ec2():
102102
# Create the user.
103+
from aws_assign_instance_profile import _assign_instance_policy
104+
103105
_assign_instance_policy()
104106
os.environ.pop("AWS_ACCESS_KEY_ID", None)
105107
os.environ.pop("AWS_SECRET_ACCESS_KEY", None)
@@ -179,6 +181,8 @@ def setup_env_creds():
179181

180182

181183
def setup_web_identity():
184+
from aws_assume_web_role import _assume_role_with_web_identity
185+
182186
# Unassign the instance profile.
183187
env = dict(
184188
AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ec2_instance_account")],
@@ -235,6 +239,33 @@ def setup_web_identity():
235239
return dict(AWS_WEB_IDENTITY_TOKEN_FILE=token_file, AWS_ROLE_ARN=role_arn)
236240

237241

242+
def setup_eks_pod_identity():
243+
if "PROJECT_DIRECTORY" not in os.environ:
244+
raise ValueError("Please define a PROJECT_DIRECTORY")
245+
246+
test_path = (
247+
Path(os.environ["PROJECT_DIRECTORY"])
248+
/ ".evergreen"
249+
/ "run-mongodb-aws-eks-test.sh"
250+
)
251+
if not test_path.exists():
252+
raise ValueError(f"Please add the file {test_path}!")
253+
254+
# Set the name for the deployment.
255+
name = f"mongodb-{random.randrange(0, 32767)}"
256+
try:
257+
subprocess.check_call(
258+
["bash", HERE / "lib" / "eks-pod-setup.sh", name], cwd=HERE / "lib"
259+
)
260+
finally:
261+
# Tear down the EKS assets.
262+
subprocess.check_call(
263+
["bash", HERE / "lib" / "eks-pod-teardown.sh", name], cwd=HERE / "lib"
264+
)
265+
266+
return dict()
267+
268+
238269
def handle_creds(creds: dict):
239270
if "USER" in creds:
240271
USER = quote_plus(creds["USER"])
@@ -243,6 +274,8 @@ def handle_creds(creds: dict):
243274
MONGODB_URI = f"mongodb://{USER}:{PASS}@localhost"
244275
else:
245276
MONGODB_URI = f"mongodb://{USER}@localhost"
277+
elif "MONGODB_URI" in creds:
278+
MONGODB_URI = creds.pop("MONGODB_URI")
246279
else:
247280
MONGODB_URI = "mongodb://localhost"
248281
MONGODB_URI = f"{MONGODB_URI}/aws?authMechanism=MONGODB-AWS"
@@ -286,6 +319,9 @@ def main():
286319
run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test")
287320
run_web_identity_cmd.set_defaults(func=setup_web_identity)
288321

322+
run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test")
323+
run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity)
324+
289325
args = parser.parse_args()
290326
func_name = args.func.__name__.replace("setup_", "").replace("_", "-")
291327
LOGGER.info("Running aws_tester.py with %s...", func_name)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
echo "Installing dependencies ... begin"
5+
rm -rf .venv
6+
python3 -m venv .venv
7+
source .venv/bin/activate
8+
pip install -U -q pip "pymongo[aws]"
9+
echo "Installing dependencies ... end"
10+
11+
# Run the Python Driver Self Test
12+
SCRIPT_DIR=$(realpath "$(dirname ${BASH_SOURCE[0]})")
13+
pushd $SCRIPT_DIR
14+
export MONGODB_URI=$1
15+
python test.py
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
# Write the secrets-export.sh file to the k8s/eks directory.
5+
EKS_DIR="../../k8s/eks"
6+
7+
cat <<EOF >> $EKS_DIR/secrets-export.sh
8+
export EKS_CLUSTER_NAME=$EKS_CLUSTER_NAME
9+
export EKS_SERVICE_ACCOUNT_NAME=$EKS_SERVICE_ACCOUNT_NAME
10+
export EKS_REGION=$EKS_REGION
11+
EOF
12+
13+
bash $EKS_DIR/setup.sh
14+
source $EKS_DIR/secrets-export.sh
15+
16+
NAME="$1"
17+
MONGODB_URI="mongodb://${NAME}:27017"
18+
APP_LABEL=mongodb-deployment
19+
MONGODB_VERSION=${MONGODB_VERSION:-latest}
20+
21+
. ../../ensure-binary.sh kubectl
22+
23+
# Delete mongodb servers over one hour old in case they were not torn down.
24+
echo "Deleting old mongodb servers..."
25+
if [ "$(uname -s)" = "Darwin" ]; then
26+
DATE="gdate"
27+
else
28+
DATE="date"
29+
fi
30+
# shellcheck disable=SC2046
31+
kubectl get deployments -l app=$APP_LABEL -o go-template --template '{{range .items}}{{.metadata.name}} {{.metadata.creationTimestamp}}{{"\n"}}{{end}}' | awk '$2 <= "'$($DATE -d'now-1 hours' -Ins --utc | sed 's/+0000/Z/')'" { print $1 }' | xargs --no-run-if-empty kubectl delete deployment
32+
# shellcheck disable=SC2046
33+
kubectl get services -l app=$APP_LABEL -o go-template --template '{{range .items}}{{.metadata.name}} {{.metadata.creationTimestamp}}{{"\n"}}{{end}}' | awk '$2 <= "'$($DATE -d'now-1 hours' -Ins --utc | sed 's/+0000/Z/')'" { print $1 }' | xargs --no-run-if-empty kubectl delete service
34+
echo "Deleting old mongodb servers... done."
35+
36+
cat <<EOF | kubectl apply -f -
37+
apiVersion: apps/v1
38+
kind: Deployment
39+
metadata:
40+
name: ${NAME}
41+
labels:
42+
app: ${APP_LABEL}
43+
spec:
44+
replicas: 1
45+
selector:
46+
matchLabels:
47+
app: ${NAME}
48+
template:
49+
metadata:
50+
labels:
51+
app: ${NAME}
52+
spec:
53+
containers:
54+
- name: mongodb
55+
image: mongodb/mongodb-enterprise-server:${MONGODB_VERSION}
56+
ports:
57+
- containerPort: 27017
58+
env:
59+
- name: MONGO_INITDB_ROOT_USERNAME
60+
value: "bob"
61+
- name: MONGO_INITDB_ROOT_PASSWORD
62+
value: "pwd123"
63+
- name: MONGODB_AWS_ACCOUNT_ARN
64+
value: "${EKS_ROLE_ARN}"
65+
args:
66+
- "--setParameter"
67+
- "authenticationMechanisms=MONGODB-AWS,SCRAM-SHA-256"
68+
EOF
69+
70+
cat <<EOF | kubectl apply -f -
71+
apiVersion: v1
72+
kind: Service
73+
metadata:
74+
name: ${NAME}
75+
labels:
76+
app: ${APP_LABEL}
77+
spec:
78+
selector:
79+
app: ${NAME}
80+
ports:
81+
- protocol: TCP
82+
port: 27017
83+
targetPort: 27017
84+
type: ClusterIP
85+
EOF
86+
87+
# Set up the server.
88+
echo "Setting up the server..."
89+
MONGODB_POD=$(kubectl get pods -l app=${NAME} -o jsonpath='{.items[0].metadata.name}')
90+
# Wait for the pod to be ready.
91+
kubectl wait --for=condition=Ready pod/${MONGODB_POD} --timeout=2000s
92+
kubectl exec ${MONGODB_POD} -- bash -c "rm -rf /tmp/test && mkdir /tmp/test"
93+
kubectl cp ./eks_pod_setup_server.js ${MONGODB_POD}:/tmp/test/setup_server.js
94+
kubectl exec ${MONGODB_POD} -- mongosh /tmp/test/setup_server.js
95+
echo "Setting up the server... done."
96+
97+
# Run the self test.
98+
echo "Running self test on eks pod..."
99+
kubectl exec ${K8S_POD_NAME} -- bash -c "rm -rf /tmp/self-test && mkdir /tmp/self-test"
100+
kubectl cp ./eks-pod-run-self-test.sh ${K8S_POD_NAME}:/tmp/self-test/run-self-test.sh
101+
kubectl cp ./eks_pod_self_test.py ${K8S_POD_NAME}:/tmp/self-test/test.py
102+
kubectl exec ${K8S_POD_NAME} -- /tmp/self-test/run-self-test.sh $MONGODB_URI
103+
echo "Running self test on eks pod... done."
104+
105+
# Set up driver test.
106+
echo "Setting up driver test files..."
107+
kubectl exec ${K8S_POD_NAME} -- bash -c "rm -rf /tmp/src"
108+
kubectl cp $PROJECT_DIRECTORY ${K8S_POD_NAME}:/tmp/src/
109+
echo "Setting up driver test files... done."
110+
111+
echo "Running the driver test command... done."
112+
echo "export MONGODB_URI=${MONGODB_URI}" >> secrets-export.sh
113+
kubectl cp ./secrets-export.sh ${K8S_POD_NAME}:/tmp/src/secrets-export.sh
114+
echo "Setting up driver test files... done."
115+
116+
# Run the driver test.
117+
echo "Running the driver test command..."
118+
MONGODB_URI="${MONGODB_URI}/aws?authMechanism=MONGODB-AWS"
119+
kubectl exec ${K8S_POD_NAME} -- bash -c "cd /tmp && source src/secrets-export.sh && bash src/.evergreen/run-mongodb-aws-eks-test.sh $MONGODB_URI"
120+
echo "Running the driver test command... done."
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
EKS_APP_NAME=$1
5+
6+
echo "Tearing down EKS assets..."
7+
. ../../ensure-binary.sh kubectl
8+
kubectl delete deployment $EKS_APP_NAME
9+
kubectl delete services $EKS_APP_NAME
10+
bash ../../k8s/eks/teardown.sh
11+
echo "Tearing down EKS assets... done."
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import os
2+
3+
from pymongo import MongoClient
4+
5+
mongodb_uri = os.environ["MONGODB_URI"]
6+
7+
print("Testing MONGODB-AWS on eks...")
8+
c = MongoClient(f"{mongodb_uri}/?authMechanism=MONGODB-AWS")
9+
c.aws.test.find_one({})
10+
c.close()
11+
print("Testing MONGODB-AWS on eks... done.")
12+
print("Self test complete!")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(function() {
2+
"use strict";
3+
4+
const AWS_ACCOUNT_ARN=process.env["MONGODB_AWS_ACCOUNT_ARN"];
5+
const admin = Mongo().getDB("admin");
6+
const external = admin.getMongo().getDB("$external");
7+
assert(admin.auth("bob", "pwd123"));
8+
9+
external.runCommand({createUser: AWS_ACCOUNT_ARN, roles:[{role: 'read', db: "aws"}]});
10+
}());

0 commit comments

Comments
 (0)