open-nomad/nomad/structs/extensions.go
Tim Gross 5a85d96322 remove end-user algorithm selection (#13190)
After internal design review, we decided to remove exposing algorithm
choice to the end-user for the initial release. We'll solve nonce
rotation by forcing rotations automatically on key GC (in a core job,
not included in this changeset). Default to AES-256 GCM for the
following criteria:

* faster implementation when hardware acceleration is available
* FIPS compliant
* implementation in pure go
* post-quantum resistance

Also fixed a bug in the decoding from keystore and switched to a 
harder-to-misuse encoding method.
2022-07-11 13:34:04 -04:00

100 lines
3.1 KiB
Go

package structs
import (
"encoding/base64"
"reflect"
)
var (
// extendedTypes is a mapping of extended types to their extension function
// TODO: the duplicates could be simplified by looking up the base type in the case of a pointer type in ConvertExt
extendedTypes = map[reflect.Type]extendFunc{
reflect.TypeOf(Node{}): nodeExt,
reflect.TypeOf(&Node{}): nodeExt,
reflect.TypeOf(CSIVolume{}): csiVolumeExt,
reflect.TypeOf(&CSIVolume{}): csiVolumeExt,
reflect.TypeOf(&RootKey{}): rootKeyExt,
reflect.TypeOf(RootKey{}): rootKeyExt,
}
)
// nodeExt ensures the node is sanitized and adds the legacy field .Drain back to encoded Node objects
func nodeExt(v interface{}) interface{} {
node := v.(*Node).Sanitize()
// transform to a struct with inlined Node fields plus the Drain field
// - using defined type (not an alias!) EmbeddedNode gives us free conversion to a distinct type
// - distinct type prevents this encoding extension from being called recursively/infinitely on the embedding
// - pointers mean the conversion function doesn't have to make a copy during conversion
type EmbeddedNode Node
return &struct {
*EmbeddedNode
Drain bool
}{
EmbeddedNode: (*EmbeddedNode)(node),
Drain: node != nil && node.DrainStrategy != nil,
}
}
func csiVolumeExt(v interface{}) interface{} {
vol := v.(*CSIVolume)
type EmbeddedCSIVolume CSIVolume
allocCount := len(vol.ReadAllocs) + len(vol.WriteAllocs)
apiVol := &struct {
*EmbeddedCSIVolume
Allocations []*AllocListStub
}{
EmbeddedCSIVolume: (*EmbeddedCSIVolume)(vol),
Allocations: make([]*AllocListStub, 0, allocCount),
}
// WriteAllocs and ReadAllocs will only ever contain the Allocation ID,
// with a null value for the Allocation; these IDs are mapped to
// allocation stubs in the Allocations field. This indirection is so the
// API can support both the UI and CLI consumer in a safely backwards
// compatible way
for _, a := range vol.ReadAllocs {
if a != nil {
apiVol.ReadAllocs[a.ID] = nil
apiVol.Allocations = append(apiVol.Allocations, a.Stub(nil))
}
}
for _, a := range vol.WriteAllocs {
if a != nil {
apiVol.WriteAllocs[a.ID] = nil
apiVol.Allocations = append(apiVol.Allocations, a.Stub(nil))
}
}
// MountFlags can contain secrets, so we always redact it but want
// to show the user that we have the value
if vol.MountOptions != nil && len(vol.MountOptions.MountFlags) > 0 {
apiVol.MountOptions.MountFlags = []string{"[REDACTED]"}
}
// would be better not to have at all but left in and redacted for
// backwards compatibility with the existing API
apiVol.Secrets = nil
return apiVol
}
// rootKeyExt safely serializes a RootKey by base64 encoding the key
// material and extracting the metadata stub. We only store the root
// key in the keystore and never in raft or return it via the API, so
// by having this extension as the default we make it slightly harder
// to misuse.
func rootKeyExt(v interface{}) interface{} {
key := v.(*RootKey)
encodedKey := base64.StdEncoding.EncodeToString(key.Key)
return &struct {
Meta *RootKeyMetaStub
Key string
}{
Meta: key.Meta.Stub(),
Key: encodedKey,
}
}