secure variables: initial state store (#12932)

Implement the core SecureVariable and RootKey structs in memdb,
provide the minimal skeleton for FSM, and a dummy storage and keyring
RPC endpoint.
This commit is contained in:
Tim Gross
2022-05-12 16:29:52 -04:00
parent 6d3e807f36
commit 9b1bea1bc1
10 changed files with 736 additions and 2 deletions

View File

@@ -54,6 +54,10 @@ var msgTypeNames = map[structs.MessageType]string{
structs.ServiceRegistrationUpsertRequestType: "ServiceRegistrationUpsertRequestType",
structs.ServiceRegistrationDeleteByIDRequestType: "ServiceRegistrationDeleteByIDRequestType",
structs.ServiceRegistrationDeleteByNodeIDRequestType: "ServiceRegistrationDeleteByNodeIDRequestType",
structs.SecureVariableUpsertRequestType: "SecureVariableUpsertRequestType",
structs.SecureVariableDeleteRequestType: "SecureVariableDeleteRequestType",
structs.RootKeyMetaUpsertRequestType: "RootKeyMetaUpsertRequestType",
structs.RootKeyMetaDeleteRequestType: "RootKeyMetaDeleteRequestType",
structs.NamespaceUpsertRequestType: "NamespaceUpsertRequestType",
structs.NamespaceDeleteRequestType: "NamespaceDeleteRequestType",
}

29
nomad/encrypter.go Normal file
View File

@@ -0,0 +1,29 @@
package nomad
import "crypto/cipher"
type Encrypter struct {
ciphers map[string]cipher.AEAD // map of key IDs to ciphers
}
func NewEncrypter() *Encrypter {
return &Encrypter{
ciphers: make(map[string]cipher.AEAD),
}
}
// Encrypt takes the serialized map[string][]byte from
// SecureVariable.UnencryptedData, generates an appropriately-sized nonce
// for the algorithm, and encrypts the data with the ciper for the
// CurrentRootKeyID. The buffer returned includes the nonce.
func (e *Encrypter) Encrypt(unencryptedData []byte, keyID string) []byte {
// TODO: actually encrypt!
return unencryptedData
}
// Decrypt takes an encrypted buffer and then root key ID. It extracts
// the nonce, decrypts the content, and returns the cleartext data.
func (e *Encrypter) Decrypt(encryptedData []byte, keyID string) ([]byte, error) {
// TODO: actually decrypt!
return encryptedData, nil
}

View File

@@ -55,6 +55,9 @@ const (
ScalingEventsSnapshot SnapshotType = 19
EventSinkSnapshot SnapshotType = 20
ServiceRegistrationSnapshot SnapshotType = 21
SecureVariablesSnapshot SnapshotType = 22
SecureVariablesQuotaSnapshot SnapshotType = 23
RootKeyMetaSnapshot SnapshotType = 24
// Namespace appliers were moved from enterprise and therefore start at 64
NamespaceSnapshot SnapshotType = 64
@@ -314,6 +317,14 @@ func (n *nomadFSM) Apply(log *raft.Log) interface{} {
return n.applyDeleteServiceRegistrationByID(msgType, buf[1:], log.Index)
case structs.ServiceRegistrationDeleteByNodeIDRequestType:
return n.applyDeleteServiceRegistrationByNodeID(msgType, buf[1:], log.Index)
case structs.SecureVariableUpsertRequestType:
return n.applySecureVariableUpsert(msgType, buf[1:], log.Index)
case structs.SecureVariableDeleteRequestType:
return n.applySecureVariableDelete(msgType, buf[1:], log.Index)
case structs.RootKeyMetaUpsertRequestType:
return n.applyRootKeyMetaUpsert(msgType, buf[1:], log.Index)
case structs.RootKeyMetaDeleteRequestType:
return n.applyRootKeyMetaDelete(msgType, buf[1:], log.Index)
}
// Check enterprise only message types.
@@ -1710,6 +1721,36 @@ func (n *nomadFSM) restoreImpl(old io.ReadCloser, filter *FSMFilter) error {
}
}
case SecureVariablesSnapshot:
variable := new(structs.SecureVariable)
if err := dec.Decode(variable); err != nil {
return err
}
if err := restore.SecureVariablesRestore(variable); err != nil {
return err
}
case SecureVariablesQuotaSnapshot:
quota := new(structs.SecureVariablesQuota)
if err := dec.Decode(quota); err != nil {
return err
}
if err := restore.SecureVariablesQuotaRestore(quota); err != nil {
return err
}
case RootKeyMetaSnapshot:
keyMeta := new(structs.RootKeyMeta)
if err := dec.Decode(keyMeta); err != nil {
return err
}
if err := restore.RootKeyMetaRestore(keyMeta); err != nil {
return err
}
default:
// Check if this is an enterprise only object being restored
restorer, ok := n.enterpriseRestorers[snapType]
@@ -1995,6 +2036,68 @@ func (f *FSMFilter) Include(item interface{}) bool {
return true
}
func (n *nomadFSM) applySecureVariableUpsert(msgType structs.MessageType, buf []byte, index uint64) interface{} {
defer metrics.MeasureSince([]string{"nomad", "fsm", "apply_secure_variable_upsert"}, time.Now())
var req structs.SecureVariablesUpsertRequest
if err := structs.Decode(buf, &req); err != nil {
panic(fmt.Errorf("failed to decode request: %v", err))
}
if err := n.state.UpsertSecureVariables(msgType, index, []*structs.SecureVariable{req.Data}); err != nil {
n.logger.Error("UpsertSecureVariables failed", "error", err)
return err
}
return nil
}
func (n *nomadFSM) applySecureVariableDelete(msgType structs.MessageType, buf []byte, index uint64) interface{} {
defer metrics.MeasureSince([]string{"nomad", "fsm", "apply_secure_variable_delete"}, time.Now())
var req structs.SecureVariablesDeleteRequest
if err := structs.Decode(buf, &req); err != nil {
panic(fmt.Errorf("failed to decode request: %v", err))
}
if err := n.state.DeleteSecureVariables(msgType, index, []string{req.Path}); err != nil {
n.logger.Error("DeleteSecureVariables failed", "error", err)
return err
}
return nil
}
func (n *nomadFSM) applyRootKeyMetaUpsert(msgType structs.MessageType, buf []byte, index uint64) interface{} {
defer metrics.MeasureSince([]string{"nomad", "fsm", "apply_root_key_meta_upsert"}, time.Now())
var req structs.KeyringUpdateRootKeyMetaRequest
if err := structs.Decode(buf, &req); err != nil {
panic(fmt.Errorf("failed to decode request: %v", err))
}
if err := n.state.UpsertRootKeyMeta(index, req.RootKeyMeta); err != nil {
n.logger.Error("UpsertRootKeyMeta failed", "error", err)
return err
}
return nil
}
func (n *nomadFSM) applyRootKeyMetaDelete(msgType structs.MessageType, buf []byte, index uint64) interface{} {
defer metrics.MeasureSince([]string{"nomad", "fsm", "apply_root_key_meta_delete"}, time.Now())
var req structs.KeyringDeleteRootKeyRequest
if err := structs.Decode(buf, &req); err != nil {
panic(fmt.Errorf("failed to decode request: %v", err))
}
if err := n.state.DeleteRootKeyMeta(index, req.KeyID); err != nil {
n.logger.Error("DeleteRootKeyMeta failed", "error", err)
return err
}
return nil
}
func (s *nomadSnapshot) Persist(sink raft.SnapshotSink) error {
defer metrics.MeasureSince([]string{"nomad", "fsm", "persist"}, time.Now())
// Register the nodes
@@ -2103,6 +2206,18 @@ func (s *nomadSnapshot) Persist(sink raft.SnapshotSink) error {
sink.Cancel()
return err
}
if err := s.persistSecureVariables(sink, encoder); err != nil {
sink.Cancel()
return err
}
if err := s.persistSecureVariablesQuotas(sink, encoder); err != nil {
sink.Cancel()
return err
}
if err := s.persistRootKeyMeta(sink, encoder); err != nil {
sink.Cancel()
return err
}
return nil
}
@@ -2661,6 +2776,75 @@ func (s *nomadSnapshot) persistServiceRegistrations(sink raft.SnapshotSink,
}
}
func (s *nomadSnapshot) persistSecureVariables(sink raft.SnapshotSink,
encoder *codec.Encoder) error {
ws := memdb.NewWatchSet()
variables, err := s.snap.SecureVariables(ws)
if err != nil {
return err
}
for {
raw := variables.Next()
if raw == nil {
break
}
variable := raw.(*structs.SecureVariable)
sink.Write([]byte{byte(SecureVariablesSnapshot)})
if err := encoder.Encode(variable); err != nil {
return err
}
}
return nil
}
func (s *nomadSnapshot) persistSecureVariablesQuotas(sink raft.SnapshotSink,
encoder *codec.Encoder) error {
ws := memdb.NewWatchSet()
quotas, err := s.snap.SecureVariablesQuotas(ws)
if err != nil {
return err
}
for {
raw := quotas.Next()
if raw == nil {
break
}
dirEntry := raw.(*structs.SecureVariablesQuota)
sink.Write([]byte{byte(SecureVariablesQuotaSnapshot)})
if err := encoder.Encode(dirEntry); err != nil {
return err
}
}
return nil
}
func (s *nomadSnapshot) persistRootKeyMeta(sink raft.SnapshotSink,
encoder *codec.Encoder) error {
ws := memdb.NewWatchSet()
keys, err := s.snap.RootKeyMetas(ws)
if err != nil {
return err
}
for {
raw := keys.Next()
if raw == nil {
break
}
key := raw.(*structs.RootKeyMeta)
sink.Write([]byte{byte(RootKeyMetaSnapshot)})
if err := encoder.Encode(key); err != nil {
return err
}
}
return nil
}
// Release is a no-op, as we just need to GC the pointer
// to the state store snapshot. There is nothing to explicitly
// cleanup.

95
nomad/keyring_endpoint.go Normal file
View File

@@ -0,0 +1,95 @@
package nomad
import (
"time"
metrics "github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/nomad/structs"
)
// KeyRing endpoint serves RPCs for secure variables key management
type KeyRing struct {
srv *Server
logger hclog.Logger
encrypter *Encrypter
}
func (k *KeyRing) Rotate(args *structs.KeyringRotateRootKeyRequest, reply *structs.KeyringRotateRootKeyResponse) error {
if done, err := k.srv.forward("KeyRing.Rotate", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "keyring", "rotate"}, time.Now())
// TODO: allow for servers to force rotation as well
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation; this just silences the structcheck lint
for keyID := range k.encrypter.ciphers {
k.logger.Trace("TODO", "key", keyID)
}
return nil
}
func (k *KeyRing) List(args *structs.KeyringListRootKeyMetaRequest, reply *structs.KeyringListRootKeyMetaResponse) error {
if done, err := k.srv.forward("KeyRing.List", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "keyring", "list"}, time.Now())
// TODO: probably need to allow for servers to list keys as well, to support replication?
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}
func (k *KeyRing) Update(args *structs.KeyringUpdateRootKeyRequest, reply *structs.KeyringUpdateRootKeyResponse) error {
if done, err := k.srv.forward("KeyRing.Update", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "keyring", "update"}, time.Now())
// TODO: need to allow for servers to update keys as well, to support replication
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}
func (k *KeyRing) Delete(args *structs.KeyringDeleteRootKeyRequest, reply *structs.KeyringDeleteRootKeyResponse) error {
if done, err := k.srv.forward("KeyRing.Delete", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "keyring", "delete"}, time.Now())
// TODO: need to allow for servers to delete keys as well, to support replication
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}

View File

@@ -0,0 +1,104 @@
package nomad
import (
"bytes"
"encoding/gob"
"time"
metrics "github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/nomad/structs"
)
// SecureVariables endpoint serves RPCs for storing and retrieving
// encrypted variables
type SecureVariables struct {
srv *Server
logger hclog.Logger
encrypter *Encrypter
}
func (sv *SecureVariables) Create(args *structs.SecureVariablesUpsertRequest, reply *structs.SecureVariablesUpsertResponse) error {
if done, err := sv.srv.forward("SecureVariables.Create", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "secure_variables", "create"}, time.Now())
// TODO: implement real ACL checks
if aclObj, err := sv.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
sv.logger.Trace("TODO") // silences structcheck lint
// TODO: placeholder for serialization and encryption
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(args.Data.UnencryptedData)
if err != nil {
return err
}
args.Data.EncryptedData.Data = sv.encrypter.Encrypt(buf.Bytes(), "TODO")
return nil
}
func (sv *SecureVariables) List(args *structs.SecureVariablesListRequest, reply *structs.SecureVariablesListResponse) error {
if done, err := sv.srv.forward("SecureVariables.List", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "secure_variables", "list"}, time.Now())
// TODO: implement real ACL checks
if aclObj, err := sv.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}
func (sv *SecureVariables) Read(args *structs.SecureVariablesReadRequest, reply *structs.SecureVariablesReadResponse) error {
if done, err := sv.srv.forward("SecureVariables.Read", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "secure_variables", "read"}, time.Now())
// TODO: implement real ACL checks
if aclObj, err := sv.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}
func (sv *SecureVariables) Update(args *structs.SecureVariablesUpsertRequest, reply *structs.SecureVariablesUpsertResponse) error {
if done, err := sv.srv.forward("SecureVariables.Update", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "secure_variables", "update"}, time.Now())
// TODO: implement real ACL checks
if aclObj, err := sv.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// TODO: implementation
return nil
}

View File

@@ -12,8 +12,11 @@ import (
const (
tableIndex = "index"
TableNamespaces = "namespaces"
TableServiceRegistrations = "service_registrations"
TableNamespaces = "namespaces"
TableServiceRegistrations = "service_registrations"
TableSecureVariables = "secure_variables"
TableSecureVariablesQuotas = "secure_variables_quota"
TableRootKeyMeta = "secure_variables_root_key_meta"
)
const (
@@ -70,6 +73,9 @@ func init() {
scalingEventTableSchema,
namespaceTableSchema,
serviceRegistrationsTableSchema,
secureVariablesTableSchema,
secureVariablesQuotasTableSchema,
secureVariablesRootKeyMetaSchema,
}...)
}
@@ -1202,3 +1208,66 @@ func serviceRegistrationsTableSchema() *memdb.TableSchema {
},
}
}
// secureVariablesTableSchema returns the MemDB schema for Nomad
// secure variables.
func secureVariablesTableSchema() *memdb.TableSchema {
return &memdb.TableSchema{
Name: TableSecureVariables,
Indexes: map[string]*memdb.IndexSchema{
indexID: {
Name: indexID,
AllowMissing: false,
Unique: true,
Indexer: &memdb.CompoundIndex{
Indexes: []memdb.Indexer{
&memdb.StringFieldIndex{
Field: "Namespace",
},
&memdb.StringFieldIndex{
Field: "Path",
},
},
},
},
},
}
}
// secureVariablesQuotasTableSchema returns the MemDB schema for Nomad
// secure variables quotas tracking
func secureVariablesQuotasTableSchema() *memdb.TableSchema {
return &memdb.TableSchema{
Name: TableSecureVariablesQuotas,
Indexes: map[string]*memdb.IndexSchema{
indexID: {
Name: indexID,
AllowMissing: false,
Unique: true,
Indexer: &memdb.StringFieldIndex{
Field: "Namespace",
Lowercase: true,
},
},
},
}
}
// secureVariablesRootKeyMetaSchema returns the MemDB schema for Nomad
// secure variables root keys
func secureVariablesRootKeyMetaSchema() *memdb.TableSchema {
return &memdb.TableSchema{
Name: TableRootKeyMeta,
Indexes: map[string]*memdb.IndexSchema{
indexID: {
Name: indexID,
AllowMissing: false,
Unique: true,
Indexer: &memdb.StringFieldIndex{
Field: "KeyID",
Lowercase: true,
},
},
},
}
}

View File

@@ -6627,3 +6627,59 @@ func (s *StateSnapshot) DenormalizeAllocationDiffSlice(allocDiffs []*structs.All
func getPreemptedAllocDesiredDescription(preemptedByAllocID string) string {
return fmt.Sprintf("Preempted by alloc ID %v", preemptedByAllocID)
}
// SecureVariables queries all the variables and is used only for
// snapshot/restore and key rotation
func (s *StateStore) SecureVariables(ws memdb.WatchSet) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
iter, err := txn.Get(TableSecureVariables, indexID)
if err != nil {
return nil, err
}
ws.Add(iter.WatchCh())
return iter, nil
}
func (s *StateStore) UpsertSecureVariables(msgType structs.MessageType, index uint64, dirEntries []*structs.SecureVariable) error {
return nil
}
func (s *StateStore) DeleteSecureVariables(msgType structs.MessageType, index uint64, paths []string) error {
return nil
}
// SecureVariablesQuotas queries all the quotas and is used only for
// snapshot/restore and key rotation
func (s *StateStore) SecureVariablesQuotas(ws memdb.WatchSet) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
iter, err := txn.Get(TableSecureVariablesQuotas, indexID)
if err != nil {
return nil, err
}
ws.Add(iter.WatchCh())
return iter, nil
}
func (s *StateStore) UpsertRootKeyMeta(index uint64, rootKeyMeta *structs.RootKeyMeta) error {
return nil
}
func (s *StateStore) DeleteRootKeyMeta(index uint64, keyID string) error {
return nil
}
func (s *StateStore) RootKeyMetas(ws memdb.WatchSet) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
iter, err := txn.Get(TableRootKeyMeta, indexID)
if err != nil {
return nil, err
}
ws.Add(iter.WatchCh())
return iter, nil
}

View File

@@ -197,3 +197,30 @@ func (r *StateRestore) ServiceRegistrationRestore(service *structs.ServiceRegist
}
return nil
}
// SecureVariablesRestore is used to restore a single secure variable
// into the secure_variables table.
func (r *StateRestore) SecureVariablesRestore(variable *structs.SecureVariable) error {
if err := r.txn.Insert(TableSecureVariables, variable); err != nil {
return fmt.Errorf("secure variable insert failed: %v", err)
}
return nil
}
// SecureVariablesQuotaRestore is used to restore a single secure variable quota
// into the secure_variables_quota table.
func (r *StateRestore) SecureVariablesQuotaRestore(quota *structs.SecureVariablesQuota) error {
if err := r.txn.Insert(TableSecureVariablesQuotas, quota); err != nil {
return fmt.Errorf("secure variable quota insert failed: %v", err)
}
return nil
}
// RootKeyMetaQuotaRestore is used to restore a single root key meta
// into the secure_variables_root_key_meta table.
func (r *StateRestore) RootKeyMetaRestore(quota *structs.RootKeyMeta) error {
if err := r.txn.Insert(TableRootKeyMeta, quota); err != nil {
return fmt.Errorf("root key meta insert failed: %v", err)
}
return nil
}

View File

@@ -0,0 +1,162 @@
package structs
import "time"
// SecureVariable is the metadata envelope for a Secure Variable
type SecureVariable struct {
Namespace string
Path string
CreateTime time.Time
CreateIndex uint64
ModifyIndex uint64
ModifyTime time.Time
// reserved for post-1.4.0 work
// LockIndex uint64
// Session string
// DeletedAt time.Time
// Version uint64
// CustomMetaData map[string]string
EncryptedData *SecureVariableData // removed during serialization
UnencryptedData map[string]string // empty until serialized
}
// SecureVariableData is the secret data for a Secure Variable
type SecureVariableData struct {
Data []byte // includes nonce
KeyID string // ID of root key used to encrypt this entry
}
// SecureVariablesQuota is used to track the total size of secure
// variables entries per namespace. The total length of
// SecureVariable.EncryptedData will be added to the SecureVariablesQuota
// table in the same transaction as a write, update, or delete.
type SecureVariablesQuota struct {
Namespace string
Size uint64
CreateIndex uint64
ModifyIndex uint64
}
type SecureVariablesUpsertRequest struct {
Data *SecureVariable
WriteRequest
}
type SecureVariablesUpsertResponse struct {
WriteMeta
}
type SecureVariablesListRequest struct {
// TODO: do we need any fields here?
QueryOptions
}
type SecureVariablesListResponse struct {
Data []*SecureVariable
QueryMeta
}
type SecureVariablesReadRequest struct {
Path string
QueryOptions
}
type SecureVariablesReadResponse struct {
Data *SecureVariable
QueryMeta
}
type SecureVariablesDeleteRequest struct {
Path string
WriteRequest
}
type SecureVariablesDeleteResponse struct {
WriteMeta
}
// RootKey is used to encrypt and decrypt secure variables. It is
// never stored in raft.
type RootKey struct {
Meta RootKeyMeta
Key []byte // serialized to keystore as base64 blob
}
// RootKeyMeta is the metadata used to refer to a RootKey. It is
// stored in raft.
type RootKeyMeta struct {
Active bool
KeyID string // UUID
Algorithm EncryptionAlgorithm
EncryptionsCount uint64
CreateTime time.Time
CreateIndex uint64
ModifyIndex uint64
}
// EncryptionAlgorithm chooses which algorithm is used for
// encrypting / decrypting entries with this key
type EncryptionAlgorithm string
const (
EncryptionAlgorithmXChaCha20 EncryptionAlgorithm = "xchacha20"
EncryptionAlgorithmAES256GCM EncryptionAlgorithm = "aes256-gcm"
)
type KeyringRotateRootKeyRequest struct {
Algorithm EncryptionAlgorithm
Full bool
WriteRequest
}
// KeyringRotateRootKeyResponse returns the full key metadata
type KeyringRotateRootKeyResponse struct {
Key *RootKeyMeta
WriteMeta
}
type KeyringListRootKeyMetaRequest struct {
// TODO: do we need any fields here?
QueryOptions
}
type KeyringListRootKeyMetaResponse struct {
Keys []*RootKeyMeta
QueryMeta
}
// KeyringUpdateRootKeyRequest is used internally for key replication
// only and for keyring restores. The RootKeyMeta will be extracted
// for applying to the FSM with the KeyringUpdateRootKeyMetaRequest
// (see below)
type KeyringUpdateRootKeyRequest struct {
RootKey *RootKey
WriteRequest
}
type KeyringUpdateRootKeyResponse struct {
WriteMeta
}
// KeyringUpdateRootKeyMetaRequest is used internally for key
// replication so that we have a request wrapper for writing the
// metadata to the FSM without including the key material
type KeyringUpdateRootKeyMetaRequest struct {
RootKeyMeta *RootKeyMeta
WriteRequest
}
type KeyringUpdateRootKeyMetaResponse struct {
WriteMeta
}
type KeyringDeleteRootKeyRequest struct {
KeyID string
WriteRequest
}
type KeyringDeleteRootKeyResponse struct {
WriteMeta
}

View File

@@ -108,6 +108,10 @@ const (
ServiceRegistrationUpsertRequestType MessageType = 47
ServiceRegistrationDeleteByIDRequestType MessageType = 48
ServiceRegistrationDeleteByNodeIDRequestType MessageType = 49
SecureVariableUpsertRequestType MessageType = 50
SecureVariableDeleteRequestType MessageType = 51
RootKeyMetaUpsertRequestType MessageType = 52
RootKeyMetaDeleteRequestType MessageType = 53
// Namespace types were moved from enterprise and therefore start at 64
NamespaceUpsertRequestType MessageType = 64