Skip to content

Commit

Permalink
coordinator: use random key for intermediate CA
Browse files Browse the repository at this point in the history
  • Loading branch information
burgerdev committed Jul 17, 2024
1 parent 587fdd7 commit 85f44f1
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 84 deletions.
2 changes: 1 addition & 1 deletion coordinator/internal/authority/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (m *Authority) syncState() error {
return fmt.Errorf("parsing manifest: %w", err)
}

meshKey, err := se.DeriveMeshCAKey(latest.TransitionHash)
meshKey, err := se.GenerateMeshCAKey()
if err != nil {
return fmt.Errorf("deriving mesh CA key: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion coordinator/internal/authority/userapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (a *Authority) SetManifest(ctx context.Context, req *userapi.SetManifestReq
}

se := a.se.Load()
meshKey, err := se.DeriveMeshCAKey(nextTransitionHash)
meshKey, err := se.GenerateMeshCAKey()
if err != nil {
return nil, status.Errorf(codes.Internal, "deriving mesh CA key: %v", err)
}
Expand Down
21 changes: 4 additions & 17 deletions coordinator/internal/seedengine/seedengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
Expand Down Expand Up @@ -92,23 +93,9 @@ func (s *SeedEngine) DerivePodSecret(policyHash [hashSize]byte) ([]byte, error)
return s.hkdfDerive(s.podStateSeed, fmt.Sprintf("POD SECRET %x", policyHash))
}

// DeriveMeshCAKey derives a secret for a mesh CA from the transaction hash and the secret seed.
func (s *SeedEngine) DeriveMeshCAKey(transactionHash [hashSize]byte) (*ecdsa.PrivateKey, error) {
if transactionHash == [hashSize]byte{} {
return nil, errors.New("transaction hash must not be empty")
}
if bytes.Equal(transactionHash[:], s.hashFun().Sum(nil)) {
return nil, errors.New("transaction hash is the hash of an empty byte slice")
}
transactionSecret, err := s.hkdfDerive(s.historySeed, fmt.Sprintf("TRANSACTION SECRET %x", transactionHash))
if err != nil {
return nil, err
}
meshCASeed, err := s.hkdfDerive(transactionSecret, "MESH CA SECRET")
if err != nil {
return nil, err
}
return s.generateECDSAPrivateKey(meshCASeed)
// GenerateMeshCAKey generates a new random key for the mesh authority.
func (s *SeedEngine) GenerateMeshCAKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
}

// RootCAKey returns the root CA key which is derived from the secret seed.
Expand Down
56 changes: 0 additions & 56 deletions coordinator/internal/seedengine/seedengine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,59 +133,3 @@ func TestSeedEngine_DerivePodSecret(t *testing.T) {
})
}
}

func TestSeedEngine_DeriveMeshCAKey(t *testing.T) {
req := require.New(t)

// transactionHash -> want
testCases := map[string]struct {
meshCAKey string // hex(x509.MarshalECPrivateKey(meshCAKey))
err bool
}{
/*
Crypto-determinism regression test cases.
DO NOT CHANGE!
*/
"8d62644ef9944dbbb1a2b1a574840cbd6b09e5e7f96ac0f82a8a37271edd983b": {meshCAKey: "3081a40201010430f1247e2b66958faf9aa524a94816da813b18a035f15daf4d35cbbb60643e97b72df91a9135ea2fab7724979e913a3422a00706052b81040022a16403620004593ff9c7ecdefe21b428416e0ae81784b954b331faf3de9612b504db1e13305b834a1126fbd6fa7073081cb3b92f6464628fc51de3baf7b78037e02b1371259fa7633bc3b99588a16f24111c56f8f6db41369619204857b7d1416534c77f3c47"},
"b838a7adb60d110d6c3c7a1dfa51b439b78386f439a092eda0d67d53cc01c02e": {meshCAKey: "3081a40201010430390804190ef6e01e34fff963a50850cd5d8345d092c413deb86c32dcdd14a1e2c7dac4317139e94842b97b335104150aa00706052b81040022a16403620004eb8541e09d18cb96c49dd66d2585597ae3f185d125ac6ff78371213d96636d74ced679370d63723eebbf1f98bb23e3cead1535cac7d595f5ff3b480dd53c7146e409956474fa4c20dc5956082d1c843343e65b827e7c91172ca5143250575591"},
"11103d1efce19d05f5aaac2c8af405136ad91dae9f64ba25c2402100ff0e03eb": {meshCAKey: "3081a40201010430d89ede876f64c8c683fbb953ec57a841bb2c2f59a3863f0c325855da40f6da0a3babe7e64d925553d03d11c4d8caa8a4a00706052b81040022a164036200045ab49a846c2b742b85415fdfebd08272bf6e32bd1a0c605cf4b1cd25dbcce4c17f847c6dd27235a4da548134c505694048a6ecbf3864815dafca842edef5451440c103f3143e538bd5aa2a9bd8897a82923ac06a24dee3f7cb25187befbab8b8"},
"d229c5714ca84d4e73b973636723e6cd5fe49f3c3e486732facfba61f94a10fc": {meshCAKey: "3081a402010104304b9320746578895fe917b55040fbfb65a806c9ca7757ff06d467a5eb9e9eec3f5a6ea5dda90734f5effa7e1ec14f4d12a00706052b81040022a164036200042e11ae0fc85bfde3a762f597d7bcafad8122749fc4c8ac614925b5c0060a2520541ff7c5c96d7c5bcb8784254f299e99fb263daf9204905f59cff7d94265b36f3b613e3625e5443e90d54b8d401e15b1a06bea5f6595891a23f2631633cd8bc3"},
"91b7513a7709d2ab92d2c1fe1e431e37f0bea18165dd908b0e6386817b0c6faf": {meshCAKey: "3081a40201010430d7dc79626cbdb350d9bd7ea95f1461dc2ebf6f06d2cff9b501e4e802dd7a2ab30bb6298b509063b3237737da45564a95a00706052b81040022a164036200047831f43068f9b4acf553a19983a15efff4cc928a4be56be2e239eecbd9446eb6aacd8ac6e13a4fc1957636a9586c26b2f8b5cc92653bc148cc30d0e5f0c46351ccdb2972802af849b5d7d24785dfde1e881a907358c6e285f3593334362730de"},
"99704c8b2a08ae9b8165a300ea05dbeae3b4c9a2096a6221aa4175bad43d53ec": {meshCAKey: "3081a40201010430f08b066f918684cb97e33449143327b651c206a8473c3d789c3091bb4c677afca0dc24d78df527523d345a75a9be4fc4a00706052b81040022a164036200041b84754f8963621c77ad7dba9a452869e96407e6646eb028ab11f46d07cac4341fb6762d500c96b6a480250e11f3ba45d664c88382d242be39632270f540dcf2f98cc66f46d0495aa713cfebb8123483e3e7667c3461f4cd65c2f8c84abbe384"},
"f2e57529d3b92832eef960b75b2d299aadf1e373473bebf28cc105dae55c5f4e": {meshCAKey: "3081a4020101043095e15f05598731a2f1e9b54b3c3e6c914fbf532be808eaa65010fb9a6d5dc18de9f1678216c82d7113e4b9f93e74cc97a00706052b81040022a16403620004259d18d11b0fa7cb30ca190f5ba637f5c400346c95084d515d313ebe40cd699b08d0a3bf0f4b1494229e5ce5d2347be90c9af51c131f627b1789da925ff2221da3e0cee4e34460fcfc3748e976fc9f608b404e7bb52de456e8d79e4fec4f2042"},
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": {err: true},
"": {err: true},
}

secretSeed, err := hex.DecodeString("ccebed634ddee7535cd593e1e200b19b780f3906d8782207fa09c59e87a07cb3")
req.NoError(err)
salt, err := hex.DecodeString("8c1b1225c5f6cb7eef6dbd8f77a1e1e149de031d6e3718e660a8b04c8e2b0037")
req.NoError(err)

se, err := New(secretSeed, salt)
req.NoError(err)

for transactionHashStr, want := range testCases {
t.Run(transactionHashStr, func(t *testing.T) {
require := require.New(t)

var transactionHash [32]byte
transactionHashSlice, err := hex.DecodeString(transactionHashStr)
require.NoError(err)
copy(transactionHash[:], transactionHashSlice)

meshCAKey, err := se.DeriveMeshCAKey(transactionHash)

if want.err {
require.Error(err)
return
}
require.NoError(err)

meshCAKeyDER, err := x509.MarshalECPrivateKey(meshCAKey)
require.NoError(err)
require.Equal(want.meshCAKey, hex.EncodeToString(meshCAKeyDER))
})
}
}
8 changes: 8 additions & 0 deletions docs/docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,11 @@ contrast recover -c "${coordinator}:1313"

Now that the Coordinator is recovered, all workloads should pass initialization and enter the running state.
You can now verify the Coordinator again, which should return the same manifest you set before.

:::warning

The recovery process invalidates the mesh CA certificate:
existing workloads won't be able to communicate with workloads newly spawned.
All workloads should be restarted after the recovery succeeded.

:::
32 changes: 23 additions & 9 deletions e2e/openssl/openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,29 @@ func TestOpenSSL(t *testing.T) {
require.NoError(c.Restart(ctx, kubeclient.Deployment{}, ct.Namespace, opensslFrontend))
require.NoError(c.WaitFor(ctx, kubeclient.Deployment{}, ct.Namespace, opensslFrontend))

for _, cert := range []string{rootCAFile, meshCAFile} {
t.Run(cert, func(t *testing.T) {
stdout, stderr, err := c.ExecDeployment(ctx, ct.Namespace, opensslBackend, []string{"/bin/bash", "-c", opensslConnectCmd("openssl-frontend:443", cert)})
if err != nil {
t.Logf("openssl with %q after recovery:\n%s", cert, stdout)
}
assert.NoError(t, err, "stderr: %q", stderr)
})
}
t.Run("root CA is still accepted after coordinator recovery", func(t *testing.T) {
stdout, stderr, err := c.ExecDeployment(ctx, ct.Namespace, opensslBackend, []string{"/bin/bash", "-c", opensslConnectCmd("openssl-frontend:443", rootCAFile)})
if err != nil {
t.Logf("openssl with %q after recovery:\n%s", rootCAFile, stdout)
}
assert.NoError(t, err, "stderr: %q", stderr)
})

t.Run("coordinator can't recover mesh CA key", func(t *testing.T) {
_, _, err := c.ExecDeployment(ctx, ct.Namespace, opensslBackend, []string{"/bin/bash", "-c", opensslConnectCmd("openssl-frontend:443", meshCAFile)})
assert.Error(t, err)
})

require.NoError(c.Restart(ctx, kubeclient.Deployment{}, ct.Namespace, opensslBackend))
require.NoError(c.WaitFor(ctx, kubeclient.Deployment{}, ct.Namespace, opensslBackend))

t.Run("mesh CA after coordinator recovery is accepted when workloads are restarted", func(t *testing.T) {
stdout, stderr, err := c.ExecDeployment(ctx, ct.Namespace, opensslBackend, []string{"/bin/bash", "-c", opensslConnectCmd("openssl-frontend:443", meshCAFile)})
if err != nil {
t.Logf("openssl with %q after recovery:\n%s", meshCAFile, stdout)
}
assert.NoError(t, err, "stderr: %q", stderr)
})
})
}

Expand Down

0 comments on commit 85f44f1

Please sign in to comment.