github.com/palantir/witchcraft-go-server/v2@v2.76.0/integration/config_test.go (about) 1 // Copyright (c) 2019 Palantir Technologies. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package integration 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "testing" 25 26 "github.com/palantir/witchcraft-go-server/v2/config" 27 "github.com/palantir/witchcraft-go-server/v2/witchcraft" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestEncryptedConfig(t *testing.T) { 33 const ( 34 decryptedValue = "hello world" 35 encryptionKey = "AES:T6H7a4WvQS9ITcNIihyUIj30K4SIrD6dB39ENJQ7oAo=" 36 encryptedValue = "${enc:eyJ0eXBlIjoiQUVTIiwibW9kZSI6IkdDTSIsImNpcGhlcnRleHQiOiJqcGl0bThQRStRekd2YlE9IiwiaXYiOiJrTHlBOEZBNzFnTDVpdkswIiwidGFnIjoicmxpcXY3amYwbWVnaGU1N0pyQ3ZzZz09In0=}" 37 decryptedMultilineValue = `-----BEGIN CERTIFICATE----- 38 cert 39 -----END CERTIFICATE----- 40 ` 41 encryptedMultilineValue = "${enc:eyJ0eXBlIjoiQUVTIiwibW9kZSI6IkdDTSIsImNpcGhlcnRleHQiOiJuTXNWOEV1L1JpWGN1TGFDbVNSRVVQUG1NbFpab0paMEN6QlNuL3BWUWwzWUhkWXlVSEZYaGY5c3N5NlFTbzVXYnp5bTlaTCtmQytxQTZNPSIsIml2IjoiRzNtSHp1SmRVU0NncXRLcyIsInRhZyI6IlI2UWZPb2UvS1hiSFZiTmZDaWNkVlE9PSJ9}" 42 ) 43 44 type message struct { 45 config.Runtime `yaml:",inline"` 46 Message string `yaml:"message"` 47 } 48 49 type messageInstall struct { 50 config.Install `yaml:",inline"` 51 Message string `yaml:"message"` 52 } 53 54 for _, test := range []struct { 55 Name string 56 ECVKeyContent string 57 InstallConfig string 58 RuntimeConfig string 59 // Use InitFn to verify config 60 Verify func(info witchcraft.InitInfo) error 61 VerifyLog func(t *testing.T, logOutput []byte) 62 }{ 63 { 64 Name: "init function sees decrypted config", 65 ECVKeyContent: encryptionKey, 66 InstallConfig: fmt.Sprintf("message: %s\nuse-console-log: true\n", encryptedValue), 67 RuntimeConfig: fmt.Sprintf("message: %s\nlogging:\n level: warn\n", encryptedValue), 68 Verify: func(info witchcraft.InitInfo) error { 69 if msg := info.InstallConfig.(*messageInstall).Message; msg != decryptedValue { 70 return fmt.Errorf("expected %q got %q", decryptedValue, msg) 71 } 72 if msg := info.RuntimeConfig.Current().(*message).Message; msg != decryptedValue { 73 return fmt.Errorf("expected %q got %q", decryptedValue, msg) 74 } 75 return nil 76 }, 77 VerifyLog: func(t *testing.T, logOutput []byte) { 78 assert.Equal(t, []string{"abort startup"}, getLogFileMessages(t, logOutput)) 79 }, 80 }, 81 { 82 Name: "encrypted config can be concatenated with other strings", 83 ECVKeyContent: encryptionKey, 84 InstallConfig: fmt.Sprintf("message: prefix%ssuffix\nuse-console-log: true\n", encryptedValue), 85 RuntimeConfig: fmt.Sprintf("message: prefix%ssuffix%s\nlogging:\n level: warn\n", encryptedValue, encryptedMultilineValue), 86 Verify: func(info witchcraft.InitInfo) error { 87 expectedInstallValue := fmt.Sprintf("prefix%ssuffix", decryptedValue) 88 if msg := info.InstallConfig.(*messageInstall).Message; msg != expectedInstallValue { 89 return fmt.Errorf("expected %q got %q", expectedInstallValue, msg) 90 } 91 expectedRuntimeValue := fmt.Sprintf("prefix%ssuffix%s", decryptedValue, decryptedMultilineValue) 92 if msg := info.RuntimeConfig.Current().(*message).Message; msg != expectedRuntimeValue { 93 return fmt.Errorf("expected %q got %q", expectedRuntimeValue, msg) 94 } 95 return nil 96 }, 97 VerifyLog: func(t *testing.T, logOutput []byte) { 98 assert.Equal(t, []string{"abort startup"}, getLogFileMessages(t, logOutput)) 99 }, 100 }, 101 { 102 Name: "encrypted multi-line value is decrypted properly", 103 ECVKeyContent: encryptionKey, 104 InstallConfig: fmt.Sprintf("message: %s\nuse-console-log: true\n", encryptedMultilineValue), 105 RuntimeConfig: fmt.Sprintf("message: %s\nlogging:\n level: warn\n", encryptedMultilineValue), 106 Verify: func(info witchcraft.InitInfo) error { 107 if msg := info.InstallConfig.(*messageInstall).Message; msg != decryptedMultilineValue { 108 return fmt.Errorf("expected %q got %q", decryptedValue, msg) 109 } 110 if msg := info.RuntimeConfig.Current().(*message).Message; msg != decryptedMultilineValue { 111 return fmt.Errorf("expected %q got %q", decryptedValue, msg) 112 } 113 return nil 114 }, 115 VerifyLog: func(t *testing.T, logOutput []byte) { 116 assert.Equal(t, []string{"abort startup"}, getLogFileMessages(t, logOutput)) 117 }, 118 }, 119 { 120 Name: "no ecv required if no encrypted config", 121 ECVKeyContent: "test_invalid_key_material", 122 InstallConfig: "message: hello install\nuse-console-log: true\n", 123 RuntimeConfig: "message: hello runtime\nlogging:\n level: warn\n", 124 Verify: func(info witchcraft.InitInfo) error { 125 if msg := info.InstallConfig.(*messageInstall).Message; msg != "hello install" { 126 return fmt.Errorf("expected %q got %q", "hello install", msg) 127 } 128 if msg := info.RuntimeConfig.Current().(*message).Message; msg != "hello runtime" { 129 return fmt.Errorf("expected %q got %q", "hello runtime", msg) 130 } 131 return nil 132 }, 133 VerifyLog: func(t *testing.T, logOutput []byte) { 134 assert.Equal(t, []string{"abort startup"}, getLogFileMessages(t, logOutput)) 135 }, 136 }, 137 { 138 Name: "warning printed if ecv key fails in runtime", 139 ECVKeyContent: "test_invalid_key_material", 140 InstallConfig: "message: hello install\nuse-console-log: true\n", 141 RuntimeConfig: fmt.Sprintf("message: %s\nlogging:\n level: warn\n", encryptedValue), 142 Verify: func(info witchcraft.InitInfo) error { 143 if msg := info.InstallConfig.(*messageInstall).Message; msg != "hello install" { 144 return fmt.Errorf("expected %q got %q", "hello install", msg) 145 } 146 if msg := info.RuntimeConfig.Current().(*message).Message; msg != encryptedValue { 147 return fmt.Errorf("expected %q got %q", encryptedValue, msg) 148 } 149 return nil 150 }, 151 VerifyLog: func(t *testing.T, logOutput []byte) { 152 assert.Equal(t, []string{"Failed to decrypt encrypted runtime configuration", "abort startup"}, getLogFileMessages(t, logOutput)) 153 }, 154 }, 155 { 156 Name: "warning printed if encrypted config cannot be decrypted in runtime", 157 ECVKeyContent: encryptionKey, 158 InstallConfig: "message: hello install\nuse-console-log: true\n", 159 RuntimeConfig: fmt.Sprintf("message: %s\nlogging:\n level: warn\n", "${enc:invalid}"), 160 Verify: func(info witchcraft.InitInfo) error { 161 if msg := info.InstallConfig.(*messageInstall).Message; msg != "hello install" { 162 return fmt.Errorf("expected %q got %q", "hello install", msg) 163 } 164 if msg := info.RuntimeConfig.Current().(*message).Message; msg != "${enc:invalid}" { 165 return fmt.Errorf("expected %q got %q", "${enc:invalid}", msg) 166 } 167 return nil 168 }, 169 VerifyLog: func(t *testing.T, logOutput []byte) { 170 assert.Equal(t, []string{"Failed to decrypt encrypted runtime configuration", "abort startup"}, getLogFileMessages(t, logOutput)) 171 }, 172 }, 173 } { 174 t.Run(test.Name, func(t *testing.T) { 175 tmpDir, err := ioutil.TempDir("", "TestEncryptedConfig_") 176 require.NoError(t, err) 177 defer func() { 178 _ = os.RemoveAll(tmpDir) 179 }() 180 ecvKeyFile := filepath.Join(tmpDir, "ecv.key") 181 err = ioutil.WriteFile(ecvKeyFile, []byte(test.ECVKeyContent), 0600) 182 require.NoError(t, err) 183 installFile := filepath.Join(tmpDir, "install.yml") 184 err = ioutil.WriteFile(installFile, []byte(test.InstallConfig), 0644) 185 require.NoError(t, err) 186 runtimeFile := filepath.Join(tmpDir, "runtime.yml") 187 err = ioutil.WriteFile(runtimeFile, []byte(test.RuntimeConfig), 0644) 188 require.NoError(t, err) 189 190 logOutputBuffer := &bytes.Buffer{} 191 server := witchcraft.NewServer(). 192 WithECVKeyFromFile(ecvKeyFile). 193 WithInstallConfigFromFile(installFile). 194 WithInstallConfigType(&messageInstall{}). 195 WithRuntimeConfigFromFile(runtimeFile). 196 WithRuntimeConfigType(&message{}). 197 WithLoggerStdoutWriter(logOutputBuffer). 198 WithDisableGoRuntimeMetrics(). 199 WithSelfSignedCertificate(). 200 WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (cleanup func(), rErr error) { 201 if err := test.Verify(info); err != nil { 202 return nil, err 203 } 204 return nil, fmt.Errorf("abort startup") 205 }) 206 // Execute server, expecting error 207 err = server.Start() 208 require.EqualError(t, err, "abort startup") 209 210 if test.VerifyLog != nil { 211 test.VerifyLog(t, logOutputBuffer.Bytes()) 212 } 213 }) 214 } 215 }