mirror of
				https://github.com/matter-labs/vault-auth-tee.git
				synced 2025-10-28 21:44:09 +01:00 
			
		
		
		
	 94c86df4d5
			
		
	
	
		94c86df4d5
		
			
		
	
	
	
	
		
			
			Add the ability to clear the `sgx_mrsigner` and `sgx_mrenclave` field. Otherwise we cannot switch from `sgx_mrenclave` to `sgx_mrsigner` based authentication. Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
		
			
				
	
	
		
			333 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // SPDX-License-Identifier: MPL-2.0
 | |
| // Copyright (c) HashiCorp, Inc.
 | |
| // Copyright (c) Matter Labs
 | |
| 
 | |
| package vault_auth_tee
 | |
| 
 | |
| import "C"
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/hex"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/sdk/framework"
 | |
| 	"github.com/hashicorp/vault/sdk/helper/tokenutil"
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| func pathListTees(b *backend) *framework.Path {
 | |
| 	return &framework.Path{
 | |
| 		Pattern: "tees/?",
 | |
| 
 | |
| 		DisplayAttrs: &framework.DisplayAttributes{
 | |
| 			OperationPrefix: operationPrefixTee,
 | |
| 			OperationSuffix: "tees",
 | |
| 			Navigation:      true,
 | |
| 			ItemType:        "Tee",
 | |
| 		},
 | |
| 
 | |
| 		Callbacks: map[logical.Operation]framework.OperationFunc{
 | |
| 			logical.ListOperation: b.pathTeeList,
 | |
| 		},
 | |
| 
 | |
| 		HelpSynopsis:    pathTeeHelpSyn,
 | |
| 		HelpDescription: pathTeeHelpDesc,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func pathTees(b *backend) *framework.Path {
 | |
| 	p := &framework.Path{
 | |
| 		Pattern: "tees/" + framework.GenericNameRegex("name"),
 | |
| 
 | |
| 		DisplayAttrs: &framework.DisplayAttributes{
 | |
| 			OperationPrefix: operationPrefixTee,
 | |
| 			OperationSuffix: "tee",
 | |
| 			Action:          "Create",
 | |
| 			ItemType:        "Tee",
 | |
| 		},
 | |
| 
 | |
| 		Fields: map[string]*framework.FieldSchema{
 | |
| 			"name": {
 | |
| 				Type:        framework.TypeString,
 | |
| 				Description: "The name of the Tee, which passes remote attestation verification",
 | |
| 			},
 | |
| 
 | |
| 			"types": {
 | |
| 				Type:        framework.TypeCommaStringSlice,
 | |
| 				Description: "The types of the TEE.",
 | |
| 			},
 | |
| 
 | |
| 			"sgx_mrsigner": {
 | |
| 				Type:        framework.TypeString,
 | |
| 				Description: `The SGX mrsigner hex value to check the attestation report against`,
 | |
| 			},
 | |
| 
 | |
| 			"sgx_mrenclave": {
 | |
| 				Type:        framework.TypeString,
 | |
| 				Description: `The SGX mrenclave hex value to check the attestation report against`,
 | |
| 			},
 | |
| 
 | |
| 			"sgx_isv_prodid": {
 | |
| 				Type:        framework.TypeInt,
 | |
| 				Description: `The SGX isv_prodid value to check the attestation report against`,
 | |
| 			},
 | |
| 
 | |
| 			"sgx_min_isv_svn": {
 | |
| 				Type:        framework.TypeInt,
 | |
| 				Description: `The SGX minimum isv_svn value to check the attestation report against`,
 | |
| 			},
 | |
| 
 | |
| 			"sgx_allowed_tcb_levels": {
 | |
| 				Type: framework.TypeCommaStringSlice,
 | |
| 				Description: `A comma seperated list of allowed SGX TCB states.
 | |
| Allowed values are: ConfigNeeded, OutOfDate, OutOfDateConfigNeeded, SwHardeningNeeded, ConfigAndSwHardeningNeeded`,
 | |
| 			},
 | |
| 
 | |
| 			"display_name": {
 | |
| 				Type:        framework.TypeString,
 | |
| 				Description: `The display name to use for clients using this certificate.`,
 | |
| 			},
 | |
| 		},
 | |
| 
 | |
| 		Callbacks: map[logical.Operation]framework.OperationFunc{
 | |
| 			logical.DeleteOperation: b.pathTeeDelete,
 | |
| 			logical.ReadOperation:   b.pathTeeRead,
 | |
| 			logical.UpdateOperation: b.pathTeeWrite,
 | |
| 		},
 | |
| 
 | |
| 		HelpSynopsis:    pathTeeHelpSyn,
 | |
| 		HelpDescription: pathTeeHelpDesc,
 | |
| 	}
 | |
| 
 | |
| 	tokenutil.AddTokenFields(p.Fields)
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| func (b *backend) Tee(ctx context.Context, s logical.Storage, n string) (*TeeEntry, error) {
 | |
| 	entry, err := s.Get(ctx, "tee/"+strings.ToLower(n))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if entry == nil {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	var result TeeEntry
 | |
| 	if err := entry.DecodeJSON(&result); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &result, nil
 | |
| }
 | |
| 
 | |
| func (b *backend) pathTeeDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
 | |
| 	err := req.Storage.Delete(ctx, "tee/"+strings.ToLower(d.Get("name").(string)))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| func (b *backend) pathTeeList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
 | |
| 	tees, err := req.Storage.List(ctx, "tee/")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return logical.ListResponse(tees), nil
 | |
| }
 | |
| 
 | |
| func (b *backend) pathTeeRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
 | |
| 	tee, err := b.Tee(ctx, req.Storage, strings.ToLower(d.Get("name").(string)))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if tee == nil {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	data := map[string]interface{}{
 | |
| 		"display_name":           tee.DisplayName,
 | |
| 		"types":                  tee.Types,
 | |
| 		"sgx_mrsigner":           tee.SgxMrsigner,
 | |
| 		"sgx_mrenclave":          tee.SgxMrenclave,
 | |
| 		"sgx_isv_prodid":         tee.SgxIsvProdid,
 | |
| 		"sgx_min_isv_svn":        tee.SgxMinIsvSvn,
 | |
| 		"sgx_allowed_tcb_levels": tee.SgxAllowedTcbLevels,
 | |
| 	}
 | |
| 	tee.PopulateTokenData(data)
 | |
| 
 | |
| 	return &logical.Response{
 | |
| 		Data: data,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (b *backend) pathTeeWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
 | |
| 	name := strings.ToLower(d.Get("name").(string))
 | |
| 
 | |
| 	tee, err := b.Tee(ctx, req.Storage, name)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if tee == nil {
 | |
| 		tee = &TeeEntry{
 | |
| 			Name: name,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Get non tokenutil fields
 | |
| 	if displayNameRaw, ok := d.GetOk("display_name"); ok {
 | |
| 		tee.DisplayName = displayNameRaw.(string)
 | |
| 	}
 | |
| 
 | |
| 	if teeTypes, ok := d.GetOk("types"); ok {
 | |
| 		tee.Types = make(map[string]bool)
 | |
| 		handled := make(map[string]bool)
 | |
| 		for _, t := range teeTypes.([]string) {
 | |
| 			// only SGX supported for now
 | |
| 			if _, ok = handled[t]; ok {
 | |
| 				return logical.ErrorResponse(fmt.Sprintf("duplicate TEE type `%s`", t)), nil
 | |
| 			}
 | |
| 			if t == "sgx" {
 | |
| 				tee.Types[t] = true
 | |
| 				handled[t] = true
 | |
| 				response, err := handleSGXConfig(d, tee)
 | |
| 				if response != nil || err != nil {
 | |
| 					return response, err
 | |
| 				}
 | |
| 			} else {
 | |
| 				return logical.ErrorResponse(fmt.Sprintf("invalid TEE type `%s`", t)), nil
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		return logical.ErrorResponse("missing TEE types"), nil
 | |
| 	}
 | |
| 
 | |
| 	// Get tokenutil fields
 | |
| 	if err := tee.ParseTokenFields(req, d); err != nil {
 | |
| 		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
 | |
| 	}
 | |
| 
 | |
| 	var resp logical.Response
 | |
| 
 | |
| 	systemDefaultTTL := b.System().DefaultLeaseTTL()
 | |
| 	if tee.TokenTTL > systemDefaultTTL {
 | |
| 		resp.AddWarning(fmt.Sprintf("Given ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenTTL/time.Second, systemDefaultTTL/time.Second))
 | |
| 	}
 | |
| 	systemMaxTTL := b.System().MaxLeaseTTL()
 | |
| 	if tee.TokenMaxTTL > systemMaxTTL {
 | |
| 		resp.AddWarning(fmt.Sprintf("Given max_ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenMaxTTL/time.Second, systemMaxTTL/time.Second))
 | |
| 	}
 | |
| 	if tee.TokenMaxTTL != 0 && tee.TokenTTL > tee.TokenMaxTTL {
 | |
| 		return logical.ErrorResponse("ttl should be shorter than max_ttl"), nil
 | |
| 	}
 | |
| 	if tee.TokenPeriod > systemMaxTTL {
 | |
| 		resp.AddWarning(fmt.Sprintf("Given period of %d seconds is greater than the backend's maximum TTL of %d seconds", tee.TokenPeriod/time.Second, systemMaxTTL/time.Second))
 | |
| 	}
 | |
| 
 | |
| 	// Default the display name to the certificate name if not given
 | |
| 	if tee.DisplayName == "" {
 | |
| 		tee.DisplayName = name
 | |
| 	}
 | |
| 
 | |
| 	// Store it
 | |
| 	entry, err := logical.StorageEntryJSON("tee/"+name, tee)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if err := req.Storage.Put(ctx, entry); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if len(resp.Warnings) == 0 {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	return &resp, nil
 | |
| }
 | |
| 
 | |
| func handleSGXConfig(d *framework.FieldData, tee *TeeEntry) (*logical.Response, error) {
 | |
| 	if sgxMrsignerRaw, ok := d.GetOk("sgx_mrsigner"); ok {
 | |
| 		tee.SgxMrsigner = strings.ToLower(sgxMrsignerRaw.(string))
 | |
| 		if tee.SgxMrsigner != "" {
 | |
| 			b, err := hex.DecodeString(tee.SgxMrsigner)
 | |
| 			if err != nil || len(b) != 32 {
 | |
| 				return logical.ErrorResponse("`sgx_mrsigner` must be 32 byte hex encoded"), nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if sgxMrenclaveRaw, ok := d.GetOk("sgx_mrenclave"); ok {
 | |
| 		tee.SgxMrenclave = strings.ToLower(sgxMrenclaveRaw.(string))
 | |
| 		if tee.SgxMrenclave != "" {
 | |
| 			b, err := hex.DecodeString(tee.SgxMrenclave)
 | |
| 			if err != nil || len(b) != 32 {
 | |
| 				return logical.ErrorResponse("`sgx_mrenclave` must be 32 byte hex encoded"), nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if tee.SgxMrsigner == "" && tee.SgxMrenclave == "" {
 | |
| 		return logical.ErrorResponse("either `sgx_mrsigner` or `sgx_mrenclave` must be set"), nil
 | |
| 	}
 | |
| 
 | |
| 	if sgxIsvProdidRaw, ok := d.GetOk("sgx_isv_prodid"); ok {
 | |
| 		tee.SgxIsvProdid = sgxIsvProdidRaw.(int)
 | |
| 	}
 | |
| 
 | |
| 	if sgxMinIsvSvnRaw, ok := d.GetOk("sgx_min_isv_svn"); ok {
 | |
| 		tee.SgxMinIsvSvn = sgxMinIsvSvnRaw.(int)
 | |
| 	}
 | |
| 
 | |
| 	if sgxAllowedTcbLevelsRaw, ok := d.GetOk("sgx_allowed_tcb_levels"); ok {
 | |
| 		tee.SgxAllowedTcbLevels = make(map[SgxQlQvResult]bool)
 | |
| 		for _, v := range sgxAllowedTcbLevelsRaw.([]string) {
 | |
| 			var state SgxQlQvResult
 | |
| 			switch v {
 | |
| 			case "Ok":
 | |
| 				state = SgxQlQvResultOk
 | |
| 			case "ConfigNeeded":
 | |
| 				state = SgxQlQvResultConfigNeeded
 | |
| 			case "OutOfDate":
 | |
| 				state = SgxQlQvResultOutOfDate
 | |
| 			case "OutOfDateConfigNeeded":
 | |
| 				state = SgxQlQvResultOutOfDateConfigNeeded
 | |
| 			case "SwHardeningNeeded":
 | |
| 				state = SgxQlQvResultSwHardeningNeeded
 | |
| 			case "ConfigAndSwHardeningNeeded":
 | |
| 				state = SgxQlQvResultConfigAndSwHardeningNeeded
 | |
| 			default:
 | |
| 				return logical.ErrorResponse("invalid sgx_allowed_tcb_levels value"), logical.ErrInvalidRequest
 | |
| 			}
 | |
| 			tee.SgxAllowedTcbLevels[state] = true
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| type TeeEntry struct {
 | |
| 	tokenutil.TokenParams
 | |
| 
 | |
| 	Name                string
 | |
| 	DisplayName         string
 | |
| 	Types               map[string]bool
 | |
| 	SgxMrsigner         string
 | |
| 	SgxMrenclave        string
 | |
| 	SgxIsvProdid        int
 | |
| 	SgxMinIsvSvn        int
 | |
| 	SgxAllowedTcbLevels map[SgxQlQvResult]bool
 | |
| }
 | |
| 
 | |
| const pathTeeHelpSyn = `
 | |
| Manage TEE remote attestation parameters used for authentication.`
 | |
| 
 | |
| const pathTeeHelpDesc = `
 | |
| This endpoint allows you to create, read, update, and delete TEEs
 | |
| that are allowed to authenticate.
 | |
| 
 | |
| Deleting a TEE will not revoke auth for prior authenticated connections.
 | |
| To do this, do a revoke on "login". If you don't need to revoke login immediately,
 | |
| then the next renew will cause the lease to expire.
 | |
| `
 |