github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/mongocryptd.go (about) 1 // Copyright (C) MongoDB, Inc. 2017-present. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7 package mongo 8 9 import ( 10 "context" 11 "os/exec" 12 "strings" 13 "time" 14 15 "go.mongodb.org/mongo-driver/mongo/options" 16 "go.mongodb.org/mongo-driver/mongo/readconcern" 17 "go.mongodb.org/mongo-driver/mongo/readpref" 18 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 19 ) 20 21 const ( 22 defaultServerSelectionTimeout = 10 * time.Second 23 defaultURI = "mongodb://localhost:27020" 24 defaultPath = "mongocryptd" 25 serverSelectionTimeoutStr = "server selection error" 26 ) 27 28 var defaultTimeoutArgs = []string{"--idleShutdownTimeoutSecs=60"} 29 var databaseOpts = options.Database().SetReadConcern(readconcern.New()).SetReadPreference(readpref.Primary()) 30 31 type mongocryptdClient struct { 32 bypassSpawn bool 33 client *Client 34 path string 35 spawnArgs []string 36 } 37 38 // newMongocryptdClient creates a client to mongocryptd. 39 // newMongocryptdClient is expected to not be called if the crypt shared library is available. 40 // The crypt shared library replaces all mongocryptd functionality. 41 func newMongocryptdClient(opts *options.AutoEncryptionOptions) (*mongocryptdClient, error) { 42 // create mcryptClient instance and spawn process if necessary 43 var bypassSpawn bool 44 var bypassAutoEncryption bool 45 46 if bypass, ok := opts.ExtraOptions["mongocryptdBypassSpawn"]; ok { 47 bypassSpawn = bypass.(bool) 48 } 49 if opts.BypassAutoEncryption != nil { 50 bypassAutoEncryption = *opts.BypassAutoEncryption 51 } 52 53 bypassQueryAnalysis := opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis 54 55 mc := &mongocryptdClient{ 56 // mongocryptd should not be spawned if any of these conditions are true: 57 // - mongocryptdBypassSpawn is passed 58 // - bypassAutoEncryption is true because mongocryptd is not used during decryption 59 // - bypassQueryAnalysis is true because mongocryptd is not used during decryption 60 bypassSpawn: bypassSpawn || bypassAutoEncryption || bypassQueryAnalysis, 61 } 62 63 if !mc.bypassSpawn { 64 mc.path, mc.spawnArgs = createSpawnArgs(opts.ExtraOptions) 65 if err := mc.spawnProcess(); err != nil { 66 return nil, err 67 } 68 } 69 70 // get connection string 71 uri := defaultURI 72 if u, ok := opts.ExtraOptions["mongocryptdURI"]; ok { 73 uri = u.(string) 74 } 75 76 // create client 77 client, err := NewClient(options.Client().ApplyURI(uri).SetServerSelectionTimeout(defaultServerSelectionTimeout)) 78 if err != nil { 79 return nil, err 80 } 81 mc.client = client 82 83 return mc, nil 84 } 85 86 // markCommand executes the given command on mongocryptd. 87 func (mc *mongocryptdClient) markCommand(ctx context.Context, dbName string, cmd bsoncore.Document) (bsoncore.Document, error) { 88 // Remove the explicit session from the context if one is set. 89 // The explicit session will be from a different client. 90 // If an explicit session is set, it is applied after automatic encryption. 91 ctx = NewSessionContext(ctx, nil) 92 db := mc.client.Database(dbName, databaseOpts) 93 94 res, err := db.RunCommand(ctx, cmd).DecodeBytes() 95 // propagate original result 96 if err == nil { 97 return bsoncore.Document(res), nil 98 } 99 // wrap original error 100 if mc.bypassSpawn || !strings.Contains(err.Error(), serverSelectionTimeoutStr) { 101 return nil, MongocryptdError{Wrapped: err} 102 } 103 104 // re-spawn and retry 105 if err = mc.spawnProcess(); err != nil { 106 return nil, err 107 } 108 res, err = db.RunCommand(ctx, cmd).DecodeBytes() 109 if err != nil { 110 return nil, MongocryptdError{Wrapped: err} 111 } 112 return bsoncore.Document(res), nil 113 } 114 115 // connect connects the underlying Client instance. This must be called before performing any mark operations. 116 func (mc *mongocryptdClient) connect(ctx context.Context) error { 117 return mc.client.Connect(ctx) 118 } 119 120 // disconnect disconnects the underlying Client instance. This should be called after all operations have completed. 121 func (mc *mongocryptdClient) disconnect(ctx context.Context) error { 122 return mc.client.Disconnect(ctx) 123 } 124 125 func (mc *mongocryptdClient) spawnProcess() error { 126 // Ignore gosec warning about subprocess launched with externally-provided path variable. 127 /* #nosec G204 */ 128 cmd := exec.Command(mc.path, mc.spawnArgs...) 129 cmd.Stdout = nil 130 cmd.Stderr = nil 131 return cmd.Start() 132 } 133 134 // createSpawnArgs creates arguments to spawn mcryptClient. It returns the path and a slice of arguments. 135 func createSpawnArgs(opts map[string]interface{}) (string, []string) { 136 var spawnArgs []string 137 138 // get command path 139 path := defaultPath 140 if p, ok := opts["mongocryptdPath"]; ok { 141 path = p.(string) 142 } 143 144 // add specified options 145 if sa, ok := opts["mongocryptdSpawnArgs"]; ok { 146 spawnArgs = append(spawnArgs, sa.([]string)...) 147 } 148 149 // add timeout options if necessary 150 var foundTimeout bool 151 for _, arg := range spawnArgs { 152 // need to use HasPrefix instead of doing an exact equality check because both 153 // mongocryptd supports both [--idleShutdownTimeoutSecs, 0] and [--idleShutdownTimeoutSecs=0] 154 if strings.HasPrefix(arg, "--idleShutdownTimeoutSecs") { 155 foundTimeout = true 156 break 157 } 158 } 159 if !foundTimeout { 160 spawnArgs = append(spawnArgs, defaultTimeoutArgs...) 161 } 162 163 return path, spawnArgs 164 }