Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 14 additions & 27 deletions coordinator/core/marbleapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ import (
"google.golang.org/grpc/status"
)

type reservedSecrets struct {
RootCA manifest.Secret
MarbleCert manifest.Secret
CoordinatorRoot manifest.Secret
CoordinatorIntermediate 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 @@ -309,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 @@ -382,7 +369,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 @@ -397,47 +384,47 @@ 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
}
coordinatorIntermediateCert, err := txdata.GetCertificate(constants.SKCoordinatorIntermediateCert)
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 @@ -447,7 +434,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
12 changes: 10 additions & 2 deletions coordinator/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,12 @@ func (m Manifest) TemplateDryRun(secrets map[string]Secret) error {
Public: []byte{0x41},
Private: []byte{0x41},
},
CoordinatorRoot: Secret{
Cert: Certificate{Raw: []byte{0x41}},
},
CoordinatorIntermediate: Secret{
Cert: Certificate{Raw: []byte{0x41}},
},
},
}
// make sure templates in file/env declarations can actually be executed
Expand Down Expand Up @@ -620,8 +626,10 @@ 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
CoordinatorIntermediate Secret
}

// SecretsWrapper is used to define the "MarbleRun" prefix when mentioned in a manifest.
Expand Down
118 changes: 115 additions & 3 deletions coordinator/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"testing"

"github.com/edgelesssys/marblerun/coordinator/quote"
Expand Down Expand Up @@ -71,7 +70,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 +223,121 @@ 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 }}",
"coordinator_intermediate": "{{ pem .MarbleRun.CoordinatorIntermediate.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,
},
"reserved coordinator intermediate does not support private": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.CoordinatorIntermediate.Private }}"
}
}
}
}
}`),
wantErr: true,
},
"reserved coordinator intermediate does not support public": {
manifest: []byte(`{
"Packages": { "backend": { "UniqueID": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }},
"Marbles": {
"backend_first": {
"Package": "backend",
"Parameters": {
"Files": {
"root_ca": "{{ pem .MarbleRun.CoordinatorIntermediate.Public }}"
}
}
}
}
}`),
wantErr: true,
},
}

for name, tc := range testCases {
Expand All @@ -248,7 +361,6 @@ func TestTemplateDryRun(t *testing.T) {
err := manifest.TemplateDryRun(manifest.Secrets)
if tc.wantErr {
assert.Error(err)
fmt.Println(err)
} else {
assert.NoError(err)
}
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/workflows/define-manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ 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.
* `.MarbleRun.CoordinatorIntermediate.Cert`: the intermediate certificate of the Coordinator; see [the public Key infrastructure and certificate authority section](../architecture/security.md#public-key-infrastructure-and-certificate-authority) for more information on how this certificate relates to `.MarbleRun.RootCA`.

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

Expand Down