open-vault/sdk/plugin/pb/translation.go
swayne275 335e4c3711
Introduce Logical Unrecoverable Error, Use it in Expiration Manager (#11477)
* build out zombie lease system

* add typo for CI

* undo test CI commit

* time equality test isn't working on CI, so let's see what this does...

* add unrecoverable proto error, make proto, go mod vendor

* zombify leases if unrecoverable error, tests

* test fix: somehow pointer in pointer rx is null after pointer rx called

* tweaks based on roy feedback

* improve zombie errors

* update which errors are unrecoverable

* combine zombie logic

* keep subset of zombie lease in memory
2021-05-03 17:56:06 -06:00

645 lines
15 KiB
Go

package pb
import (
"encoding/json"
"errors"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/hashicorp/vault/sdk/helper/errutil"
"github.com/hashicorp/vault/sdk/helper/parseutil"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/hashicorp/vault/sdk/logical"
)
const (
ErrTypeUnknown uint32 = iota
ErrTypeUserError
ErrTypeInternalError
ErrTypeCodedError
ErrTypeStatusBadRequest
ErrTypeUnsupportedOperation
ErrTypeUnsupportedPath
ErrTypeInvalidRequest
ErrTypePermissionDenied
ErrTypeMultiAuthzPending
ErrTypeUnrecoverable
)
func ProtoErrToErr(e *ProtoError) error {
if e == nil {
return nil
}
var err error
switch e.ErrType {
case ErrTypeUnknown:
err = errors.New(e.ErrMsg)
case ErrTypeUserError:
err = errutil.UserError{Err: e.ErrMsg}
case ErrTypeInternalError:
err = errutil.InternalError{Err: e.ErrMsg}
case ErrTypeCodedError:
err = logical.CodedError(int(e.ErrCode), e.ErrMsg)
case ErrTypeStatusBadRequest:
err = &logical.StatusBadRequest{Err: e.ErrMsg}
case ErrTypeUnsupportedOperation:
err = logical.ErrUnsupportedOperation
case ErrTypeUnsupportedPath:
err = logical.ErrUnsupportedPath
case ErrTypeInvalidRequest:
err = logical.ErrInvalidRequest
case ErrTypePermissionDenied:
err = logical.ErrPermissionDenied
case ErrTypeMultiAuthzPending:
err = logical.ErrMultiAuthzPending
case ErrTypeUnrecoverable:
err = logical.ErrUnrecoverable
}
return err
}
func ErrToProtoErr(e error) *ProtoError {
if e == nil {
return nil
}
pbErr := &ProtoError{
ErrMsg: e.Error(),
ErrType: ErrTypeUnknown,
}
switch e.(type) {
case errutil.UserError:
pbErr.ErrType = ErrTypeUserError
case errutil.InternalError:
pbErr.ErrType = ErrTypeInternalError
case logical.HTTPCodedError:
pbErr.ErrType = ErrTypeCodedError
pbErr.ErrCode = int64(e.(logical.HTTPCodedError).Code())
case *logical.StatusBadRequest:
pbErr.ErrType = ErrTypeStatusBadRequest
}
switch {
case e == logical.ErrUnsupportedOperation:
pbErr.ErrType = ErrTypeUnsupportedOperation
case e == logical.ErrUnsupportedPath:
pbErr.ErrType = ErrTypeUnsupportedPath
case e == logical.ErrInvalidRequest:
pbErr.ErrType = ErrTypeInvalidRequest
case e == logical.ErrPermissionDenied:
pbErr.ErrType = ErrTypePermissionDenied
case e == logical.ErrMultiAuthzPending:
pbErr.ErrType = ErrTypeMultiAuthzPending
case e == logical.ErrUnrecoverable:
pbErr.ErrType = ErrTypeUnrecoverable
}
return pbErr
}
func ErrToString(e error) string {
if e == nil {
return ""
}
return e.Error()
}
func LogicalStorageEntryToProtoStorageEntry(e *logical.StorageEntry) *StorageEntry {
if e == nil {
return nil
}
return &StorageEntry{
Key: e.Key,
Value: e.Value,
SealWrap: e.SealWrap,
}
}
func ProtoStorageEntryToLogicalStorageEntry(e *StorageEntry) *logical.StorageEntry {
if e == nil {
return nil
}
return &logical.StorageEntry{
Key: e.Key,
Value: e.Value,
SealWrap: e.SealWrap,
}
}
func ProtoLeaseOptionsToLogicalLeaseOptions(l *LeaseOptions) (logical.LeaseOptions, error) {
if l == nil {
return logical.LeaseOptions{}, nil
}
t, err := ptypes.Timestamp(l.IssueTime)
return logical.LeaseOptions{
TTL: time.Duration(l.TTL),
Renewable: l.Renewable,
Increment: time.Duration(l.Increment),
IssueTime: t,
MaxTTL: time.Duration(l.MaxTTL),
}, err
}
func LogicalLeaseOptionsToProtoLeaseOptions(l logical.LeaseOptions) (*LeaseOptions, error) {
t, err := ptypes.TimestampProto(l.IssueTime)
if err != nil {
return nil, err
}
return &LeaseOptions{
TTL: int64(l.TTL),
Renewable: l.Renewable,
Increment: int64(l.Increment),
IssueTime: t,
MaxTTL: int64(l.MaxTTL),
}, err
}
func ProtoSecretToLogicalSecret(s *Secret) (*logical.Secret, error) {
if s == nil {
return nil, nil
}
data := map[string]interface{}{}
err := json.Unmarshal([]byte(s.InternalData), &data)
if err != nil {
return nil, err
}
lease, err := ProtoLeaseOptionsToLogicalLeaseOptions(s.LeaseOptions)
if err != nil {
return nil, err
}
return &logical.Secret{
LeaseOptions: lease,
InternalData: data,
LeaseID: s.LeaseID,
}, nil
}
func LogicalSecretToProtoSecret(s *logical.Secret) (*Secret, error) {
if s == nil {
return nil, nil
}
buf, err := json.Marshal(s.InternalData)
if err != nil {
return nil, err
}
lease, err := LogicalLeaseOptionsToProtoLeaseOptions(s.LeaseOptions)
if err != nil {
return nil, err
}
return &Secret{
LeaseOptions: lease,
InternalData: string(buf[:]),
LeaseID: s.LeaseID,
}, err
}
func LogicalRequestToProtoRequest(r *logical.Request) (*Request, error) {
if r == nil {
return nil, nil
}
buf, err := json.Marshal(r.Data)
if err != nil {
return nil, err
}
secret, err := LogicalSecretToProtoSecret(r.Secret)
if err != nil {
return nil, err
}
auth, err := LogicalAuthToProtoAuth(r.Auth)
if err != nil {
return nil, err
}
headers := map[string]*Header{}
for k, v := range r.Headers {
headers[k] = &Header{Header: v}
}
return &Request{
ID: r.ID,
ReplicationCluster: r.ReplicationCluster,
Operation: string(r.Operation),
Path: r.Path,
Data: string(buf[:]),
Secret: secret,
Auth: auth,
Headers: headers,
ClientToken: r.ClientToken,
ClientTokenAccessor: r.ClientTokenAccessor,
DisplayName: r.DisplayName,
MountPoint: r.MountPoint,
MountType: r.MountType,
MountAccessor: r.MountAccessor,
WrapInfo: LogicalRequestWrapInfoToProtoRequestWrapInfo(r.WrapInfo),
ClientTokenRemainingUses: int64(r.ClientTokenRemainingUses),
Connection: LogicalConnectionToProtoConnection(r.Connection),
EntityID: r.EntityID,
PolicyOverride: r.PolicyOverride,
Unauthenticated: r.Unauthenticated,
}, nil
}
func ProtoRequestToLogicalRequest(r *Request) (*logical.Request, error) {
if r == nil {
return nil, nil
}
data := map[string]interface{}{}
err := json.Unmarshal([]byte(r.Data), &data)
if err != nil {
return nil, err
}
secret, err := ProtoSecretToLogicalSecret(r.Secret)
if err != nil {
return nil, err
}
auth, err := ProtoAuthToLogicalAuth(r.Auth)
if err != nil {
return nil, err
}
var headers map[string][]string
if len(r.Headers) > 0 {
headers = make(map[string][]string, len(r.Headers))
for k, v := range r.Headers {
headers[k] = v.Header
}
}
return &logical.Request{
ID: r.ID,
ReplicationCluster: r.ReplicationCluster,
Operation: logical.Operation(r.Operation),
Path: r.Path,
Data: data,
Secret: secret,
Auth: auth,
Headers: headers,
ClientToken: r.ClientToken,
ClientTokenAccessor: r.ClientTokenAccessor,
DisplayName: r.DisplayName,
MountPoint: r.MountPoint,
MountType: r.MountType,
MountAccessor: r.MountAccessor,
WrapInfo: ProtoRequestWrapInfoToLogicalRequestWrapInfo(r.WrapInfo),
ClientTokenRemainingUses: int(r.ClientTokenRemainingUses),
Connection: ProtoConnectionToLogicalConnection(r.Connection),
EntityID: r.EntityID,
PolicyOverride: r.PolicyOverride,
Unauthenticated: r.Unauthenticated,
}, nil
}
func LogicalConnectionToProtoConnection(c *logical.Connection) *Connection {
if c == nil {
return nil
}
return &Connection{
RemoteAddr: c.RemoteAddr,
}
}
func ProtoConnectionToLogicalConnection(c *Connection) *logical.Connection {
if c == nil {
return nil
}
return &logical.Connection{
RemoteAddr: c.RemoteAddr,
}
}
func LogicalRequestWrapInfoToProtoRequestWrapInfo(i *logical.RequestWrapInfo) *RequestWrapInfo {
if i == nil {
return nil
}
return &RequestWrapInfo{
TTL: int64(i.TTL),
Format: i.Format,
SealWrap: i.SealWrap,
}
}
func ProtoRequestWrapInfoToLogicalRequestWrapInfo(i *RequestWrapInfo) *logical.RequestWrapInfo {
if i == nil {
return nil
}
return &logical.RequestWrapInfo{
TTL: time.Duration(i.TTL),
Format: i.Format,
SealWrap: i.SealWrap,
}
}
func ProtoResponseToLogicalResponse(r *Response) (*logical.Response, error) {
if r == nil {
return nil, nil
}
secret, err := ProtoSecretToLogicalSecret(r.Secret)
if err != nil {
return nil, err
}
auth, err := ProtoAuthToLogicalAuth(r.Auth)
if err != nil {
return nil, err
}
data := map[string]interface{}{}
err = json.Unmarshal([]byte(r.Data), &data)
if err != nil {
return nil, err
}
wrapInfo, err := ProtoResponseWrapInfoToLogicalResponseWrapInfo(r.WrapInfo)
if err != nil {
return nil, err
}
var headers map[string][]string
if len(r.Headers) > 0 {
headers = make(map[string][]string, len(r.Headers))
for k, v := range r.Headers {
headers[k] = v.Header
}
}
return &logical.Response{
Secret: secret,
Auth: auth,
Data: data,
Redirect: r.Redirect,
Warnings: r.Warnings,
WrapInfo: wrapInfo,
Headers: headers,
}, nil
}
func ProtoResponseWrapInfoToLogicalResponseWrapInfo(i *ResponseWrapInfo) (*wrapping.ResponseWrapInfo, error) {
if i == nil {
return nil, nil
}
t, err := ptypes.Timestamp(i.CreationTime)
if err != nil {
return nil, err
}
return &wrapping.ResponseWrapInfo{
TTL: time.Duration(i.TTL),
Token: i.Token,
Accessor: i.Accessor,
CreationTime: t,
WrappedAccessor: i.WrappedAccessor,
WrappedEntityID: i.WrappedEntityID,
Format: i.Format,
CreationPath: i.CreationPath,
SealWrap: i.SealWrap,
}, nil
}
func LogicalResponseWrapInfoToProtoResponseWrapInfo(i *wrapping.ResponseWrapInfo) (*ResponseWrapInfo, error) {
if i == nil {
return nil, nil
}
t, err := ptypes.TimestampProto(i.CreationTime)
if err != nil {
return nil, err
}
return &ResponseWrapInfo{
TTL: int64(i.TTL),
Token: i.Token,
Accessor: i.Accessor,
CreationTime: t,
WrappedAccessor: i.WrappedAccessor,
WrappedEntityID: i.WrappedEntityID,
Format: i.Format,
CreationPath: i.CreationPath,
SealWrap: i.SealWrap,
}, nil
}
func LogicalResponseToProtoResponse(r *logical.Response) (*Response, error) {
if r == nil {
return nil, nil
}
secret, err := LogicalSecretToProtoSecret(r.Secret)
if err != nil {
return nil, err
}
auth, err := LogicalAuthToProtoAuth(r.Auth)
if err != nil {
return nil, err
}
buf, err := json.Marshal(r.Data)
if err != nil {
return nil, err
}
wrapInfo, err := LogicalResponseWrapInfoToProtoResponseWrapInfo(r.WrapInfo)
if err != nil {
return nil, err
}
headers := map[string]*Header{}
for k, v := range r.Headers {
headers[k] = &Header{Header: v}
}
return &Response{
Secret: secret,
Auth: auth,
Data: string(buf[:]),
Redirect: r.Redirect,
Warnings: r.Warnings,
WrapInfo: wrapInfo,
Headers: headers,
}, nil
}
func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) {
if a == nil {
return nil, nil
}
buf, err := json.Marshal(a.InternalData)
if err != nil {
return nil, err
}
lo, err := LogicalLeaseOptionsToProtoLeaseOptions(a.LeaseOptions)
if err != nil {
return nil, err
}
boundCIDRs := make([]string, len(a.BoundCIDRs))
for i, cidr := range a.BoundCIDRs {
boundCIDRs[i] = cidr.String()
}
return &Auth{
LeaseOptions: lo,
TokenType: uint32(a.TokenType),
InternalData: string(buf[:]),
DisplayName: a.DisplayName,
Policies: a.Policies,
TokenPolicies: a.TokenPolicies,
IdentityPolicies: a.IdentityPolicies,
NoDefaultPolicy: a.NoDefaultPolicy,
Metadata: a.Metadata,
ClientToken: a.ClientToken,
Accessor: a.Accessor,
Period: int64(a.Period),
NumUses: int64(a.NumUses),
EntityID: a.EntityID,
Alias: a.Alias,
GroupAliases: a.GroupAliases,
BoundCIDRs: boundCIDRs,
ExplicitMaxTTL: int64(a.ExplicitMaxTTL),
}, nil
}
func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) {
if a == nil {
return nil, nil
}
data := map[string]interface{}{}
err := json.Unmarshal([]byte(a.InternalData), &data)
if err != nil {
return nil, err
}
lo, err := ProtoLeaseOptionsToLogicalLeaseOptions(a.LeaseOptions)
if err != nil {
return nil, err
}
boundCIDRs, err := parseutil.ParseAddrs(a.BoundCIDRs)
if err != nil {
return nil, err
}
if len(boundCIDRs) == 0 {
// On inbound auths, if auth.BoundCIDRs is empty, it will be nil.
// Let's match that behavior outbound.
boundCIDRs = nil
}
return &logical.Auth{
LeaseOptions: lo,
TokenType: logical.TokenType(a.TokenType),
InternalData: data,
DisplayName: a.DisplayName,
Policies: a.Policies,
TokenPolicies: a.TokenPolicies,
IdentityPolicies: a.IdentityPolicies,
NoDefaultPolicy: a.NoDefaultPolicy,
Metadata: a.Metadata,
ClientToken: a.ClientToken,
Accessor: a.Accessor,
Period: time.Duration(a.Period),
NumUses: int(a.NumUses),
EntityID: a.EntityID,
Alias: a.Alias,
GroupAliases: a.GroupAliases,
BoundCIDRs: boundCIDRs,
ExplicitMaxTTL: time.Duration(a.ExplicitMaxTTL),
}, nil
}
func LogicalTokenEntryToProtoTokenEntry(t *logical.TokenEntry) *TokenEntry {
if t == nil {
return nil
}
boundCIDRs := make([]string, len(t.BoundCIDRs))
for i, cidr := range t.BoundCIDRs {
boundCIDRs[i] = cidr.String()
}
return &TokenEntry{
ID: t.ID,
Accessor: t.Accessor,
Parent: t.Parent,
Policies: t.Policies,
Path: t.Path,
Meta: t.Meta,
DisplayName: t.DisplayName,
NumUses: int64(t.NumUses),
CreationTime: t.CreationTime,
TTL: int64(t.TTL),
ExplicitMaxTTL: int64(t.ExplicitMaxTTL),
Role: t.Role,
Period: int64(t.Period),
EntityID: t.EntityID,
BoundCIDRs: boundCIDRs,
NamespaceID: t.NamespaceID,
CubbyholeID: t.CubbyholeID,
Type: uint32(t.Type),
}
}
func ProtoTokenEntryToLogicalTokenEntry(t *TokenEntry) (*logical.TokenEntry, error) {
if t == nil {
return nil, nil
}
boundCIDRs, err := parseutil.ParseAddrs(t.BoundCIDRs)
if err != nil {
return nil, err
}
if len(boundCIDRs) == 0 {
// On inbound auths, if auth.BoundCIDRs is empty, it will be nil.
// Let's match that behavior outbound.
boundCIDRs = nil
}
return &logical.TokenEntry{
ID: t.ID,
Accessor: t.Accessor,
Parent: t.Parent,
Policies: t.Policies,
Path: t.Path,
Meta: t.Meta,
DisplayName: t.DisplayName,
NumUses: int(t.NumUses),
CreationTime: t.CreationTime,
TTL: time.Duration(t.TTL),
ExplicitMaxTTL: time.Duration(t.ExplicitMaxTTL),
Role: t.Role,
Period: time.Duration(t.Period),
EntityID: t.EntityID,
BoundCIDRs: boundCIDRs,
NamespaceID: t.NamespaceID,
CubbyholeID: t.CubbyholeID,
Type: logical.TokenType(t.Type),
}, nil
}