mirror of
https://github.com/matter-labs/vault-auth-tee.git
synced 2025-07-21 15:53:55 +02:00
feat: initial commit
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
commit
c2411a45a7
29 changed files with 6542 additions and 0 deletions
331
tee/path_tees.go
Normal file
331
tee/path_tees.go
Normal file
|
@ -0,0 +1,331 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// Copyright (c) Matter Labs
|
||||
|
||||
package 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"
|
||||
|
||||
"github.com/matter-labs/vault-auth-tee/ratee"
|
||||
)
|
||||
|
||||
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 && sgxMrsignerRaw.(string) != "" {
|
||||
tee.SgxMrsigner = strings.ToLower(sgxMrsignerRaw.(string))
|
||||
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 && sgxMrenclaveRaw.(string) != "" {
|
||||
tee.SgxMrenclave = strings.ToLower(sgxMrenclaveRaw.(string))
|
||||
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[ratee.SgxQlQvResult]bool)
|
||||
for _, v := range sgxAllowedTcbLevelsRaw.([]string) {
|
||||
var state ratee.SgxQlQvResult
|
||||
switch v {
|
||||
case "Ok":
|
||||
state = ratee.SgxQlQvResultOk
|
||||
case "ConfigNeeded":
|
||||
state = ratee.SgxQlQvResultConfigNeeded
|
||||
case "OutOfDate":
|
||||
state = ratee.SgxQlQvResultOutOfDate
|
||||
case "OutOfDateConfigNeeded":
|
||||
state = ratee.SgxQlQvResultOutOfDateConfigNeeded
|
||||
case "SwHardeningNeeded":
|
||||
state = ratee.SgxQlQvResultSwHardeningNeeded
|
||||
case "ConfigAndSwHardeningNeeded":
|
||||
state = ratee.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[ratee.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.
|
||||
`
|
Loading…
Add table
Add a link
Reference in a new issue