mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
add ce side code and stubs for rotation manager
* add ce side code and stubs * add changelog * style refactor * try to use APIPath as mount point instead of request field * fix linter * return a response struct instead of a pure timestamp * add issue time to response * add ttl to GetRotationInformation response * rename field for clarity * update ttl to just seconds * rename next and last rotation time field; describe what they are * rename function * catch up to ent PR * fix patch merge mistake
This commit is contained in:
parent
720bd68c7d
commit
8f522a2bca
3
changelog/31053.txt
Normal file
3
changelog/31053.txt
Normal file
@ -0,0 +1,3 @@
|
||||
```release-note:improvement
|
||||
sdk: add stub code for retrieving rotation schedule information
|
||||
```
|
||||
@ -87,13 +87,19 @@ func (p *AutomatedRotationParams) PopulateAutomatedRotationData(m map[string]int
|
||||
}
|
||||
|
||||
func (p *AutomatedRotationParams) ShouldRegisterRotationJob() bool {
|
||||
return p.RotationSchedule != "" || p.RotationPeriod != 0
|
||||
return p.HasNonzeroRotationValues()
|
||||
}
|
||||
|
||||
func (p *AutomatedRotationParams) ShouldDeregisterRotationJob() bool {
|
||||
return p.DisableAutomatedRotation || (p.RotationSchedule == "" && p.RotationPeriod == 0)
|
||||
}
|
||||
|
||||
// HasNonzeroRotationValues returns true if either of the primary rotation values (RotationSchedule or RotationPeriod)
|
||||
// are not the zero value.
|
||||
func (p *AutomatedRotationParams) HasNonzeroRotationValues() bool {
|
||||
return p.RotationSchedule != "" || p.RotationPeriod != 0
|
||||
}
|
||||
|
||||
// AddAutomatedRotationFields adds plugin identity token fields to the given
|
||||
// field schema map.
|
||||
func AddAutomatedRotationFields(m map[string]*framework.FieldSchema) {
|
||||
|
||||
@ -102,6 +102,9 @@ type SystemView interface {
|
||||
// GenerateIdentityToken returns an identity token for the requesting plugin.
|
||||
GenerateIdentityToken(ctx context.Context, req *pluginutil.IdentityTokenRequest) (*pluginutil.IdentityTokenResponse, error)
|
||||
|
||||
// GetRotationInformation gets rotation information from the system about an established rotation job.
|
||||
GetRotationInformation(ctx context.Context, req *rotation.RotationInfoRequest) (*rotation.RotationInfoResponse, error)
|
||||
|
||||
// RegisterRotationJob returns a rotation ID after registering a
|
||||
// rotation job for the requesting plugin.
|
||||
// NOTE: This method is intended for use only by HashiCorp Vault Enterprise plugins.
|
||||
@ -301,6 +304,10 @@ func (d StaticSystemView) APILockShouldBlockRequest() (bool, error) {
|
||||
return d.APILockShouldBlockRequestVal, nil
|
||||
}
|
||||
|
||||
func (d StaticSystemView) GetRotationInformation(ctx context.Context, req *rotation.RotationInfoRequest) (*rotation.RotationInfoResponse, error) {
|
||||
return nil, errors.New("GetRotationInformation is not implemented in StaticSystemView")
|
||||
}
|
||||
|
||||
func (d StaticSystemView) RegisterRotationJob(_ context.Context, _ *rotation.RotationJobConfigureRequest) (rotationID string, err error) {
|
||||
return "", errors.New("RegisterRotationJob is not implemented in StaticSystemView")
|
||||
}
|
||||
|
||||
@ -227,6 +227,21 @@ func (s *gRPCSystemViewClient) GenerateIdentityToken(ctx context.Context, req *p
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewClient) GetRotationInformation(ctx context.Context, req *rotation.RotationInfoRequest) (*rotation.RotationInfoResponse, error) {
|
||||
resp, err := s.client.GetRotationInformation(ctx, &pb.RotationInfoRequest{
|
||||
MountPath: req.ReqPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rotation.RotationInfoResponse{
|
||||
NextVaultRotation: time.Unix(resp.ExpireTime, 0),
|
||||
LastVaultRotation: time.Unix(resp.IssueTime, 0),
|
||||
TTL: resp.TTL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewClient) RegisterRotationJob(ctx context.Context, req *rotation.RotationJobConfigureRequest) (id string, retErr error) {
|
||||
cfgReq := &pb.RegisterRotationJobRequest{
|
||||
Job: &pb.RotationJobInput{
|
||||
@ -464,6 +479,27 @@ func (s *gRPCSystemViewServer) GenerateIdentityToken(ctx context.Context, req *p
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewServer) GetRotationInformation(ctx context.Context, req *pb.RotationInfoRequest) (*pb.RotationInfoReply, error) {
|
||||
if s.impl == nil {
|
||||
return nil, errMissingSystemView
|
||||
}
|
||||
|
||||
cfgReq := &rotation.RotationInfoRequest{
|
||||
ReqPath: req.MountPath,
|
||||
}
|
||||
|
||||
resp, err := s.impl.GetRotationInformation(ctx, cfgReq)
|
||||
if err != nil {
|
||||
return &pb.RotationInfoReply{}, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &pb.RotationInfoReply{
|
||||
IssueTime: resp.LastVaultRotation.Unix(),
|
||||
ExpireTime: resp.NextVaultRotation.Unix(),
|
||||
TTL: resp.TTL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewServer) RegisterRotationJob(ctx context.Context, req *pb.RegisterRotationJobRequest) (*pb.RegisterRotationJobResponse, error) {
|
||||
if s.impl == nil {
|
||||
return nil, errMissingSystemView
|
||||
|
||||
@ -5,6 +5,7 @@ package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
@ -137,6 +138,16 @@ func expectInternalValue(t *testing.T, client *api.Client, mountPath, expected s
|
||||
|
||||
func (b *backend) rotateRootCredential(ctx context.Context, req *logical.Request) error {
|
||||
b.Logger().Debug("mock rotateRootCredential")
|
||||
|
||||
cfg, err := b.configEntry(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.FailRotate {
|
||||
return errors.New("mock plugin was asked to fail to rotate")
|
||||
}
|
||||
|
||||
b.internal = "rotated"
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -16,7 +16,11 @@ import (
|
||||
func pathConfig(b *backend) *framework.Path {
|
||||
p := &framework.Path{
|
||||
Pattern: "config",
|
||||
Fields: map[string]*framework.FieldSchema{},
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"fail_rotate": {
|
||||
Type: framework.TypeBool,
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.CreateOperation: b.pathConfigUpdate,
|
||||
logical.UpdateOperation: b.pathConfigUpdate,
|
||||
@ -37,6 +41,10 @@ func (b *backend) pathConfigUpdate(ctx context.Context, req *logical.Request, da
|
||||
conf = &config{}
|
||||
}
|
||||
|
||||
if failRotateRaw, ok := data.GetOk("fail_rotate"); ok {
|
||||
conf.FailRotate = failRotateRaw.(bool)
|
||||
}
|
||||
|
||||
if err := conf.ParseAutomatedRotationFields(data); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
@ -89,6 +97,18 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data
|
||||
configData := map[string]interface{}{}
|
||||
conf.PopulateAutomatedRotationData(configData)
|
||||
|
||||
if conf.HasNonzeroRotationValues() {
|
||||
resp, err := b.System().GetRotationInformation(ctx, &rotation.RotationInfoRequest{ReqPath: req.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp != nil {
|
||||
configData["expire_time"] = resp.NextVaultRotation.Unix()
|
||||
configData["creation_time"] = resp.LastVaultRotation.Unix()
|
||||
configData["ttl"] = int64(resp.TTL)
|
||||
}
|
||||
}
|
||||
|
||||
return &logical.Response{
|
||||
Data: configData,
|
||||
}, nil
|
||||
@ -127,5 +147,6 @@ func (b *backend) configEntry(ctx context.Context, s logical.Storage) (*config,
|
||||
}
|
||||
|
||||
type config struct {
|
||||
FailRotate bool
|
||||
automatedrotationutil.AutomatedRotationParams
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -617,6 +617,16 @@ message GenerateIdentityTokenResponse {
|
||||
int64 ttl = 2;
|
||||
}
|
||||
|
||||
message RotationInfoRequest {
|
||||
string mount_path = 1;
|
||||
}
|
||||
|
||||
message RotationInfoReply {
|
||||
int64 issue_time = 1;
|
||||
int64 expire_time = 2;
|
||||
int64 ttl = 3;
|
||||
}
|
||||
|
||||
message RegisterRotationJobRequest {
|
||||
RotationJobInput job = 1;
|
||||
}
|
||||
@ -703,6 +713,9 @@ service SystemView {
|
||||
// GenerateIdentityToken returns an identity token for the requesting plugin.
|
||||
rpc GenerateIdentityToken(GenerateIdentityTokenRequest) returns (GenerateIdentityTokenResponse);
|
||||
|
||||
// GetRotationInformation returns the time remaining in a rotation job for the requested credential.
|
||||
rpc GetRotationInformation(RotationInfoRequest) returns (RotationInfoReply);
|
||||
|
||||
// RegisterRotationJob returns a rotation ID for the requested plugin credential.
|
||||
rpc RegisterRotationJob(RegisterRotationJobRequest) returns (RegisterRotationJobResponse);
|
||||
|
||||
|
||||
@ -688,6 +688,7 @@ const (
|
||||
SystemView_GeneratePasswordFromPolicy_FullMethodName = "/pb.SystemView/GeneratePasswordFromPolicy"
|
||||
SystemView_ClusterInfo_FullMethodName = "/pb.SystemView/ClusterInfo"
|
||||
SystemView_GenerateIdentityToken_FullMethodName = "/pb.SystemView/GenerateIdentityToken"
|
||||
SystemView_GetRotationInformation_FullMethodName = "/pb.SystemView/GetRotationInformation"
|
||||
SystemView_RegisterRotationJob_FullMethodName = "/pb.SystemView/RegisterRotationJob"
|
||||
SystemView_DeregisterRotationJob_FullMethodName = "/pb.SystemView/DeregisterRotationJob"
|
||||
)
|
||||
@ -741,6 +742,8 @@ type SystemViewClient interface {
|
||||
ClusterInfo(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ClusterInfoReply, error)
|
||||
// GenerateIdentityToken returns an identity token for the requesting plugin.
|
||||
GenerateIdentityToken(ctx context.Context, in *GenerateIdentityTokenRequest, opts ...grpc.CallOption) (*GenerateIdentityTokenResponse, error)
|
||||
// GetRotationInformation returns the time remaining in a rotation job for the requested credential.
|
||||
GetRotationInformation(ctx context.Context, in *RotationInfoRequest, opts ...grpc.CallOption) (*RotationInfoReply, error)
|
||||
// RegisterRotationJob returns a rotation ID for the requested plugin credential.
|
||||
RegisterRotationJob(ctx context.Context, in *RegisterRotationJobRequest, opts ...grpc.CallOption) (*RegisterRotationJobResponse, error)
|
||||
// DeregisterRotationJob returns any errors in de-registering a credential from the Rotation Manager.
|
||||
@ -895,6 +898,16 @@ func (c *systemViewClient) GenerateIdentityToken(ctx context.Context, in *Genera
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *systemViewClient) GetRotationInformation(ctx context.Context, in *RotationInfoRequest, opts ...grpc.CallOption) (*RotationInfoReply, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RotationInfoReply)
|
||||
err := c.cc.Invoke(ctx, SystemView_GetRotationInformation_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *systemViewClient) RegisterRotationJob(ctx context.Context, in *RegisterRotationJobRequest, opts ...grpc.CallOption) (*RegisterRotationJobResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RegisterRotationJobResponse)
|
||||
@ -964,6 +977,8 @@ type SystemViewServer interface {
|
||||
ClusterInfo(context.Context, *Empty) (*ClusterInfoReply, error)
|
||||
// GenerateIdentityToken returns an identity token for the requesting plugin.
|
||||
GenerateIdentityToken(context.Context, *GenerateIdentityTokenRequest) (*GenerateIdentityTokenResponse, error)
|
||||
// GetRotationInformation returns the time remaining in a rotation job for the requested credential.
|
||||
GetRotationInformation(context.Context, *RotationInfoRequest) (*RotationInfoReply, error)
|
||||
// RegisterRotationJob returns a rotation ID for the requested plugin credential.
|
||||
RegisterRotationJob(context.Context, *RegisterRotationJobRequest) (*RegisterRotationJobResponse, error)
|
||||
// DeregisterRotationJob returns any errors in de-registering a credential from the Rotation Manager.
|
||||
@ -1020,6 +1035,9 @@ func (UnimplementedSystemViewServer) ClusterInfo(context.Context, *Empty) (*Clus
|
||||
func (UnimplementedSystemViewServer) GenerateIdentityToken(context.Context, *GenerateIdentityTokenRequest) (*GenerateIdentityTokenResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GenerateIdentityToken not implemented")
|
||||
}
|
||||
func (UnimplementedSystemViewServer) GetRotationInformation(context.Context, *RotationInfoRequest) (*RotationInfoReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetRotationInformation not implemented")
|
||||
}
|
||||
func (UnimplementedSystemViewServer) RegisterRotationJob(context.Context, *RegisterRotationJobRequest) (*RegisterRotationJobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RegisterRotationJob not implemented")
|
||||
}
|
||||
@ -1299,6 +1317,24 @@ func _SystemView_GenerateIdentityToken_Handler(srv interface{}, ctx context.Cont
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SystemView_GetRotationInformation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RotationInfoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SystemViewServer).GetRotationInformation(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: SystemView_GetRotationInformation_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SystemViewServer).GetRotationInformation(ctx, req.(*RotationInfoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SystemView_RegisterRotationJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RegisterRotationJobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -1398,6 +1434,10 @@ var SystemView_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GenerateIdentityToken",
|
||||
Handler: _SystemView_GenerateIdentityToken_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetRotationInformation",
|
||||
Handler: _SystemView_GetRotationInformation_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RegisterRotationJob",
|
||||
Handler: _SystemView_RegisterRotationJob_Handler,
|
||||
|
||||
@ -49,6 +49,25 @@ type RotationJobDeregisterRequest struct {
|
||||
ReqPath string
|
||||
}
|
||||
|
||||
// RotationInfoRequest is the request struct used by SystemView.GetRotationInformation.
|
||||
type RotationInfoRequest struct {
|
||||
// ReqPath is the plugin-local path to the credential, and needs to match the ReqPath value that
|
||||
// was supplied in schedule creation with RegisterRotationJob
|
||||
ReqPath string
|
||||
}
|
||||
|
||||
// RotationInfoResponse is the response struct returned by SystemView.GetRotationInformation.
|
||||
type RotationInfoResponse struct {
|
||||
// NextVaultRotation is the scheduled time of the next rotation.
|
||||
NextVaultRotation time.Time
|
||||
|
||||
// LastVaultRotation is the time of the prior rotation.
|
||||
LastVaultRotation time.Time
|
||||
|
||||
// TTL is integer seconds until next rotation, conventionally clamped to 0 (i.e., will not be negative)
|
||||
TTL int64
|
||||
}
|
||||
|
||||
func (s *RotationJob) Validate() error {
|
||||
if s.MountPoint == "" {
|
||||
return fmt.Errorf("MountPoint is required")
|
||||
|
||||
@ -355,6 +355,17 @@ func (d dynamicSystemView) GenerateIdentityToken(ctx context.Context, req *plugi
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d dynamicSystemView) GetRotationInformation(ctx context.Context, req *rotation.RotationInfoRequest) (*rotation.RotationInfoResponse, error) {
|
||||
// sanity check
|
||||
mountEntry := d.mountEntry
|
||||
if mountEntry == nil {
|
||||
return nil, fmt.Errorf("no mount entry")
|
||||
}
|
||||
nsCtx := namespace.ContextWithNamespace(ctx, mountEntry.Namespace())
|
||||
|
||||
return d.core.GetRotationInformation(nsCtx, mountEntry.APIPath(), req)
|
||||
}
|
||||
|
||||
func (d dynamicSystemView) RegisterRotationJob(ctx context.Context, req *rotation.RotationJobConfigureRequest) (string, error) {
|
||||
mountEntry := d.mountEntry
|
||||
if mountEntry == nil {
|
||||
|
||||
@ -24,10 +24,17 @@ func (c *Core) stopRotation() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) GetRotationInformation(_ context.Context, _ string, _ *rotation.RotationInfoRequest) (*rotation.RotationInfoResponse, error) {
|
||||
return nil, automatedrotationutil.ErrRotationManagerUnsupported
|
||||
}
|
||||
|
||||
func (c *Core) RegisterRotationJob(_ context.Context, _ *rotation.RotationJob) (string, error) {
|
||||
return "", automatedrotationutil.ErrRotationManagerUnsupported
|
||||
}
|
||||
|
||||
// The DeregisterRotationJob stub returns nil instead of an error because it is intended to be valid to send a deregister
|
||||
// request for a non-existent job. As a result, the plugin sends a deregister request whenever the relevant rotation
|
||||
// values are unset. This means that for a plugin running in CE Vault, it will _always_ try to send a deregister request.
|
||||
func (c *Core) DeregisterRotationJob(_ context.Context, _ *rotation.RotationJobDeregisterRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user