Skip to content

Commit 41e66d4

Browse files
authored
CDRIVER-4435 Add support for GCP service accounts in GCP KMS (#1140)
CDRIVER-4435 create gcp token through HTTP request and add integration testing
1 parent 4a6de77 commit 41e66d4

21 files changed

+957
-25
lines changed

.evergreen/compile-test-gcpkms.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
set -o errexit
3+
set -o pipefail
4+
set -o nounset
5+
6+
# Working directory is expected to be mongo-c-driver repo.
7+
ROOT=$(pwd)
8+
INSTALL_DIR=$ROOT/install
9+
. .evergreen/find-cmake.sh
10+
echo "Installing libmongocrypt ... begin"
11+
git clone --depth=1 https://github.com/mongodb/libmongocrypt --branch 1.6.0
12+
$CMAKE -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
13+
-DBUILD_TESTING=OFF \
14+
"-H$ROOT/libmongocrypt" \
15+
"-B$ROOT/libmongocrypt"
16+
$CMAKE --build "$ROOT/libmongocrypt" --target install
17+
echo "Installing libmongocrypt ... end"
18+
19+
echo "Compile test-gcpkms ... begin"
20+
# Disable unnecessary dependencies. test-gcpkms is copied to a remote host for testing, which may not have all dependent libraries.
21+
$CMAKE \
22+
-DENABLE_SASL=OFF \
23+
-DENABLE_SNAPPY=OFF \
24+
-DENABLE_ZSTD=OFF \
25+
-DENABLE_ZLIB=OFF \
26+
-DENABLE_ICU=OFF \
27+
-DENABLE_SRV=OFF \
28+
-DENABLE_CLIENT_SIDE_ENCRYPTION=ON \
29+
-DCMAKE_PREFIX_PATH=$INSTALL_DIR \
30+
.
31+
$CMAKE --build . --target test-gcpkms
32+
echo "Compile test-gcpkms ... end"

.evergreen/compile-unix.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ pkg-config --modversion libssl || true
229229

230230
if [ "$COMPILE_LIBMONGOCRYPT" = "ON" ]; then
231231
# Build libmongocrypt, using the previously fetched installed source.
232-
git clone https://github.com/mongodb/libmongocrypt --branch 1.5.2
232+
git clone https://github.com/mongodb/libmongocrypt --branch 1.6.0
233233

234234
mkdir libmongocrypt/cmake-build
235235
cd libmongocrypt/cmake-build

.evergreen/compile-windows.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ fi
120120

121121
if [ "$COMPILE_LIBMONGOCRYPT" = "ON" ]; then
122122
# Build libmongocrypt, using the previously fetched installed source.
123-
git clone https://github.com/mongodb/libmongocrypt --branch 1.5.2
123+
git clone https://github.com/mongodb/libmongocrypt --branch 1.6.0
124124
mkdir libmongocrypt/cmake-build
125125
cd libmongocrypt/cmake-build
126126
"$CMAKE" -G "$CC" "-DCMAKE_PREFIX_PATH=${INSTALL_DIR}/lib/cmake" -DENABLE_SHARED_BSON=ON -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" ../

.evergreen/config.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24180,6 +24180,67 @@ tasks:
2418024180
KEY_VAULT_ENDPOINT='${testazurekms_keyvaultendpoint}' \
2418124181
EXPECT_ERROR='Error from Azure IMDS server' \
2418224182
./mongoc/src/libmongoc/test-azurekms
24183+
- name: testgcpkms-task
24184+
commands:
24185+
- func: fetch source
24186+
- command: shell.exec
24187+
params:
24188+
shell: bash
24189+
script: |-
24190+
set -o errexit
24191+
echo "Building test-gcpkms ... begin"
24192+
pushd mongoc
24193+
./.evergreen/compile-test-gcpkms.sh
24194+
popd
24195+
echo "Building test-gcpkms ... end"
24196+
echo "Copying files ... begin"
24197+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
24198+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
24199+
export GCPKMS_ZONE=${GCPKMS_ZONE}
24200+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
24201+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
24202+
mkdir testgcpkms
24203+
cp ./mongoc/src/libmongoc/test-gcpkms ./mongoc/install/lib/libmongocrypt.* testgcpkms
24204+
tar czf testgcpkms.tgz testgcpkms/*
24205+
GCPKMS_SRC="testgcpkms.tgz" GCPKMS_DST=$GCPKMS_INSTANCENAME: $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/copy-file.sh
24206+
echo "Copying files ... end"
24207+
echo "Untarring file ... begin"
24208+
GCPKMS_CMD="tar xf testgcpkms.tgz" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh
24209+
echo "Untarring file ... end"
24210+
- command: shell.exec
24211+
type: test
24212+
params:
24213+
shell: bash
24214+
script: |-
24215+
set -o errexit
24216+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
24217+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
24218+
export GCPKMS_ZONE=${GCPKMS_ZONE}
24219+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
24220+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
24221+
GCPKMS_CMD="LD_LIBRARY_PATH=./testgcpkms MONGODB_URI='mongodb://localhost:27017' ./testgcpkms/test-gcpkms" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh
24222+
- name: testgcpkms-fail-task
24223+
commands:
24224+
- func: fetch source
24225+
- command: shell.exec
24226+
params:
24227+
shell: bash
24228+
script: |-
24229+
set -o errexit
24230+
pushd mongoc
24231+
./.evergreen/compile-test-gcpkms.sh
24232+
popd
24233+
- command: shell.exec
24234+
type: test
24235+
params:
24236+
shell: bash
24237+
script: |-
24238+
set -o errexit
24239+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
24240+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
24241+
export GCPKMS_ZONE=${GCPKMS_ZONE}
24242+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
24243+
LD_LIBRARY_PATH=$(pwd)/install MONGODB_URI='mongodb://localhost:27017' EXPECT_ERROR='Failed to connect to: metadata.google.internal' ./mongoc/src/libmongoc/test-gcpkms
2418324244
task_groups:
2418424245
- name: testazurekms_task_group
2418524246
setup_group:
@@ -24221,6 +24282,39 @@ task_groups:
2422124282
setup_group_timeout_secs: 1800
2422224283
tasks:
2422324284
- testazurekms-task
24285+
- name: testgcpkms_task_group
24286+
setup_group:
24287+
- command: shell.exec
24288+
params:
24289+
shell: bash
24290+
script: |-
24291+
set -o errexit
24292+
git clone --depth=1 https://github.com/mongodb-labs/drivers-evergreen-tools.git
24293+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
24294+
echo '${testgcpkms_key_file}' > /tmp/testgcpkms_key_file.json
24295+
export GCPKMS_KEYFILE=/tmp/testgcpkms_key_file.json
24296+
export GCPKMS_DRIVERS_TOOLS=$DRIVERS_TOOLS
24297+
export GCPKMS_SERVICEACCOUNT="${testgcpkms_service_account}"
24298+
$DRIVERS_TOOLS/.evergreen/csfle/gcpkms/create-and-setup-instance.sh
24299+
- command: expansions.update
24300+
params:
24301+
file: testgcpkms-expansions.yml
24302+
teardown_group:
24303+
- command: shell.exec
24304+
params:
24305+
shell: bash
24306+
script: |-
24307+
set -o errexit
24308+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
24309+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
24310+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
24311+
export GCPKMS_ZONE=${GCPKMS_ZONE}
24312+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
24313+
$DRIVERS_TOOLS/.evergreen/csfle/gcpkms/delete-instance.sh
24314+
setup_group_can_fail_task: true
24315+
setup_group_timeout_secs: 1800
24316+
tasks:
24317+
- testgcpkms-task
2422424318
buildvariants:
2422524319
- name: releng
2422624320
display_name: '**Release Archive Creator'
@@ -24990,3 +25084,10 @@ buildvariants:
2499025084
- testazurekms_task_group
2499125085
- testazurekms-fail-task
2499225086
batchtime: 20160
25087+
- name: testgcpkms-variant
25088+
display_name: GCP KMS
25089+
run_on: debian11-small
25090+
tasks:
25091+
- testgcpkms_task_group
25092+
- testgcpkms-fail-task
25093+
batchtime: 20160

build/cmake/LoadTests.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ endif ()
2727
string (REPLACE "\n" ";" lines "${tests_out}")
2828

2929
# TODO: Allow individual test cases to specify the fixtures they want.
30-
set (all_fixtures "mongoc/fixtures/fake_imds")
30+
set (all_fixtures "mongoc/fixtures/fake_kms_provider_server")
3131
set (all_env
32-
MCD_TEST_AZURE_IMDS_HOST=localhost:14987 # Refer: Fixtures.cmake
32+
TEST_KMS_PROVIDER_HOST=localhost:14987 # Refer: Fixtures.cmake
3333
)
3434

3535
# Generate the test definitions

build/cmake/TestFixtures.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ mongo_define_subprocess_fixture(
4444
SPAWN_WAIT 0.2
4545
COMMAND
4646
"$<TARGET_FILE:Python3::Interpreter>" -u --
47-
"${_MONGOC_BUILD_SCRIPT_DIR}/bottle.py" fake_azure:imds
47+
"${_MONGOC_BUILD_SCRIPT_DIR}/bottle.py" fake_kms_provider_server:kms_provider
4848
--bind localhost:14987 # Port 14987 chosen arbitrarily
4949
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env python
2+
#
3+
#Copyright 2022 - present MongoDB, Inc.
4+
#
5+
#Licensed under the Apache License, Version 2.0(the "License");
6+
#you may not use this file except in compliance with the License.
7+
#You may obtain a copy of the License at
8+
#
9+
#http: // www.apache.org/licenses/LICENSE-2.0
10+
#
11+
#Unless required by applicable law or agreed to in writing, software
12+
#distributed under the License is distributed on an "AS IS" BASIS,
13+
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
#See the License for the specific language governing permissions and
15+
#limitations under the License.
16+
17+
from collections import OrderedDict as OD
18+
19+
from evergreen_config_generator.functions import (shell_exec, func)
20+
from evergreen_config_generator.tasks import (NamedTask)
21+
from evergreen_config_generator.variants import (Variant)
22+
from evergreen_config_generator.taskgroups import (TaskGroup)
23+
24+
def _create_tasks():
25+
passtask = NamedTask (task_name="testgcpkms-task")
26+
27+
passtask.commands = [
28+
func("fetch source"),
29+
shell_exec (r'''
30+
echo "Building test-gcpkms ... begin"
31+
pushd mongoc
32+
./.evergreen/compile-test-gcpkms.sh
33+
popd
34+
echo "Building test-gcpkms ... end"
35+
echo "Copying files ... begin"
36+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
37+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
38+
export GCPKMS_ZONE=${GCPKMS_ZONE}
39+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
40+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
41+
mkdir testgcpkms
42+
cp ./mongoc/src/libmongoc/test-gcpkms ./mongoc/install/lib/libmongocrypt.* testgcpkms
43+
tar czf testgcpkms.tgz testgcpkms/*
44+
GCPKMS_SRC="testgcpkms.tgz" GCPKMS_DST=$GCPKMS_INSTANCENAME: $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/copy-file.sh
45+
echo "Copying files ... end"
46+
echo "Untarring file ... begin"
47+
GCPKMS_CMD="tar xf testgcpkms.tgz" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh
48+
echo "Untarring file ... end"
49+
''', test=False),
50+
shell_exec (r'''
51+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
52+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
53+
export GCPKMS_ZONE=${GCPKMS_ZONE}
54+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
55+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
56+
GCPKMS_CMD="LD_LIBRARY_PATH=./testgcpkms MONGODB_URI='mongodb://localhost:27017' ./testgcpkms/test-gcpkms" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh
57+
''')]
58+
59+
failtask = NamedTask(task_name="testgcpkms-fail-task")
60+
failtask.commands = [
61+
func("fetch source"),
62+
shell_exec (r'''
63+
pushd mongoc
64+
./.evergreen/compile-test-gcpkms.sh
65+
popd''', test=False),
66+
shell_exec (r'''
67+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
68+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
69+
export GCPKMS_ZONE=${GCPKMS_ZONE}
70+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
71+
LD_LIBRARY_PATH=$(pwd)/install MONGODB_URI='mongodb://localhost:27017' EXPECT_ERROR='Failed to connect to: metadata.google.internal' ./mongoc/src/libmongoc/test-gcpkms''')]
72+
73+
return [passtask, failtask]
74+
75+
def _create_variant():
76+
return Variant(
77+
name="testgcpkms-variant",
78+
display_name="GCP KMS",
79+
# GCP Virtual Machine created is Debian 11.
80+
run_on="debian11-small", tasks=[
81+
"testgcpkms_task_group",
82+
"testgcpkms-fail-task"
83+
], batchtime=20160) # Use a batchtime of 14 days as suggested by the CSFLE test README
84+
85+
def _create_task_group():
86+
task_group = TaskGroup(name="testgcpkms_task_group")
87+
task_group.setup_group_can_fail_task = True
88+
task_group.setup_group_timeout_secs = 1800 # 30 minutes
89+
task_group.setup_group = [
90+
# Create and set up a GCE instance using driver tools script
91+
shell_exec(r'''
92+
git clone --depth=1 https://github.com/mongodb-labs/drivers-evergreen-tools.git
93+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
94+
echo '${testgcpkms_key_file}' > /tmp/testgcpkms_key_file.json
95+
export GCPKMS_KEYFILE=/tmp/testgcpkms_key_file.json
96+
export GCPKMS_DRIVERS_TOOLS=$DRIVERS_TOOLS
97+
export GCPKMS_SERVICEACCOUNT="${testgcpkms_service_account}"
98+
$DRIVERS_TOOLS/.evergreen/csfle/gcpkms/create-and-setup-instance.sh''', test=False),
99+
100+
# Load the GCPKMS_GCLOUD, GCPKMS_INSTANCE, GCPKMS_PROJECT, and GCPKMS_ZONE expansions.
101+
OD([('command', 'expansions.update'),
102+
('params', OD([
103+
('file', 'testgcpkms-expansions.yml')]))])]
104+
105+
task_group.teardown_group = [
106+
shell_exec(r'''
107+
DRIVERS_TOOLS=$(pwd)/drivers-evergreen-tools
108+
export GCPKMS_GCLOUD=${GCPKMS_GCLOUD}
109+
export GCPKMS_PROJECT=${GCPKMS_PROJECT}
110+
export GCPKMS_ZONE=${GCPKMS_ZONE}
111+
export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME}
112+
$DRIVERS_TOOLS/.evergreen/csfle/gcpkms/delete-instance.sh''', test=False)
113+
]
114+
task_group.tasks= ["testgcpkms-task"]
115+
return task_group
116+
117+
def testgcpkms_generate(all_tasks, all_variants, all_task_groups):
118+
all_tasks.extend(_create_tasks())
119+
all_variants.append(_create_variant())
120+
all_task_groups.append(_create_task_group())

build/fake_azure.py renamed to build/fake_kms_provider_server.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import bottle
99
from bottle import Bottle, HTTPResponse
1010

11-
imds = Bottle(autojson=True)
12-
"""An Azure IMDS server"""
11+
kms_provider = Bottle(autojson=True)
12+
"""A mock server for Azure IMDS and GCP metadata"""
1313

1414
from typing import TYPE_CHECKING, Any, Callable, Iterable, cast, overload
1515

@@ -83,8 +83,37 @@ def wrapped():
8383
def test_params() -> 'dict[str, str]':
8484
return parse_qs(request.headers.get('X-MongoDB-HTTP-TestParams', ''))
8585

86+
@kms_provider.get('/computeMetadata/v1/instance/service-accounts/default/token')
87+
@handle_asserts
88+
def get_gcp_token():
89+
metadata_header = request.headers.get("Metadata-Flavor")
90+
assert metadata_header == 'Google'
91+
92+
case = test_params().get('case')
93+
print('Case is:', case)
94+
if case == '404':
95+
return HTTPResponse(status=404)
96+
97+
if case == 'bad-json':
98+
return b'{"access-token": }'
99+
100+
if case == 'empty-json':
101+
return b'{}'
102+
103+
if case == 'giant':
104+
return _gen_giant()
105+
106+
if case == 'slow':
107+
return _slow()
108+
109+
assert case in (None, ''), 'Unknown HTTP test case "{}"'.format(case)
110+
111+
return {
112+
'access_token' : 'google-cookie',
113+
'token_type' : 'Bearer'
114+
}
86115

87-
@imds.get('/metadata/identity/oauth2/token')
116+
@kms_provider.get('/metadata/identity/oauth2/token')
88117
@handle_asserts
89118
def get_oauth2_token():
90119
api_version = request.query['api-version']
@@ -145,7 +174,7 @@ def _slow() -> Iterable[bytes]:
145174

146175
if __name__ == '__main__':
147176
print(
148-
'RECOMMENDED: Run this script using bottle.py (e.g. [{} {}/bottle.py fake_azure:imds])'
177+
'RECOMMENDED: Run this script using bottle.py (e.g. [{} {}/bottle.py fake_kms_provider_server:kms_provider])'
149178
.format(sys.executable,
150179
Path(__file__).resolve().parent))
151-
imds.run()
180+
kms_provider.run()

build/generate-evergreen-config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
from evergreen_config_lib.tasks import all_tasks
4343
from evergreen_config_lib.variants import all_variants
4444
from evergreen_config_lib.taskgroups import all_task_groups
45+
from evergreen_config_lib.testgcpkms import testgcpkms_generate
4546
from evergreen_config_lib.testazurekms import testazurekms_generate
4647

4748
testazurekms_generate (all_tasks, all_variants, all_task_groups)
49+
testgcpkms_generate(all_tasks, all_variants, all_task_groups)
4850

4951
config = OD([
5052
('stepback', True),

0 commit comments

Comments
 (0)