Skip to content

Commit

Permalink
Support injection of Coordinator Root Cert into Marble environment
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Weiße <[email protected]>
  • Loading branch information
daniel-weisse committed Jan 7, 2025
1 parent 9b4066f commit 97330a2
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 30 deletions.
38 changes: 13 additions & 25 deletions coordinator/core/marbleapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,6 @@ import (
"google.golang.org/grpc/status"
)

type reservedSecrets struct {
RootCA manifest.Secret
MarbleCert manifest.Secret
CoordinatorRoot manifest.Secret
}

// Defines the "MarbleRun" prefix when mentioned in a manifest.
type secretsWrapper struct {
MarbleRun reservedSecrets
Secrets map[string]manifest.Secret
}

// Activate implements the MarbleAPI function to authenticate a marble (implements the MarbleServer interface).
//
// Verifies the marble's integrity and subsequently provides the marble with a certificate for authentication and application-specific parameters as defined in the Coordinator's manifest.
Expand Down Expand Up @@ -308,15 +296,15 @@ func (c *Core) generateCertFromCSR(txdata storeGetter, csrReq []byte, pubk ecdsa
}

// customizeParameters replaces the placeholders in the manifest's parameters with the actual values.
func customizeParameters(params manifest.Parameters, specialSecrets reservedSecrets, userSecrets map[string]manifest.Secret) (*rpc.Parameters, error) {
func customizeParameters(params manifest.Parameters, specialSecrets manifest.ReservedSecrets, userSecrets map[string]manifest.Secret) (*rpc.Parameters, error) {
customParams := rpc.Parameters{
Argv: params.Argv,
Files: make(map[string][]byte),
Env: make(map[string][]byte),
}

// Wrap the authentication secrets to have the "MarbleRun" prefix in front of them when mentioned in a manifest
secretsWrapped := secretsWrapper{
secretsWrapped := manifest.SecretsWrapper{
MarbleRun: specialSecrets,
Secrets: userSecrets,
}
Expand Down Expand Up @@ -377,7 +365,7 @@ func customizeParameters(params manifest.Parameters, specialSecrets reservedSecr
return &customParams, nil
}

func parseSecrets(data string, tplFunc template.FuncMap, secretsWrapped secretsWrapper) (string, error) {
func parseSecrets(data string, tplFunc template.FuncMap, secretsWrapped manifest.SecretsWrapper) (string, error) {
var templateResult bytes.Buffer

tpl, err := template.New("data").Funcs(tplFunc).Parse(data)
Expand All @@ -392,43 +380,43 @@ func parseSecrets(data string, tplFunc template.FuncMap, secretsWrapped secretsW
return templateResult.String(), nil
}

func (c *Core) generateMarbleAuthSecrets(txdata storeGetter, req *rpc.ActivationReq, marbleUUID uuid.UUID) (reservedSecrets, error) {
func (c *Core) generateMarbleAuthSecrets(txdata storeGetter, req *rpc.ActivationReq, marbleUUID uuid.UUID) (manifest.ReservedSecrets, error) {
// generate key-pair for marble
privk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}
encodedPrivKey, err := x509.MarshalPKCS8PrivateKey(privk)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}
encodedPubKey, err := x509.MarshalPKIXPublicKey(&privk.PublicKey)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}

// Generate Marble certificate
certRaw, err := c.generateCertFromCSR(txdata, req.GetCSR(), privk.PublicKey, req.GetMarbleType(), marbleUUID.String())
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}

marbleCert, err := x509.ParseCertificate(certRaw)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}

marbleRootCert, err := txdata.GetCertificate(constants.SKMarbleRootCert)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}
coordinatorRootCert, err := txdata.GetCertificate(constants.SKCoordinatorRootCert)
if err != nil {
return reservedSecrets{}, err
return manifest.ReservedSecrets{}, err
}

// customize marble's parameters
authSecrets := reservedSecrets{
authSecrets := manifest.ReservedSecrets{
RootCA: manifest.Secret{Cert: manifest.Certificate(*marbleRootCert)},
MarbleCert: manifest.Secret{Cert: manifest.Certificate(*marbleCert), Public: encodedPubKey, Private: encodedPrivKey},
CoordinatorRoot: manifest.Secret{Cert: manifest.Certificate(*coordinatorRootCert)},
Expand All @@ -437,7 +425,7 @@ func (c *Core) generateMarbleAuthSecrets(txdata storeGetter, req *rpc.Activation
return authSecrets, nil
}

func (c *Core) setTTLSConfig(txdata storeGetter, marble *manifest.Marble, specialSecrets reservedSecrets, userSecrets map[string]manifest.Secret) error {
func (c *Core) setTTLSConfig(txdata storeGetter, marble *manifest.Marble, specialSecrets manifest.ReservedSecrets, userSecrets map[string]manifest.Secret) error {
if len(marble.TLS) == 0 {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions coordinator/core/marbleapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,12 @@ func TestParseSecrets(t *testing.T) {
"emptysecret": {},
}

testReservedSecrets := reservedSecrets{
testReservedSecrets := manifest.ReservedSecrets{
RootCA: manifest.Secret{Public: []byte{0, 0, 42}, Private: []byte{0, 0, 7}},
MarbleCert: manifest.Secret{Public: []byte{42, 0, 0}, Private: []byte{7, 0, 0}},
}

testWrappedSecrets := secretsWrapper{
testWrappedSecrets := manifest.SecretsWrapper{
MarbleRun: testReservedSecrets,
Secrets: testSecrets,
}
Expand Down
8 changes: 6 additions & 2 deletions coordinator/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,9 @@ func (m Manifest) TemplateDryRun(secrets map[string]Secret) error {
Public: []byte{0x41},
Private: []byte{0x41},
},
CoordinatorRoot: Secret{
Cert: Certificate{Raw: []byte{0x41}},
},
},
}
// make sure templates in file/env declarations can actually be executed
Expand Down Expand Up @@ -620,8 +623,9 @@ func (m Manifest) CheckUpdate(originalPackages map[string]quote.PackagePropertie

// ReservedSecrets is a tuple of secrets reserved for a single Marble.
type ReservedSecrets struct {
RootCA Secret
MarbleCert Secret
RootCA Secret
MarbleCert Secret
CoordinatorRoot Secret
}

// SecretsWrapper is used to define the "MarbleRun" prefix when mentioned in a manifest.
Expand Down
83 changes: 82 additions & 1 deletion coordinator/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ func TestFile(t *testing.T) {
func TestTemplateDryRun(t *testing.T) {
testCases := map[string]struct {
manifest []byte
secrets map[string]Secret
wantErr bool
}{
"valid": {
Expand Down Expand Up @@ -225,6 +224,88 @@ func TestTemplateDryRun(t *testing.T) {
}`),
wantErr: true,
},
"reserved secrets": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.RootCA.Cert }}",
"marble_cert": "{{ pem .MarbleRun.MarbleCert.Cert }}",
"marble_key": "{{ pem .MarbleRun.MarbleCert.Private }}",
"coordinator_root": "{{ pem .MarbleRun.CoordinatorRoot.Cert }}"
}
}
}
}
}`),
},
"reserved root ca does not support private": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.RootCA.Private }}"
}
}
}
}
}`),
wantErr: true,
},
"reserved root ca does not support public": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.RootCA.Public }}"
}
}
}
}
}`),
wantErr: true,
},
"reserved coordinator root does not support private": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.CoordinatorRoot.Private }}"
}
}
}
}
}`),
wantErr: true,
},
"reserved coordinator root does not support public": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.CoordinatorRoot.Public }}"
}
}
}
}
}`),
wantErr: true,
},
}

for name, tc := range testCases {
Expand Down
1 change: 1 addition & 0 deletions docs/docs/workflows/define-manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ The following named keys and certificates are always available.
* `.MarbleRun.RootCA.Cert`: the root certificate of the cluster issued by the Coordinator; this can be used to verify the certificates of all Marbles in the cluster.
* `.MarbleRun.MarbleCert.Cert`: the Marble's certificate; this is issued by the `.MarbleRun.RootCA.Cert` and is for Marble-to-Marble and Marble-to-client authentication.
* `.MarbleRun.MarbleCert.Private`: the Marble's private key corresponding to `.MarbleRun.MarbleCert.Cert`
* `.MarbleRun.CoordinatorRoot.Cert`: the root certificate of the Coordinator; this can be used to verify Marbles in the cluster across multiple manifest updates.

Check warning on line 180 in docs/docs/workflows/define-manifest.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Microsoft.Passive] 'be used' looks like passive voice. Raw Output: {"message": "[Microsoft.Passive] 'be used' looks like passive voice.", "location": {"path": "docs/docs/workflows/define-manifest.md", "range": {"start": {"line": 180, "column": 88}}}, "severity": "INFO"}

Check warning on line 180 in docs/docs/workflows/define-manifest.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Microsoft.ComplexWords] Consider using 'many' instead of 'multiple'. Raw Output: {"message": "[Microsoft.ComplexWords] Consider using 'many' instead of 'multiple'.", "location": {"path": "docs/docs/workflows/define-manifest.md", "range": {"start": {"line": 180, "column": 136}}}, "severity": "INFO"}

Finally, the optional field `MaxActivations` can be used to restrict the number of distinct instances that can be created of a Marble.

Expand Down

0 comments on commit 97330a2

Please sign in to comment.