1package command 2 3import ( 4 "context" 5 "encoding/base64" 6 "path" 7 "testing" 8 9 "github.com/hashicorp/go-hclog" 10 wrapping "github.com/hashicorp/go-kms-wrapping" 11 "github.com/hashicorp/vault/api" 12 "github.com/hashicorp/vault/builtin/logical/transit" 13 commandseal "github.com/hashicorp/vault/command/server/seal" 14 "github.com/hashicorp/vault/helper/testhelpers" 15 "github.com/hashicorp/vault/helper/testhelpers/teststorage" 16 vaulthttp "github.com/hashicorp/vault/http" 17 "github.com/hashicorp/vault/sdk/helper/logging" 18 "github.com/hashicorp/vault/sdk/logical" 19 "github.com/hashicorp/vault/vault" 20 "github.com/hashicorp/vault/vault/seal" 21) 22 23type transitSealServer struct { 24 *vault.TestCluster 25} 26 27func newTransitSealServer(t *testing.T) *transitSealServer { 28 conf := &vault.CoreConfig{ 29 LogicalBackends: map[string]logical.Factory{ 30 "transit": transit.Factory, 31 }, 32 } 33 opts := &vault.TestClusterOptions{ 34 NumCores: 1, 35 HandlerFunc: vaulthttp.Handler, 36 Logger: logging.NewVaultLogger(hclog.Trace).Named(t.Name()).Named("transit"), 37 } 38 teststorage.InmemBackendSetup(conf, opts) 39 cluster := vault.NewTestCluster(t, conf, opts) 40 cluster.Start() 41 42 if err := cluster.Cores[0].Client.Sys().Mount("transit", &api.MountInput{ 43 Type: "transit", 44 }); err != nil { 45 t.Fatal(err) 46 } 47 48 return &transitSealServer{cluster} 49} 50 51func (tss *transitSealServer) makeKey(t *testing.T, key string) { 52 client := tss.Cores[0].Client 53 // Create default aesgcm key 54 if _, err := client.Logical().Write(path.Join("transit", "keys", key), nil); err != nil { 55 t.Fatal(err) 56 } 57 if _, err := client.Logical().Write(path.Join("transit", "keys", key, "config"), map[string]interface{}{ 58 "deletion_allowed": true, 59 }); err != nil { 60 t.Fatal(err) 61 } 62} 63 64func (tss *transitSealServer) makeSeal(t *testing.T, key string) vault.Seal { 65 client := tss.Cores[0].Client 66 wrapperConfig := map[string]string{ 67 "address": client.Address(), 68 "token": client.Token(), 69 "mount_path": "transit", 70 "key_name": key, 71 "tls_ca_cert": tss.CACertPEMFile, 72 } 73 transitSeal, _, err := commandseal.GetTransitKMSFunc(nil, wrapperConfig) 74 if err != nil { 75 t.Fatalf("error setting wrapper config: %v", err) 76 } 77 78 return vault.NewAutoSeal(&seal.Access{ 79 Wrapper: transitSeal, 80 }) 81} 82 83func verifyBarrierConfig(t *testing.T, cfg *vault.SealConfig, sealType string, shares, threshold, stored int) { 84 t.Helper() 85 if cfg.Type != sealType { 86 t.Fatalf("bad seal config: %#v, expected type=%q", cfg, sealType) 87 } 88 if cfg.SecretShares != shares { 89 t.Fatalf("bad seal config: %#v, expected SecretShares=%d", cfg, shares) 90 } 91 if cfg.SecretThreshold != threshold { 92 t.Fatalf("bad seal config: %#v, expected SecretThreshold=%d", cfg, threshold) 93 } 94 if cfg.StoredShares != stored { 95 t.Fatalf("bad seal config: %#v, expected StoredShares=%d", cfg, stored) 96 } 97} 98 99func TestSealMigration_ShamirToAuto(t *testing.T) { 100 t.Parallel() 101 t.Run("inmem", func(t *testing.T) { 102 t.Parallel() 103 testSealMigrationShamirToAuto(t, teststorage.InmemBackendSetup) 104 }) 105 106 t.Run("file", func(t *testing.T) { 107 t.Parallel() 108 testSealMigrationShamirToAuto(t, teststorage.FileBackendSetup) 109 }) 110 111 t.Run("consul", func(t *testing.T) { 112 t.Parallel() 113 testSealMigrationShamirToAuto(t, teststorage.ConsulBackendSetup) 114 }) 115 116 t.Run("raft", func(t *testing.T) { 117 t.Parallel() 118 testSealMigrationShamirToAuto(t, teststorage.RaftBackendSetup) 119 }) 120} 121 122func testSealMigrationShamirToAuto(t *testing.T, setup teststorage.ClusterSetupMutator) { 123 tcluster := newTransitSealServer(t) 124 defer tcluster.Cleanup() 125 126 conf, opts := teststorage.ClusterSetup(&vault.CoreConfig{ 127 DisableSealWrap: true, 128 }, &vault.TestClusterOptions{ 129 HandlerFunc: vaulthttp.Handler, 130 SkipInit: true, 131 NumCores: 3, 132 }, 133 setup, 134 ) 135 opts.SetupFunc = nil 136 cluster := vault.NewTestCluster(t, conf, opts) 137 tcluster.makeKey(t, "key1") 138 autoSeal := tcluster.makeSeal(t, "key1") 139 cluster.Start() 140 defer cluster.Cleanup() 141 142 client := cluster.Cores[0].Client 143 initResp, err := client.Sys().Init(&api.InitRequest{ 144 SecretShares: 5, 145 SecretThreshold: 3, 146 }) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 var resp *api.SealStatusResponse 152 for _, key := range initResp.KeysB64 { 153 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key}) 154 if err != nil { 155 t.Fatal(err) 156 } 157 if resp == nil || !resp.Sealed { 158 break 159 } 160 } 161 if resp == nil || resp.Sealed { 162 t.Fatalf("expected unsealed state; got %#v", resp) 163 } 164 165 testhelpers.WaitForActiveNode(t, cluster) 166 rootToken := initResp.RootToken 167 client.SetToken(rootToken) 168 if err := client.Sys().Seal(); err != nil { 169 t.Fatal(err) 170 } 171 172 if err := adjustCoreForSealMigration(cluster.Logger, cluster.Cores[0].Core, autoSeal, nil); err != nil { 173 t.Fatal(err) 174 } 175 176 for _, key := range initResp.KeysB64 { 177 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key}) 178 if err == nil { 179 t.Fatal("expected error due to lack of migrate parameter") 180 } 181 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key, Migrate: true}) 182 if err != nil { 183 t.Fatal(err) 184 } 185 if resp == nil || !resp.Sealed { 186 break 187 } 188 } 189 if resp == nil || resp.Sealed { 190 t.Fatalf("expected unsealed state; got %#v", resp) 191 } 192 193 testhelpers.WaitForActiveNode(t, cluster) 194 // Seal and unseal again to verify that things are working fine 195 if err := client.Sys().Seal(); err != nil { 196 t.Fatal(err) 197 } 198 199 // Now the barrier unseal keys are actually the recovery keys. 200 // Seal the transit cluster; we expect the unseal of our other cluster 201 // to fail as a result. 202 tcluster.EnsureCoresSealed(t) 203 for _, key := range initResp.KeysB64 { 204 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key}) 205 if err != nil { 206 break 207 } 208 if resp == nil || !resp.Sealed { 209 break 210 } 211 } 212 if err == nil || resp != nil { 213 t.Fatalf("expected sealed state; got %#v", resp) 214 } 215 216 tcluster.UnsealCores(t) 217 for _, key := range initResp.KeysB64 { 218 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key}) 219 if err != nil { 220 t.Fatal(err) 221 } 222 if resp == nil || !resp.Sealed { 223 break 224 } 225 } 226 if resp == nil || resp.Sealed { 227 t.Fatalf("expected unsealed state; got %#v", resp) 228 } 229 230 // Make sure the seal configs were updated correctly 231 b, r, err := cluster.Cores[0].Core.PhysicalSealConfigs(context.Background()) 232 if err != nil { 233 t.Fatal(err) 234 } 235 verifyBarrierConfig(t, b, wrapping.Transit, 1, 1, 1) 236 verifyBarrierConfig(t, r, wrapping.Shamir, 5, 3, 0) 237} 238 239/* 240func TestSealMigration_AutoToAuto(t *testing.T) { 241 t.Parallel() 242 t.Run("inmem", func(t *testing.T) { 243 t.Parallel() 244 testSealMigrationAutoToAuto(t, teststorage.InmemBackendSetup) 245 }) 246 247 t.Run("file", func(t *testing.T) { 248 t.Parallel() 249 testSealMigrationAutoToAuto(t, teststorage.FileBackendSetup) 250 }) 251 252 t.Run("consul", func(t *testing.T) { 253 t.Parallel() 254 testSealMigrationAutoToAuto(t, teststorage.ConsulBackendSetup) 255 }) 256 257 t.Run("raft", func(t *testing.T) { 258 t.Parallel() 259 testSealMigrationAutoToAuto(t, teststorage.RaftBackendSetup) 260 }) 261} 262*/ 263 264func testSealMigrationAutoToAuto(t *testing.T, setup teststorage.ClusterSetupMutator) { 265 tcluster := newTransitSealServer(t) 266 defer tcluster.Cleanup() 267 tcluster.makeKey(t, "key1") 268 tcluster.makeKey(t, "key2") 269 var seals []vault.Seal 270 271 conf, opts := teststorage.ClusterSetup(&vault.CoreConfig{ 272 DisableSealWrap: true, 273 }, &vault.TestClusterOptions{ 274 HandlerFunc: vaulthttp.Handler, 275 SkipInit: true, 276 NumCores: 3, 277 SealFunc: func() vault.Seal { 278 tseal := tcluster.makeSeal(t, "key1") 279 seals = append(seals, tseal) 280 return tseal 281 }, 282 }, 283 setup, 284 ) 285 opts.SetupFunc = nil 286 cluster := vault.NewTestCluster(t, conf, opts) 287 cluster.Start() 288 defer cluster.Cleanup() 289 290 client := cluster.Cores[0].Client 291 initResp, err := client.Sys().Init(&api.InitRequest{ 292 RecoveryShares: 5, 293 RecoveryThreshold: 3, 294 }) 295 if err != nil { 296 t.Fatal(err) 297 } 298 rootToken := initResp.RootToken 299 client.SetToken(rootToken) 300 for _, k := range initResp.RecoveryKeysB64 { 301 b, _ := base64.RawStdEncoding.DecodeString(k) 302 cluster.RecoveryKeys = append(cluster.RecoveryKeys, b) 303 } 304 305 testhelpers.WaitForActiveNode(t, cluster) 306 307 if err := client.Sys().Seal(); err != nil { 308 t.Fatal(err) 309 } 310 311 logger := cluster.Logger.Named("shamir") 312 autoSeal2 := tcluster.makeSeal(t, "key2") 313 if err := adjustCoreForSealMigration(logger, cluster.Cores[0].Core, autoSeal2, seals[0]); err != nil { 314 t.Fatal(err) 315 } 316 317 // Although we're unsealing using the recovery keys, this is still an 318 // autounseal; if we stopped the transit cluster this would fail. 319 var resp *api.SealStatusResponse 320 for _, key := range initResp.RecoveryKeysB64 { 321 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key}) 322 if err == nil { 323 t.Fatal("expected error due to lack of migrate parameter") 324 } 325 resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key, Migrate: true}) 326 if err != nil { 327 t.Fatal(err) 328 } 329 if resp == nil || !resp.Sealed { 330 break 331 } 332 } 333 if resp == nil || resp.Sealed { 334 t.Fatalf("expected unsealed state; got %#v", resp) 335 } 336 337 testhelpers.WaitForActiveNode(t, cluster) 338 339 // Seal and unseal again to verify that things are working fine 340 if err := client.Sys().Seal(); err != nil { 341 t.Fatal(err) 342 } 343 344 // Delete the original seal's transit key. 345 _, err = tcluster.Cores[0].Client.Logical().Delete(path.Join("transit", "keys", "key1")) 346 if err != nil { 347 t.Fatal(err) 348 } 349 350 err = cluster.Cores[0].Core.UnsealWithStoredKeys(context.Background()) 351 if err != nil { 352 t.Fatal(err) 353 } 354} 355