google.golang.org/grpc@v1.74.2/internal/testutils/xds/e2e/bootstrap.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 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 */ 18 19 package e2e 20 21 import ( 22 "encoding/json" 23 "fmt" 24 "os" 25 "path" 26 "testing" 27 28 "google.golang.org/grpc/internal/xds/bootstrap" 29 "google.golang.org/grpc/testdata" 30 ) 31 32 // DefaultFileWatcherConfig is a helper function to create a default certificate 33 // provider plugin configuration. The test is expected to have setup the files 34 // appropriately before this configuration is used to instantiate providers. 35 func DefaultFileWatcherConfig(certPath, keyPath, caPath string) json.RawMessage { 36 return json.RawMessage(fmt.Sprintf(`{ 37 "plugin_name": "file_watcher", 38 "config": { 39 "certificate_file": %q, 40 "private_key_file": %q, 41 "ca_certificate_file": %q, 42 "refresh_interval": "600s" 43 } 44 }`, certPath, keyPath, caPath)) 45 } 46 47 // SPIFFEFileWatcherConfig is a helper function to create a default certificate 48 // provider plugin configuration. The test is expected to have setup the files 49 // appropriately before this configuration is used to instantiate providers. 50 func SPIFFEFileWatcherConfig(certPath, keyPath, caPath, spiffeBundleMapPath string) json.RawMessage { 51 return json.RawMessage(fmt.Sprintf(`{ 52 "plugin_name": "file_watcher", 53 "config": { 54 "certificate_file": %q, 55 "private_key_file": %q, 56 "ca_certificate_file": %q, 57 "spiffe_trust_bundle_map_file": %q, 58 "refresh_interval": "600s" 59 } 60 }`, certPath, keyPath, caPath, spiffeBundleMapPath)) 61 } 62 63 // SPIFFEBootstrapContents creates a bootstrap configuration with the given node 64 // ID and server URI. It also creates certificate provider configuration using 65 // SPIFFE certificates and sets the listener resource name template to be used 66 // on the server side. 67 func SPIFFEBootstrapContents(t *testing.T, nodeID, serverURI string) []byte { 68 t.Helper() 69 70 // Create a directory to hold certs and key files used on the server side. 71 serverDir, err := createTmpDirWithCerts("testServerSideXDSSPIFFE*", "spiffe_end2end/server_spiffe.pem", "spiffe_end2end/server.key", "spiffe_end2end/ca.pem", "spiffe_end2end/server_spiffebundle.json") 72 if err != nil { 73 t.Fatalf("Failed to create bootstrap configuration: %v", err) 74 } 75 76 // Create a directory to hold certs and key files used on the client side. 77 clientDir, err := createTmpDirWithCerts("testClientSideXDSSPIFFE*", "spiffe_end2end/client_spiffe.pem", "spiffe_end2end/client.key", "spiffe_end2end/ca.pem", "spiffe_end2end/client_spiffebundle.json") 78 if err != nil { 79 t.Fatalf("Failed to create bootstrap configuration: %v", err) 80 } 81 82 // Create certificate providers section of the bootstrap config with entries 83 // for both the client and server sides. 84 cpc := map[string]json.RawMessage{ 85 ServerSideCertProviderInstance: SPIFFEFileWatcherConfig(path.Join(serverDir, certFile), path.Join(serverDir, keyFile), path.Join(serverDir, rootFile), path.Join(serverDir, spiffeBundleMapFile)), 86 ClientSideCertProviderInstance: SPIFFEFileWatcherConfig(path.Join(clientDir, certFile), path.Join(clientDir, keyFile), path.Join(clientDir, rootFile), path.Join(clientDir, spiffeBundleMapFile)), 87 } 88 89 // Create the bootstrap configuration. 90 bs, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 91 Servers: []byte(fmt.Sprintf(`[{ 92 "server_uri": "passthrough:///%s", 93 "channel_creds": [{"type": "insecure"}] 94 }]`, serverURI)), 95 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 96 CertificateProviders: cpc, 97 ServerListenerResourceNameTemplate: ServerListenerResourceNameTemplate, 98 }) 99 if err != nil { 100 t.Fatalf("Failed to create bootstrap configuration: %v", err) 101 } 102 return bs 103 104 } 105 106 // DefaultBootstrapContents creates a default bootstrap configuration with the 107 // given node ID and server URI. It also creates certificate provider 108 // configuration and sets the listener resource name template to be used on the 109 // server side. 110 func DefaultBootstrapContents(t *testing.T, nodeID, serverURI string) []byte { 111 t.Helper() 112 113 // Create a directory to hold certs and key files used on the server side. 114 serverDir, err := createTmpDirWithCerts("testServerSideXDS*", "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem", "") 115 if err != nil { 116 t.Fatalf("Failed to create bootstrap configuration: %v", err) 117 } 118 119 // Create a directory to hold certs and key files used on the client side. 120 clientDir, err := createTmpDirWithCerts("testClientSideXDS*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/server_ca_cert.pem", "") 121 if err != nil { 122 t.Fatalf("Failed to create bootstrap configuration: %v", err) 123 } 124 125 // Create certificate providers section of the bootstrap config with entries 126 // for both the client and server sides. 127 cpc := map[string]json.RawMessage{ 128 ServerSideCertProviderInstance: DefaultFileWatcherConfig(path.Join(serverDir, certFile), path.Join(serverDir, keyFile), path.Join(serverDir, rootFile)), 129 ClientSideCertProviderInstance: DefaultFileWatcherConfig(path.Join(clientDir, certFile), path.Join(clientDir, keyFile), path.Join(clientDir, rootFile)), 130 } 131 132 // Create the bootstrap configuration. 133 bs, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 134 Servers: []byte(fmt.Sprintf(`[{ 135 "server_uri": "passthrough:///%s", 136 "channel_creds": [{"type": "insecure"}] 137 }]`, serverURI)), 138 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 139 CertificateProviders: cpc, 140 ServerListenerResourceNameTemplate: ServerListenerResourceNameTemplate, 141 }) 142 if err != nil { 143 t.Fatalf("Failed to create bootstrap configuration: %v", err) 144 } 145 return bs 146 } 147 148 const ( 149 // Names of files inside tempdir, for certprovider plugin to watch. 150 certFile = "cert.pem" 151 keyFile = "key.pem" 152 rootFile = "ca.pem" 153 spiffeBundleMapFile = "spiffe_bundle_map.json" 154 ) 155 156 func createTmpFile(src, dst string) error { 157 data, err := os.ReadFile(src) 158 if err != nil { 159 return fmt.Errorf("os.ReadFile(%q) failed: %v", src, err) 160 } 161 if err := os.WriteFile(dst, data, os.ModePerm); err != nil { 162 return fmt.Errorf("os.WriteFile(%q) failed: %v", dst, err) 163 } 164 return nil 165 } 166 167 // createTmpDirWithCerts creates a temporary directory under the system default 168 // tempDir with the given dirPattern. It also reads from certSrc, keySrc and 169 // rootSrc files and creates appropriate files under the newly create tempDir. 170 // Returns the path of the created tempDir if successful, and an error 171 // otherwise. 172 func createTmpDirWithCerts(dirPattern, certSrc, keySrc, rootSrc, spiffeBundleMapSrc string) (string, error) { 173 // Create a temp directory. Passing an empty string for the first argument 174 // uses the system temp directory. 175 dir, err := os.MkdirTemp("", dirPattern) 176 if err != nil { 177 return "", fmt.Errorf("os.MkdirTemp() failed: %v", err) 178 } 179 180 if err := createTmpFile(testdata.Path(certSrc), path.Join(dir, certFile)); err != nil { 181 return "", err 182 } 183 if err := createTmpFile(testdata.Path(keySrc), path.Join(dir, keyFile)); err != nil { 184 return "", err 185 } 186 if err := createTmpFile(testdata.Path(rootSrc), path.Join(dir, rootFile)); err != nil { 187 return "", err 188 } 189 if spiffeBundleMapSrc != "" { 190 if err := createTmpFile(testdata.Path(spiffeBundleMapSrc), path.Join(dir, spiffeBundleMapFile)); err != nil { 191 return "", err 192 } 193 } 194 return dir, nil 195 }