Skip to content

Commit b8abadc

Browse files
GODRIVER-2411 Allow loading the new crypt_shared library. (#958)
Co-authored-by: Kevin Albertson <[email protected]>
1 parent e3f53de commit b8abadc

15 files changed

+444
-93
lines changed

.evergreen/config.yml

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ stepback: true
1212
# Actual testing tasks are marked with `type: test`
1313
command_type: setup
1414

15+
# Fail builds when pre tasks fail.
16+
pre_error_fails_task: true
17+
1518
# Protect ourself against rogue test case, or curl gone wild, that runs forever
1619
# 12 minutes is the longest we'll ever run
1720
exec_timeout_secs: 3600 # 12 minutes is the longest we'll ever run
@@ -159,6 +162,32 @@ functions:
159162
# initialize submodules
160163
git submodule init
161164
git submodule update
165+
- command: shell.exec
166+
params:
167+
working_dir: src/go.mongodb.org/mongo-driver
168+
script: |
169+
${PREPARE_SHELL}
170+
171+
# Skip the crypt_shared library download if there is no crypt_shared build for the current
172+
# platform. Don't try to determine this automatically to prevent misconfiguration errors
173+
# from silently skipping the crypt_shared download and changing the testing conditions.
174+
if [ "${SKIP_CRYPT_SHARED_LIB_DOWNLOAD}" = "true" ]; then
175+
echo "There is no crypt_shared library for this platform, skipping download..."
176+
exit 0
177+
fi
178+
179+
# Download the crypt_shared dynamic library for the current platform to the current working
180+
# directory. The run-tests.sh and versioned API test scripts expect to find a
181+
# mongo_crypt_v1.* file in the "src/go.mongodb.org/mongo-driver" working directory.
182+
# TODO(GODRIVER-2437): Update version to "latest-stable" once the crypt_shared library has a
183+
# feature-complete stable release.
184+
${PYTHON3_BINARY} $DRIVERS_TOOLS/.evergreen/mongodl.py \
185+
--component crypt_shared \
186+
--version latest \
187+
--edition enterprise \
188+
--out . \
189+
--only "**/mongo_crypt_v1.*" \
190+
--strip-path-components 1
162191
163192
install-linters:
164193
- command: shell.exec
@@ -274,8 +303,8 @@ functions:
274303
cat $i | tr -d '\r' > $i.new
275304
mv $i.new $i
276305
done
277-
# Copy client certificate because symlinks do not work on Windows.
278-
cp ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem ${MONGO_ORCHESTRATION_HOME}/lib/client.pem
306+
# Copy client certificate because symlinks do not work on Windows. Ignore any copy errors.
307+
cp ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem ${MONGO_ORCHESTRATION_HOME}/lib/client.pem || echo "Ignoring copy error"
279308
280309
make-files-executable:
281310
- command: shell.exec
@@ -460,6 +489,33 @@ functions:
460489
fi
461490
. ${DRIVERS_TOOLS}/.evergreen/csfle/set-temp-creds.sh
462491
492+
# If the task doesn't have the SKIP_CRYPT_SHARED_LIB_DOWNLOAD variable set, try to find the
493+
# crypt_shared library downloaded in the "prepare-resources" task and set the CRYPT_SHARED_LIB_PATH
494+
# environment variable with a path to the file.
495+
if [ "${SKIP_CRYPT_SHARED_LIB_DOWNLOAD}" != "true" ]; then
496+
# Find the crypt_shared library file in the current directory and set the CRYPT_SHARED_LIB_PATH to
497+
# the path of that file. Only look for .so, .dll, or .dylib files to prevent matching any other
498+
# downloaded files.
499+
export CRYPT_SHARED_LIB_PATH="$(find $(pwd) -maxdepth 1 -type f \
500+
-name 'mongo_crypt_v1.so' -o \
501+
-name 'mongo_crypt_v1.dll' -o \
502+
-name 'mongo_crypt_v1.dylib')"
503+
504+
# Expect that we always find a crypt_shared library file and set the CRYPT_SHARED_LIB_PATH
505+
# environment variable. If we didn't, print an error message and exit.
506+
if [ -z "$CRYPT_SHARED_LIB_PATH" ]; then
507+
echo 'SKIP_CRYPT_SHARED_LIB_DOWNLOAD is not "true", but CRYPT_SHARED_LIB_PATH is empty. Exiting.'
508+
exit 1
509+
fi
510+
511+
# If we're on Windows, convert the "cygdrive" path to Windows-style paths.
512+
if [ "Windows_NT" = "$OS" ]; then
513+
export CRYPT_SHARED_LIB_PATH=$(cygpath -m $CRYPT_SHARED_LIB_PATH)
514+
fi
515+
516+
echo "CRYPT_SHARED_LIB_PATH=$CRYPT_SHARED_LIB_PATH"
517+
fi
518+
463519
export GOFLAGS=-mod=vendor
464520
AUTH="${AUTH}" \
465521
SSL="${SSL}" \
@@ -652,7 +708,9 @@ functions:
652708
- command: shell.exec
653709
params:
654710
script: |
655-
DRIVERS_TOOLS=${DRIVERS_TOOLS} MONGODB_URI=${MONGODB_URI} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop
711+
# Attempt to shut down a running load balancer. Ignore any errors that happen if the load
712+
# balancer is not running.
713+
DRIVERS_TOOLS=${DRIVERS_TOOLS} MONGODB_URI=${MONGODB_URI} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop || echo "Ignoring load balancer stop error"
656714
657715
add-aws-auth-variables-to-file:
658716
- command: shell.exec
@@ -1916,6 +1974,9 @@ axes:
19161974
# to prevent attempting to link the client-side encryption (libmongocrypt) binaries when
19171975
# running Go tests.
19181976
GO_BUILD_TAGS: ""
1977+
# There is no crypt_shared library download for Ubuntu 14.04. Skip downloading it and let the
1978+
# tests use mongocryptd instead.
1979+
SKIP_CRYPT_SHARED_LIB_DOWNLOAD: "true"
19191980

19201981
# OSes that require >= 3.2 for SSL
19211982
- id: os-ssl-32
@@ -1936,6 +1997,9 @@ axes:
19361997
variables:
19371998
GO_DIST: "/opt/golang/go1.17"
19381999
PYTHON3_BINARY: "/opt/python/3.8/bin/python3"
2000+
# There is no crypt_shared library download for Ubuntu 16.04. Skip downloading it and let the
2001+
# tests use mongocryptd instead.
2002+
SKIP_CRYPT_SHARED_LIB_DOWNLOAD: "true"
19392003
- id: "osx-go-1-17"
19402004
display_name: "MacOS 10.14"
19412005
run_on: macos-1014

.evergreen/run-tests.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,33 @@ if [ -z ${GO_BUILD_TAGS+x} ]; then
8484
GO_BUILD_TAGS="cse"
8585
fi
8686

87+
# If the task doesn't have the SKIP_CRYPT_SHARED_LIB_DOWNLOAD variable set, try to find the
88+
# crypt_shared library downloaded in the "prepare-resources" task and set the CRYPT_SHARED_LIB_PATH
89+
# environment variable with a path to the file.
90+
if [ "${SKIP_CRYPT_SHARED_LIB_DOWNLOAD}" != "true" ]; then
91+
# Find the crypt_shared library file in the current directory and set the CRYPT_SHARED_LIB_PATH to
92+
# the path of that file. Only look for .so, .dll, or .dylib files to prevent matching any other
93+
# downloaded files.
94+
export CRYPT_SHARED_LIB_PATH="$(find $(pwd) -maxdepth 1 -type f \
95+
-name 'mongo_crypt_v1.so' -o \
96+
-name 'mongo_crypt_v1.dll' -o \
97+
-name 'mongo_crypt_v1.dylib')"
98+
99+
# Expect that we always find a crypt_shared library file and set the CRYPT_SHARED_LIB_PATH
100+
# environment variable. If we didn't, print an error message and exit.
101+
if [ -z "$CRYPT_SHARED_LIB_PATH" ]; then
102+
echo 'SKIP_CRYPT_SHARED_LIB_DOWNLOAD is not "true", but CRYPT_SHARED_LIB_PATH is empty. Exiting.'
103+
exit 1
104+
fi
105+
106+
# If we're on Windows, convert the "cygdrive" path to Windows-style paths.
107+
if [ "Windows_NT" = "$OS" ]; then
108+
export CRYPT_SHARED_LIB_PATH=$(cygpath -m $CRYPT_SHARED_LIB_PATH)
109+
fi
110+
111+
echo "CRYPT_SHARED_LIB_PATH=$CRYPT_SHARED_LIB_PATH"
112+
fi
113+
87114
AUTH=${AUTH} \
88115
SSL=${SSL} \
89116
MONGO_GO_DRIVER_CA_FILE=${MONGO_GO_DRIVER_CA_FILE} \

.golangci.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ issues:
9898
linters:
9999
- unused
100100
- structcheck
101-
# Disable "unused" linter for "crypt.go" because the linter doesn't work correctly without
102-
# enabling CGO.
103-
- path: x/mongo/driver/crypt.go
101+
# Disable "unused" linter for code files that depend on the "mongocrypt.MongoCrypt" type because
102+
# the linter build doesn't work correctly with CGO enabled. As a result, all calls to a
103+
# "mongocrypt.MongoCrypt" API appear to always panic (see mongocrypt_not_enabled.go), leading
104+
# to confusing messages about unused code.
105+
- path: x/mongo/driver/crypt.go|mongo/(crypt_retrievers|mongocryptd).go
104106
linters:
105107
- unused
106108
# Ignore "TLS MinVersion too low", "TLS InsecureSkipVerify set true", and "Use of weak random

mongo/client.go

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
2727
"go.mongodb.org/mongo-driver/x/mongo/driver"
2828
"go.mongodb.org/mongo-driver/x/mongo/driver/auth"
29+
"go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt"
30+
mcopts "go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/options"
2931
"go.mongodb.org/mongo-driver/x/mongo/driver/ocsp"
3032
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
3133
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
@@ -71,7 +73,7 @@ type Client struct {
7173
// client-side encryption fields
7274
keyVaultClientFLE *Client
7375
keyVaultCollFLE *Collection
74-
mongocryptdFLE *mcryptClient
76+
mongocryptdFLE *mongocryptdClient
7577
cryptFLE driver.Crypt
7678
metadataClientFLE *Client
7779
internalClientFLE *Client
@@ -720,10 +722,23 @@ func (c *Client) configureAutoEncryption(clientOpts *options.ClientOptions) erro
720722
if err := c.configureMetadataClientFLE(clientOpts); err != nil {
721723
return err
722724
}
723-
if err := c.configureMongocryptdClientFLE(clientOpts.AutoEncryptionOptions); err != nil {
725+
726+
mc, err := c.newMongoCrypt(clientOpts.AutoEncryptionOptions)
727+
if err != nil {
724728
return err
725729
}
726-
return c.configureCryptFLE(clientOpts.AutoEncryptionOptions)
730+
731+
// If the crypt_shared library was loaded successfully, signal to the mongocryptd client creator
732+
// that it can bypass spawning mongocryptd.
733+
cryptSharedLibAvailable := mc.CryptSharedLibVersionString() != ""
734+
mongocryptdFLE, err := newMongocryptdClient(cryptSharedLibAvailable, clientOpts.AutoEncryptionOptions)
735+
if err != nil {
736+
return err
737+
}
738+
c.mongocryptdFLE = mongocryptdFLE
739+
740+
c.configureCryptFLE(mc, clientOpts.AutoEncryptionOptions)
741+
return nil
727742
}
728743

729744
func (c *Client) getOrCreateInternalClient(clientOpts *options.ClientOptions) (*Client, error) {
@@ -778,19 +793,13 @@ func (c *Client) configureMetadataClientFLE(clientOpts *options.ClientOptions) e
778793
return err
779794
}
780795

781-
func (c *Client) configureMongocryptdClientFLE(opts *options.AutoEncryptionOptions) error {
782-
var err error
783-
c.mongocryptdFLE, err = newMcryptClient(opts)
784-
return err
785-
}
786-
787-
func (c *Client) configureCryptFLE(opts *options.AutoEncryptionOptions) error {
796+
func (c *Client) newMongoCrypt(opts *options.AutoEncryptionOptions) (*mongocrypt.MongoCrypt, error) {
788797
// convert schemas in SchemaMap to bsoncore documents
789798
cryptSchemaMap := make(map[string]bsoncore.Document)
790799
for k, v := range opts.SchemaMap {
791800
schema, err := transformBsoncoreDocument(c.registry, v, true, "schemaMap")
792801
if err != nil {
793-
return err
802+
return nil, err
794803
}
795804
cryptSchemaMap[k] = schema
796805
}
@@ -800,21 +809,74 @@ func (c *Client) configureCryptFLE(opts *options.AutoEncryptionOptions) error {
800809
for k, v := range opts.EncryptedFieldsMap {
801810
encryptedFields, err := transformBsoncoreDocument(c.registry, v, true, "encryptedFieldsMap")
802811
if err != nil {
803-
return err
812+
return nil, err
804813
}
805814
cryptEncryptedFieldsMap[k] = encryptedFields
806815
}
807816

808817
kmsProviders, err := transformBsoncoreDocument(c.registry, opts.KmsProviders, true, "kmsProviders")
809818
if err != nil {
810-
return fmt.Errorf("error creating KMS providers document: %v", err)
819+
return nil, fmt.Errorf("error creating KMS providers document: %v", err)
820+
}
821+
822+
// Set the crypt_shared library override path from the "cryptSharedLibPath" extra option if one
823+
// was set.
824+
cryptSharedLibPath := ""
825+
if val, ok := opts.ExtraOptions["cryptSharedLibPath"]; ok {
826+
str, ok := val.(string)
827+
if !ok {
828+
return nil, fmt.Errorf(
829+
`expected AutoEncryption extra option "cryptSharedLibPath" to be a string, but is a %T`, val)
830+
}
831+
cryptSharedLibPath = str
832+
}
833+
834+
// Explicitly disable loading the crypt_shared library if requested. Note that this is ONLY
835+
// intended for use from tests; there is no supported public API for explicitly disabling
836+
// loading the crypt_shared library.
837+
cryptSharedLibDisabled := false
838+
if v, ok := opts.ExtraOptions["__cryptSharedLibDisabledForTestOnly"]; ok {
839+
cryptSharedLibDisabled = v.(bool)
840+
}
841+
842+
bypassAutoEncryption := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption
843+
bypassQueryAnalysis := opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis
844+
845+
mc, err := mongocrypt.NewMongoCrypt(mcopts.MongoCrypt().
846+
SetKmsProviders(kmsProviders).
847+
SetLocalSchemaMap(cryptSchemaMap).
848+
SetBypassQueryAnalysis(bypassQueryAnalysis).
849+
SetEncryptedFieldsMap(cryptEncryptedFieldsMap).
850+
SetCryptSharedLibDisabled(cryptSharedLibDisabled || bypassAutoEncryption).
851+
SetCryptSharedLibOverridePath(cryptSharedLibPath))
852+
if err != nil {
853+
return nil, err
811854
}
812855

813-
// configure options
814-
var bypass bool
815-
if opts.BypassAutoEncryption != nil {
816-
bypass = *opts.BypassAutoEncryption
856+
var cryptSharedLibRequired bool
857+
if val, ok := opts.ExtraOptions["cryptSharedLibRequired"]; ok {
858+
b, ok := val.(bool)
859+
if !ok {
860+
return nil, fmt.Errorf(
861+
`expected AutoEncryption extra option "cryptSharedLibRequired" to be a bool, but is a %T`, val)
862+
}
863+
cryptSharedLibRequired = b
817864
}
865+
866+
// If the "cryptSharedLibRequired" extra option is set to true, check the MongoCrypt version
867+
// string to confirm that the library was successfully loaded. If the version string is empty,
868+
// return an error indicating that we couldn't load the crypt_shared library.
869+
if cryptSharedLibRequired && mc.CryptSharedLibVersionString() == "" {
870+
return nil, errors.New(
871+
`AutoEncryption extra option "cryptSharedLibRequired" is true, but we failed to load the crypt_shared library`)
872+
}
873+
874+
return mc, nil
875+
}
876+
877+
//nolint:unused // the unused linter thinks that this function is unreachable because "c.newMongoCrypt" always panics without the "cse" build tag set.
878+
func (c *Client) configureCryptFLE(mc *mongocrypt.MongoCrypt, opts *options.AutoEncryptionOptions) {
879+
bypass := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption
818880
kr := keyRetriever{coll: c.keyVaultCollFLE}
819881
var cir collInfoRetriever
820882
// If bypass is true, c.metadataClientFLE is nil and the collInfoRetriever
@@ -824,20 +886,14 @@ func (c *Client) configureCryptFLE(opts *options.AutoEncryptionOptions) error {
824886
cir = collInfoRetriever{client: c.metadataClientFLE}
825887
}
826888

827-
cryptOpts := &driver.CryptOptions{
889+
c.cryptFLE = driver.NewCrypt(&driver.CryptOptions{
890+
MongoCrypt: mc,
828891
CollInfoFn: cir.cryptCollInfo,
829892
KeyFn: kr.cryptKeys,
830893
MarkFn: c.mongocryptdFLE.markCommand,
831-
KmsProviders: kmsProviders,
832894
TLSConfig: opts.TLSConfig,
833895
BypassAutoEncryption: bypass,
834-
SchemaMap: cryptSchemaMap,
835-
BypassQueryAnalysis: opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis,
836-
EncryptedFieldsMap: cryptEncryptedFieldsMap,
837-
}
838-
839-
c.cryptFLE, err = driver.NewCrypt(cryptOpts)
840-
return err
896+
})
841897
}
842898

843899
// validSession returns an error if the session doesn't belong to the client

0 commit comments

Comments
 (0)