mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-31 18:48:08 +00:00
Batch tokens (#755)
This commit is contained in:
@@ -271,4 +271,5 @@ type TokenCreateRequest struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
NumUses int `json:"num_uses"`
|
||||
Renewable *bool `json:"renewable,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
@@ -73,46 +73,8 @@ func (c *Sys) DisableAuth(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Structures for the requests/resposne are all down here. They aren't
|
||||
// individually documented because the map almost directly to the raw HTTP API
|
||||
// documentation. Please refer to that documentation for more details.
|
||||
|
||||
type EnableAuthOptions struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Config AuthConfigInput `json:"config"`
|
||||
Local bool `json:"local"`
|
||||
PluginName string `json:"plugin_name,omitempty"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
Options map[string]string `json:"options" mapstructure:"options"`
|
||||
}
|
||||
|
||||
type AuthConfigInput struct {
|
||||
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
|
||||
type AuthMount struct {
|
||||
Type string `json:"type" mapstructure:"type"`
|
||||
Description string `json:"description" mapstructure:"description"`
|
||||
Accessor string `json:"accessor" mapstructure:"accessor"`
|
||||
Config AuthConfigOutput `json:"config" mapstructure:"config"`
|
||||
Local bool `json:"local" mapstructure:"local"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
Options map[string]string `json:"options" mapstructure:"options"`
|
||||
}
|
||||
|
||||
type AuthConfigOutput struct {
|
||||
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
// Rather than duplicate, we can use modern Go's type aliasing
|
||||
type EnableAuthOptions = MountInput
|
||||
type AuthConfigInput = MountConfigInput
|
||||
type AuthMount = MountOutput
|
||||
type AuthConfigOutput = MountConfigOutput
|
||||
|
||||
@@ -132,10 +132,10 @@ type MountInput struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Config MountConfigInput `json:"config"`
|
||||
Options map[string]string `json:"options"`
|
||||
Local bool `json:"local"`
|
||||
PluginName string `json:"plugin_name,omitempty"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
Options map[string]string `json:"options"`
|
||||
}
|
||||
|
||||
type MountConfigInput struct {
|
||||
@@ -149,6 +149,7 @@ type MountConfigInput struct {
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
type MountOutput struct {
|
||||
@@ -170,4 +171,5 @@ type MountConfigOutput struct {
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
Metadata: auth.Metadata,
|
||||
EntityID: auth.EntityID,
|
||||
RemainingUses: req.ClientTokenRemainingUses,
|
||||
TokenType: auth.TokenType.String(),
|
||||
},
|
||||
|
||||
Request: AuditRequest{
|
||||
@@ -304,6 +305,8 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies,
|
||||
Metadata: resp.Auth.Metadata,
|
||||
NumUses: resp.Auth.NumUses,
|
||||
EntityID: resp.Auth.EntityID,
|
||||
TokenType: resp.Auth.TokenType.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,16 +337,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
Type: "response",
|
||||
Error: errString,
|
||||
Auth: AuditAuth{
|
||||
ClientToken: auth.ClientToken,
|
||||
Accessor: auth.Accessor,
|
||||
DisplayName: auth.DisplayName,
|
||||
Policies: auth.Policies,
|
||||
TokenPolicies: auth.TokenPolicies,
|
||||
IdentityPolicies: auth.IdentityPolicies,
|
||||
ExternalNamespacePolicies: auth.ExternalNamespacePolicies,
|
||||
Metadata: auth.Metadata,
|
||||
ClientToken: auth.ClientToken,
|
||||
Accessor: auth.Accessor,
|
||||
RemainingUses: req.ClientTokenRemainingUses,
|
||||
EntityID: auth.EntityID,
|
||||
TokenType: auth.TokenType.String(),
|
||||
},
|
||||
|
||||
Request: AuditRequest{
|
||||
@@ -437,6 +441,7 @@ type AuditAuth struct {
|
||||
NumUses int `json:"num_uses,omitempty"`
|
||||
RemainingUses int `json:"remaining_uses,omitempty"`
|
||||
EntityID string `json:"entity_id"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type AuditSecret struct {
|
||||
|
||||
@@ -37,7 +37,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
||||
ExpectedStr string
|
||||
}{
|
||||
"auth, request": {
|
||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
||||
&logical.Auth{
|
||||
ClientToken: "foo",
|
||||
Accessor: "bar",
|
||||
DisplayName: "testtoken",
|
||||
Policies: []string{"root"},
|
||||
TokenType: logical.TokenTypeService,
|
||||
},
|
||||
&logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "/foo",
|
||||
@@ -56,7 +62,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
||||
expectedResultStr,
|
||||
},
|
||||
"auth, request with prefix": {
|
||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
||||
&logical.Auth{
|
||||
ClientToken: "foo",
|
||||
Accessor: "bar",
|
||||
DisplayName: "testtoken",
|
||||
Policies: []string{"root"},
|
||||
TokenType: logical.TokenTypeService,
|
||||
},
|
||||
&logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "/foo",
|
||||
@@ -127,5 +139,5 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
|
||||
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null,"entity_id":"","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
|
||||
`
|
||||
|
||||
@@ -36,7 +36,13 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
||||
ExpectedStr string
|
||||
}{
|
||||
"auth, request": {
|
||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
||||
&logical.Auth{
|
||||
ClientToken: "foo",
|
||||
Accessor: "bar",
|
||||
DisplayName: "testtoken",
|
||||
Policies: []string{"root"},
|
||||
TokenType: logical.TokenTypeService,
|
||||
},
|
||||
&logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "/foo",
|
||||
@@ -53,11 +59,17 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
||||
errors.New("this is an error"),
|
||||
"",
|
||||
"",
|
||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||
fooSalted),
|
||||
},
|
||||
"auth, request with prefix": {
|
||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
||||
&logical.Auth{
|
||||
ClientToken: "foo",
|
||||
Accessor: "bar",
|
||||
DisplayName: "testtoken",
|
||||
Policies: []string{"root"},
|
||||
TokenType: logical.TokenTypeService,
|
||||
},
|
||||
&logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "/foo",
|
||||
@@ -74,7 +86,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
||||
errors.New("this is an error"),
|
||||
"",
|
||||
"@cee: ",
|
||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||
fooSalted),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -304,6 +304,15 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
|
||||
BoundCIDRs: tokenBoundCIDRs,
|
||||
}
|
||||
|
||||
switch role.TokenType {
|
||||
case "default":
|
||||
auth.TokenType = logical.TokenTypeDefault
|
||||
case "batch":
|
||||
auth.TokenType = logical.TokenTypeBatch
|
||||
case "service":
|
||||
auth.TokenType = logical.TokenTypeService
|
||||
}
|
||||
|
||||
return &logical.Response{
|
||||
Auth: auth,
|
||||
}, nil
|
||||
|
||||
@@ -84,6 +84,9 @@ type roleStorageEntry struct {
|
||||
// SecretIDPrefix is the storage prefix for persisting secret IDs. This
|
||||
// differs based on whether the secret IDs are cluster local or not.
|
||||
SecretIDPrefix string `json:"secret_id_prefix" mapstructure:"secret_id_prefix"`
|
||||
|
||||
// TokenType is the type of token to generate
|
||||
TokenType string `json:"token_type" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
// roleIDStorageEntry represents the reverse mapping from RoleID to Role
|
||||
@@ -196,6 +199,11 @@ TTL will be set to the value of this parameter.`,
|
||||
Description: `If set, the secret IDs generated using this role will be cluster local. This
|
||||
can only be set during role creation and once set, it can't be reset later.`,
|
||||
},
|
||||
"token_type": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "default",
|
||||
Description: `The type of token to generate ("service" or "batch"), or "default" to use the default`,
|
||||
},
|
||||
},
|
||||
ExistenceCheck: b.pathRoleExistenceCheck,
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
@@ -1007,6 +1015,30 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
|
||||
role.TokenMaxTTL = time.Second * time.Duration(data.Get("token_max_ttl").(int))
|
||||
}
|
||||
|
||||
tokenType := role.TokenType
|
||||
if tokenTypeRaw, ok := data.GetOk("token_type"); ok {
|
||||
tokenType = tokenTypeRaw.(string)
|
||||
switch tokenType {
|
||||
case "":
|
||||
tokenType = "default"
|
||||
case "service", "batch", "default":
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenType)), nil
|
||||
}
|
||||
} else if req.Operation == logical.CreateOperation {
|
||||
tokenType = data.Get("token_type").(string)
|
||||
}
|
||||
role.TokenType = tokenType
|
||||
|
||||
if role.TokenType == "batch" {
|
||||
if role.Period != 0 {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil
|
||||
}
|
||||
if role.TokenNumUses != 0 {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with limited use count"), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the TokenTTL value provided is less than the TokenMaxTTL.
|
||||
// Sanitizing the TTL and MaxTTL is not required now and can be performed
|
||||
// at credential issue time.
|
||||
@@ -1061,6 +1093,7 @@ func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *
|
||||
"token_num_uses": role.TokenNumUses,
|
||||
"token_ttl": role.TokenTTL / time.Second,
|
||||
"local_secret_ids": false,
|
||||
"token_type": role.TokenType,
|
||||
}
|
||||
|
||||
if role.SecretIDPrefix == secretIDLocalPrefix {
|
||||
|
||||
@@ -1159,6 +1159,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {
|
||||
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // returned for backwards compatibility
|
||||
"token_bound_cidrs": []string{},
|
||||
"token_type": "default",
|
||||
}
|
||||
|
||||
var expectedStruct roleStorageEntry
|
||||
@@ -1637,6 +1638,7 @@ func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) {
|
||||
"token_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // provided for backwards compatibility
|
||||
"token_type": "default",
|
||||
}
|
||||
|
||||
var expectedStruct roleStorageEntry
|
||||
|
||||
@@ -30,6 +30,7 @@ type AuthEnableCommand struct {
|
||||
flagOptions map[string]string
|
||||
flagLocal bool
|
||||
flagSealWrap bool
|
||||
flagTokenType string
|
||||
flagVersion int
|
||||
}
|
||||
|
||||
@@ -162,6 +163,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
|
||||
Usage: "Enable seal wrapping of critical values in the secrets engine.",
|
||||
})
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: flagNameTokenType,
|
||||
Target: &c.flagTokenType,
|
||||
Usage: "Sets a forced token type for the mount.",
|
||||
})
|
||||
|
||||
f.IntVar(&IntVar{
|
||||
Name: "version",
|
||||
Target: &c.flagVersion,
|
||||
@@ -257,6 +264,10 @@ func (c *AuthEnableCommand) Run(args []string) int {
|
||||
if fl.Name == flagNamePassthroughRequestHeaders {
|
||||
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
|
||||
}
|
||||
|
||||
if fl.Name == flagNameTokenType {
|
||||
authOpts.Config.TokenType = c.flagTokenType
|
||||
}
|
||||
})
|
||||
|
||||
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
|
||||
|
||||
@@ -143,7 +143,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
|
||||
}
|
||||
}
|
||||
|
||||
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Replication | Seal Wrap | Options | Description"}
|
||||
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | Options | Description"}
|
||||
for _, path := range paths {
|
||||
mount := auths[path]
|
||||
|
||||
@@ -155,13 +155,14 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
|
||||
replication = "local"
|
||||
}
|
||||
|
||||
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
|
||||
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
|
||||
path,
|
||||
mount.Type,
|
||||
mount.Accessor,
|
||||
mount.Config.PluginName,
|
||||
defaultTTL,
|
||||
maxTTL,
|
||||
mount.Config.TokenType,
|
||||
replication,
|
||||
mount.SealWrap,
|
||||
mount.Options,
|
||||
|
||||
@@ -25,6 +25,7 @@ type AuthTuneCommand struct {
|
||||
flagListingVisibility string
|
||||
flagMaxLeaseTTL time.Duration
|
||||
flagOptions map[string]string
|
||||
flagTokenType string
|
||||
flagVersion int
|
||||
}
|
||||
|
||||
@@ -112,6 +113,12 @@ func (c *AuthTuneCommand) Flags() *FlagSets {
|
||||
"This can be specified multiple times.",
|
||||
})
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: flagNameTokenType,
|
||||
Target: &c.flagTokenType,
|
||||
Usage: "Sets a forced token type for the mount.",
|
||||
})
|
||||
|
||||
f.IntVar(&IntVar{
|
||||
Name: "version",
|
||||
Target: &c.flagVersion,
|
||||
@@ -184,6 +191,10 @@ func (c *AuthTuneCommand) Run(args []string) int {
|
||||
if fl.Name == flagNameListingVisibility {
|
||||
mountConfigInput.ListingVisibility = c.flagListingVisibility
|
||||
}
|
||||
|
||||
if fl.Name == flagNameTokenType {
|
||||
mountConfigInput.TokenType = c.flagTokenType
|
||||
}
|
||||
})
|
||||
|
||||
// Append /auth (since that's where auths live) and a trailing slash to
|
||||
|
||||
@@ -92,6 +92,8 @@ const (
|
||||
flagNameListingVisibility = "listing-visibility"
|
||||
// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
|
||||
flagNamePassthroughRequestHeaders = "passthrough-request-headers"
|
||||
// flagNameTokenType is the flag name used to force a specific token type
|
||||
flagNameTokenType = "token-type"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -26,6 +26,7 @@ type TokenCreateCommand struct {
|
||||
flagNoDefaultPolicy bool
|
||||
flagUseLimit int
|
||||
flagRole string
|
||||
flagType string
|
||||
flagMetadata map[string]string
|
||||
flagPolicies []string
|
||||
|
||||
@@ -153,6 +154,13 @@ func (c *TokenCreateCommand) Flags() *FlagSets {
|
||||
"must have permission for \"auth/token/create/<role>\".",
|
||||
})
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: "type",
|
||||
Target: &c.flagType,
|
||||
Default: "service",
|
||||
Usage: `The type of token to create. Can be "service" or "batch".`,
|
||||
})
|
||||
|
||||
f.StringMapVar(&StringMapVar{
|
||||
Name: "metadata",
|
||||
Target: &c.flagMetadata,
|
||||
@@ -213,6 +221,10 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
||||
}
|
||||
}
|
||||
|
||||
if c.flagType == "batch" {
|
||||
c.flagRenewable = false
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
@@ -231,6 +243,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
||||
Renewable: &c.flagRenewable,
|
||||
ExplicitMaxTTL: c.flagExplicitMaxTTL.String(),
|
||||
Period: c.flagPeriod.String(),
|
||||
Type: c.flagType,
|
||||
}
|
||||
|
||||
var secret *api.Secret
|
||||
|
||||
@@ -104,7 +104,18 @@ func Canonicalize(nsPath string) string {
|
||||
func SplitIDFromString(input string) (string, string) {
|
||||
prefix := ""
|
||||
slashIdx := strings.LastIndex(input, "/")
|
||||
if slashIdx > 0 {
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(input, "b."):
|
||||
prefix = "b."
|
||||
input = input[2:]
|
||||
|
||||
case strings.HasPrefix(input, "s."):
|
||||
prefix = "s."
|
||||
input = input[2:]
|
||||
|
||||
case slashIdx > 0:
|
||||
// Leases will never have a b./s. to start
|
||||
if slashIdx == len(input)-1 {
|
||||
return input, ""
|
||||
}
|
||||
|
||||
@@ -48,6 +48,21 @@ func TestSplitIDFromString(t *testing.T) {
|
||||
"",
|
||||
"foo.foo/",
|
||||
},
|
||||
{
|
||||
"b.foo",
|
||||
"",
|
||||
"b.foo",
|
||||
},
|
||||
{
|
||||
"s.foo",
|
||||
"",
|
||||
"s.foo",
|
||||
},
|
||||
{
|
||||
"t.foo",
|
||||
"foo",
|
||||
"t",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range tcases {
|
||||
|
||||
@@ -5,14 +5,33 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/vault/helper/consts"
|
||||
"github.com/hashicorp/vault/helper/xor"
|
||||
vaulthttp "github.com/hashicorp/vault/http"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
"github.com/hashicorp/vault/physical/inmem"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
"github.com/mitchellh/go-testing-interface"
|
||||
testing "github.com/mitchellh/go-testing-interface"
|
||||
)
|
||||
|
||||
type ReplicatedTestClusters struct {
|
||||
PerfPrimaryCluster *vault.TestCluster
|
||||
PerfSecondaryCluster *vault.TestCluster
|
||||
PerfPrimaryDRCluster *vault.TestCluster
|
||||
PerfSecondaryDRCluster *vault.TestCluster
|
||||
}
|
||||
|
||||
func (r *ReplicatedTestClusters) Cleanup() {
|
||||
r.PerfPrimaryCluster.Cleanup()
|
||||
r.PerfSecondaryCluster.Cleanup()
|
||||
r.PerfPrimaryDRCluster.Cleanup()
|
||||
r.PerfSecondaryDRCluster.Cleanup()
|
||||
}
|
||||
|
||||
// Generates a root token on the target cluster.
|
||||
func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string {
|
||||
token, err := GenerateRootWithError(t, cluster, drToken)
|
||||
@@ -127,6 +146,65 @@ func WaitForReplicationState(t testing.T, c *vault.Core, state consts.Replicatio
|
||||
}
|
||||
}
|
||||
|
||||
func GetClusterAndCore(t testing.T, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) {
|
||||
inm, err := inmem.NewTransactionalInmem(nil, logger)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inmha, err := inmem.NewInmemHA(nil, logger)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
coreConfig := &vault.CoreConfig{
|
||||
Physical: inm,
|
||||
HAPhysical: inmha.(physical.HABackend),
|
||||
}
|
||||
|
||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
Logger: logger,
|
||||
})
|
||||
cluster.Start()
|
||||
|
||||
cores := cluster.Cores
|
||||
core := cores[0]
|
||||
|
||||
vault.TestWaitActive(t, core.Core)
|
||||
|
||||
return cluster, core
|
||||
}
|
||||
|
||||
func GetFourReplicatedClusters(t testing.T) *ReplicatedTestClusters {
|
||||
ret := &ReplicatedTestClusters{}
|
||||
|
||||
logger := log.New(&log.LoggerOptions{
|
||||
Mutex: &sync.Mutex{},
|
||||
Level: log.Trace,
|
||||
})
|
||||
// Set this lower so that state populates quickly to standby nodes
|
||||
vault.HeartbeatInterval = 2 * time.Second
|
||||
|
||||
ret.PerfPrimaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri"))
|
||||
|
||||
ret.PerfSecondaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec"))
|
||||
|
||||
ret.PerfPrimaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri-dr"))
|
||||
|
||||
ret.PerfSecondaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec-dr"))
|
||||
|
||||
SetupFourClusterReplication(t, ret.PerfPrimaryCluster, ret.PerfSecondaryCluster, ret.PerfPrimaryDRCluster, ret.PerfSecondaryDRCluster)
|
||||
|
||||
// Wait until poison pills have been read
|
||||
time.Sleep(45 * time.Second)
|
||||
EnsureCoresUnsealed(t, ret.PerfPrimaryCluster)
|
||||
EnsureCoresUnsealed(t, ret.PerfSecondaryCluster)
|
||||
EnsureCoresUnsealed(t, ret.PerfPrimaryDRCluster)
|
||||
EnsureCoresUnsealed(t, ret.PerfSecondaryDRCluster)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) {
|
||||
// Enable dr primary
|
||||
_, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
|
||||
@@ -137,7 +215,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
||||
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary)
|
||||
|
||||
// Enable performance primary
|
||||
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/enable", nil)
|
||||
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/enable", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -167,7 +245,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
||||
EnsureCoresUnsealed(t, perfDRSecondary)
|
||||
|
||||
// get performance token
|
||||
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/secondary-token", map[string]interface{}{
|
||||
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/secondary-token", map[string]interface{}{
|
||||
"id": "1",
|
||||
})
|
||||
if err != nil {
|
||||
@@ -177,7 +255,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
||||
token = secret.WrapInfo.Token
|
||||
|
||||
// enable performace secondary
|
||||
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/secondary/enable", map[string]interface{}{
|
||||
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/performance/secondary/enable", map[string]interface{}{
|
||||
"token": token,
|
||||
"ca_file": perfPrimary.CACertPEMFile,
|
||||
})
|
||||
|
||||
@@ -421,9 +421,16 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle
|
||||
return
|
||||
}
|
||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||
if !perfStandbyAlwaysForwardPaths.HasPath(path) {
|
||||
switch {
|
||||
case !perfStandbyAlwaysForwardPaths.HasPath(path):
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
case strings.HasPrefix(path, "auth/token/create/"):
|
||||
isBatch, err := core.IsBatchTokenCreationRequest(r.Context(), path)
|
||||
if err == nil && isBatch {
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
|
||||
"github.com/hashicorp/vault/helper/consts"
|
||||
@@ -58,8 +59,8 @@ func TestLogical(t *testing.T) {
|
||||
testResponseBody(t, resp, &actual)
|
||||
delete(actual, "lease_id")
|
||||
expected["request_id"] = actual["request_id"]
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad:\nactual:\n%#v\nexpected:\n%#v", actual, expected)
|
||||
if diff := deep.Equal(actual, expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
|
||||
// DELETE
|
||||
@@ -163,6 +164,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
|
||||
"explicit_max_ttl": json.Number("0"),
|
||||
"expire_time": nil,
|
||||
"entity_id": "",
|
||||
"type": "service",
|
||||
},
|
||||
"warnings": nilWarnings,
|
||||
"wrap_info": nil,
|
||||
@@ -177,8 +179,8 @@ func TestLogical_StandbyRedirect(t *testing.T) {
|
||||
actual["data"] = actualDataMap
|
||||
expected["request_id"] = actual["request_id"]
|
||||
delete(actual, "lease_id")
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: got %#v; expected %#v", actual, expected)
|
||||
if diff := deep.Equal(actual, expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
|
||||
//// DELETE to standby
|
||||
@@ -214,6 +216,7 @@ func TestLogical_CreateToken(t *testing.T) {
|
||||
"lease_duration": json.Number("0"),
|
||||
"renewable": false,
|
||||
"entity_id": "",
|
||||
"token_type": "service",
|
||||
},
|
||||
"warnings": nilWarnings,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
@@ -32,6 +33,8 @@ func TestSysAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -45,6 +48,8 @@ func TestSysAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -98,6 +103,8 @@ func TestSysEnableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -110,6 +117,8 @@ func TestSysEnableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -123,6 +132,8 @@ func TestSysEnableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -135,6 +146,8 @@ func TestSysEnableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
@@ -153,8 +166,8 @@ func TestSysEnableAuth(t *testing.T) {
|
||||
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
|
||||
if diff := deep.Equal(actual, expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +202,8 @@ func TestSysDisableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"description": "token based credentials",
|
||||
"type": "token",
|
||||
@@ -202,6 +217,8 @@ func TestSysDisableAuth(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("0"),
|
||||
"max_lease_ttl": json.Number("0"),
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
"force_no_cache": false,
|
||||
},
|
||||
"description": "token based credentials",
|
||||
"type": "token",
|
||||
@@ -222,8 +239,8 @@ func TestSysDisableAuth(t *testing.T) {
|
||||
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
|
||||
if diff := deep.Equal(actual, expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,12 +280,14 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
|
||||
"force_no_cache": false,
|
||||
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
||||
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
||||
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
||||
"token_type": "default-service",
|
||||
}
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["request_id"] = actual["request_id"]
|
||||
@@ -302,10 +321,12 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
}
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["request_id"] = actual["request_id"]
|
||||
@@ -336,10 +357,12 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
}
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["request_id"] = actual["request_id"]
|
||||
@@ -370,11 +393,13 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"listing_visibility": "unauth",
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"default_lease_ttl": json.Number("2764800"),
|
||||
"max_lease_ttl": json.Number("2764800"),
|
||||
"force_no_cache": false,
|
||||
"listing_visibility": "unauth",
|
||||
"token_type": "default-service",
|
||||
}
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["request_id"] = actual["request_id"]
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/vault/helper/pgpkeys"
|
||||
"github.com/hashicorp/vault/helper/xor"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
@@ -333,6 +334,7 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
|
||||
"explicit_max_ttl": json.Number("0"),
|
||||
"expire_time": nil,
|
||||
"entity_id": "",
|
||||
"type": "service",
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||
@@ -431,6 +433,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
||||
"explicit_max_ttl": json.Number("0"),
|
||||
"expire_time": nil,
|
||||
"entity_id": "",
|
||||
"type": "service",
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||
@@ -440,7 +443,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
||||
expected["creation_time"] = actual["data"].(map[string]interface{})["creation_time"]
|
||||
expected["accessor"] = actual["data"].(map[string]interface{})["accessor"]
|
||||
|
||||
if !reflect.DeepEqual(actual["data"], expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual["data"])
|
||||
if diff := deep.Equal(actual["data"], expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ type Auth struct {
|
||||
// change the perceived path of the lease, even though they don't change
|
||||
// the request path itself.
|
||||
CreationPath string `json:"creation_path"`
|
||||
|
||||
// TokenType is the type of token being requested
|
||||
TokenType TokenType `json:"token_type"`
|
||||
}
|
||||
|
||||
func (a *Auth) GoString() string {
|
||||
|
||||
@@ -528,6 +528,8 @@ type Auth struct {
|
||||
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
||||
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
||||
ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,16,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"`
|
||||
// TokenType is the type of token being requested
|
||||
TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -670,6 +672,13 @@ func (m *Auth) GetExplicitMaxTTL() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Auth) GetTokenType() uint32 {
|
||||
if m != nil {
|
||||
return m.TokenType
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type TokenEntry struct {
|
||||
ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"`
|
||||
@@ -688,6 +697,7 @@ type TokenEntry struct {
|
||||
BoundCIDRs []string `sentinel:"" protobuf:"bytes,15,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"`
|
||||
NamespaceID string `sentinel:"" protobuf:"bytes,16,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"`
|
||||
CubbyholeID string `sentinel:"" protobuf:"bytes,17,opt,name=cubbyhole_id,json=cubbyholeId,proto3" json:"cubbyhole_id,omitempty"`
|
||||
Type uint32 `sentinel:"" protobuf:"varint,18,opt,name=type,proto3" json:"type,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -837,6 +847,13 @@ func (m *TokenEntry) GetCubbyholeID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TokenEntry) GetType() uint32 {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type LeaseOptions struct {
|
||||
TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"`
|
||||
Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable,proto3" json:"renewable,omitempty"`
|
||||
@@ -3614,159 +3631,161 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{
|
||||
func init() { proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor_25821d34acc7c5ef) }
|
||||
|
||||
var fileDescriptor_25821d34acc7c5ef = []byte{
|
||||
// 2462 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x5f, 0x73, 0xdb, 0xc6,
|
||||
0x11, 0x1f, 0xfe, 0x27, 0x97, 0xff, 0xa4, 0xb3, 0xa2, 0xc2, 0x8c, 0x53, 0x33, 0x48, 0x6d, 0x2b,
|
||||
0xae, 0x4d, 0xd9, 0x4a, 0xd3, 0x38, 0xed, 0x24, 0x1d, 0x45, 0x56, 0x1c, 0x35, 0x52, 0xa2, 0x81,
|
||||
0xe8, 0xa6, 0xff, 0x66, 0x90, 0x23, 0x70, 0xa2, 0x30, 0x02, 0x01, 0xf4, 0x70, 0x90, 0xc5, 0xa7,
|
||||
0x7e, 0x8b, 0xbe, 0xf4, 0x43, 0xf4, 0xad, 0xd3, 0xb7, 0xbe, 0x75, 0x3a, 0xd3, 0xe7, 0x7e, 0x8d,
|
||||
0x7e, 0x86, 0xce, 0xed, 0x1d, 0x40, 0x80, 0xa4, 0x62, 0x67, 0xa6, 0x7d, 0xbb, 0xdb, 0xdd, 0xdb,
|
||||
0xbb, 0xdb, 0xfb, 0xed, 0x6f, 0x17, 0x24, 0xdc, 0xf5, 0xc3, 0xa9, 0xe7, 0x50, 0x7f, 0x37, 0xf2,
|
||||
0x93, 0xa9, 0x17, 0xec, 0x46, 0x93, 0xdd, 0x09, 0x75, 0x2e, 0x59, 0xe0, 0x8e, 0x22, 0x1e, 0x8a,
|
||||
0x90, 0x94, 0xa3, 0xc9, 0xe0, 0xee, 0x34, 0x0c, 0xa7, 0x3e, 0xdb, 0x45, 0xc9, 0x24, 0x39, 0xdf,
|
||||
0x15, 0xde, 0x8c, 0xc5, 0x82, 0xce, 0x22, 0x65, 0x34, 0xd8, 0x4e, 0xbd, 0x78, 0x2e, 0x0b, 0x84,
|
||||
0x27, 0xe6, 0x5a, 0xbe, 0x55, 0xf4, 0xae, 0xa4, 0x66, 0x03, 0x6a, 0x87, 0xb3, 0x48, 0xcc, 0xcd,
|
||||
0x21, 0xd4, 0xbf, 0x60, 0xd4, 0x65, 0x9c, 0x6c, 0x43, 0xfd, 0x02, 0x47, 0x46, 0x69, 0x58, 0xd9,
|
||||
0x69, 0x59, 0x7a, 0x66, 0xfe, 0x0e, 0xe0, 0x54, 0xae, 0x39, 0xe4, 0x3c, 0xe4, 0xe4, 0x36, 0x34,
|
||||
0x19, 0xe7, 0xb6, 0x98, 0x47, 0xcc, 0x28, 0x0d, 0x4b, 0x3b, 0x5d, 0xab, 0xc1, 0x38, 0x1f, 0xcf,
|
||||
0x23, 0x46, 0x7e, 0x00, 0x72, 0x68, 0xcf, 0xe2, 0xa9, 0x51, 0x1e, 0x96, 0xa4, 0x07, 0xc6, 0xf9,
|
||||
0x49, 0x3c, 0x4d, 0xd7, 0x38, 0xa1, 0xcb, 0x8c, 0xca, 0xb0, 0xb4, 0x53, 0xc1, 0x35, 0x07, 0xa1,
|
||||
0xcb, 0xcc, 0x3f, 0x95, 0xa0, 0x76, 0x4a, 0xc5, 0x45, 0x4c, 0x08, 0x54, 0x79, 0x18, 0x0a, 0xbd,
|
||||
0x39, 0x8e, 0xc9, 0x0e, 0xf4, 0x93, 0x80, 0x26, 0xe2, 0x42, 0xde, 0xc8, 0xa1, 0x82, 0xb9, 0x46,
|
||||
0x19, 0xd5, 0xcb, 0x62, 0xf2, 0x1e, 0x74, 0xfd, 0xd0, 0xa1, 0xbe, 0x1d, 0x8b, 0x90, 0xd3, 0xa9,
|
||||
0xdc, 0x47, 0xda, 0x75, 0x50, 0x78, 0xa6, 0x64, 0xe4, 0x21, 0x6c, 0xc6, 0x8c, 0xfa, 0xf6, 0x2b,
|
||||
0x4e, 0xa3, 0xcc, 0xb0, 0xaa, 0x1c, 0x4a, 0xc5, 0x37, 0x9c, 0x46, 0xda, 0xd6, 0xfc, 0x7b, 0x1d,
|
||||
0x1a, 0x16, 0xfb, 0x43, 0xc2, 0x62, 0x41, 0x7a, 0x50, 0xf6, 0x5c, 0xbc, 0x6d, 0xcb, 0x2a, 0x7b,
|
||||
0x2e, 0x19, 0x01, 0xb1, 0x58, 0xe4, 0xcb, 0xad, 0xbd, 0x30, 0x38, 0xf0, 0x93, 0x58, 0x30, 0xae,
|
||||
0xef, 0xbc, 0x46, 0x43, 0xee, 0x40, 0x2b, 0x8c, 0x18, 0x47, 0x19, 0x06, 0xa0, 0x65, 0x2d, 0x04,
|
||||
0xf2, 0xe2, 0x11, 0x15, 0x17, 0x46, 0x15, 0x15, 0x38, 0x96, 0x32, 0x97, 0x0a, 0x6a, 0xd4, 0x94,
|
||||
0x4c, 0x8e, 0x89, 0x09, 0xf5, 0x98, 0x39, 0x9c, 0x09, 0xa3, 0x3e, 0x2c, 0xed, 0xb4, 0xf7, 0x60,
|
||||
0x14, 0x4d, 0x46, 0x67, 0x28, 0xb1, 0xb4, 0x86, 0xdc, 0x81, 0xaa, 0x8c, 0x8b, 0xd1, 0x40, 0x8b,
|
||||
0xa6, 0xb4, 0xd8, 0x4f, 0xc4, 0x85, 0x85, 0x52, 0xb2, 0x07, 0x0d, 0xf5, 0xa6, 0xb1, 0xd1, 0x1c,
|
||||
0x56, 0x76, 0xda, 0x7b, 0x86, 0x34, 0xd0, 0xb7, 0x1c, 0x29, 0x18, 0xc4, 0x87, 0x81, 0xe0, 0x73,
|
||||
0x2b, 0x35, 0x24, 0xef, 0x42, 0xc7, 0xf1, 0x3d, 0x16, 0x08, 0x5b, 0x84, 0x97, 0x2c, 0x30, 0x5a,
|
||||
0x78, 0xa2, 0xb6, 0x92, 0x8d, 0xa5, 0x88, 0xec, 0xc1, 0x5b, 0x79, 0x13, 0x9b, 0x3a, 0x0e, 0x8b,
|
||||
0xe3, 0x90, 0x1b, 0x80, 0xb6, 0xb7, 0x72, 0xb6, 0xfb, 0x5a, 0x25, 0xdd, 0xba, 0x5e, 0x1c, 0xf9,
|
||||
0x74, 0x6e, 0x07, 0x74, 0xc6, 0x8c, 0xb6, 0x72, 0xab, 0x65, 0x5f, 0xd1, 0x19, 0x23, 0x77, 0xa1,
|
||||
0x3d, 0x0b, 0x93, 0x40, 0xd8, 0x51, 0xe8, 0x05, 0xc2, 0xe8, 0xa0, 0x05, 0xa0, 0xe8, 0x54, 0x4a,
|
||||
0xc8, 0x3b, 0xa0, 0x66, 0x0a, 0x8c, 0x5d, 0x15, 0x57, 0x94, 0x20, 0x1c, 0xef, 0x41, 0x4f, 0xa9,
|
||||
0xb3, 0xf3, 0xf4, 0xd0, 0xa4, 0x8b, 0xd2, 0xec, 0x24, 0x4f, 0xa0, 0x85, 0x78, 0xf0, 0x82, 0xf3,
|
||||
0xd0, 0xe8, 0x63, 0xdc, 0x6e, 0xe5, 0xc2, 0x22, 0x31, 0x71, 0x14, 0x9c, 0x87, 0x56, 0xf3, 0x95,
|
||||
0x1e, 0x91, 0x4f, 0xe0, 0xed, 0xc2, 0x7d, 0x39, 0x9b, 0x51, 0x2f, 0xf0, 0x82, 0xa9, 0x9d, 0xc4,
|
||||
0x2c, 0x36, 0x36, 0x10, 0xe1, 0x46, 0xee, 0xd6, 0x56, 0x6a, 0xf0, 0x32, 0x66, 0x31, 0x79, 0x1b,
|
||||
0x5a, 0x2a, 0x41, 0x6d, 0xcf, 0x35, 0x36, 0xf1, 0x48, 0x4d, 0x25, 0x38, 0x72, 0xc9, 0x03, 0xe8,
|
||||
0x47, 0xa1, 0xef, 0x39, 0x73, 0x3b, 0xbc, 0x62, 0x9c, 0x7b, 0x2e, 0x33, 0xc8, 0xb0, 0xb4, 0xd3,
|
||||
0xb4, 0x7a, 0x4a, 0xfc, 0xb5, 0x96, 0xae, 0x4b, 0x8d, 0x5b, 0x68, 0xb8, 0x92, 0x1a, 0x23, 0x00,
|
||||
0x27, 0x0c, 0x02, 0xe6, 0x20, 0xfc, 0xb6, 0xf0, 0x86, 0x3d, 0x79, 0xc3, 0x83, 0x4c, 0x6a, 0xe5,
|
||||
0x2c, 0x06, 0x9f, 0x43, 0x27, 0x0f, 0x05, 0xb2, 0x01, 0x95, 0x4b, 0x36, 0xd7, 0xf0, 0x97, 0x43,
|
||||
0x32, 0x84, 0xda, 0x15, 0xf5, 0x13, 0x86, 0x90, 0xd7, 0x40, 0x54, 0x4b, 0x2c, 0xa5, 0xf8, 0x59,
|
||||
0xf9, 0x59, 0xc9, 0xfc, 0x73, 0x0d, 0xaa, 0x12, 0x7c, 0xe4, 0x43, 0xe8, 0xfa, 0x8c, 0xc6, 0xcc,
|
||||
0x0e, 0x23, 0xb9, 0x41, 0x8c, 0xae, 0xda, 0x7b, 0x1b, 0x72, 0xd9, 0xb1, 0x54, 0x7c, 0xad, 0xe4,
|
||||
0x56, 0xc7, 0xcf, 0xcd, 0x64, 0x4a, 0x7b, 0x81, 0x60, 0x3c, 0xa0, 0xbe, 0x8d, 0xc9, 0xa0, 0x12,
|
||||
0xac, 0x93, 0x0a, 0x9f, 0xcb, 0xa4, 0x58, 0xc6, 0x51, 0x65, 0x15, 0x47, 0x03, 0x68, 0x62, 0xec,
|
||||
0x3c, 0x16, 0xeb, 0x64, 0xcf, 0xe6, 0x64, 0x0f, 0x9a, 0x33, 0x26, 0xa8, 0xce, 0x35, 0x99, 0x12,
|
||||
0xdb, 0x69, 0xce, 0x8c, 0x4e, 0xb4, 0x42, 0x25, 0x44, 0x66, 0xb7, 0x92, 0x11, 0xf5, 0xd5, 0x8c,
|
||||
0x18, 0x40, 0x33, 0x03, 0x5d, 0x43, 0xbd, 0x70, 0x3a, 0x97, 0x34, 0x1b, 0x31, 0xee, 0x85, 0xae,
|
||||
0xd1, 0x44, 0xa0, 0xe8, 0x99, 0x24, 0xc9, 0x20, 0x99, 0x29, 0x08, 0xb5, 0x14, 0x49, 0x06, 0xc9,
|
||||
0x6c, 0x15, 0x31, 0xb0, 0x84, 0x98, 0x1f, 0x41, 0x8d, 0xfa, 0x1e, 0x8d, 0x31, 0x85, 0xe4, 0xcb,
|
||||
0x6a, 0xbe, 0x1f, 0xed, 0x4b, 0xa9, 0xa5, 0x94, 0xe4, 0x03, 0xe8, 0x4e, 0x79, 0x98, 0x44, 0x36,
|
||||
0x4e, 0x59, 0x6c, 0x74, 0xf0, 0xb6, 0xcb, 0xd6, 0x1d, 0x34, 0xda, 0x57, 0x36, 0x32, 0x03, 0x27,
|
||||
0x61, 0x12, 0xb8, 0xb6, 0xe3, 0xb9, 0x3c, 0x36, 0xba, 0x18, 0x3c, 0x40, 0xd1, 0x81, 0x94, 0xc8,
|
||||
0x14, 0x53, 0x29, 0x90, 0x05, 0xb8, 0x87, 0x36, 0x5d, 0x94, 0x9e, 0xa6, 0x51, 0xfe, 0x31, 0x6c,
|
||||
0xa6, 0x45, 0x69, 0x61, 0xd9, 0x47, 0xcb, 0x8d, 0x54, 0x91, 0x19, 0xef, 0xc0, 0x06, 0xbb, 0x96,
|
||||
0x14, 0xea, 0x09, 0x7b, 0x46, 0xaf, 0x6d, 0x21, 0x7c, 0x9d, 0x52, 0xbd, 0x54, 0x7e, 0x42, 0xaf,
|
||||
0xc7, 0xc2, 0x1f, 0xfc, 0x1c, 0xba, 0x85, 0x37, 0x5a, 0x83, 0xd4, 0xad, 0x3c, 0x52, 0x5b, 0x79,
|
||||
0x74, 0xfe, 0xb5, 0x0a, 0x80, 0x8f, 0xa5, 0x96, 0x2e, 0x53, 0x7c, 0xfe, 0x05, 0xcb, 0x6b, 0x5e,
|
||||
0x90, 0x72, 0x16, 0x08, 0x8d, 0x36, 0x3d, 0xfb, 0x4e, 0xa0, 0xa5, 0x24, 0x5f, 0xcb, 0x91, 0xfc,
|
||||
0x23, 0xa8, 0x4a, 0x50, 0x19, 0xf5, 0x05, 0x17, 0x2f, 0x4e, 0x84, 0xf0, 0x53, 0xd0, 0x43, 0xab,
|
||||
0x15, 0xa4, 0x37, 0x56, 0x91, 0x9e, 0x87, 0x50, 0xb3, 0x08, 0xa1, 0xf7, 0xa0, 0xeb, 0x70, 0x86,
|
||||
0x05, 0xc7, 0x96, 0x9d, 0x83, 0x86, 0x58, 0x27, 0x15, 0x8e, 0xbd, 0x19, 0x93, 0xf1, 0x93, 0xd1,
|
||||
0x06, 0x54, 0xc9, 0xe1, 0xda, 0xc7, 0x68, 0xaf, 0x7b, 0x0c, 0x55, 0xbe, 0x7d, 0xa6, 0x69, 0x1a,
|
||||
0xc7, 0x39, 0xa8, 0x77, 0x0b, 0x50, 0x2f, 0xe0, 0xb9, 0xb7, 0x84, 0xe7, 0x25, 0xd0, 0xf5, 0x57,
|
||||
0x40, 0xf7, 0x2e, 0x74, 0x64, 0x00, 0xe2, 0x88, 0x3a, 0x4c, 0x3a, 0xd8, 0x50, 0x81, 0xc8, 0x64,
|
||||
0x47, 0x2e, 0xa6, 0x68, 0x32, 0x99, 0xcc, 0x2f, 0x42, 0x9f, 0x2d, 0x58, 0xb6, 0x9d, 0xc9, 0x8e,
|
||||
0xdc, 0xc1, 0x47, 0xd0, 0xca, 0x22, 0xfc, 0xbd, 0x80, 0xf3, 0x97, 0x12, 0x74, 0xf2, 0xac, 0x25,
|
||||
0x17, 0x8f, 0xc7, 0xc7, 0xb8, 0xb8, 0x62, 0xc9, 0xa1, 0xac, 0xf7, 0x9c, 0x05, 0xec, 0x15, 0x9d,
|
||||
0xf8, 0xca, 0x41, 0xd3, 0x5a, 0x08, 0xa4, 0xd6, 0x0b, 0x1c, 0xce, 0x66, 0x29, 0x82, 0x2a, 0xd6,
|
||||
0x42, 0x40, 0x3e, 0x06, 0xf0, 0xe2, 0x38, 0x61, 0xea, 0x95, 0xaa, 0x98, 0xd3, 0x83, 0x91, 0x6a,
|
||||
0xfe, 0x46, 0x69, 0xf3, 0x37, 0x1a, 0xa7, 0xcd, 0x9f, 0xd5, 0x42, 0x6b, 0x7c, 0xbe, 0x6d, 0xa8,
|
||||
0xcb, 0xc7, 0x18, 0x1f, 0x23, 0xca, 0x2a, 0x96, 0x9e, 0x99, 0x7f, 0x84, 0xba, 0x6a, 0x13, 0xfe,
|
||||
0xaf, 0x4c, 0x7c, 0x1b, 0x9a, 0xca, 0xb7, 0xe7, 0xea, 0xbc, 0x68, 0xe0, 0xfc, 0xc8, 0x35, 0xff,
|
||||
0x55, 0x82, 0xa6, 0xc5, 0xe2, 0x28, 0x0c, 0x62, 0x96, 0x6b, 0x63, 0x4a, 0xaf, 0x6d, 0x63, 0xca,
|
||||
0x6b, 0xdb, 0x98, 0xb4, 0x39, 0xaa, 0xe4, 0x9a, 0xa3, 0x01, 0x34, 0x39, 0x73, 0x3d, 0xce, 0x1c,
|
||||
0xa1, 0x1b, 0xa9, 0x6c, 0x2e, 0x75, 0xaf, 0x28, 0x97, 0xf5, 0x37, 0x46, 0x92, 0x6f, 0x59, 0xd9,
|
||||
0x9c, 0x3c, 0xcd, 0x57, 0x7f, 0xd5, 0x57, 0x6d, 0xa9, 0xea, 0xaf, 0x8e, 0xbb, 0x5a, 0xfe, 0xcd,
|
||||
0x7f, 0x96, 0x61, 0x63, 0x59, 0xbd, 0x06, 0x04, 0x5b, 0x50, 0x53, 0xf5, 0x41, 0x23, 0x48, 0xac,
|
||||
0x54, 0x86, 0xca, 0x12, 0xaf, 0xfc, 0x62, 0x39, 0x47, 0x5f, 0xff, 0xfa, 0xc5, 0xfc, 0x7d, 0x1f,
|
||||
0x36, 0xe4, 0x29, 0x23, 0xe6, 0x2e, 0x7a, 0x1e, 0x45, 0x38, 0x7d, 0x2d, 0xcf, 0xba, 0x9e, 0x87,
|
||||
0xb0, 0x99, 0x9a, 0x2e, 0x52, 0xb1, 0x5e, 0xb0, 0x3d, 0x4c, 0x33, 0x72, 0x1b, 0xea, 0xe7, 0x21,
|
||||
0x9f, 0x51, 0xa1, 0x39, 0x47, 0xcf, 0x0a, 0x9c, 0x82, 0xe4, 0xd6, 0x54, 0xb0, 0x48, 0x85, 0xb2,
|
||||
0xaf, 0x97, 0xb9, 0x9e, 0xf5, 0xdc, 0x48, 0x3a, 0x4d, 0xab, 0x99, 0xf6, 0xda, 0xe6, 0xaf, 0xa1,
|
||||
0xbf, 0xd4, 0x66, 0xad, 0x09, 0xe4, 0x62, 0xfb, 0x72, 0x61, 0xfb, 0x82, 0xe7, 0xca, 0x92, 0xe7,
|
||||
0xdf, 0xc0, 0xe6, 0x17, 0x34, 0x70, 0x7d, 0xa6, 0xfd, 0xef, 0xf3, 0x69, 0x2c, 0x1b, 0x46, 0xdd,
|
||||
0xf5, 0xdb, 0x9a, 0xec, 0xbb, 0x56, 0x4b, 0x4b, 0x8e, 0x5c, 0x72, 0x0f, 0x1a, 0x5c, 0x59, 0x6b,
|
||||
0xe0, 0xb5, 0x73, 0x7d, 0xa0, 0x95, 0xea, 0xcc, 0x6f, 0x81, 0x14, 0x5c, 0xcb, 0x86, 0x7f, 0x4e,
|
||||
0x76, 0x24, 0x00, 0x15, 0x28, 0x34, 0xb0, 0x3b, 0x79, 0x1c, 0x59, 0x99, 0x96, 0x0c, 0xa1, 0xc2,
|
||||
0x38, 0xd7, 0x5b, 0x60, 0x23, 0xb6, 0xf8, 0xbc, 0xb2, 0xa4, 0xca, 0xfc, 0x09, 0x6c, 0x9e, 0x45,
|
||||
0xcc, 0xf1, 0xa8, 0x8f, 0x9f, 0x46, 0x6a, 0x83, 0xbb, 0x50, 0x93, 0x41, 0x4e, 0x73, 0xb6, 0x85,
|
||||
0x0b, 0x51, 0xad, 0xe4, 0xe6, 0xb7, 0x60, 0xa8, 0x73, 0x1d, 0x5e, 0x7b, 0xb1, 0x60, 0x81, 0xc3,
|
||||
0x0e, 0x2e, 0x98, 0x73, 0xf9, 0x3f, 0xbc, 0xf9, 0x15, 0xdc, 0x5e, 0xb7, 0x43, 0x7a, 0xbe, 0xb6,
|
||||
0x23, 0x67, 0xf6, 0xb9, 0xa4, 0x6a, 0xdc, 0xa3, 0x69, 0x01, 0x8a, 0x3e, 0x97, 0x12, 0xf9, 0x8e,
|
||||
0x4c, 0xae, 0x8b, 0x35, 0x25, 0xea, 0x59, 0x1a, 0x8f, 0xca, 0xcd, 0xf1, 0xf8, 0x5b, 0x09, 0x5a,
|
||||
0x67, 0x4c, 0x24, 0x11, 0xde, 0xe5, 0x6d, 0x68, 0x4d, 0x78, 0x78, 0xc9, 0xf8, 0xe2, 0x2a, 0x4d,
|
||||
0x25, 0x38, 0x72, 0xc9, 0x53, 0xa8, 0x1f, 0x84, 0xc1, 0xb9, 0x37, 0xc5, 0x0f, 0xc5, 0xf6, 0xde,
|
||||
0x6d, 0xc5, 0x2e, 0x7a, 0xed, 0x48, 0xe9, 0x54, 0x59, 0xd5, 0x86, 0x64, 0x08, 0x6d, 0xfd, 0xb9,
|
||||
0xfd, 0xf2, 0xe5, 0xd1, 0xf3, 0xb4, 0x83, 0xcc, 0x89, 0x06, 0x1f, 0x43, 0x3b, 0xb7, 0xf0, 0x7b,
|
||||
0x55, 0x8b, 0x1f, 0x02, 0xe0, 0xee, 0x2a, 0x46, 0x1b, 0xea, 0xaa, 0x7a, 0xa5, 0xbc, 0xda, 0x5d,
|
||||
0x68, 0xc9, 0x8f, 0x15, 0xa5, 0x26, 0x50, 0xcd, 0x7d, 0x57, 0xe3, 0xd8, 0xbc, 0x07, 0x9b, 0x47,
|
||||
0xc1, 0x15, 0xf5, 0x3d, 0x97, 0x0a, 0xf6, 0x25, 0x9b, 0x63, 0x08, 0x56, 0x4e, 0x60, 0x9e, 0x41,
|
||||
0x47, 0x7f, 0xb9, 0xbe, 0xd1, 0x19, 0x3b, 0xfa, 0x8c, 0xdf, 0x9d, 0x44, 0xef, 0x43, 0x5f, 0x3b,
|
||||
0x3d, 0xf6, 0x74, 0x0a, 0xc9, 0x92, 0xce, 0xd9, 0xb9, 0x77, 0xad, 0x5d, 0xeb, 0x99, 0xf9, 0x0c,
|
||||
0x36, 0x72, 0xa6, 0xd9, 0x75, 0x2e, 0xd9, 0x3c, 0x4e, 0xbf, 0xe8, 0xe5, 0x38, 0x8d, 0x40, 0x79,
|
||||
0x11, 0x01, 0x13, 0x7a, 0x7a, 0xe5, 0x0b, 0x26, 0x6e, 0xb8, 0xdd, 0x97, 0xd9, 0x41, 0x5e, 0x30,
|
||||
0xed, 0xfc, 0x3e, 0xd4, 0x98, 0xbc, 0x69, 0xbe, 0x84, 0xe5, 0x23, 0x60, 0x29, 0xf5, 0x9a, 0x0d,
|
||||
0x9f, 0x65, 0x1b, 0x9e, 0x26, 0x6a, 0xc3, 0x37, 0xf4, 0x65, 0xbe, 0x97, 0x1d, 0xe3, 0x34, 0x11,
|
||||
0x37, 0xbd, 0xe8, 0x3d, 0xd8, 0xd4, 0x46, 0xcf, 0x99, 0xcf, 0x04, 0xbb, 0xe1, 0x4a, 0xf7, 0x81,
|
||||
0x14, 0xcc, 0x6e, 0x72, 0x77, 0x07, 0x9a, 0xe3, 0xf1, 0x71, 0xa6, 0x2d, 0x72, 0xa3, 0xf9, 0x09,
|
||||
0x6c, 0x9e, 0x25, 0x6e, 0x78, 0xca, 0xbd, 0x2b, 0xcf, 0x67, 0x53, 0xb5, 0x59, 0xda, 0x6b, 0x96,
|
||||
0x72, 0xbd, 0xe6, 0xda, 0x6a, 0x64, 0xee, 0x00, 0x29, 0x2c, 0xcf, 0xde, 0x2d, 0x4e, 0xdc, 0x50,
|
||||
0xa7, 0x30, 0x8e, 0xcd, 0x1d, 0xe8, 0x8c, 0xa9, 0xac, 0xf7, 0xae, 0xb2, 0x31, 0xa0, 0x21, 0xd4,
|
||||
0x5c, 0x9b, 0xa5, 0x53, 0x73, 0x0f, 0xb6, 0x0e, 0xa8, 0x73, 0xe1, 0x05, 0xd3, 0xe7, 0x5e, 0x2c,
|
||||
0x1b, 0x1e, 0xbd, 0x62, 0x00, 0x4d, 0x57, 0x0b, 0xf4, 0x92, 0x6c, 0x6e, 0x3e, 0x86, 0xb7, 0x72,
|
||||
0x3f, 0x9b, 0x9c, 0x09, 0x9a, 0xc6, 0x63, 0x0b, 0x6a, 0xb1, 0x9c, 0xe1, 0x8a, 0x9a, 0xa5, 0x26,
|
||||
0xe6, 0x57, 0xb0, 0x95, 0x2f, 0xc0, 0xb2, 0xfd, 0x48, 0x2f, 0x8e, 0x8d, 0x41, 0x29, 0xd7, 0x18,
|
||||
0xe8, 0x98, 0x95, 0x17, 0xf5, 0x64, 0x03, 0x2a, 0xbf, 0xfc, 0x66, 0xac, 0xc1, 0x2e, 0x87, 0xe6,
|
||||
0xef, 0xe5, 0xf6, 0x45, 0x7f, 0x6a, 0xfb, 0x42, 0x77, 0x50, 0x7a, 0x93, 0xee, 0x60, 0x0d, 0xde,
|
||||
0x1e, 0xc3, 0xe6, 0x89, 0x1f, 0x3a, 0x97, 0x87, 0x41, 0x2e, 0x1a, 0x06, 0x34, 0x58, 0x90, 0x0f,
|
||||
0x46, 0x3a, 0x35, 0x1f, 0x40, 0xff, 0x38, 0x74, 0xa8, 0x7f, 0x12, 0x26, 0x81, 0xc8, 0xa2, 0x80,
|
||||
0xbf, 0x63, 0x69, 0x53, 0x35, 0x31, 0x1f, 0x43, 0x4f, 0x97, 0xe8, 0xe0, 0x3c, 0x4c, 0x99, 0x71,
|
||||
0x51, 0xcc, 0x4b, 0xc5, 0xbe, 0xda, 0x3c, 0x86, 0xfe, 0xc2, 0x5c, 0xf9, 0x7d, 0x00, 0x75, 0xa5,
|
||||
0xd6, 0x77, 0xeb, 0x67, 0x5f, 0x83, 0xca, 0xd2, 0xd2, 0xea, 0x35, 0x97, 0x9a, 0x41, 0xef, 0x14,
|
||||
0x7f, 0x4f, 0x3c, 0x0c, 0xae, 0x94, 0xb3, 0x23, 0x20, 0xea, 0x17, 0x46, 0x9b, 0x05, 0x57, 0x1e,
|
||||
0x0f, 0x03, 0xec, 0x6f, 0x4b, 0xba, 0x85, 0x49, 0x1d, 0x67, 0x8b, 0x52, 0x0b, 0x6b, 0x33, 0x5a,
|
||||
0x16, 0xad, 0x8d, 0x21, 0x2c, 0x7e, 0xad, 0x90, 0xa5, 0x86, 0xb3, 0x59, 0x28, 0x98, 0x4d, 0x5d,
|
||||
0x37, 0xcd, 0x16, 0x50, 0xa2, 0x7d, 0xd7, 0xe5, 0x7b, 0xff, 0x29, 0x43, 0xe3, 0x33, 0x45, 0xe0,
|
||||
0xe4, 0x53, 0xe8, 0x16, 0xca, 0x35, 0x79, 0x0b, 0x7f, 0xae, 0x58, 0x6e, 0x0e, 0x06, 0xdb, 0x2b,
|
||||
0x62, 0x75, 0xaf, 0x27, 0xd0, 0xc9, 0x17, 0x63, 0x82, 0x85, 0x17, 0x7f, 0x3b, 0x1d, 0xa0, 0xa7,
|
||||
0xd5, 0x4a, 0x7d, 0x06, 0x5b, 0xeb, 0xca, 0x24, 0xb9, 0xb3, 0xd8, 0x61, 0xb5, 0x44, 0x0f, 0xde,
|
||||
0xb9, 0x49, 0x9b, 0x96, 0xd7, 0xc6, 0x81, 0xcf, 0x68, 0x90, 0x44, 0xf9, 0x13, 0x2c, 0x86, 0xe4,
|
||||
0x29, 0x74, 0x0b, 0x85, 0x42, 0xdd, 0x73, 0xa5, 0x76, 0xe4, 0x97, 0xdc, 0x87, 0x1a, 0x16, 0x27,
|
||||
0xd2, 0x2d, 0x54, 0xc9, 0x41, 0x2f, 0x9b, 0xaa, 0xbd, 0x87, 0x50, 0xc5, 0x5f, 0xd4, 0x72, 0x1b,
|
||||
0xe3, 0x8a, 0xac, 0x72, 0xed, 0xfd, 0xbb, 0x04, 0x8d, 0xf4, 0x57, 0xd6, 0xa7, 0x50, 0x95, 0x35,
|
||||
0x80, 0xdc, 0xca, 0xd1, 0x68, 0x5a, 0x3f, 0x06, 0x5b, 0x4b, 0x42, 0xb5, 0xc1, 0x08, 0x2a, 0x2f,
|
||||
0x98, 0x20, 0x24, 0xa7, 0xd4, 0xc5, 0x60, 0x70, 0xab, 0x28, 0xcb, 0xec, 0x4f, 0x93, 0xa2, 0xbd,
|
||||
0xe6, 0xf2, 0x82, 0x7d, 0xc6, 0xd2, 0x1f, 0x41, 0x5d, 0xb1, 0xac, 0x0a, 0xca, 0x0a, 0x3f, 0xab,
|
||||
0xc7, 0x5f, 0xe5, 0xe3, 0xbd, 0x7f, 0x54, 0x01, 0xce, 0xe6, 0xb1, 0x60, 0xb3, 0x5f, 0x79, 0xec,
|
||||
0x15, 0x79, 0x08, 0xfd, 0xe7, 0xec, 0x9c, 0x26, 0xbe, 0xc0, 0xaf, 0x25, 0xc9, 0x26, 0xb9, 0x98,
|
||||
0x60, 0xc3, 0x97, 0x91, 0xf5, 0x7d, 0x68, 0x9f, 0xd0, 0xeb, 0xd7, 0xdb, 0x7d, 0x0a, 0xdd, 0x02,
|
||||
0x07, 0xeb, 0x23, 0x2e, 0xb3, 0xba, 0x3e, 0xe2, 0x2a, 0x5b, 0xdf, 0x87, 0x86, 0x66, 0xe6, 0xfc,
|
||||
0x1e, 0x58, 0xc3, 0x0a, 0x8c, 0xfd, 0x53, 0xe8, 0x2f, 0xf1, 0x72, 0xde, 0x1e, 0x7f, 0x7d, 0x58,
|
||||
0xcb, 0xdb, 0xcf, 0xe4, 0xd7, 0x4e, 0x91, 0x9b, 0xf3, 0x0b, 0x6f, 0x2b, 0x3e, 0x5c, 0x47, 0xde,
|
||||
0x2f, 0x8a, 0xdf, 0x49, 0xf8, 0x95, 0x68, 0x2c, 0xd3, 0x67, 0x4a, 0xde, 0xa9, 0xa3, 0x75, 0x34,
|
||||
0xfc, 0x04, 0x3a, 0x79, 0x06, 0x5d, 0x49, 0xc1, 0x55, 0x7a, 0x7d, 0x04, 0xb0, 0x20, 0xd1, 0xbc,
|
||||
0x3d, 0xc2, 0x63, 0x99, 0x5f, 0x3f, 0x04, 0x58, 0x50, 0xa3, 0x42, 0x55, 0x91, 0x59, 0xd5, 0xb2,
|
||||
0x65, 0xfa, 0x7c, 0x08, 0xad, 0x8c, 0xce, 0xf2, 0x7b, 0xa0, 0x83, 0x22, 0x3b, 0x7e, 0x36, 0xfa,
|
||||
0xed, 0xa3, 0xa9, 0x27, 0x2e, 0x92, 0xc9, 0xc8, 0x09, 0x67, 0xbb, 0x17, 0x34, 0xbe, 0xf0, 0x9c,
|
||||
0x90, 0x47, 0xbb, 0x57, 0x12, 0x4c, 0xbb, 0x2b, 0x7f, 0x00, 0x4d, 0xea, 0xf8, 0xb1, 0xf7, 0xc1,
|
||||
0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xbe, 0xe0, 0xab, 0x1c, 0x1a, 0x00, 0x00,
|
||||
// 2483 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x72, 0x1b, 0xc7,
|
||||
0x11, 0x2e, 0x00, 0xc4, 0x5f, 0xe3, 0x8f, 0x18, 0xd1, 0xcc, 0x0a, 0x96, 0x23, 0x78, 0x1d, 0x49,
|
||||
0xb4, 0x22, 0x81, 0x12, 0x1d, 0xc7, 0x72, 0x52, 0x76, 0x8a, 0xa6, 0x68, 0x99, 0x31, 0x69, 0xb3,
|
||||
0x96, 0x50, 0x9c, 0xbf, 0x2a, 0x78, 0xb0, 0x3b, 0x04, 0xb7, 0xb8, 0xd8, 0xdd, 0xcc, 0xce, 0x52,
|
||||
0xc4, 0x29, 0x6f, 0x91, 0xd7, 0xc8, 0x35, 0x95, 0x4b, 0x6e, 0xa9, 0x54, 0x72, 0xce, 0x6b, 0xe4,
|
||||
0x19, 0x52, 0xd3, 0x33, 0xfb, 0x07, 0x80, 0x96, 0x5c, 0x95, 0xdc, 0x66, 0xba, 0x7b, 0x7a, 0x66,
|
||||
0x7a, 0xbe, 0xfe, 0xba, 0x17, 0x80, 0xbb, 0x5e, 0x30, 0x73, 0x6d, 0xea, 0xed, 0x86, 0x5e, 0x3c,
|
||||
0x73, 0xfd, 0xdd, 0x70, 0xba, 0x3b, 0xa5, 0xf6, 0x25, 0xf3, 0x9d, 0x51, 0xc8, 0x03, 0x11, 0x90,
|
||||
0x72, 0x38, 0x1d, 0xdc, 0x9d, 0x05, 0xc1, 0xcc, 0x63, 0xbb, 0x28, 0x99, 0xc6, 0xe7, 0xbb, 0xc2,
|
||||
0x9d, 0xb3, 0x48, 0xd0, 0x79, 0xa8, 0x8c, 0x06, 0xdb, 0x89, 0x17, 0xd7, 0x61, 0xbe, 0x70, 0xc5,
|
||||
0x42, 0xcb, 0xb7, 0x8a, 0xde, 0x95, 0xd4, 0xac, 0x43, 0xf5, 0x70, 0x1e, 0x8a, 0x85, 0x39, 0x84,
|
||||
0xda, 0x17, 0x8c, 0x3a, 0x8c, 0x93, 0x6d, 0xa8, 0x5d, 0xe0, 0xc8, 0x28, 0x0d, 0x2b, 0x3b, 0x4d,
|
||||
0x4b, 0xcf, 0xcc, 0xdf, 0x01, 0x9c, 0xca, 0x35, 0x87, 0x9c, 0x07, 0x9c, 0xdc, 0x86, 0x06, 0xe3,
|
||||
0x7c, 0x22, 0x16, 0x21, 0x33, 0x4a, 0xc3, 0xd2, 0x4e, 0xc7, 0xaa, 0x33, 0xce, 0xc7, 0x8b, 0x90,
|
||||
0x91, 0x1f, 0x80, 0x1c, 0x4e, 0xe6, 0xd1, 0xcc, 0x28, 0x0f, 0x4b, 0xd2, 0x03, 0xe3, 0xfc, 0x24,
|
||||
0x9a, 0x25, 0x6b, 0xec, 0xc0, 0x61, 0x46, 0x65, 0x58, 0xda, 0xa9, 0xe0, 0x9a, 0x83, 0xc0, 0x61,
|
||||
0xe6, 0x9f, 0x4a, 0x50, 0x3d, 0xa5, 0xe2, 0x22, 0x22, 0x04, 0x36, 0x78, 0x10, 0x08, 0xbd, 0x39,
|
||||
0x8e, 0xc9, 0x0e, 0xf4, 0x62, 0x9f, 0xc6, 0xe2, 0x42, 0xde, 0xc8, 0xa6, 0x82, 0x39, 0x46, 0x19,
|
||||
0xd5, 0xcb, 0x62, 0xf2, 0x1e, 0x74, 0xbc, 0xc0, 0xa6, 0xde, 0x24, 0x12, 0x01, 0xa7, 0x33, 0xb9,
|
||||
0x8f, 0xb4, 0x6b, 0xa3, 0xf0, 0x4c, 0xc9, 0xc8, 0x43, 0xe8, 0x47, 0x8c, 0x7a, 0x93, 0x57, 0x9c,
|
||||
0x86, 0xa9, 0xe1, 0x86, 0x72, 0x28, 0x15, 0xdf, 0x70, 0x1a, 0x6a, 0x5b, 0xf3, 0x6f, 0x35, 0xa8,
|
||||
0x5b, 0xec, 0x0f, 0x31, 0x8b, 0x04, 0xe9, 0x42, 0xd9, 0x75, 0xf0, 0xb6, 0x4d, 0xab, 0xec, 0x3a,
|
||||
0x64, 0x04, 0xc4, 0x62, 0xa1, 0x27, 0xb7, 0x76, 0x03, 0xff, 0xc0, 0x8b, 0x23, 0xc1, 0xb8, 0xbe,
|
||||
0xf3, 0x1a, 0x0d, 0xb9, 0x03, 0xcd, 0x20, 0x64, 0x1c, 0x65, 0x18, 0x80, 0xa6, 0x95, 0x09, 0xe4,
|
||||
0xc5, 0x43, 0x2a, 0x2e, 0x8c, 0x0d, 0x54, 0xe0, 0x58, 0xca, 0x1c, 0x2a, 0xa8, 0x51, 0x55, 0x32,
|
||||
0x39, 0x26, 0x26, 0xd4, 0x22, 0x66, 0x73, 0x26, 0x8c, 0xda, 0xb0, 0xb4, 0xd3, 0xda, 0x83, 0x51,
|
||||
0x38, 0x1d, 0x9d, 0xa1, 0xc4, 0xd2, 0x1a, 0x72, 0x07, 0x36, 0x64, 0x5c, 0x8c, 0x3a, 0x5a, 0x34,
|
||||
0xa4, 0xc5, 0x7e, 0x2c, 0x2e, 0x2c, 0x94, 0x92, 0x3d, 0xa8, 0xab, 0x37, 0x8d, 0x8c, 0xc6, 0xb0,
|
||||
0xb2, 0xd3, 0xda, 0x33, 0xa4, 0x81, 0xbe, 0xe5, 0x48, 0xc1, 0x20, 0x3a, 0xf4, 0x05, 0x5f, 0x58,
|
||||
0x89, 0x21, 0x79, 0x17, 0xda, 0xb6, 0xe7, 0x32, 0x5f, 0x4c, 0x44, 0x70, 0xc9, 0x7c, 0xa3, 0x89,
|
||||
0x27, 0x6a, 0x29, 0xd9, 0x58, 0x8a, 0xc8, 0x1e, 0xbc, 0x95, 0x37, 0x99, 0x50, 0xdb, 0x66, 0x51,
|
||||
0x14, 0x70, 0x03, 0xd0, 0xf6, 0x56, 0xce, 0x76, 0x5f, 0xab, 0xa4, 0x5b, 0xc7, 0x8d, 0x42, 0x8f,
|
||||
0x2e, 0x26, 0x3e, 0x9d, 0x33, 0xa3, 0xa5, 0xdc, 0x6a, 0xd9, 0x57, 0x74, 0xce, 0xc8, 0x5d, 0x68,
|
||||
0xcd, 0x83, 0xd8, 0x17, 0x93, 0x30, 0x70, 0x7d, 0x61, 0xb4, 0xd1, 0x02, 0x50, 0x74, 0x2a, 0x25,
|
||||
0xe4, 0x1d, 0x50, 0x33, 0x05, 0xc6, 0x8e, 0x8a, 0x2b, 0x4a, 0x10, 0x8e, 0xf7, 0xa0, 0xab, 0xd4,
|
||||
0xe9, 0x79, 0xba, 0x68, 0xd2, 0x41, 0x69, 0x7a, 0x92, 0x27, 0xd0, 0x44, 0x3c, 0xb8, 0xfe, 0x79,
|
||||
0x60, 0xf4, 0x30, 0x6e, 0xb7, 0x72, 0x61, 0x91, 0x98, 0x38, 0xf2, 0xcf, 0x03, 0xab, 0xf1, 0x4a,
|
||||
0x8f, 0xc8, 0x27, 0xf0, 0x76, 0xe1, 0xbe, 0x9c, 0xcd, 0xa9, 0xeb, 0xbb, 0xfe, 0x6c, 0x12, 0x47,
|
||||
0x2c, 0x32, 0x36, 0x11, 0xe1, 0x46, 0xee, 0xd6, 0x56, 0x62, 0xf0, 0x32, 0x62, 0x11, 0x79, 0x1b,
|
||||
0x9a, 0x2a, 0x41, 0x27, 0xae, 0x63, 0xf4, 0xf1, 0x48, 0x0d, 0x25, 0x38, 0x72, 0xc8, 0x03, 0xe8,
|
||||
0x85, 0x81, 0xe7, 0xda, 0x8b, 0x49, 0x70, 0xc5, 0x38, 0x77, 0x1d, 0x66, 0x90, 0x61, 0x69, 0xa7,
|
||||
0x61, 0x75, 0x95, 0xf8, 0x6b, 0x2d, 0x5d, 0x97, 0x1a, 0xb7, 0xd0, 0x70, 0x25, 0x35, 0x46, 0x00,
|
||||
0x76, 0xe0, 0xfb, 0xcc, 0x46, 0xf8, 0x6d, 0xe1, 0x0d, 0xbb, 0xf2, 0x86, 0x07, 0xa9, 0xd4, 0xca,
|
||||
0x59, 0x0c, 0x3e, 0x87, 0x76, 0x1e, 0x0a, 0x64, 0x13, 0x2a, 0x97, 0x6c, 0xa1, 0xe1, 0x2f, 0x87,
|
||||
0x64, 0x08, 0xd5, 0x2b, 0xea, 0xc5, 0x0c, 0x21, 0xaf, 0x81, 0xa8, 0x96, 0x58, 0x4a, 0xf1, 0xb3,
|
||||
0xf2, 0xb3, 0x92, 0xf9, 0xd7, 0x2a, 0x6c, 0x48, 0xf0, 0x91, 0x0f, 0xa1, 0xe3, 0x31, 0x1a, 0xb1,
|
||||
0x49, 0x10, 0xca, 0x0d, 0x22, 0x74, 0xd5, 0xda, 0xdb, 0x94, 0xcb, 0x8e, 0xa5, 0xe2, 0x6b, 0x25,
|
||||
0xb7, 0xda, 0x5e, 0x6e, 0x26, 0x53, 0xda, 0xf5, 0x05, 0xe3, 0x3e, 0xf5, 0x26, 0x98, 0x0c, 0x2a,
|
||||
0xc1, 0xda, 0x89, 0xf0, 0xb9, 0x4c, 0x8a, 0x65, 0x1c, 0x55, 0x56, 0x71, 0x34, 0x80, 0x06, 0xc6,
|
||||
0xce, 0x65, 0x91, 0x4e, 0xf6, 0x74, 0x4e, 0xf6, 0xa0, 0x31, 0x67, 0x82, 0xea, 0x5c, 0x93, 0x29,
|
||||
0xb1, 0x9d, 0xe4, 0xcc, 0xe8, 0x44, 0x2b, 0x54, 0x42, 0xa4, 0x76, 0x2b, 0x19, 0x51, 0x5b, 0xcd,
|
||||
0x88, 0x01, 0x34, 0x52, 0xd0, 0xd5, 0xd5, 0x0b, 0x27, 0x73, 0x49, 0xb3, 0x21, 0xe3, 0x6e, 0xe0,
|
||||
0x18, 0x0d, 0x04, 0x8a, 0x9e, 0x49, 0x92, 0xf4, 0xe3, 0xb9, 0x82, 0x50, 0x53, 0x91, 0xa4, 0x1f,
|
||||
0xcf, 0x57, 0x11, 0x03, 0x4b, 0x88, 0xf9, 0x11, 0x54, 0xa9, 0xe7, 0xd2, 0x08, 0x53, 0x48, 0xbe,
|
||||
0xac, 0xe6, 0xfb, 0xd1, 0xbe, 0x94, 0x5a, 0x4a, 0x49, 0x3e, 0x80, 0xce, 0x8c, 0x07, 0x71, 0x38,
|
||||
0xc1, 0x29, 0x8b, 0x8c, 0x36, 0xde, 0x76, 0xd9, 0xba, 0x8d, 0x46, 0xfb, 0xca, 0x46, 0x66, 0xe0,
|
||||
0x34, 0x88, 0x7d, 0x67, 0x62, 0xbb, 0x0e, 0x8f, 0x8c, 0x0e, 0x06, 0x0f, 0x50, 0x74, 0x20, 0x25,
|
||||
0x32, 0xc5, 0x54, 0x0a, 0xa4, 0x01, 0xee, 0xa2, 0x4d, 0x07, 0xa5, 0xa7, 0x49, 0x94, 0x7f, 0x0c,
|
||||
0xfd, 0xa4, 0x28, 0x65, 0x96, 0x3d, 0xb4, 0xdc, 0x4c, 0x14, 0xa9, 0xf1, 0x0e, 0x6c, 0xb2, 0x6b,
|
||||
0x49, 0xa1, 0xae, 0x98, 0xcc, 0xe9, 0xf5, 0x44, 0x08, 0x4f, 0xa7, 0x54, 0x37, 0x91, 0x9f, 0xd0,
|
||||
0xeb, 0xb1, 0xf0, 0x64, 0xfe, 0xab, 0xdd, 0x31, 0xff, 0xfb, 0x58, 0x8c, 0x9a, 0x28, 0x91, 0xf9,
|
||||
0x3f, 0xf8, 0x39, 0x74, 0x0a, 0x4f, 0xb8, 0x06, 0xc8, 0x5b, 0x79, 0x20, 0x37, 0xf3, 0xe0, 0xfd,
|
||||
0xe7, 0x06, 0x00, 0xbe, 0xa5, 0x5a, 0xba, 0x5c, 0x01, 0xf2, 0x0f, 0x5c, 0x5e, 0xf3, 0xc0, 0x94,
|
||||
0x33, 0x5f, 0x68, 0x30, 0xea, 0xd9, 0x77, 0xe2, 0x30, 0xa9, 0x01, 0xd5, 0x5c, 0x0d, 0x78, 0x04,
|
||||
0x1b, 0x12, 0x73, 0x46, 0x2d, 0xa3, 0xea, 0xec, 0x44, 0x88, 0x4e, 0x85, 0x4c, 0xb4, 0x5a, 0x49,
|
||||
0x84, 0xfa, 0x6a, 0x22, 0xe4, 0x11, 0xd6, 0x28, 0x22, 0xec, 0x3d, 0xe8, 0xd8, 0x9c, 0x61, 0x3d,
|
||||
0x9a, 0xc8, 0xc6, 0x42, 0x23, 0xb0, 0x9d, 0x08, 0xc7, 0xee, 0x9c, 0xc9, 0xf8, 0xc9, 0xc7, 0x00,
|
||||
0x54, 0xc9, 0xe1, 0xda, 0xb7, 0x6a, 0xad, 0x7d, 0x2b, 0xac, 0xee, 0x1e, 0xd3, 0x2c, 0x8e, 0xe3,
|
||||
0x5c, 0x26, 0x74, 0x0a, 0x99, 0x50, 0x80, 0x7b, 0x77, 0x09, 0xee, 0x4b, 0x98, 0xec, 0xad, 0x60,
|
||||
0xf2, 0x5d, 0x68, 0xcb, 0x00, 0x44, 0x21, 0xb5, 0x99, 0x74, 0xb0, 0xa9, 0x02, 0x91, 0xca, 0x8e,
|
||||
0x1c, 0xcc, 0xe0, 0x78, 0x3a, 0x5d, 0x5c, 0x04, 0x1e, 0xcb, 0x48, 0xb8, 0x95, 0xca, 0x8e, 0x1c,
|
||||
0x79, 0x5e, 0x44, 0x15, 0x41, 0x54, 0xe1, 0x78, 0xf0, 0x11, 0x34, 0xd3, 0xa8, 0x7f, 0x2f, 0x30,
|
||||
0xfd, 0xb9, 0x04, 0xed, 0x3c, 0xd1, 0xc9, 0xc5, 0xe3, 0xf1, 0x31, 0x2e, 0xae, 0x58, 0x72, 0x28,
|
||||
0x5b, 0x04, 0xce, 0x7c, 0xf6, 0x8a, 0x4e, 0x3d, 0xe5, 0xa0, 0x61, 0x65, 0x02, 0xa9, 0x75, 0x7d,
|
||||
0x9b, 0xb3, 0x79, 0x82, 0xaa, 0x8a, 0x95, 0x09, 0xc8, 0xc7, 0x00, 0x6e, 0x14, 0xc5, 0x4c, 0xbd,
|
||||
0xdc, 0x06, 0xd2, 0xc0, 0x60, 0xa4, 0xfa, 0xc5, 0x51, 0xd2, 0x2f, 0x8e, 0xc6, 0x49, 0xbf, 0x68,
|
||||
0x35, 0xd1, 0x1a, 0x9f, 0x74, 0x1b, 0x6a, 0xf2, 0x81, 0xc6, 0xc7, 0x88, 0xbc, 0x8a, 0xa5, 0x67,
|
||||
0xe6, 0x1f, 0xa1, 0xa6, 0x3a, 0x8b, 0xff, 0x2b, 0x79, 0xdf, 0x86, 0x86, 0xf2, 0xed, 0x3a, 0x3a,
|
||||
0x57, 0xea, 0x38, 0x3f, 0x72, 0xcc, 0x7f, 0x95, 0xa0, 0x61, 0xb1, 0x28, 0x0c, 0xfc, 0x88, 0xe5,
|
||||
0x3a, 0x9f, 0xd2, 0x6b, 0x3b, 0x9f, 0xf2, 0xda, 0xce, 0x27, 0xe9, 0xa7, 0x2a, 0xb9, 0x7e, 0x6a,
|
||||
0x00, 0x0d, 0xce, 0x1c, 0x97, 0x33, 0x5b, 0xe8, 0xde, 0x2b, 0x9d, 0x4b, 0xdd, 0x2b, 0xca, 0x65,
|
||||
0xc9, 0x8e, 0xb0, 0x2e, 0x34, 0xad, 0x74, 0x4e, 0x9e, 0xe6, 0x1b, 0x06, 0xd5, 0x8a, 0x6d, 0xa9,
|
||||
0x86, 0x41, 0x1d, 0x77, 0xb5, 0x63, 0x30, 0xff, 0x51, 0x86, 0xcd, 0x65, 0xf5, 0x1a, 0x10, 0x6c,
|
||||
0x41, 0x55, 0x95, 0x14, 0x8d, 0x20, 0xb1, 0x52, 0x4c, 0x2a, 0x4b, 0x5c, 0xf3, 0x8b, 0xe5, 0xbc,
|
||||
0x7d, 0xfd, 0xeb, 0x17, 0x73, 0xfa, 0x7d, 0xd8, 0x94, 0xa7, 0x0c, 0x99, 0x93, 0xb5, 0x49, 0x8a,
|
||||
0x84, 0x7a, 0x5a, 0x9e, 0x36, 0x4a, 0x0f, 0xa1, 0x9f, 0x98, 0x66, 0xe9, 0x59, 0x2b, 0xd8, 0x1e,
|
||||
0x26, 0x59, 0xba, 0x0d, 0xb5, 0xf3, 0x80, 0xcf, 0xa9, 0xd0, 0x3c, 0xa4, 0x67, 0x05, 0x9e, 0x41,
|
||||
0xc2, 0x6b, 0x28, 0x58, 0x24, 0x42, 0xf9, 0x29, 0x20, 0xf3, 0x3f, 0x6d, 0xd3, 0x91, 0x88, 0x1a,
|
||||
0x56, 0x23, 0x69, 0xcf, 0xcd, 0x5f, 0x43, 0x6f, 0xa9, 0x33, 0x5b, 0x13, 0xc8, 0x6c, 0xfb, 0x72,
|
||||
0x61, 0xfb, 0x82, 0xe7, 0xca, 0x92, 0xe7, 0xdf, 0x40, 0xff, 0x0b, 0xea, 0x3b, 0x1e, 0xd3, 0xfe,
|
||||
0xf7, 0xf9, 0x2c, 0x92, 0x35, 0x46, 0x7f, 0x28, 0x4c, 0x74, 0x01, 0xe8, 0x58, 0x4d, 0x2d, 0x39,
|
||||
0x72, 0xc8, 0x3d, 0xa8, 0x73, 0x65, 0xad, 0x81, 0xd7, 0xca, 0xb5, 0x8e, 0x56, 0xa2, 0x33, 0xbf,
|
||||
0x05, 0x52, 0x70, 0x2d, 0xbf, 0x11, 0x16, 0x64, 0x47, 0x02, 0x50, 0x81, 0x42, 0x03, 0xbb, 0x9d,
|
||||
0xc7, 0x91, 0x95, 0x6a, 0xc9, 0x10, 0x2a, 0x8c, 0x73, 0xbd, 0x05, 0xf6, 0x6e, 0xd9, 0x17, 0x99,
|
||||
0x25, 0x55, 0xe6, 0x4f, 0xa0, 0x7f, 0x16, 0x32, 0xdb, 0xa5, 0x1e, 0x7e, 0x4d, 0xa9, 0x0d, 0xee,
|
||||
0x42, 0x55, 0x06, 0x39, 0xc9, 0xd9, 0x26, 0x2e, 0x44, 0xb5, 0x92, 0x9b, 0xdf, 0x82, 0xa1, 0xce,
|
||||
0x75, 0x78, 0xed, 0x46, 0x82, 0xf9, 0x36, 0x3b, 0xb8, 0x60, 0xf6, 0xe5, 0xff, 0xf0, 0xe6, 0x57,
|
||||
0x70, 0x7b, 0xdd, 0x0e, 0xc9, 0xf9, 0x5a, 0xb6, 0x9c, 0x4d, 0xce, 0x25, 0x7d, 0xe3, 0x1e, 0x0d,
|
||||
0x0b, 0x50, 0xf4, 0xb9, 0x94, 0xc8, 0x77, 0x64, 0x72, 0x5d, 0xa4, 0x29, 0x51, 0xcf, 0x92, 0x78,
|
||||
0x54, 0x6e, 0x8e, 0xc7, 0x5f, 0x4a, 0xd0, 0x3c, 0x63, 0x22, 0x0e, 0xf1, 0x2e, 0x6f, 0x43, 0x73,
|
||||
0xca, 0x83, 0x4b, 0xc6, 0xb3, 0xab, 0x34, 0x94, 0xe0, 0xc8, 0x21, 0x4f, 0xa1, 0x76, 0x10, 0xf8,
|
||||
0xe7, 0xee, 0x0c, 0xbf, 0x2d, 0x5b, 0x7b, 0xb7, 0x15, 0xbb, 0xe8, 0xb5, 0x23, 0xa5, 0x53, 0xa5,
|
||||
0x56, 0x1b, 0x92, 0x21, 0xb4, 0xf4, 0x17, 0xfa, 0xcb, 0x97, 0x47, 0xcf, 0x93, 0xa6, 0x33, 0x27,
|
||||
0x1a, 0x7c, 0x0c, 0xad, 0xdc, 0xc2, 0xef, 0x55, 0x2d, 0x7e, 0x08, 0x80, 0xbb, 0xab, 0x18, 0x6d,
|
||||
0xaa, 0xab, 0xea, 0x95, 0xf2, 0x6a, 0x77, 0xa1, 0x29, 0xfb, 0x1b, 0xa5, 0x4e, 0xea, 0x54, 0x29,
|
||||
0xab, 0x53, 0xe6, 0x3d, 0xe8, 0x1f, 0xf9, 0x57, 0xd4, 0x73, 0x1d, 0x2a, 0xd8, 0x97, 0x6c, 0x81,
|
||||
0x21, 0x58, 0x39, 0x81, 0x79, 0x06, 0x6d, 0xfd, 0xb1, 0xfb, 0x46, 0x67, 0x6c, 0xeb, 0x33, 0x7e,
|
||||
0x77, 0x12, 0xbd, 0x0f, 0x3d, 0xed, 0xf4, 0xd8, 0xd5, 0x29, 0x24, 0xcb, 0x3c, 0x67, 0xe7, 0xee,
|
||||
0xb5, 0x76, 0xad, 0x67, 0xe6, 0x33, 0xd8, 0xcc, 0x99, 0xa6, 0xd7, 0xb9, 0x64, 0x8b, 0x28, 0xf9,
|
||||
0x11, 0x40, 0x8e, 0x93, 0x08, 0x94, 0xb3, 0x08, 0x98, 0xd0, 0xd5, 0x2b, 0x5f, 0x30, 0x71, 0xc3,
|
||||
0xed, 0xbe, 0x4c, 0x0f, 0xf2, 0x82, 0x69, 0xe7, 0xf7, 0xa1, 0xca, 0xe4, 0x4d, 0xf3, 0x25, 0x2c,
|
||||
0x1f, 0x01, 0x4b, 0xa9, 0xd7, 0x6c, 0xf8, 0x2c, 0xdd, 0xf0, 0x34, 0x56, 0x1b, 0xbe, 0xa1, 0x2f,
|
||||
0xf3, 0xbd, 0xf4, 0x18, 0xa7, 0xb1, 0xb8, 0xe9, 0x45, 0xef, 0x41, 0x5f, 0x1b, 0x3d, 0x67, 0x1e,
|
||||
0x13, 0xec, 0x86, 0x2b, 0xdd, 0x07, 0x52, 0x30, 0xbb, 0xc9, 0xdd, 0x1d, 0x68, 0x8c, 0xc7, 0xc7,
|
||||
0xa9, 0xb6, 0xc8, 0x8d, 0xe6, 0x27, 0xd0, 0x3f, 0x8b, 0x9d, 0xe0, 0x94, 0xbb, 0x57, 0xae, 0xc7,
|
||||
0x66, 0x6a, 0xb3, 0xa4, 0xff, 0x2c, 0xe5, 0xfa, 0xcf, 0xb5, 0xd5, 0xc8, 0xdc, 0x01, 0x52, 0x58,
|
||||
0x9e, 0xbe, 0x5b, 0x14, 0x3b, 0x81, 0x4e, 0x61, 0x1c, 0x9b, 0x3b, 0xd0, 0x1e, 0x53, 0x59, 0xef,
|
||||
0x1d, 0x65, 0x63, 0x40, 0x5d, 0xa8, 0xb9, 0x36, 0x4b, 0xa6, 0xe6, 0x1e, 0x6c, 0x1d, 0x50, 0xfb,
|
||||
0xc2, 0xf5, 0x67, 0xcf, 0xdd, 0x48, 0x36, 0x3c, 0x7a, 0xc5, 0x00, 0x1a, 0x8e, 0x16, 0xe8, 0x25,
|
||||
0xe9, 0xdc, 0x7c, 0x0c, 0x6f, 0xe5, 0x7e, 0x69, 0x39, 0x13, 0x34, 0x89, 0xc7, 0x16, 0x54, 0x23,
|
||||
0x39, 0xc3, 0x15, 0x55, 0x4b, 0x4d, 0xcc, 0xaf, 0x60, 0x2b, 0x5f, 0x80, 0x65, 0xfb, 0x91, 0x5c,
|
||||
0x1c, 0x1b, 0x83, 0x52, 0xae, 0x31, 0xd0, 0x31, 0x2b, 0x67, 0xf5, 0x64, 0x13, 0x2a, 0xbf, 0xfc,
|
||||
0x66, 0xac, 0xc1, 0x2e, 0x87, 0xe6, 0xef, 0xe5, 0xf6, 0x45, 0x7f, 0x6a, 0xfb, 0x42, 0x77, 0x50,
|
||||
0x7a, 0x93, 0xee, 0x60, 0x0d, 0xde, 0x1e, 0x43, 0xff, 0xc4, 0x0b, 0xec, 0xcb, 0x43, 0x3f, 0x17,
|
||||
0x0d, 0x03, 0xea, 0xcc, 0xcf, 0x07, 0x23, 0x99, 0x9a, 0x0f, 0xa0, 0x77, 0x1c, 0xd8, 0xd4, 0x3b,
|
||||
0x09, 0x62, 0x5f, 0xa4, 0x51, 0xc0, 0x9f, 0xbe, 0xb4, 0xa9, 0x9a, 0x98, 0x8f, 0xa1, 0xab, 0x4b,
|
||||
0xb4, 0x7f, 0x1e, 0x24, 0xcc, 0x98, 0x15, 0xf3, 0x52, 0xb1, 0xd7, 0x36, 0x8f, 0xa1, 0x97, 0x99,
|
||||
0x2b, 0xbf, 0x0f, 0xa0, 0xa6, 0xd4, 0xfa, 0x6e, 0xbd, 0xf4, 0x03, 0x52, 0x59, 0x5a, 0x5a, 0xbd,
|
||||
0xe6, 0x52, 0x73, 0xe8, 0x9e, 0xe2, 0x4f, 0x90, 0x87, 0xfe, 0x95, 0x72, 0x76, 0x04, 0x44, 0xfd,
|
||||
0x28, 0x39, 0x61, 0xfe, 0x95, 0xcb, 0x03, 0x1f, 0xfb, 0xdb, 0x92, 0x6e, 0x61, 0x12, 0xc7, 0xe9,
|
||||
0xa2, 0xc4, 0xc2, 0xea, 0x87, 0xcb, 0xa2, 0xb5, 0x31, 0x84, 0xec, 0x07, 0x0e, 0x59, 0x6a, 0x38,
|
||||
0x9b, 0x07, 0x82, 0x4d, 0xa8, 0xe3, 0x24, 0xd9, 0x02, 0x4a, 0xb4, 0xef, 0x38, 0x7c, 0xef, 0x3f,
|
||||
0x65, 0xa8, 0x7f, 0xa6, 0x08, 0x9c, 0x7c, 0x0a, 0x9d, 0x42, 0xb9, 0x26, 0x6f, 0xe1, 0x2f, 0x1c,
|
||||
0xcb, 0xcd, 0xc1, 0x60, 0x7b, 0x45, 0xac, 0xee, 0xf5, 0x04, 0xda, 0xf9, 0x62, 0x4c, 0xb0, 0xf0,
|
||||
0xe2, 0xcf, 0xad, 0x03, 0xf4, 0xb4, 0x5a, 0xa9, 0xcf, 0x60, 0x6b, 0x5d, 0x99, 0x24, 0x77, 0xb2,
|
||||
0x1d, 0x56, 0x4b, 0xf4, 0xe0, 0x9d, 0x9b, 0xb4, 0x49, 0x79, 0xad, 0x1f, 0x78, 0x8c, 0xfa, 0x71,
|
||||
0x98, 0x3f, 0x41, 0x36, 0x24, 0x4f, 0xa1, 0x53, 0x28, 0x14, 0xea, 0x9e, 0x2b, 0xb5, 0x23, 0xbf,
|
||||
0xe4, 0x3e, 0x54, 0xb1, 0x38, 0x91, 0x4e, 0xa1, 0x4a, 0x0e, 0xba, 0xe9, 0x54, 0xed, 0x3d, 0x84,
|
||||
0x0d, 0xfc, 0x11, 0x2e, 0xb7, 0x31, 0xae, 0x48, 0x2b, 0xd7, 0xde, 0xbf, 0x4b, 0x50, 0x4f, 0x7e,
|
||||
0x98, 0x7d, 0x0a, 0x1b, 0xb2, 0x06, 0x90, 0x5b, 0x39, 0x1a, 0x4d, 0xea, 0xc7, 0x60, 0x6b, 0x49,
|
||||
0xa8, 0x36, 0x18, 0x41, 0xe5, 0x05, 0x13, 0x84, 0xe4, 0x94, 0xba, 0x18, 0x0c, 0x6e, 0x15, 0x65,
|
||||
0xa9, 0xfd, 0x69, 0x5c, 0xb4, 0xd7, 0x5c, 0x5e, 0xb0, 0x4f, 0x59, 0xfa, 0x23, 0xa8, 0x29, 0x96,
|
||||
0x55, 0x41, 0x59, 0xe1, 0x67, 0xf5, 0xf8, 0xab, 0x7c, 0xbc, 0xf7, 0xf7, 0x0d, 0x80, 0xb3, 0x45,
|
||||
0x24, 0xd8, 0xfc, 0x57, 0x2e, 0x7b, 0x45, 0x1e, 0x42, 0xef, 0x39, 0x3b, 0xa7, 0xb1, 0x27, 0xf0,
|
||||
0x6b, 0x49, 0xb2, 0x49, 0x2e, 0x26, 0xd8, 0xf0, 0xa5, 0x64, 0x7d, 0x1f, 0x5a, 0x27, 0xf4, 0xfa,
|
||||
0xf5, 0x76, 0x9f, 0x42, 0xa7, 0xc0, 0xc1, 0xfa, 0x88, 0xcb, 0xac, 0xae, 0x8f, 0xb8, 0xca, 0xd6,
|
||||
0xf7, 0xa1, 0xae, 0x99, 0x39, 0xbf, 0x07, 0xd6, 0xb0, 0x02, 0x63, 0xff, 0x14, 0x7a, 0x4b, 0xbc,
|
||||
0x9c, 0xb7, 0xc7, 0x5f, 0x24, 0xd6, 0xf2, 0xf6, 0x33, 0xf9, 0xb5, 0x53, 0xe4, 0xe6, 0xfc, 0xc2,
|
||||
0xdb, 0x8a, 0x0f, 0xd7, 0x91, 0xf7, 0x8b, 0xe2, 0x77, 0x12, 0x7e, 0x25, 0x1a, 0xcb, 0xf4, 0x99,
|
||||
0x90, 0x77, 0xe2, 0x68, 0x1d, 0x0d, 0x3f, 0x81, 0x76, 0x9e, 0x41, 0x57, 0x52, 0x70, 0x95, 0x5e,
|
||||
0x1f, 0x01, 0x64, 0x24, 0x9a, 0xb7, 0x47, 0x78, 0x2c, 0xf3, 0xeb, 0x87, 0x00, 0x19, 0x35, 0x2a,
|
||||
0x54, 0x15, 0x99, 0x55, 0x2d, 0x5b, 0xa6, 0xcf, 0x87, 0xd0, 0x4c, 0xe9, 0x2c, 0xbf, 0x07, 0x3a,
|
||||
0x28, 0xb2, 0xe3, 0x67, 0xa3, 0xdf, 0x3e, 0x9a, 0xb9, 0xe2, 0x22, 0x9e, 0x8e, 0xec, 0x60, 0xbe,
|
||||
0x7b, 0x41, 0xa3, 0x0b, 0xd7, 0x0e, 0x78, 0xb8, 0x7b, 0x25, 0xc1, 0xb4, 0xbb, 0xf2, 0x9f, 0xd1,
|
||||
0xb4, 0x86, 0x1f, 0x7b, 0x1f, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x93, 0x15, 0xb9, 0x42, 0x4f,
|
||||
0x1a, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -207,6 +207,9 @@ message Auth {
|
||||
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
||||
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
||||
int64 explicit_max_ttl = 16;
|
||||
|
||||
// TokenType is the type of token being requested
|
||||
uint32 token_type = 17;
|
||||
}
|
||||
|
||||
message TokenEntry {
|
||||
@@ -227,6 +230,7 @@ message TokenEntry {
|
||||
repeated string bound_cidrs = 15;
|
||||
string namespace_id = 16;
|
||||
string cubbyhole_id = 17;
|
||||
uint32 type = 18;
|
||||
}
|
||||
|
||||
message LeaseOptions {
|
||||
|
||||
@@ -486,6 +486,7 @@ func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) {
|
||||
|
||||
return &Auth{
|
||||
LeaseOptions: lo,
|
||||
TokenType: uint32(a.TokenType),
|
||||
InternalData: string(buf[:]),
|
||||
DisplayName: a.DisplayName,
|
||||
Policies: a.Policies,
|
||||
@@ -532,6 +533,7 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) {
|
||||
|
||||
return &logical.Auth{
|
||||
LeaseOptions: lo,
|
||||
TokenType: logical.TokenType(a.TokenType),
|
||||
InternalData: data,
|
||||
DisplayName: a.DisplayName,
|
||||
Policies: a.Policies,
|
||||
@@ -578,6 +580,7 @@ func LogicalTokenEntryToProtoTokenEntry(t *logical.TokenEntry) *TokenEntry {
|
||||
BoundCIDRs: boundCIDRs,
|
||||
NamespaceID: t.NamespaceID,
|
||||
CubbyholeID: t.CubbyholeID,
|
||||
Type: uint32(t.Type),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,5 +617,6 @@ func ProtoTokenEntryToLogicalTokenEntry(t *TokenEntry) (*logical.TokenEntry, err
|
||||
BoundCIDRs: boundCIDRs,
|
||||
NamespaceID: t.NamespaceID,
|
||||
CubbyholeID: t.CubbyholeID,
|
||||
Type: logical.TokenType(t.Type),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -213,6 +213,20 @@ func Test(tt TestT, c TestCase) {
|
||||
return
|
||||
}
|
||||
|
||||
tokenInfo, err := client.Auth().Token().LookupSelf()
|
||||
if err != nil {
|
||||
tt.Fatal("error looking up token: ", err)
|
||||
return
|
||||
}
|
||||
var tokenPolicies []string
|
||||
if tokenPoliciesRaw, ok := tokenInfo.Data["policies"]; ok {
|
||||
if tokenPoliciesSliceRaw, ok := tokenPoliciesRaw.([]interface{}); ok {
|
||||
for _, p := range tokenPoliciesSliceRaw {
|
||||
tokenPolicies = append(tokenPolicies, p.(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make requests
|
||||
var revoke []*logical.Request
|
||||
for i, s := range c.Steps {
|
||||
@@ -228,6 +242,12 @@ func Test(tt TestT, c TestCase) {
|
||||
}
|
||||
if !s.Unauthenticated {
|
||||
req.ClientToken = client.Token()
|
||||
req.SetTokenEntry(&logical.TokenEntry{
|
||||
ID: req.ClientToken,
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Policies: tokenPolicies,
|
||||
DisplayName: tokenInfo.Data["display_name"].(string),
|
||||
})
|
||||
}
|
||||
if s.RemoteAddr != "" {
|
||||
req.Connection = &logical.Connection{RemoteAddr: s.RemoteAddr}
|
||||
|
||||
@@ -6,8 +6,49 @@ import (
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
)
|
||||
|
||||
type TokenType uint8
|
||||
|
||||
const (
|
||||
// TokenTypeDefault means "use the default, if any, that is currently set
|
||||
// on the mount". If not set, results in a Service token.
|
||||
TokenTypeDefault TokenType = iota
|
||||
|
||||
// TokenTypeService is a "normal" Vault token for long-lived services
|
||||
TokenTypeService
|
||||
|
||||
// TokenTypeBatch is a batch token
|
||||
TokenTypeBatch
|
||||
|
||||
// TokenTypeDefaultService, configured on a mount, means that if
|
||||
// TokenTypeDefault is sent back by the mount, create Service tokens
|
||||
TokenTypeDefaultService
|
||||
|
||||
// TokenTypeDefaultBatch, configured on a mount, means that if
|
||||
// TokenTypeDefault is sent back by the mount, create Batch tokens
|
||||
TokenTypeDefaultBatch
|
||||
)
|
||||
|
||||
func (t TokenType) String() string {
|
||||
switch t {
|
||||
case TokenTypeDefault:
|
||||
return "default"
|
||||
case TokenTypeService:
|
||||
return "service"
|
||||
case TokenTypeBatch:
|
||||
return "batch"
|
||||
case TokenTypeDefaultService:
|
||||
return "default-service"
|
||||
case TokenTypeDefaultBatch:
|
||||
return "default-batch"
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// TokenEntry is used to represent a given token
|
||||
type TokenEntry struct {
|
||||
Type TokenType `json:"type" mapstructure:"type" structs:"type" sentinel:""`
|
||||
|
||||
// ID of this entry, generally a random UUID
|
||||
ID string `json:"id" mapstructure:"id" structs:"id" sentinel:""`
|
||||
|
||||
@@ -107,6 +148,9 @@ func (te *TokenEntry) SentinelGet(key string) (interface{}, error) {
|
||||
|
||||
case "meta", "metadata":
|
||||
return te.Meta, nil
|
||||
|
||||
case "type":
|
||||
return te.Type.String(), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
@@ -124,5 +168,6 @@ func (te *TokenEntry) SentinelKeys() []string {
|
||||
"creation_time_unix",
|
||||
"meta",
|
||||
"metadata",
|
||||
"type",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func LogicalResponseToHTTPResponse(input *Response) *HTTPResponse {
|
||||
LeaseDuration: int(input.Auth.TTL.Seconds()),
|
||||
Renewable: input.Auth.Renewable,
|
||||
EntityID: input.Auth.EntityID,
|
||||
TokenType: input.Auth.TokenType.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +69,12 @@ func HTTPResponseToLogicalResponse(input *HTTPResponse) *Response {
|
||||
}
|
||||
logicalResp.Auth.Renewable = input.Auth.Renewable
|
||||
logicalResp.Auth.TTL = time.Second * time.Duration(input.Auth.LeaseDuration)
|
||||
switch input.Auth.TokenType {
|
||||
case "service":
|
||||
logicalResp.Auth.TokenType = TokenTypeService
|
||||
case "batch":
|
||||
logicalResp.Auth.TokenType = TokenTypeBatch
|
||||
}
|
||||
}
|
||||
|
||||
return logicalResp
|
||||
@@ -94,6 +101,7 @@ type HTTPAuth struct {
|
||||
LeaseDuration int `json:"lease_duration"`
|
||||
Renewable bool `json:"renewable"`
|
||||
EntityID string `json:"entity_id"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type HTTPWrapInfo struct {
|
||||
|
||||
@@ -572,7 +572,7 @@ ssl_storage_port: 7001
|
||||
#
|
||||
# Setting listen_address to 0.0.0.0 is always wrong.
|
||||
#
|
||||
listen_address: 172.17.0.2
|
||||
listen_address: 172.17.0.3
|
||||
|
||||
# Set listen_address OR listen_interface, not both. Interfaces must correspond
|
||||
# to a single address, IP aliasing is not supported.
|
||||
|
||||
@@ -661,6 +661,10 @@ func (c *Core) setupCredentials(ctx context.Context) error {
|
||||
if entry.Type == "token" {
|
||||
c.tokenStore = backend.(*TokenStore)
|
||||
|
||||
// At some point when this isn't beta we may persist this but for
|
||||
// now always set it on mount
|
||||
entry.Config.TokenType = logical.TokenTypeDefaultService
|
||||
|
||||
// this is loaded *after* the normal mounts, including cubbyhole
|
||||
c.router.tokenStoreSaltFunc = c.tokenStore.Salt
|
||||
if !c.IsDRSecondary() {
|
||||
|
||||
@@ -845,7 +845,11 @@ func (b *AESGCMBarrier) encrypt(path string, term uint32, gcm cipher.AEAD, plain
|
||||
case AESGCMVersion1:
|
||||
out = gcm.Seal(out, nonce, plain, nil)
|
||||
case AESGCMVersion2:
|
||||
out = gcm.Seal(out, nonce, plain, []byte(path))
|
||||
aad := []byte(nil)
|
||||
if path != "" {
|
||||
aad = []byte(path)
|
||||
}
|
||||
out = gcm.Seal(out, nonce, plain, aad)
|
||||
default:
|
||||
panic("Unknown AESGCM version")
|
||||
}
|
||||
@@ -865,7 +869,11 @@ func (b *AESGCMBarrier) decrypt(path string, gcm cipher.AEAD, cipher []byte) ([]
|
||||
case AESGCMVersion1:
|
||||
return gcm.Open(out, nonce, raw, nil)
|
||||
case AESGCMVersion2:
|
||||
return gcm.Open(out, nonce, raw, []byte(path))
|
||||
aad := []byte(nil)
|
||||
if path != "" {
|
||||
aad = []byte(path)
|
||||
}
|
||||
return gcm.Open(out, nonce, raw, aad)
|
||||
default:
|
||||
return nil, fmt.Errorf("version bytes mis-match")
|
||||
}
|
||||
|
||||
@@ -1047,6 +1047,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
|
||||
// Audit-log the request before going any further
|
||||
auth := &logical.Auth{
|
||||
ClientToken: req.ClientToken,
|
||||
Accessor: req.ClientTokenAccessor,
|
||||
}
|
||||
if te != nil {
|
||||
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
||||
@@ -1057,6 +1058,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
|
||||
auth.Metadata = te.Meta
|
||||
auth.DisplayName = te.DisplayName
|
||||
auth.EntityID = te.EntityID
|
||||
auth.TokenType = te.Type
|
||||
}
|
||||
|
||||
logInput := &audit.LogInput{
|
||||
|
||||
@@ -317,6 +317,7 @@ func TestCore_HandleRequest_Lease(t *testing.T) {
|
||||
// Read the key
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = nil
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = c.HandleRequest(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -359,6 +360,7 @@ func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) {
|
||||
// Read the key
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = nil
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = c.HandleRequest(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -401,6 +403,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
|
||||
// Read the key
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = nil
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = c.HandleRequest(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -409,7 +412,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
if resp.Secret.TTL != c.defaultLeaseTTL {
|
||||
t.Fatalf("bad: %#v, %d", resp.Secret, c.defaultLeaseTTL)
|
||||
t.Fatalf("bad: %d, %d", resp.Secret.TTL/time.Second, c.defaultLeaseTTL/time.Second)
|
||||
}
|
||||
if resp.Secret.LeaseID == "" {
|
||||
t.Fatalf("bad: %#v", resp.Secret)
|
||||
@@ -481,7 +484,7 @@ func TestCore_HandleRequest_NoSlash(t *testing.T) {
|
||||
// Test a root path is denied if non-root
|
||||
func TestCore_HandleRequest_RootPath(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
|
||||
req := &logical.Request{
|
||||
Operation: logical.ReadOperation,
|
||||
@@ -516,7 +519,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Child token (non-root) but with 'test' policy should have access
|
||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
req = &logical.Request{
|
||||
Operation: logical.ReadOperation,
|
||||
Path: "sys/policy", // root protected!
|
||||
@@ -534,7 +537,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
|
||||
// Check that standard permissions work
|
||||
func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
|
||||
req := &logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
@@ -554,7 +557,7 @@ func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
||||
// Check that standard permissions work
|
||||
func TestCore_HandleRequest_PermissionAllowed(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||
|
||||
// Set the 'test' policy object to permit access to secret/
|
||||
req := &logical.Request{
|
||||
@@ -719,6 +722,7 @@ func TestCore_HandleLogin_Token(t *testing.T) {
|
||||
TTL: time.Hour * 24,
|
||||
CreationTime: te.CreationTime,
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(te, expect) {
|
||||
@@ -884,6 +888,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) {
|
||||
ClientToken: root,
|
||||
}
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
@@ -1020,6 +1025,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
||||
CreationTime: te.CreationTime,
|
||||
TTL: time.Hour * 24 * 32,
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
if !reflect.DeepEqual(te, expect) {
|
||||
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
||||
@@ -1066,6 +1072,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) {
|
||||
CreationTime: te.CreationTime,
|
||||
TTL: time.Hour * 24 * 32,
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
if !reflect.DeepEqual(te, expect) {
|
||||
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
||||
@@ -1837,6 +1844,7 @@ func TestCore_HandleRequest_InternalData(t *testing.T) {
|
||||
Path: "foo/test",
|
||||
ClientToken: root,
|
||||
}
|
||||
lreq.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1909,6 +1917,7 @@ func TestCore_RenewSameLease(t *testing.T) {
|
||||
// Read the key
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = nil
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -2078,6 +2087,7 @@ path "secret/*" {
|
||||
// Read the key
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = nil
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
||||
@@ -873,6 +873,26 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment
|
||||
le.ExpireTime = resp.Secret.ExpirationTime()
|
||||
le.LastRenewalTime = time.Now()
|
||||
|
||||
// If the token it's associated with is a batch token, constrain lease
|
||||
// times
|
||||
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||
te, err := m.tokenStore.Lookup(ctx, le.ClientToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if te == nil {
|
||||
return nil, errors.New("cannot renew lease, no valid associated token")
|
||||
}
|
||||
tokenLeaseTimes, err := m.FetchLeaseTimesByToken(ctx, te)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if le.ExpireTime.After(tokenLeaseTimes.ExpireTime) {
|
||||
resp.Secret.TTL = tokenLeaseTimes.ExpireTime.Sub(le.LastRenewalTime)
|
||||
le.ExpireTime = tokenLeaseTimes.ExpireTime
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
m.pendingLock.Lock()
|
||||
if err := m.persistEntry(ctx, le); err != nil {
|
||||
@@ -1012,7 +1032,8 @@ func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request
|
||||
func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, resp *logical.Response) (id string, retErr error) {
|
||||
defer metrics.MeasureSince([]string{"expire", "register"}, time.Now())
|
||||
|
||||
if req.ClientToken == "" {
|
||||
te := req.TokenEntry()
|
||||
if te == nil {
|
||||
return "", fmt.Errorf("cannot register a lease with an empty client token")
|
||||
}
|
||||
|
||||
@@ -1046,6 +1067,7 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
||||
le := &leaseEntry{
|
||||
LeaseID: leaseID,
|
||||
ClientToken: req.ClientToken,
|
||||
ClientTokenType: te.Type,
|
||||
Path: req.Path,
|
||||
Data: resp.Data,
|
||||
Secret: resp.Secret,
|
||||
@@ -1078,15 +1100,36 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
||||
}
|
||||
}()
|
||||
|
||||
// If the token is a batch token, we want to constrain the maximum lifetime
|
||||
// by the token's lifetime
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
tokenLeaseTimes, err := m.FetchLeaseTimesByToken(ctx, te)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if le.ExpireTime.After(tokenLeaseTimes.ExpireTime) {
|
||||
le.ExpireTime = tokenLeaseTimes.ExpireTime
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the entry
|
||||
if err := m.persistEntry(ctx, le); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Maintain secondary index by token
|
||||
if err := m.createIndexByToken(ctx, le); err != nil {
|
||||
// Maintain secondary index by token, except for orphan batch tokens
|
||||
switch {
|
||||
case te.Type != logical.TokenTypeBatch:
|
||||
if err := m.createIndexByToken(ctx, le, le.ClientToken); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case te.Parent != "":
|
||||
// If it's a non-orphan batch token, assign the secondary index to its
|
||||
// parent
|
||||
if err := m.createIndexByToken(ctx, le, te.Parent); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Setup revocation timer if there is a lease
|
||||
m.updatePending(le, resp.Secret.LeaseTotal())
|
||||
@@ -1101,8 +1144,12 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
||||
func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenEntry, auth *logical.Auth) error {
|
||||
defer metrics.MeasureSince([]string{"expire", "register-auth"}, time.Now())
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
return errors.New("cannot register a lease for a batch token")
|
||||
}
|
||||
|
||||
if auth.ClientToken == "" {
|
||||
return fmt.Errorf("cannot register an auth lease with an empty token")
|
||||
return errors.New("cannot register an auth lease with an empty token")
|
||||
}
|
||||
|
||||
if strings.Contains(te.Path, "..") {
|
||||
@@ -1152,9 +1199,24 @@ func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenE
|
||||
|
||||
// FetchLeaseTimesByToken is a helper function to use token values to compute
|
||||
// the leaseID, rather than pushing that logic back into the token store.
|
||||
// As a special case, for a batch token it simply returns the information
|
||||
// encoded on it.
|
||||
func (m *ExpirationManager) FetchLeaseTimesByToken(ctx context.Context, te *logical.TokenEntry) (*leaseEntry, error) {
|
||||
defer metrics.MeasureSince([]string{"expire", "fetch-lease-times-by-token"}, time.Now())
|
||||
|
||||
if te == nil {
|
||||
return nil, errors.New("cannot fetch lease times for nil token")
|
||||
}
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
issueTime := time.Unix(te.CreationTime, 0)
|
||||
return &leaseEntry{
|
||||
IssueTime: issueTime,
|
||||
ExpireTime: issueTime.Add(te.TTL),
|
||||
ClientTokenType: logical.TokenTypeBatch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1273,6 +1335,10 @@ func (m *ExpirationManager) revokeEntry(ctx context.Context, le *leaseEntry) err
|
||||
// Revocation of login tokens is special since we can by-pass the
|
||||
// backend and directly interact with the token store
|
||||
if le.Auth != nil {
|
||||
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||
return errors.New("batch tokens cannot be revoked")
|
||||
}
|
||||
|
||||
if err := m.tokenStore.revokeTree(ctx, le); err != nil {
|
||||
return errwrap.Wrapf("failed to revoke token: {{err}}", err)
|
||||
}
|
||||
@@ -1319,6 +1385,10 @@ func (m *ExpirationManager) renewEntry(ctx context.Context, le *leaseEntry, incr
|
||||
// renewAuthEntry is used to attempt renew of an auth entry. Only the token
|
||||
// store should get the actual token ID intact.
|
||||
func (m *ExpirationManager) renewAuthEntry(ctx context.Context, req *logical.Request, le *leaseEntry, increment time.Duration) (*logical.Response, error) {
|
||||
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||
return logical.ErrorResponse("batch tokens cannot be renewed"), nil
|
||||
}
|
||||
|
||||
auth := *le.Auth
|
||||
auth.IssueTime = le.IssueTime
|
||||
auth.Increment = increment
|
||||
@@ -1446,10 +1516,10 @@ func (m *ExpirationManager) deleteEntry(ctx context.Context, le *leaseEntry) err
|
||||
}
|
||||
|
||||
// createIndexByToken creates a secondary index from the token to a lease entry
|
||||
func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEntry) error {
|
||||
func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEntry, token string) error {
|
||||
tokenNS := namespace.RootNamespace
|
||||
saltCtx := namespace.ContextWithNamespace(ctx, namespace.RootNamespace)
|
||||
_, nsID := namespace.SplitIDFromString(le.ClientToken)
|
||||
_, nsID := namespace.SplitIDFromString(token)
|
||||
if nsID != "" {
|
||||
tokenNS, err := NamespaceByID(ctx, nsID, m.core)
|
||||
if err != nil {
|
||||
@@ -1460,7 +1530,7 @@ func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEnt
|
||||
}
|
||||
}
|
||||
|
||||
saltedID, err := m.tokenStore.SaltID(saltCtx, le.ClientToken)
|
||||
saltedID, err := m.tokenStore.SaltID(saltCtx, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1674,6 +1744,7 @@ func (m *ExpirationManager) emitMetrics() {
|
||||
type leaseEntry struct {
|
||||
LeaseID string `json:"lease_id"`
|
||||
ClientToken string `json:"client_token"`
|
||||
ClientTokenType logical.TokenType `json:"token_type"`
|
||||
Path string `json:"path"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
Secret *logical.Secret `json:"secret"`
|
||||
@@ -1691,24 +1762,29 @@ func (le *leaseEntry) encode() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (le *leaseEntry) renewable() (bool, error) {
|
||||
var err error
|
||||
switch {
|
||||
// If there is no entry, cannot review
|
||||
case le == nil || le.ExpireTime.IsZero():
|
||||
err = fmt.Errorf("lease not found or lease is not renewable")
|
||||
// If there is no entry, cannot review to renew
|
||||
case le == nil:
|
||||
return false, fmt.Errorf("lease not found")
|
||||
|
||||
case le.ExpireTime.IsZero():
|
||||
return false, fmt.Errorf("lease is not renewable")
|
||||
|
||||
case le.ClientTokenType == logical.TokenTypeBatch:
|
||||
return false, nil
|
||||
|
||||
// Determine if the lease is expired
|
||||
case le.ExpireTime.Before(time.Now()):
|
||||
err = fmt.Errorf("lease expired")
|
||||
return false, fmt.Errorf("lease expired")
|
||||
|
||||
// Determine if the lease is renewable
|
||||
case le.Secret != nil && !le.Secret.Renewable:
|
||||
err = fmt.Errorf("lease is not renewable")
|
||||
return false, fmt.Errorf("lease is not renewable")
|
||||
|
||||
case le.Auth != nil && !le.Auth.Renewable:
|
||||
err = fmt.Errorf("lease is not renewable")
|
||||
return false, fmt.Errorf("lease is not renewable")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ func TestExpiration_Tidy(t *testing.T) {
|
||||
Path: "invalid/lease/" + fmt.Sprintf("%d", i+1),
|
||||
ClientToken: "invalidtoken",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "invalidtoken", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -396,6 +397,7 @@ func TestExpiration_Restore(t *testing.T) {
|
||||
Path: path,
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -452,6 +454,7 @@ func TestExpiration_Register(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -539,7 +542,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) {
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
}
|
||||
resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0)
|
||||
if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) {
|
||||
if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) {
|
||||
t.Fatalf("bad: err:%v resp:%#v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
@@ -578,6 +581,7 @@ func TestExpiration_Revoke(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -624,6 +628,7 @@ func TestExpiration_RevokeOnExpire(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -687,6 +692,7 @@ func TestExpiration_RevokePrefix(t *testing.T) {
|
||||
Path: path,
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -755,6 +761,7 @@ func TestExpiration_RevokeByToken(t *testing.T) {
|
||||
Path: path,
|
||||
ClientToken: "foobarbaz",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -843,6 +850,7 @@ func TestExpiration_RevokeByToken_Blocking(t *testing.T) {
|
||||
Path: path,
|
||||
ClientToken: "foobarbaz",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -1145,6 +1153,7 @@ func TestExpiration_Renew(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -1215,6 +1224,7 @@ func TestExpiration_Renew_NotRenewable(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -1265,6 +1275,7 @@ func TestExpiration_Renew_RevokeOnExpire(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: "foobar",
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -1407,7 +1418,7 @@ func TestExpiration_revokeEntry_token(t *testing.T) {
|
||||
if err := exp.persistEntry(namespace.TestContext(), le); err != nil {
|
||||
t.Fatalf("error persisting entry: %v", err)
|
||||
}
|
||||
if err := exp.createIndexByToken(namespace.TestContext(), le); err != nil {
|
||||
if err := exp.createIndexByToken(namespace.TestContext(), le, le.ClientToken); err != nil {
|
||||
t.Fatalf("error creating secondary index: %v", err)
|
||||
}
|
||||
exp.updatePending(le, le.Secret.LeaseTotal())
|
||||
@@ -1802,6 +1813,7 @@ func TestExpiration_RevokeForce(t *testing.T) {
|
||||
Path: "badrenew/creds",
|
||||
ClientToken: root,
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
|
||||
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
@@ -1850,6 +1862,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) {
|
||||
Path: "badrenew/creds",
|
||||
ClientToken: root,
|
||||
}
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
|
||||
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
|
||||
420
vault/external_tests/token/batch_token_test.go
Normal file
420
vault/external_tests/token/batch_token_test.go
Normal file
@@ -0,0 +1,420 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/builtin/credential/approle"
|
||||
vaulthttp "github.com/hashicorp/vault/http"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func TestBatchTokens(t *testing.T) {
|
||||
coreConfig := &vault.CoreConfig{
|
||||
LogicalBackends: map[string]logical.Factory{
|
||||
"kv": vault.LeasedPassthroughBackendFactory,
|
||||
},
|
||||
CredentialBackends: map[string]logical.Factory{
|
||||
"approle": approle.Factory,
|
||||
},
|
||||
}
|
||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
})
|
||||
cluster.Start()
|
||||
defer cluster.Cleanup()
|
||||
|
||||
core := cluster.Cores[0].Core
|
||||
vault.TestWaitActive(t, core)
|
||||
client := cluster.Cores[0].Client
|
||||
rootToken := client.Token()
|
||||
var err error
|
||||
|
||||
// Set up a KV path
|
||||
err = client.Sys().Mount("kv", &api.MountInput{
|
||||
Type: "kv",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"ttl": "5m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write the test policy
|
||||
err = client.Sys().PutPolicy("test", `
|
||||
path "kv/*" {
|
||||
capabilities = ["read"]
|
||||
}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Mount the auth backend
|
||||
err = client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
|
||||
Type: "approle",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Tune the mount
|
||||
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
||||
DefaultLeaseTTL: "5s",
|
||||
MaxLeaseTTL: "5s",
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create role
|
||||
resp, err := client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
||||
"policies": "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Get role_id
|
||||
resp, err = client.Logical().Read("auth/approle/role/test/role-id")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response for fetching the role-id")
|
||||
}
|
||||
roleID := resp.Data["role_id"]
|
||||
|
||||
// Get secret_id
|
||||
resp, err = client.Logical().Write("auth/approle/role/test/secret-id", map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response for fetching the secret-id")
|
||||
}
|
||||
secretID := resp.Data["secret_id"]
|
||||
|
||||
// Login
|
||||
testLogin := func(mountTuneType, roleType string, batch bool) string {
|
||||
t.Helper()
|
||||
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
||||
TokenType: mountTuneType,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
||||
"token_type": roleType,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
|
||||
"role_id": roleID,
|
||||
"secret_id": secretID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response for login")
|
||||
}
|
||||
if resp.Auth == nil {
|
||||
t.Fatal("expected auth object from response")
|
||||
}
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatal("expected a client token")
|
||||
}
|
||||
if batch && !strings.HasPrefix(resp.Auth.ClientToken, "b.") {
|
||||
t.Fatal("expected a batch token")
|
||||
}
|
||||
if !batch && strings.HasPrefix(resp.Auth.ClientToken, "b.") {
|
||||
t.Fatal("expected a non-batch token")
|
||||
}
|
||||
return resp.Auth.ClientToken
|
||||
}
|
||||
testLogin("service", "default", false)
|
||||
testLogin("service", "batch", false)
|
||||
testLogin("service", "service", false)
|
||||
testLogin("batch", "default", true)
|
||||
testLogin("batch", "batch", true)
|
||||
testLogin("batch", "service", true)
|
||||
testLogin("default-service", "default", false)
|
||||
testLogin("default-service", "batch", true)
|
||||
testLogin("default-service", "service", false)
|
||||
testLogin("default-batch", "default", true)
|
||||
testLogin("default-batch", "batch", true)
|
||||
testLogin("default-batch", "service", false)
|
||||
|
||||
finalToken := testLogin("batch", "batch", true)
|
||||
|
||||
client.SetToken(finalToken)
|
||||
resp, err = client.Logical().Read("kv/foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Data["foo"].(string) != "bar" {
|
||||
t.Fatal("bad")
|
||||
}
|
||||
if resp.LeaseID == "" {
|
||||
t.Fatal("expected lease")
|
||||
}
|
||||
if !resp.Renewable {
|
||||
t.Fatal("expected renewable")
|
||||
}
|
||||
if resp.LeaseDuration > 5 {
|
||||
t.Fatalf("lease duration too big: %d", resp.LeaseDuration)
|
||||
}
|
||||
leaseID := resp.LeaseID
|
||||
|
||||
lastDuration := resp.LeaseDuration
|
||||
for i := 0; i < 3; i++ {
|
||||
time.Sleep(time.Second)
|
||||
resp, err = client.Sys().Renew(leaseID, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.LeaseDuration >= lastDuration {
|
||||
t.Fatal("expected duration to go down")
|
||||
}
|
||||
lastDuration = resp.LeaseDuration
|
||||
}
|
||||
|
||||
client.SetToken(rootToken)
|
||||
time.Sleep(2 * time.Second)
|
||||
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||
"lease_id": leaseID,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchToken_ParentLeaseRevoke(t *testing.T) {
|
||||
coreConfig := &vault.CoreConfig{
|
||||
LogicalBackends: map[string]logical.Factory{
|
||||
"kv": vault.LeasedPassthroughBackendFactory,
|
||||
},
|
||||
CredentialBackends: map[string]logical.Factory{
|
||||
"approle": approle.Factory,
|
||||
},
|
||||
}
|
||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
})
|
||||
cluster.Start()
|
||||
defer cluster.Cleanup()
|
||||
|
||||
core := cluster.Cores[0].Core
|
||||
vault.TestWaitActive(t, core)
|
||||
client := cluster.Cores[0].Client
|
||||
rootToken := client.Token()
|
||||
var err error
|
||||
|
||||
// Set up a KV path
|
||||
err = client.Sys().Mount("kv", &api.MountInput{
|
||||
Type: "kv",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"ttl": "5m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write the test policy
|
||||
err = client.Sys().PutPolicy("test", `
|
||||
path "kv/*" {
|
||||
capabilities = ["read"]
|
||||
}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a second root token
|
||||
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
|
||||
Policies: []string{"root"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootToken2 := secret.Auth.ClientToken
|
||||
|
||||
// Use this new token to create a batch token
|
||||
client.SetToken(rootToken2)
|
||||
secret, err = client.Auth().Token().Create(&api.TokenCreateRequest{
|
||||
Policies: []string{"test"},
|
||||
Type: "batch",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
batchToken := secret.Auth.ClientToken
|
||||
client.SetToken(batchToken)
|
||||
_, err = client.Auth().Token().LookupSelf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret.Auth.ClientToken[0:2] != "b." {
|
||||
t.Fatal(secret.Auth.ClientToken)
|
||||
}
|
||||
|
||||
// Get a lease with the batch token
|
||||
resp, err := client.Logical().Read("kv/foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Data["foo"].(string) != "bar" {
|
||||
t.Fatal("bad")
|
||||
}
|
||||
if resp.LeaseID == "" {
|
||||
t.Fatal("expected lease")
|
||||
}
|
||||
leaseID := resp.LeaseID
|
||||
|
||||
// Check the lease
|
||||
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||
"lease_id": leaseID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Revoke the parent
|
||||
client.SetToken(rootToken2)
|
||||
err = client.Auth().Token().RevokeSelf("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify the batch token is not usable anymore
|
||||
client.SetToken(rootToken)
|
||||
_, err = client.Auth().Token().Lookup(batchToken)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
// Verify the lease has been revoked
|
||||
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||
"lease_id": leaseID,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_Roles_Batch(t *testing.T) {
|
||||
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
})
|
||||
cluster.Start()
|
||||
defer cluster.Cleanup()
|
||||
|
||||
core := cluster.Cores[0].Core
|
||||
vault.TestWaitActive(t, core)
|
||||
client := cluster.Cores[0].Client
|
||||
rootToken := client.Token()
|
||||
|
||||
var err error
|
||||
var secret *api.Secret
|
||||
|
||||
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||
"bound_cidrs": []string{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||
Policies: []string{"default"},
|
||||
Type: "batch",
|
||||
}, "testrole")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client.SetToken(secret.Auth.ClientToken)
|
||||
_, err = client.Auth().Token().LookupSelf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret.Auth.ClientToken[0:2] == "b." {
|
||||
t.Fatal(secret.Auth.ClientToken)
|
||||
}
|
||||
|
||||
// Test batch
|
||||
client.SetToken(rootToken)
|
||||
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||
"token_type": "batch",
|
||||
})
|
||||
// Orphan not set so we should error
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||
"token_type": "batch",
|
||||
"orphan": true,
|
||||
})
|
||||
// Renewable set so we should error
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||
"token_type": "batch",
|
||||
"orphan": true,
|
||||
"renewable": false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||
Policies: []string{"default"},
|
||||
Type: "service",
|
||||
}, "testrole")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client.SetToken(secret.Auth.ClientToken)
|
||||
_, err = client.Auth().Token().LookupSelf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret.Auth.ClientToken[0:2] != "b." {
|
||||
t.Fatal(secret.Auth.ClientToken)
|
||||
}
|
||||
|
||||
// Back to normal
|
||||
client.SetToken(rootToken)
|
||||
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||
"token_type": "service",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||
Policies: []string{"default"},
|
||||
Type: "batch",
|
||||
}, "testrole")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client.SetToken(secret.Auth.ClientToken)
|
||||
_, err = client.Auth().Token().LookupSelf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret.Auth.ClientToken[0:2] == "b." {
|
||||
t.Fatal(secret.Auth.ClientToken)
|
||||
}
|
||||
}
|
||||
@@ -223,6 +223,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
|
||||
// Audit-log the request before going any further
|
||||
auth := &logical.Auth{
|
||||
ClientToken: req.ClientToken,
|
||||
Accessor: req.ClientTokenAccessor,
|
||||
}
|
||||
if te != nil {
|
||||
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
||||
@@ -233,6 +234,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
|
||||
auth.Metadata = te.Meta
|
||||
auth.DisplayName = te.DisplayName
|
||||
auth.EntityID = te.EntityID
|
||||
auth.TokenType = te.Type
|
||||
}
|
||||
|
||||
logInput := &audit.LogInput{
|
||||
|
||||
@@ -601,6 +601,9 @@ func mountInfo(entry *MountEntry) map[string]interface{} {
|
||||
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
||||
}
|
||||
if entry.Table == credentialTableType {
|
||||
entryConfig["token_type"] = entry.Config.TokenType.String()
|
||||
}
|
||||
|
||||
info["config"] = entryConfig
|
||||
|
||||
@@ -951,6 +954,10 @@ func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) (
|
||||
},
|
||||
}
|
||||
|
||||
if mountEntry.Table == credentialTableType {
|
||||
resp.Data["token_type"] = mountEntry.Config.TokenType.String()
|
||||
}
|
||||
|
||||
if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
|
||||
resp.Data["audit_non_hmac_request_keys"] = rawVal.([]string)
|
||||
}
|
||||
@@ -1192,6 +1199,44 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string,
|
||||
}
|
||||
}
|
||||
|
||||
if rawVal, ok := data.GetOk("token_type"); ok {
|
||||
if !strings.HasPrefix(path, "auth/") {
|
||||
return logical.ErrorResponse(fmt.Sprintf("'token_type' can only be modified on auth mounts")), logical.ErrInvalidRequest
|
||||
}
|
||||
if mountEntry.Type == "token" || mountEntry.Type == "ns_token" {
|
||||
return logical.ErrorResponse(fmt.Sprintf("'token_type' cannot be set for 'token' or 'ns_token' auth mounts")), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
tokenType := logical.TokenTypeDefaultService
|
||||
ttString := rawVal.(string)
|
||||
|
||||
switch ttString {
|
||||
case "", "default-service":
|
||||
case "default-batch":
|
||||
tokenType = logical.TokenTypeDefaultBatch
|
||||
case "service":
|
||||
tokenType = logical.TokenTypeService
|
||||
case "batch":
|
||||
tokenType = logical.TokenTypeBatch
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"invalid value for 'token_type'")), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
oldVal := mountEntry.Config.TokenType
|
||||
mountEntry.Config.TokenType = tokenType
|
||||
|
||||
// Update the mount table
|
||||
if err := b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local); err != nil {
|
||||
mountEntry.Config.TokenType = oldVal
|
||||
return handleError(err)
|
||||
}
|
||||
|
||||
if b.Core.logger.IsInfo() {
|
||||
b.Core.logger.Info("mount tuning of token_type successful", "path", path, "token_type", ttString)
|
||||
}
|
||||
}
|
||||
|
||||
if rawVal, ok := data.GetOk("passthrough_request_headers"); ok {
|
||||
headers := rawVal.([]string)
|
||||
|
||||
@@ -1467,37 +1512,10 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques
|
||||
continue
|
||||
}
|
||||
|
||||
info := map[string]interface{}{
|
||||
"type": entry.Type,
|
||||
"description": entry.Description,
|
||||
"accessor": entry.Accessor,
|
||||
"local": entry.Local,
|
||||
"seal_wrap": entry.SealWrap,
|
||||
"options": entry.Options,
|
||||
}
|
||||
entryConfig := map[string]interface{}{
|
||||
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
|
||||
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
|
||||
"plugin_name": entry.Config.PluginName,
|
||||
}
|
||||
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
|
||||
entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string)
|
||||
}
|
||||
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
|
||||
entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string)
|
||||
}
|
||||
// Even though empty value is valid for ListingVisibility, we can ignore
|
||||
// this case during mount since there's nothing to unset/hide.
|
||||
if len(entry.Config.ListingVisibility) > 0 {
|
||||
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
||||
}
|
||||
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
||||
}
|
||||
|
||||
info["config"] = entryConfig
|
||||
info := mountInfo(entry)
|
||||
resp.Data[entry.Path] = info
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -1569,6 +1587,20 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
|
||||
logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
switch apiConfig.TokenType {
|
||||
case "", "default-service":
|
||||
config.TokenType = logical.TokenTypeDefaultService
|
||||
case "default-batch":
|
||||
config.TokenType = logical.TokenTypeDefaultBatch
|
||||
case "service":
|
||||
config.TokenType = logical.TokenTypeService
|
||||
case "batch":
|
||||
config.TokenType = logical.TokenTypeBatch
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"invalid value for 'token_type'")), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
switch logicalType {
|
||||
case "":
|
||||
return logical.ErrorResponse(
|
||||
@@ -3581,6 +3613,10 @@ This path responds to the following HTTP methods.
|
||||
"A list of headers to whitelist and pass from the request to the backend.",
|
||||
"",
|
||||
},
|
||||
"token_type": {
|
||||
"The type of token to issue (service or batch).",
|
||||
"",
|
||||
},
|
||||
"raw": {
|
||||
"Write, Read, and Delete data directly in the Storage backend.",
|
||||
"",
|
||||
|
||||
@@ -1010,6 +1010,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]),
|
||||
},
|
||||
"token_type": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: strings.TrimSpace(sysHelp["token_type"][0]),
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/go-test/deep"
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||
@@ -447,7 +448,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) {
|
||||
rootCheckFunc(t, resp)
|
||||
|
||||
// Create a non-root token
|
||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
|
||||
nonRootCheckFunc := func(t *testing.T, resp *logical.Response) {
|
||||
expected1 := []string{"create", "sudo", "update"}
|
||||
@@ -549,7 +550,7 @@ func testCapabilities(t *testing.T, endpoint string) {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, endpoint)
|
||||
if endpoint == "capabilities-self" {
|
||||
req.ClientToken = "tokenid"
|
||||
@@ -605,7 +606,7 @@ func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||
|
||||
te, err = core.tokenStore.Lookup(namespace.TestContext(), "tokenid")
|
||||
if err != nil {
|
||||
@@ -696,6 +697,7 @@ func TestSystemBackend_leases(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -742,6 +744,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -790,6 +793,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
||||
// Generate multiple leases
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -800,6 +804,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -839,6 +844,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/bar")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -886,6 +892,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -922,6 +929,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -976,7 +984,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
||||
if resp2.Data == nil {
|
||||
t.Fatal("nil data")
|
||||
}
|
||||
if resp.Secret.TTL != 180*time.Second {
|
||||
if resp.Secret.TTL != time.Second*180 {
|
||||
t.Fatalf("bad lease duration: %v", resp.Secret.TTL)
|
||||
}
|
||||
}
|
||||
@@ -990,7 +998,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
||||
if resp.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
|
||||
@@ -1001,7 +1009,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
||||
if resp.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
@@ -1015,7 +1023,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
||||
if resp.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
|
||||
@@ -1026,7 +1034,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
||||
if resp.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
@@ -1050,6 +1058,7 @@ func TestSystemBackend_revoke(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1074,13 +1083,14 @@ func TestSystemBackend_revoke(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
if resp3.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", *resp3)
|
||||
}
|
||||
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1103,6 +1113,7 @@ func TestSystemBackend_revoke(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1192,6 +1203,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1216,8 +1228,8 @@ func TestSystemBackend_revokePrefix(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
if resp3.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %v", *resp3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,6 +1252,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) {
|
||||
// Read a key with a LeaseID
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||
req.ClientToken = root
|
||||
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -1264,8 +1277,8 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
if resp3.Data["error"] != "lease not found" {
|
||||
t.Fatalf("bad: %#v", *resp3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1415,14 +1428,16 @@ func TestSystemBackend_authTable(t *testing.T) {
|
||||
"default_lease_ttl": int64(0),
|
||||
"max_lease_ttl": int64(0),
|
||||
"plugin_name": "",
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
"options": map[string]string(nil),
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
|
||||
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1466,7 +1481,9 @@ func TestSystemBackend_enableAuth(t *testing.T) {
|
||||
"config": map[string]interface{}{
|
||||
"default_lease_ttl": int64(2100),
|
||||
"max_lease_ttl": int64(2700),
|
||||
"force_no_cache": false,
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"local": true,
|
||||
"seal_wrap": true,
|
||||
@@ -1480,14 +1497,16 @@ func TestSystemBackend_enableAuth(t *testing.T) {
|
||||
"default_lease_ttl": int64(0),
|
||||
"max_lease_ttl": int64(0),
|
||||
"plugin_name": "",
|
||||
"force_no_cache": false,
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"local": false,
|
||||
"seal_wrap": false,
|
||||
"options": map[string]string(nil),
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
|
||||
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2300,6 +2319,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
||||
"max_lease_ttl": int64(0),
|
||||
"force_no_cache": false,
|
||||
"plugin_name": "",
|
||||
"token_type": "default-service",
|
||||
},
|
||||
"type": "token",
|
||||
"description": "token based credentials",
|
||||
@@ -2309,8 +2329,8 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("got: %#v \n\n expect: %#v", resp.Data, exp)
|
||||
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
|
||||
// Mount-tune an auth mount
|
||||
@@ -2391,7 +2411,7 @@ func TestSystemBackend_InternalUIMount(t *testing.T) {
|
||||
t.Fatalf("Bad Response: %#v", resp)
|
||||
}
|
||||
|
||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"secret"})
|
||||
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"secret"})
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/kv")
|
||||
req.ClientToken = "tokenid"
|
||||
|
||||
@@ -226,6 +226,7 @@ type MountConfig struct {
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||
TokenType logical.TokenType `json:"token_type" structs:"token_type" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
// APIMountConfig is an embedded struct of api.MountConfigInput
|
||||
@@ -238,6 +239,7 @@ type APIMountConfig struct {
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||
TokenType string `json:"token_type" structs:"token_type" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the mount entry
|
||||
|
||||
@@ -295,6 +295,7 @@ func TestCore_Unmount_Cleanup(t *testing.T) {
|
||||
Path: "test/foo",
|
||||
ClientToken: root,
|
||||
}
|
||||
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
@@ -415,6 +416,7 @@ func TestCore_Remount_Cleanup(t *testing.T) {
|
||||
Path: "test/foo",
|
||||
ClientToken: root,
|
||||
}
|
||||
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
||||
@@ -269,19 +269,22 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
|
||||
// whether a particular resource exists. Then we can mark it as an update
|
||||
// or creation as appropriate.
|
||||
if req.Operation == logical.CreateOperation || req.Operation == logical.UpdateOperation {
|
||||
checkExists, resourceExists, err := c.router.RouteExistenceCheck(ctx, req)
|
||||
existsResp, checkExists, resourceExists, err := c.router.RouteExistenceCheck(ctx, req)
|
||||
switch err {
|
||||
case logical.ErrUnsupportedPath:
|
||||
// fail later via bad path to avoid confusing items in the log
|
||||
checkExists = false
|
||||
case nil:
|
||||
// Continue on
|
||||
if existsResp != nil && existsResp.IsError() {
|
||||
return nil, te, existsResp.Error()
|
||||
}
|
||||
// Otherwise, continue on
|
||||
default:
|
||||
c.logger.Error("failed to run existence check", "error", err)
|
||||
if _, ok := err.(errutil.UserError); ok {
|
||||
return nil, nil, err
|
||||
return nil, te, err
|
||||
} else {
|
||||
return nil, nil, ErrInternalError
|
||||
return nil, te, ErrInternalError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +319,7 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
|
||||
auth.ExternalNamespacePolicies = identityPolicies
|
||||
// Store the entity ID in the request object
|
||||
req.EntityID = te.EntityID
|
||||
auth.TokenType = te.Type
|
||||
}
|
||||
|
||||
// Check the standard non-root ACLs. Return the token entry if it's not
|
||||
@@ -744,6 +748,19 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
||||
return nil, auth, retErr
|
||||
}
|
||||
resp.Secret.LeaseID = leaseID
|
||||
|
||||
// Get the actual time of the lease
|
||||
le, err := c.expiration.FetchLeaseTimes(ctx, leaseID)
|
||||
if err != nil {
|
||||
c.logger.Error("failed to fetch updated lease time", "request_path", req.Path, "error", err)
|
||||
retErr = multierror.Append(retErr, ErrInternalError)
|
||||
return nil, auth, retErr
|
||||
}
|
||||
// We round here because the clock will have already started
|
||||
// ticking, so we'll end up always returning 299 instead of 300 or
|
||||
// 26399 instead of 26400, say, even if it's just a few
|
||||
// microseconds. This provides a nicer UX.
|
||||
resp.Secret.TTL = le.ExpireTime.Sub(time.Now()).Round(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,6 +794,9 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
||||
}
|
||||
|
||||
resp.Auth.TokenPolicies = policyutil.SanitizePolicies(resp.Auth.Policies, policyutil.DoNotAddDefaultPolicy)
|
||||
switch resp.Auth.TokenType {
|
||||
case logical.TokenTypeBatch:
|
||||
case logical.TokenTypeService:
|
||||
if err := c.expiration.RegisterAuth(ctx, &logical.TokenEntry{
|
||||
Path: resp.Auth.CreationPath,
|
||||
NamespaceID: ns.ID,
|
||||
@@ -786,6 +806,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
||||
retErr = multierror.Append(retErr, ErrInternalError)
|
||||
return nil, auth, retErr
|
||||
}
|
||||
}
|
||||
|
||||
// We do these later since it's not meaningful for backends/expmgr to
|
||||
// have what is purely a snapshot of current identity policies, and
|
||||
@@ -1031,7 +1052,14 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re
|
||||
}
|
||||
}
|
||||
|
||||
registerFunc, funcGetErr := getAuthRegisterFunc(c)
|
||||
var registerFunc RegisterAuthFunc
|
||||
var funcGetErr error
|
||||
// Batch tokens should not be forwarded to perf standby
|
||||
if auth.TokenType == logical.TokenTypeBatch {
|
||||
registerFunc = c.RegisterAuth
|
||||
} else {
|
||||
registerFunc, funcGetErr = getAuthRegisterFunc(c)
|
||||
}
|
||||
if funcGetErr != nil {
|
||||
retErr = multierror.Append(retErr, funcGetErr)
|
||||
return nil, auth, retErr
|
||||
@@ -1083,6 +1111,7 @@ func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path st
|
||||
Policies: auth.TokenPolicies,
|
||||
NamespaceID: ns.ID,
|
||||
ExplicitMaxTTL: auth.ExplicitMaxTTL,
|
||||
Type: auth.TokenType,
|
||||
}
|
||||
|
||||
if err := c.tokenStore.create(ctx, &te); err != nil {
|
||||
@@ -1095,12 +1124,18 @@ func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path st
|
||||
auth.Accessor = te.Accessor
|
||||
auth.TTL = te.TTL
|
||||
|
||||
switch auth.TokenType {
|
||||
case logical.TokenTypeBatch:
|
||||
// Ensure it's not marked renewable since it isn't
|
||||
auth.Renewable = false
|
||||
case logical.TokenTypeService:
|
||||
// Register with the expiration manager
|
||||
if err := c.expiration.RegisterAuth(ctx, &te, auth); err != nil {
|
||||
c.tokenStore.revokeOrphan(ctx, te.ID)
|
||||
c.logger.Error("failed to register token lease", "request_path", path, "error", err)
|
||||
return ErrInternalError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -468,9 +468,9 @@ func (r *Router) Route(ctx context.Context, req *logical.Request) (*logical.Resp
|
||||
}
|
||||
|
||||
// RouteExistenceCheck is used to route a given existence check request
|
||||
func (r *Router) RouteExistenceCheck(ctx context.Context, req *logical.Request) (bool, bool, error) {
|
||||
_, ok, exists, err := r.routeCommon(ctx, req, true)
|
||||
return ok, exists, err
|
||||
func (r *Router) RouteExistenceCheck(ctx context.Context, req *logical.Request) (*logical.Response, bool, bool, error) {
|
||||
resp, ok, exists, err := r.routeCommon(ctx, req, true)
|
||||
return resp, ok, exists, err
|
||||
}
|
||||
|
||||
func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) {
|
||||
@@ -547,11 +547,17 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
||||
return nil, false, false, nil
|
||||
}
|
||||
|
||||
if req.TokenEntry() == nil {
|
||||
te := req.TokenEntry()
|
||||
|
||||
if te == nil {
|
||||
return nil, false, false, fmt.Errorf("nil token entry")
|
||||
}
|
||||
|
||||
switch req.TokenEntry().NamespaceID {
|
||||
if te.Type != logical.TokenTypeService {
|
||||
return logical.ErrorResponse(`cubbyhole operations are only supported by "service" type tokens`), false, false, nil
|
||||
}
|
||||
|
||||
switch te.NamespaceID {
|
||||
case namespace.RootNamespaceID:
|
||||
// In order for the token store to revoke later, we need to have the same
|
||||
// salted ID, so we double-salt what's going to the cubbyhole backend
|
||||
@@ -562,10 +568,10 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
||||
req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken))
|
||||
|
||||
default:
|
||||
if req.TokenEntry().CubbyholeID == "" {
|
||||
if te.CubbyholeID == "" {
|
||||
return nil, false, false, fmt.Errorf("empty cubbyhole id")
|
||||
}
|
||||
req.ClientToken = req.TokenEntry().CubbyholeID
|
||||
req.ClientToken = te.CubbyholeID
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -644,20 +650,20 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
||||
return nil, ok, exists, err
|
||||
} else {
|
||||
resp, err := re.backend.HandleRequest(ctx, req)
|
||||
// When a token gets renewed, the request hits this path and reaches
|
||||
// token store. Token store delegates the renewal to the expiration
|
||||
// manager. Expiration manager in-turn creates a different logical
|
||||
// request and forwards the request to the auth backend that had
|
||||
// initially authenticated the login request. The forwarding to auth
|
||||
// backend will make this code path hit for the second time for the
|
||||
// same renewal request. The accessors in the Alias structs should be
|
||||
// of the auth backend and not of the token store. Therefore, avoiding
|
||||
// the overwriting of accessors by having a check for path prefix
|
||||
// having "renew". This gets applied for "renew" and "renew-self"
|
||||
// requests.
|
||||
if resp != nil &&
|
||||
resp.Auth != nil &&
|
||||
!strings.HasPrefix(req.Path, "renew") {
|
||||
resp.Auth != nil {
|
||||
// When a token gets renewed, the request hits this path and
|
||||
// reaches token store. Token store delegates the renewal to the
|
||||
// expiration manager. Expiration manager in-turn creates a
|
||||
// different logical request and forwards the request to the auth
|
||||
// backend that had initially authenticated the login request. The
|
||||
// forwarding to auth backend will make this code path hit for the
|
||||
// second time for the same renewal request. The accessors in the
|
||||
// Alias structs should be of the auth backend and not of the token
|
||||
// store. Therefore, avoiding the overwriting of accessors by
|
||||
// having a check for path prefix having "renew". This gets applied
|
||||
// for "renew" and "renew-self" requests.
|
||||
if !strings.HasPrefix(req.Path, "renew") {
|
||||
if resp.Auth.Alias != nil {
|
||||
resp.Auth.Alias.MountAccessor = re.mountEntry.Accessor
|
||||
}
|
||||
@@ -666,6 +672,26 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
||||
}
|
||||
}
|
||||
|
||||
switch re.mountEntry.Type {
|
||||
case "token", "ns_token":
|
||||
// Nothing; we respect what the token store is telling us and
|
||||
// we don't allow tuning
|
||||
default:
|
||||
switch re.mountEntry.Config.TokenType {
|
||||
case logical.TokenTypeService, logical.TokenTypeBatch:
|
||||
resp.Auth.TokenType = re.mountEntry.Config.TokenType
|
||||
case logical.TokenTypeDefault, logical.TokenTypeDefaultService:
|
||||
if resp.Auth.TokenType == logical.TokenTypeDefault {
|
||||
resp.Auth.TokenType = logical.TokenTypeService
|
||||
}
|
||||
case logical.TokenTypeDefaultBatch:
|
||||
if resp.Auth.TokenType == logical.TokenTypeDefault {
|
||||
resp.Auth.TokenType = logical.TokenTypeBatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp, false, false, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
@@ -31,6 +33,7 @@ import (
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
"github.com/hashicorp/vault/logical/plugin/pb"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
@@ -183,6 +186,12 @@ func (ts *TokenStore) paths() []*framework.Path {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`,
|
||||
},
|
||||
|
||||
"token_type": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "service",
|
||||
Description: "The type of token to generate, service or batch",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
@@ -473,6 +482,8 @@ type TokenStore struct {
|
||||
|
||||
core *Core
|
||||
|
||||
batchTokenEncryptor BarrierEncryptor
|
||||
|
||||
baseBarrierView *BarrierView
|
||||
idBarrierView *BarrierView
|
||||
accessorBarrierView *BarrierView
|
||||
@@ -515,6 +526,7 @@ func NewTokenStore(ctx context.Context, logger log.Logger, core *Core, config *l
|
||||
t := &TokenStore{
|
||||
activeContext: ctx,
|
||||
core: core,
|
||||
batchTokenEncryptor: core.barrier,
|
||||
baseBarrierView: view,
|
||||
idBarrierView: view.SubView(idPrefix),
|
||||
accessorBarrierView: view.SubView(accessorPrefix),
|
||||
@@ -630,6 +642,9 @@ type tsRoleEntry struct {
|
||||
|
||||
// The set of CIDRs that tokens generated using this role will be bound to
|
||||
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
||||
|
||||
// The type of token this role should issue
|
||||
TokenType logical.TokenType `json:"token_type" mapstructure:"token_type"`
|
||||
}
|
||||
|
||||
type accessorEntry struct {
|
||||
@@ -673,6 +688,7 @@ func (ts *TokenStore) rootToken(ctx context.Context) (*logical.TokenEntry, error
|
||||
DisplayName: "root",
|
||||
CreationTime: time.Now().Unix(),
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
if err := ts.create(ctx, te); err != nil {
|
||||
return nil, err
|
||||
@@ -771,14 +787,6 @@ func (ts *TokenStore) createAccessor(ctx context.Context, entry *logical.TokenEn
|
||||
// a newly generated ID if not provided.
|
||||
func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) error {
|
||||
defer metrics.MeasureSince([]string{"token", "create"}, time.Now())
|
||||
// Generate an ID if necessary
|
||||
if entry.ID == "" {
|
||||
var err error
|
||||
entry.ID, err = base62.Random(TokenLength, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core)
|
||||
if err != nil {
|
||||
@@ -788,6 +796,24 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
return namespace.ErrNoNamespace
|
||||
}
|
||||
|
||||
entry.Policies = policyutil.SanitizePolicies(entry.Policies, policyutil.DoNotAddDefaultPolicy)
|
||||
|
||||
switch entry.Type {
|
||||
case logical.TokenTypeDefault, logical.TokenTypeService:
|
||||
// In case it was default, force to service
|
||||
entry.Type = logical.TokenTypeService
|
||||
|
||||
// Generate an ID if necessary
|
||||
userSelectedID := true
|
||||
if entry.ID == "" {
|
||||
userSelectedID = false
|
||||
var err error
|
||||
entry.ID, err = base62.Random(TokenLength, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tokenNS.ID != namespace.RootNamespaceID {
|
||||
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
||||
if entry.CubbyholeID == "" {
|
||||
@@ -799,12 +825,14 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
}
|
||||
}
|
||||
|
||||
// If the user didn't specifically pick the ID, e.g. because they were
|
||||
// sudo/root, check for collision; otherwise trust the process
|
||||
if userSelectedID {
|
||||
exist, _ := ts.lookupInternal(ctx, entry.ID, false, true)
|
||||
if exist != nil {
|
||||
return fmt.Errorf("cannot create a token with a duplicate ID")
|
||||
}
|
||||
|
||||
entry.Policies = policyutil.SanitizePolicies(entry.Policies, policyutil.DoNotAddDefaultPolicy)
|
||||
}
|
||||
|
||||
err = ts.createAccessor(ctx, entry)
|
||||
if err != nil {
|
||||
@@ -812,6 +840,55 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
}
|
||||
|
||||
return ts.storeCommon(ctx, entry, true)
|
||||
|
||||
case logical.TokenTypeBatch:
|
||||
// Ensure fields we don't support/care about are nilled, proto marshal,
|
||||
// encrypt, skip persistence
|
||||
entry.ID = ""
|
||||
pEntry := &pb.TokenEntry{
|
||||
Parent: entry.Parent,
|
||||
Policies: entry.Policies,
|
||||
Path: entry.Path,
|
||||
Meta: entry.Meta,
|
||||
DisplayName: entry.DisplayName,
|
||||
CreationTime: entry.CreationTime,
|
||||
TTL: int64(entry.TTL),
|
||||
Role: entry.Role,
|
||||
EntityID: entry.EntityID,
|
||||
NamespaceID: entry.NamespaceID,
|
||||
Type: uint32(entry.Type),
|
||||
}
|
||||
|
||||
boundCIDRs := make([]string, len(entry.BoundCIDRs))
|
||||
for i, cidr := range entry.BoundCIDRs {
|
||||
boundCIDRs[i] = cidr.String()
|
||||
}
|
||||
pEntry.BoundCIDRs = boundCIDRs
|
||||
|
||||
mEntry, err := proto.Marshal(pEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eEntry, err := ts.batchTokenEncryptor.Encrypt(ctx, "", mEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bEntry := base64.RawURLEncoding.EncodeToString(eEntry)
|
||||
entry.ID = fmt.Sprintf("b.%s", bEntry)
|
||||
|
||||
if tokenNS.ID != namespace.RootNamespaceID {
|
||||
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("cannot create a token of type %d", entry.Type)
|
||||
}
|
||||
|
||||
return errors.New("unreachable code")
|
||||
}
|
||||
|
||||
// Store is used to store an updated token entry without writing the
|
||||
@@ -975,6 +1052,11 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntr
|
||||
return nil, fmt.Errorf("cannot lookup blank token")
|
||||
}
|
||||
|
||||
// If it starts with "b-" it's a batch token
|
||||
if len(id) > 2 && id[0:2] == "b." {
|
||||
return ts.lookupBatchToken(ctx, id)
|
||||
}
|
||||
|
||||
lock := locksutil.LockForKey(ts.tokenLocks, id)
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
@@ -982,8 +1064,8 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntr
|
||||
return ts.lookupInternal(ctx, id, false, false)
|
||||
}
|
||||
|
||||
// lookupTainted is used to find a token that may or maynot be tainted given its
|
||||
// ID. It acquires a read lock, then calls lookupInternal.
|
||||
// lookupTainted is used to find a token that may or may not be tainted given
|
||||
// its ID. It acquires a read lock, then calls lookupInternal.
|
||||
func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.TokenEntry, error) {
|
||||
defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now())
|
||||
if id == "" {
|
||||
@@ -997,6 +1079,48 @@ func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.To
|
||||
return ts.lookupInternal(ctx, id, false, true)
|
||||
}
|
||||
|
||||
func (ts *TokenStore) lookupBatchToken(ctx context.Context, id string) (*logical.TokenEntry, error) {
|
||||
// Strip the b. from the front and namespace ID from the back
|
||||
bEntry, _ := namespace.SplitIDFromString(id[2:])
|
||||
|
||||
eEntry, err := base64.RawURLEncoding.DecodeString(bEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mEntry, err := ts.batchTokenEncryptor.Decrypt(ctx, "", eEntry)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pEntry := new(pb.TokenEntry)
|
||||
if err := proto.Unmarshal(mEntry, pEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
te, err := pb.ProtoTokenEntryToLogicalTokenEntry(pEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if time.Now().After(time.Unix(te.CreationTime, 0).Add(te.TTL)) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if te.Parent != "" {
|
||||
pte, err := ts.Lookup(ctx, te.Parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pte == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
te.ID = id
|
||||
return te, nil
|
||||
}
|
||||
|
||||
// lookupInternal is used to find a token given its (possibly salted) ID. If
|
||||
// tainted is true, entries that are in some revocation state (currently,
|
||||
// indicated by num uses < 0), the entry will be returned anyways
|
||||
@@ -1006,6 +1130,11 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
||||
return nil, errwrap.Wrapf("failed to find namespace in context: {{err}}", err)
|
||||
}
|
||||
|
||||
// If it starts with "b." it's a batch token
|
||||
if len(id) > 2 && id[0:2] == "b." {
|
||||
return ts.lookupBatchToken(ctx, id)
|
||||
}
|
||||
|
||||
var raw *logical.StorageEntry
|
||||
lookupID := id
|
||||
|
||||
@@ -1063,6 +1192,11 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
||||
entry.NamespaceID = namespace.RootNamespaceID
|
||||
}
|
||||
|
||||
// This will be the upgrade case
|
||||
if entry.Type == logical.TokenTypeDefault {
|
||||
entry.Type = logical.TokenTypeService
|
||||
}
|
||||
|
||||
persistNeeded := false
|
||||
|
||||
// Upgrade the deprecated fields
|
||||
@@ -1490,6 +1624,15 @@ func (ts *TokenStore) revokeTreeInternal(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) IsBatchTokenCreationRequest(ctx context.Context, path string) (bool, error) {
|
||||
name := strings.TrimPrefix(path, "auth/token/create/")
|
||||
roleEntry, err := c.tokenStore.tokenStoreRole(ctx, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return roleEntry.TokenType == logical.TokenTypeBatch, nil
|
||||
}
|
||||
|
||||
// handleCreateAgainstRole handles the auth/token/create path for a role
|
||||
func (ts *TokenStore) handleCreateAgainstRole(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
name := d.Get("role_name").(string)
|
||||
@@ -1924,6 +2067,9 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
if parent == nil {
|
||||
return logical.ErrorResponse("parent token lookup failed: no parent found"), logical.ErrInvalidRequest
|
||||
}
|
||||
if parent.Type == logical.TokenTypeBatch {
|
||||
return logical.ErrorResponse("batch tokens cannot create more tokens"), nil
|
||||
}
|
||||
|
||||
// A token with a restricted number of uses cannot create a new token
|
||||
// otherwise it could escape the restriction count.
|
||||
@@ -1949,6 +2095,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
DisplayName string `mapstructure:"display_name"`
|
||||
NumUses int `mapstructure:"num_uses"`
|
||||
Period string
|
||||
Type string `mapstructure:"type"`
|
||||
}
|
||||
if err := mapstructure.WeakDecode(req.Data, &data); err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
@@ -1982,6 +2129,56 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
}
|
||||
}
|
||||
|
||||
renewable := true
|
||||
if data.Renewable != nil {
|
||||
renewable = *data.Renewable
|
||||
}
|
||||
|
||||
tokenType := logical.TokenTypeService
|
||||
tokenTypeStr := data.Type
|
||||
if role != nil {
|
||||
switch role.TokenType {
|
||||
case logical.TokenTypeDefault, logical.TokenTypeService:
|
||||
tokenTypeStr = logical.TokenTypeService.String()
|
||||
case logical.TokenTypeBatch:
|
||||
tokenTypeStr = logical.TokenTypeBatch.String()
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf("role being used for token creation contains invalid token type %q", role.TokenType.String())), nil
|
||||
}
|
||||
}
|
||||
switch tokenTypeStr {
|
||||
case "", "service":
|
||||
case "batch":
|
||||
var badReason string
|
||||
switch {
|
||||
case data.ExplicitMaxTTL != "":
|
||||
dur, err := parseutil.ParseDurationSecond(data.ExplicitMaxTTL)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(`"explicit_max_ttl" value could not be parsed`), nil
|
||||
}
|
||||
if dur != 0 {
|
||||
badReason = "explicit_max_ttl"
|
||||
}
|
||||
case data.NumUses != 0:
|
||||
badReason = "num_uses"
|
||||
case data.Period != "":
|
||||
dur, err := parseutil.ParseDurationSecond(data.Period)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(`"period" value could not be parsed`), nil
|
||||
}
|
||||
if dur != 0 {
|
||||
badReason = "period"
|
||||
}
|
||||
}
|
||||
if badReason != "" {
|
||||
return logical.ErrorResponse(fmt.Sprintf("batch tokens cannot have %q set", badReason)), nil
|
||||
}
|
||||
tokenType = logical.TokenTypeBatch
|
||||
renewable = false
|
||||
default:
|
||||
return logical.ErrorResponse("invalid 'token_type' value"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Verify the number of uses is positive
|
||||
if data.NumUses < 0 {
|
||||
return logical.ErrorResponse("number of uses cannot be negative"),
|
||||
@@ -2002,11 +2199,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
NumUses: data.NumUses,
|
||||
CreationTime: time.Now().Unix(),
|
||||
NamespaceID: ns.ID,
|
||||
}
|
||||
|
||||
renewable := true
|
||||
if data.Renewable != nil {
|
||||
renewable = *data.Renewable
|
||||
Type: tokenType,
|
||||
}
|
||||
|
||||
// If the role is not nil, we add the role name as part of the token's
|
||||
@@ -2169,12 +2362,19 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
}
|
||||
}
|
||||
|
||||
if strutil.StrListContains(te.Policies, "root") {
|
||||
// Prevent attempts to create a root token without an actual root token as parent.
|
||||
// This is to thwart privilege escalation by tokens having 'sudo' privileges.
|
||||
if strutil.StrListContains(data.Policies, "root") && !strutil.StrListContains(parent.Policies, "root") {
|
||||
if !strutil.StrListContains(parent.Policies, "root") {
|
||||
return logical.ErrorResponse("root tokens may not be created without parent token being root"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
// Batch tokens cannot be revoked so we should never have root batch tokens
|
||||
return logical.ErrorResponse("batch tokens cannot be root tokens"), nil
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// NOTE: Do not modify policies below this line. We need the checks above
|
||||
// to be the last checks as they must look at the final policy set.
|
||||
@@ -2208,7 +2408,8 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
|
||||
// At this point, it is clear whether the token is going to be an orphan or
|
||||
// not. If the token is not going to be an orphan, inherit the parent's
|
||||
// entity identifier into the child token.
|
||||
// entity identifier into the child token. We must also verify that, if
|
||||
// it's not an orphan, the parent isn't a batch token.
|
||||
if te.Parent != "" {
|
||||
te.EntityID = parent.EntityID
|
||||
}
|
||||
@@ -2269,8 +2470,10 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
te.TTL = dur
|
||||
}
|
||||
|
||||
// Set the lesser period/explicit max TTL if defined both in arguments and in role
|
||||
if role != nil {
|
||||
// Set the lesser period/explicit max TTL if defined both in arguments and
|
||||
// in role. Batch tokens will error out if not set via role, but here we
|
||||
// need to explicitly check
|
||||
if role != nil && te.Type != logical.TokenTypeBatch {
|
||||
if role.ExplicitMaxTTL != 0 {
|
||||
switch {
|
||||
case explicitMaxTTLToUse == 0:
|
||||
@@ -2345,6 +2548,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
Period: periodToUse,
|
||||
ExplicitMaxTTL: explicitMaxTTLToUse,
|
||||
CreationPath: te.Path,
|
||||
TokenType: te.Type,
|
||||
}
|
||||
|
||||
for _, p := range te.Policies {
|
||||
@@ -2364,34 +2568,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
||||
// in a way that revokes all child tokens. Normally, using sys/revoke/leaseID will revoke
|
||||
// the token and all children anyways, but that is only available when there is a lease.
|
||||
func (ts *TokenStore) handleRevokeSelf(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
te, err := ts.Lookup(ctx, req.ClientToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if te == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenNS == nil {
|
||||
return nil, namespace.ErrNoNamespace
|
||||
}
|
||||
|
||||
revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS)
|
||||
leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, te)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ts.expiration.Revoke(revokeCtx, leaseID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return ts.revokeCommon(ctx, req, data, req.ClientToken)
|
||||
}
|
||||
|
||||
// handleRevokeTree handles the auth/token/revoke/id path for revocation of tokens
|
||||
@@ -2408,6 +2585,20 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
||||
urltoken = true
|
||||
}
|
||||
|
||||
if resp, err := ts.revokeCommon(ctx, req, data, id); resp != nil || err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if urltoken {
|
||||
resp := &logical.Response{}
|
||||
resp.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ts *TokenStore) revokeCommon(ctx context.Context, req *logical.Request, data *framework.FieldData, id string) (*logical.Response, error) {
|
||||
te, err := ts.Lookup(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2416,6 +2607,10 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
return logical.ErrorResponse("batch tokens cannot be revoked"), nil
|
||||
}
|
||||
|
||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2435,12 +2630,6 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if urltoken {
|
||||
resp := &logical.Response{}
|
||||
resp.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -2477,6 +2666,10 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque
|
||||
return logical.ErrorResponse("token to revoke not found"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
return logical.ErrorResponse("batch tokens cannot be revoked"), nil
|
||||
}
|
||||
|
||||
// Revoke and orphan
|
||||
if err := ts.revokeOrphan(ctx, id); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
@@ -2545,6 +2738,7 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da
|
||||
"ttl": int64(0),
|
||||
"explicit_max_ttl": int64(out.ExplicitMaxTTL.Seconds()),
|
||||
"entity_id": out.EntityID,
|
||||
"type": out.Type.String(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2645,8 +2839,14 @@ func (ts *TokenStore) handleRenew(ctx context.Context, req *logical.Request, dat
|
||||
return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
var resp *logical.Response
|
||||
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
return logical.ErrorResponse("batch tokens cannot be renewed"), nil
|
||||
}
|
||||
|
||||
// Renew the token and its children
|
||||
resp, err := ts.expiration.RenewToken(ctx, req, te, increment)
|
||||
resp, err = ts.expiration.RenewToken(ctx, req, te, increment)
|
||||
|
||||
if urltoken {
|
||||
resp.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`)
|
||||
@@ -2761,6 +2961,7 @@ func (ts *TokenStore) tokenStoreRoleRead(ctx context.Context, req *logical.Reque
|
||||
"orphan": role.Orphan,
|
||||
"path_suffix": role.PathSuffix,
|
||||
"renewable": role.Renewable,
|
||||
"token_type": role.TokenType.String(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2898,6 +3099,38 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic
|
||||
entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true)
|
||||
}
|
||||
|
||||
tokenType := entry.TokenType
|
||||
tokenTypeRaw, ok := data.GetOk("token_type")
|
||||
if ok {
|
||||
tokenTypeStr := tokenTypeRaw.(string)
|
||||
switch tokenTypeStr {
|
||||
case "", "service":
|
||||
tokenType = logical.TokenTypeService
|
||||
case "batch":
|
||||
tokenType = logical.TokenTypeBatch
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenTypeStr)), nil
|
||||
}
|
||||
} else if req.Operation == logical.CreateOperation {
|
||||
tokenType = logical.TokenTypeService
|
||||
}
|
||||
entry.TokenType = tokenType
|
||||
|
||||
if entry.TokenType == logical.TokenTypeBatch {
|
||||
if !entry.Orphan {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate non-orphan tokens"), nil
|
||||
}
|
||||
if entry.Period != 0 {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil
|
||||
}
|
||||
if entry.Renewable {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate renewable tokens"), nil
|
||||
}
|
||||
if entry.ExplicitMaxTTL != 0 {
|
||||
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with an explicit max TTL"), nil
|
||||
}
|
||||
}
|
||||
|
||||
ns, err := namespace.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/errwrap"
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/helper/locksutil"
|
||||
@@ -286,10 +288,22 @@ func getBackendConfig(c *Core) *logical.BackendConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
||||
func testMakeBatchTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
||||
testMakeTokenViaBackend(t, ts, root, client, ttl, policy, true)
|
||||
}
|
||||
|
||||
func testMakeServiceTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
||||
testMakeTokenViaBackend(t, ts, root, client, ttl, policy, false)
|
||||
}
|
||||
|
||||
func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string, batch bool) {
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = root
|
||||
if batch {
|
||||
req.Data["type"] = "batch"
|
||||
} else {
|
||||
req.Data["id"] = client
|
||||
}
|
||||
req.Data["policies"] = policy
|
||||
req.Data["ttl"] = ttl
|
||||
resp := testMakeTokenViaRequest(t, ts, req)
|
||||
@@ -301,22 +315,27 @@ func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl str
|
||||
|
||||
func testMakeTokenViaRequest(t testing.TB, ts *TokenStore, req *logical.Request) *logical.Response {
|
||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
t.Fatalf("got nil token from create call")
|
||||
}
|
||||
// Let the caller handle the error
|
||||
if resp.IsError() {
|
||||
return resp
|
||||
}
|
||||
|
||||
te := &logical.TokenEntry{
|
||||
Path: resp.Auth.CreationPath,
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
}
|
||||
|
||||
if resp.Auth.TokenType != logical.TokenTypeBatch {
|
||||
if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, resp.Auth); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
te, err = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if err != nil {
|
||||
@@ -333,9 +352,15 @@ func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry)
|
||||
if te.NamespaceID == "" {
|
||||
te.NamespaceID = namespace.RootNamespaceID
|
||||
}
|
||||
if te.CreationTime == 0 {
|
||||
te.CreationTime = time.Now().Unix()
|
||||
}
|
||||
if err := ts.create(namespace.RootContext(nil), te); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if te.Type == logical.TokenTypeDefault {
|
||||
te.Type = logical.TokenTypeService
|
||||
}
|
||||
auth := &logical.Auth{
|
||||
NumUses: te.NumUses,
|
||||
DisplayName: te.DisplayName,
|
||||
@@ -351,16 +376,37 @@ func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry)
|
||||
Period: te.Period,
|
||||
ExplicitMaxTTL: te.ExplicitMaxTTL,
|
||||
CreationPath: te.Path,
|
||||
TokenType: te.Type,
|
||||
}
|
||||
if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, auth); err != nil {
|
||||
err := ts.expiration.RegisterAuth(namespace.TestContext(), te, auth)
|
||||
switch err {
|
||||
case nil:
|
||||
if te.Type == logical.TokenTypeBatch {
|
||||
t.Fatal("expected error from trying to register auth with batch token")
|
||||
}
|
||||
default:
|
||||
if te.Type != logical.TokenTypeBatch {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
||||
func testMakeServiceTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
||||
testMakeTokenViaCore(t, c, root, client, ttl, policy, false, nil)
|
||||
}
|
||||
|
||||
func testMakeBatchTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
||||
testMakeTokenViaCore(t, c, root, client, ttl, policy, true, nil)
|
||||
}
|
||||
|
||||
func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string, batch bool, outAuth *logical.Auth) {
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create")
|
||||
req.ClientToken = root
|
||||
if batch {
|
||||
req.Data["type"] = "batch"
|
||||
} else {
|
||||
req.Data["id"] = client
|
||||
}
|
||||
req.Data["policies"] = policy
|
||||
req.Data["ttl"] = ttl
|
||||
|
||||
@@ -368,10 +414,15 @@ func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, polic
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
if !batch {
|
||||
if resp.Auth.ClientToken != client {
|
||||
t.Fatalf("bad: %#v", *resp)
|
||||
}
|
||||
}
|
||||
if outAuth != nil && resp != nil && resp.Auth != nil {
|
||||
*outAuth = *resp.Auth
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_AccessorIndex(t *testing.T) {
|
||||
c, _, _ := TestCoreUnsealed(t)
|
||||
@@ -404,13 +455,27 @@ func TestTokenStore_AccessorIndex(t *testing.T) {
|
||||
if aEntry.TokenID != ent.ID {
|
||||
t.Fatalf("bad: got\n%s\nexpected\n%s\n", aEntry.TokenID, ent.ID)
|
||||
}
|
||||
|
||||
// Make sure a batch token doesn't get an accessor
|
||||
ent.Type = logical.TokenTypeBatch
|
||||
testMakeTokenDirectly(t, ts, ent)
|
||||
|
||||
out, err = ts.Lookup(namespace.TestContext(), ent.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Ensure that accessor is created
|
||||
if out == nil || out.Accessor != "" {
|
||||
t.Fatalf("bad: %#v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
|
||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
out, err := ts.Lookup(namespace.TestContext(), "tokenid")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
@@ -448,7 +513,7 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) {
|
||||
|
||||
testKeys := []string{"token1", "token2", "token3", "token4"}
|
||||
for _, key := range testKeys {
|
||||
testMakeTokenViaBackend(t, ts, root, key, "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, key, "", []string{"foo"})
|
||||
}
|
||||
|
||||
// Revoke root to make the number of accessors match
|
||||
@@ -534,7 +599,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) {
|
||||
rootToken, err := ts.rootToken(namespace.TestContext())
|
||||
root := rootToken.ID
|
||||
|
||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
|
||||
auth := &logical.Auth{
|
||||
ClientToken: "tokenid",
|
||||
@@ -587,7 +652,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) {
|
||||
}
|
||||
|
||||
// Now test without registering the token through the expiration manager
|
||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||
out, err = ts.Lookup(namespace.TestContext(), "tokenid")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
@@ -639,6 +704,27 @@ func TestTokenStore_RootToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_NoRootBatch(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create")
|
||||
req.ClientToken = root
|
||||
req.Data["type"] = "batch"
|
||||
req.Data["policies"] = "root"
|
||||
req.Data["ttl"] = "5m"
|
||||
|
||||
resp, err := c.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected response")
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected error, got %#v", *resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_CreateLookup(t *testing.T) {
|
||||
c, _, _ := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
@@ -930,6 +1016,7 @@ func TestTokenStore_Revoke_Leases(t *testing.T) {
|
||||
Path: "noop/foo",
|
||||
ClientToken: ent.ID,
|
||||
}
|
||||
req.SetTokenEntry(ent)
|
||||
resp := &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -1226,6 +1313,19 @@ func TestTokenStore_HandleRequest_NonAssignable(t *testing.T) {
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected error; response is %#v", *resp)
|
||||
}
|
||||
|
||||
// Batch tokens too
|
||||
req.Data["type"] = "batch"
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("got a nil response")
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected error; response is %#v", *resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) {
|
||||
@@ -1250,6 +1350,7 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) {
|
||||
Path: "auth/token/create",
|
||||
DisplayName: "token-foo-bar-baz",
|
||||
TTL: 0,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if err != nil {
|
||||
@@ -1269,7 +1370,15 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) {
|
||||
req.ClientToken = root
|
||||
req.Data["num_uses"] = "1"
|
||||
|
||||
// Make sure batch tokens can't do limited use counts
|
||||
req.Data["type"] = "batch"
|
||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||
if resp == nil || !resp.IsError() {
|
||||
t.Fatalf("expected error: resp: %#v", resp)
|
||||
}
|
||||
|
||||
delete(req.Data, "type")
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
@@ -1284,6 +1393,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) {
|
||||
DisplayName: "token",
|
||||
NumUses: 1,
|
||||
TTL: 0,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if err != nil {
|
||||
@@ -1337,7 +1447,15 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) {
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = root
|
||||
|
||||
// Make sure batch tokens won't automatically assign root
|
||||
req.Data["type"] = "batch"
|
||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||
if resp == nil || !resp.IsError() {
|
||||
t.Fatalf("expected error: resp: %#v", resp)
|
||||
}
|
||||
|
||||
delete(req.Data, "type")
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
@@ -1351,6 +1469,7 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) {
|
||||
Path: "auth/token/create",
|
||||
DisplayName: "token",
|
||||
TTL: 0,
|
||||
Type: logical.TokenTypeService,
|
||||
}
|
||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if err != nil {
|
||||
@@ -1408,16 +1527,26 @@ func TestTokenStore_HandleRequest_CreateToken_RootID(t *testing.T) {
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
if resp.Auth.ClientToken != "foobar" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
// Retry with batch; batch should not actually accept a custom ID
|
||||
req.Data["type"] = "batch"
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
out, _ := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if out.ID == "foobar" {
|
||||
t.Fatalf("bad: %#v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = "client"
|
||||
@@ -1431,12 +1560,22 @@ func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) {
|
||||
if resp.Data["error"] != "root or sudo privileges required to specify token id" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
// Retry with batch
|
||||
req.Data["type"] = "batch"
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v resp: %#v", err, resp)
|
||||
}
|
||||
if resp.Data["error"] != "root or sudo privileges required to specify token id" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = "client"
|
||||
@@ -1449,12 +1588,28 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) {
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
ent := &logical.TokenEntry{
|
||||
NamespaceID: namespace.RootNamespaceID,
|
||||
Path: "test",
|
||||
Policies: []string{"foo", "bar"},
|
||||
TTL: time.Hour,
|
||||
}
|
||||
testMakeTokenDirectly(t, ts, ent)
|
||||
req.ClientToken = ent.ID
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_InvalidSubset(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = "client"
|
||||
@@ -1480,7 +1635,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_RootChild(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testMakeTokenViaBackend(t, ts, root, "sudoClient", "", []string{"test1"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "sudoClient", "", []string{"test1"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = "sudoClient"
|
||||
@@ -1555,7 +1710,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_RootChild(t *testing.T) {
|
||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_NoParent(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||
req.ClientToken = "client"
|
||||
@@ -1633,6 +1788,18 @@ func TestTokenStore_HandleRequest_CreateToken_Metadata(t *testing.T) {
|
||||
if !reflect.DeepEqual(out.Meta, meta) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta)
|
||||
}
|
||||
|
||||
// Test with batch tokens
|
||||
req.Data["type"] = "batch"
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
out, _ = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if !reflect.DeepEqual(out.Meta, meta) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
||||
@@ -1675,6 +1842,19 @@ func TestTokenStore_HandleRequest_CreateToken_TTL(t *testing.T) {
|
||||
if !resp.Auth.Renewable {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
// Test batch tokens
|
||||
req.Data["type"] = "batch"
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
if resp.Auth.TTL != time.Hour {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
if resp.Auth.Renewable {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||
@@ -1684,7 +1864,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||
rootToken, err := ts.rootToken(namespace.TestContext())
|
||||
root := rootToken.ID
|
||||
|
||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
|
||||
te, err := ts.Lookup(namespace.TestContext(), "child")
|
||||
if err != nil {
|
||||
@@ -1706,7 +1886,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
|
||||
te, err = ts.Lookup(namespace.TestContext(), "sub-child")
|
||||
if err != nil {
|
||||
@@ -1760,8 +1940,8 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||
}
|
||||
|
||||
// Now test without registering the tokens through the expiration manager
|
||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "revoke")
|
||||
req.Data = map[string]interface{}{
|
||||
@@ -1798,8 +1978,8 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||
func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "revoke-orphan")
|
||||
req.Data = map[string]interface{}{
|
||||
@@ -1850,7 +2030,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
|
||||
func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"foo"})
|
||||
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"foo"})
|
||||
|
||||
out, err := ts.Lookup(namespace.TestContext(), "child")
|
||||
if err != nil {
|
||||
@@ -1883,6 +2063,11 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
testTokenStore_HandleRequest_Lookup(t, false)
|
||||
testTokenStore_HandleRequest_Lookup(t, true)
|
||||
}
|
||||
|
||||
func testTokenStore_HandleRequest_Lookup(t *testing.T, batch bool) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||
@@ -1911,6 +2096,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
"explicit_max_ttl": int64(0),
|
||||
"expire_time": nil,
|
||||
"entity_id": "",
|
||||
"type": "service",
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
@@ -1922,63 +2108,20 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
||||
}
|
||||
|
||||
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
||||
outAuth := new(logical.Auth)
|
||||
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"}, batch, outAuth)
|
||||
|
||||
// Test via GET
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||
req.Data = map[string]interface{}{
|
||||
"token": "client",
|
||||
}
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
exp = map[string]interface{}{
|
||||
"id": "client",
|
||||
"accessor": resp.Data["accessor"],
|
||||
"policies": []string{"default", "foo"},
|
||||
"path": "auth/token/create",
|
||||
"meta": map[string]string(nil),
|
||||
"display_name": "token",
|
||||
"orphan": false,
|
||||
"num_uses": 0,
|
||||
"creation_ttl": int64(3600),
|
||||
"ttl": int64(3600),
|
||||
"explicit_max_ttl": int64(0),
|
||||
"renewable": true,
|
||||
"entity_id": "",
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
t.Fatalf("creation time was zero")
|
||||
}
|
||||
delete(resp.Data, "creation_time")
|
||||
if resp.Data["issue_time"].(time.Time).IsZero() {
|
||||
t.Fatal("issue time is default time")
|
||||
}
|
||||
delete(resp.Data, "issue_time")
|
||||
if resp.Data["expire_time"].(time.Time).IsZero() {
|
||||
t.Fatal("expire time is default time")
|
||||
}
|
||||
delete(resp.Data, "expire_time")
|
||||
|
||||
// Depending on timing of the test this may have ticked down, so accept 3599
|
||||
if resp.Data["ttl"].(int64) == 3599 {
|
||||
resp.Data["ttl"] = int64(3600)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
||||
tokenType := "service"
|
||||
expID := "client"
|
||||
if batch {
|
||||
tokenType = "batch"
|
||||
expID = outAuth.ClientToken
|
||||
}
|
||||
|
||||
// Test via POST
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||
req.Data = map[string]interface{}{
|
||||
"token": "client",
|
||||
"token": expID,
|
||||
}
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
@@ -1989,7 +2132,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
}
|
||||
|
||||
exp = map[string]interface{}{
|
||||
"id": "client",
|
||||
"id": expID,
|
||||
"accessor": resp.Data["accessor"],
|
||||
"policies": []string{"default", "foo"},
|
||||
"path": "auth/token/create",
|
||||
@@ -2000,8 +2143,9 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
"creation_ttl": int64(3600),
|
||||
"ttl": int64(3600),
|
||||
"explicit_max_ttl": int64(0),
|
||||
"renewable": true,
|
||||
"renewable": !batch,
|
||||
"entity_id": "",
|
||||
"type": tokenType,
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
@@ -2022,28 +2166,80 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
resp.Data["ttl"] = int64(3600)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
||||
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
|
||||
// Test via POST
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||
req.Data = map[string]interface{}{
|
||||
"token": expID,
|
||||
}
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
exp = map[string]interface{}{
|
||||
"id": expID,
|
||||
"accessor": resp.Data["accessor"],
|
||||
"policies": []string{"default", "foo"},
|
||||
"path": "auth/token/create",
|
||||
"meta": map[string]string(nil),
|
||||
"display_name": "token",
|
||||
"orphan": false,
|
||||
"num_uses": 0,
|
||||
"creation_ttl": int64(3600),
|
||||
"ttl": int64(3600),
|
||||
"explicit_max_ttl": int64(0),
|
||||
"renewable": !batch,
|
||||
"entity_id": "",
|
||||
"type": tokenType,
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
t.Fatalf("creation time was zero")
|
||||
}
|
||||
delete(resp.Data, "creation_time")
|
||||
if resp.Data["issue_time"].(time.Time).IsZero() {
|
||||
t.Fatal("issue time is default time")
|
||||
}
|
||||
delete(resp.Data, "issue_time")
|
||||
if resp.Data["expire_time"].(time.Time).IsZero() {
|
||||
t.Fatal("expire time is default time")
|
||||
}
|
||||
delete(resp.Data, "expire_time")
|
||||
|
||||
// Depending on timing of the test this may have ticked down, so accept 3599
|
||||
if resp.Data["ttl"].(int64) == 3599 {
|
||||
resp.Data["ttl"] = int64(3600)
|
||||
}
|
||||
|
||||
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
|
||||
// Test last_renewal_time functionality
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "renew")
|
||||
req.Data = map[string]interface{}{
|
||||
"token": "client",
|
||||
}
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||
req.Data = map[string]interface{}{
|
||||
"token": "client",
|
||||
"token": expID,
|
||||
}
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
if batch && !resp.IsError() || !batch && resp.IsError() {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
req.Path = "lookup"
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
@@ -2051,15 +2247,19 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
if !batch {
|
||||
if resp.Data["last_renewal_time"].(int64) == 0 {
|
||||
t.Fatalf("last_renewal_time was zero")
|
||||
}
|
||||
} else if _, ok := resp.Data["last_renewal_time"]; ok {
|
||||
t.Fatal("expected zero last renewal time")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
||||
testMakeServiceTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
||||
|
||||
req := logical.TestRequest(t, logical.ReadOperation, "lookup-self")
|
||||
req.ClientToken = "client"
|
||||
@@ -2085,6 +2285,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
||||
"ttl": int64(3600),
|
||||
"explicit_max_ttl": int64(0),
|
||||
"entity_id": "",
|
||||
"type": "service",
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
@@ -2257,6 +2458,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
||||
"path_suffix": "happenin",
|
||||
"explicit_max_ttl": int64(0),
|
||||
"renewable": true,
|
||||
"token_type": "service",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
@@ -2302,6 +2504,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
||||
"path_suffix": "happenin",
|
||||
"explicit_max_ttl": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "service",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
@@ -2339,6 +2542,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
||||
"path_suffix": "happenin",
|
||||
"period": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "service",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
@@ -2675,7 +2879,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
ts := c.tokenStore
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "roles/test")
|
||||
req := logical.TestRequest(t, logical.CreateOperation, "roles/test")
|
||||
req.ClientToken = root
|
||||
req.Data = map[string]interface{}{
|
||||
"path_suffix": "happenin",
|
||||
@@ -2690,6 +2894,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) {
|
||||
}
|
||||
|
||||
req.Path = "create/test"
|
||||
req.Operation = logical.UpdateOperation
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
@@ -4138,6 +4343,8 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) {
|
||||
Path: "prod/aws/foo",
|
||||
ClientToken: tut,
|
||||
}
|
||||
req.SetTokenEntry(testTokenEntry)
|
||||
|
||||
resp = &logical.Response{
|
||||
Secret: &logical.Secret{
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
@@ -4224,3 +4431,101 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) {
|
||||
t.Fatal("found leases")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_Batch_CannotCreateChildren(t *testing.T) {
|
||||
var resp *logical.Response
|
||||
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
ts := core.tokenStore
|
||||
|
||||
req := &logical.Request{
|
||||
Path: "create",
|
||||
ClientToken: root,
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"policies": []string{"policy1"},
|
||||
"type": "batch",
|
||||
},
|
||||
}
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||
}
|
||||
|
||||
req.ClientToken = resp.Auth.ClientToken
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_Batch_CannotRevoke(t *testing.T) {
|
||||
var resp *logical.Response
|
||||
var err error
|
||||
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
ts := core.tokenStore
|
||||
|
||||
req := &logical.Request{
|
||||
Path: "create",
|
||||
ClientToken: root,
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"policies": []string{"policy1"},
|
||||
"type": "batch",
|
||||
},
|
||||
}
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||
}
|
||||
|
||||
req.Path = "revoke"
|
||||
req.Data["token"] = resp.Auth.ClientToken
|
||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_Batch_NoCubbyhole(t *testing.T) {
|
||||
var resp *logical.Response
|
||||
var err error
|
||||
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
ts := core.tokenStore
|
||||
|
||||
req := &logical.Request{
|
||||
Path: "create",
|
||||
ClientToken: root,
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"policies": []string{"policy1"},
|
||||
"type": "batch",
|
||||
},
|
||||
}
|
||||
resp = testMakeTokenViaRequest(t, ts, req)
|
||||
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||
}
|
||||
|
||||
te, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Path = "cubbyhole/foo"
|
||||
req.Operation = logical.CreateOperation
|
||||
req.ClientToken = te.ID
|
||||
req.SetTokenEntry(te)
|
||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||
if err != nil && !errwrap.Contains(err, logical.ErrInvalidRequest.Error()) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ in-memory). It is only made for development or experimentation.
|
||||
|
||||
## Properties
|
||||
|
||||
The properties of the dev server:
|
||||
The properties of the dev server (some can be overridden with command line
|
||||
flags or by specifying a configuration file):
|
||||
|
||||
* **Initialized and unsealed** - The server will be automatically initialized
|
||||
and unsealed. You don't need to use `vault operator unseal`. It is ready
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
---
|
||||
layout: "docs"
|
||||
page_title: "Basic Concepts"
|
||||
page_title: "Concepts"
|
||||
sidebar_current: "docs-concepts"
|
||||
description: |-
|
||||
Basic concepts that are important to understand for Vault usage.
|
||||
Concepts that are important to understand for Vault usage.
|
||||
---
|
||||
|
||||
# Basic Concepts
|
||||
# Concepts
|
||||
|
||||
This section covers some high level basic concepts that are important
|
||||
to understand for day to day Vault usage and operation. Every page in
|
||||
this section is recommended reading for anyone consuming or operating
|
||||
Vault.
|
||||
This section covers some concepts that are important to understand for day to
|
||||
day Vault usage and operation. Every page in this section is recommended
|
||||
reading for anyone consuming or operating Vault.
|
||||
|
||||
Please use the navigation to the left to learn more about a topic.
|
||||
|
||||
@@ -8,12 +8,12 @@ description: |-
|
||||
|
||||
# Lease, Renew, and Revoke
|
||||
|
||||
With every dynamic secret and authentication token, Vault creates a _lease_:
|
||||
metadata containing information such as a time duration, renewability, and
|
||||
more. Vault promises that the data will be valid for the given duration, or
|
||||
Time To Live (TTL). Once the lease is expired, Vault can automatically revoke
|
||||
the data, and the consumer of the secret can no longer be certain that it is
|
||||
valid.
|
||||
With every dynamic secret and `service` type authentication token, Vault
|
||||
creates a _lease_: metadata containing information such as a time duration,
|
||||
renewability, and more. Vault promises that the data will be valid for the
|
||||
given duration, or Time To Live (TTL). Once the lease is expired, Vault can
|
||||
automatically revoke the data, and the consumer of the secret can no longer be
|
||||
certain that it is valid.
|
||||
|
||||
The benefit should be clear: consumers of secrets need to check in with
|
||||
Vault routinely to either renew the lease (if allowed) or request a
|
||||
@@ -35,7 +35,8 @@ or automatically by Vault. When a lease is expired, Vault will automatically
|
||||
revoke that lease.
|
||||
|
||||
**Note**: The [Key/Value Backend](/docs/secrets/kv/index.html) which stores
|
||||
arbitrary secrets does not issue leases.
|
||||
arbitrary secrets does not issue leases although it will sometimes return a
|
||||
lease duration; see the documentation for more information.
|
||||
|
||||
## Lease IDs
|
||||
|
||||
@@ -61,14 +62,6 @@ so often.
|
||||
As a result, the return value of renewals should be carefully inspected to
|
||||
determine what the new lease is.
|
||||
|
||||
**Note**: Prior to version 0.3, Vault documentation and help text did not
|
||||
distinguish sufficiently between a _lease_ and a _lease duration_. Starting
|
||||
with version 0.3, Vault will start migrating to the term _ttl_ to describe
|
||||
lease durations, at least for user-facing text. As _lease duration_ is still a
|
||||
legitimate (but more verbose) description, there are currently no plans to
|
||||
change the JSON key used in responses, in order to retain
|
||||
backwards-compatibility.
|
||||
|
||||
## Prefix-based Revocation
|
||||
|
||||
In addition to revoking a single secret, operators with proper access control
|
||||
|
||||
@@ -31,9 +31,15 @@ renewal time, and more.
|
||||
|
||||
Read on for a deeper dive into token concepts.
|
||||
|
||||
## Token Concepts
|
||||
## Token Types
|
||||
|
||||
### The Token Store
|
||||
As of Vault 1.0, there are two types of tokens: `service` tokens and `batch`
|
||||
tokens. A section near the bottom of this page contains detailed information
|
||||
about their differences, but it is useful to understand other token concepts
|
||||
first. The features in the following sections all apply to service tokens, and
|
||||
their applicability to batch tokens is discussed later.
|
||||
|
||||
## The Token Store
|
||||
|
||||
Often in documentation or in help channels, the "token store" is referenced.
|
||||
This is the same as the [`token` authentication
|
||||
@@ -42,13 +48,13 @@ backend in that it is responsible for creating and storing tokens, and cannot
|
||||
be disabled. It is also the only auth method that has no login
|
||||
capability -- all actions require existing authenticated tokens.
|
||||
|
||||
### Root Tokens
|
||||
## Root Tokens
|
||||
|
||||
Root tokens are tokens that have the `root` policy attached to them. Root
|
||||
tokens can do anything in Vault. _Anything_. In addition, they are the only
|
||||
type of token within Vault that can be set to never expire without any renewal
|
||||
needed. As a result, it is purposefully hard to create root tokens; in fact, as
|
||||
of version 0.6.1, there are only three ways to create root tokens:
|
||||
needed. As a result, it is purposefully hard to create root tokens; in fact
|
||||
there are only three ways to create root tokens:
|
||||
|
||||
1. The initial root token generated at `vault operator init` time -- this token has no
|
||||
expiration
|
||||
@@ -70,7 +76,7 @@ whenever a root token is live. This way multiple people can verify as to the
|
||||
tasks performed with the root token, and that the token was revoked immediately
|
||||
after these tasks were completed.
|
||||
|
||||
### Token Hierarchies and Orphan Tokens
|
||||
## Token Hierarchies and Orphan Tokens
|
||||
|
||||
Normally, when a token holder creates new tokens, these tokens will be created
|
||||
as children of the original token; tokens they create will be children of them;
|
||||
@@ -93,7 +99,7 @@ endpoint, which revokes the given token but rather than revoke the rest of the
|
||||
tree, it instead sets the tokens' immediate children to be orphans. Use with
|
||||
caution!
|
||||
|
||||
### Token Accessors
|
||||
## Token Accessors
|
||||
|
||||
When tokens are created, a token accessor is also created and returned. This
|
||||
accessor is a value that acts as a reference to a token and can only be used to
|
||||
@@ -121,7 +127,7 @@ dangerous endpoint (since listing all of the accessors means that they can then
|
||||
be used to revoke all tokens), it also provides a way to audit and revoke the
|
||||
currently-active set of tokens.
|
||||
|
||||
### Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs
|
||||
## Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs
|
||||
|
||||
Every non-root token has a time-to-live (TTL) associated with it, which is a
|
||||
current period of validity since either the token's creation time or last
|
||||
@@ -137,7 +143,7 @@ token is a periodic token (available for creation by `root`/`sudo` users, token
|
||||
store roles, or some auth methods), has an explicit maximum TTL
|
||||
attached, or neither.
|
||||
|
||||
#### The General Case
|
||||
### The General Case
|
||||
|
||||
In the general case, where there is neither a period nor explicit maximum TTL
|
||||
value set on the token, the token's lifetime since it was created will be
|
||||
@@ -165,7 +171,7 @@ current value and the client may want to reauthenticate and acquire a new
|
||||
token. However, outside of direct operator interaction, Vault will never revoke
|
||||
a token before the returned TTL has expired.
|
||||
|
||||
#### Explicit Max TTLs
|
||||
### Explicit Max TTLs
|
||||
|
||||
Tokens can have an explicit max TTL set on them. This value becomes a hard
|
||||
limit on the token's lifetime -- no matter what the values in (1), (2), and (3)
|
||||
@@ -173,7 +179,7 @@ from the general case are, the token cannot live past this explicitly-set
|
||||
value. This has an effect even when using periodic tokens to escape the normal
|
||||
TTL mechanism.
|
||||
|
||||
#### Periodic Tokens
|
||||
### Periodic Tokens
|
||||
|
||||
In some cases, having a token be revoked would be problematic -- for instance,
|
||||
if a long-running service needs to maintain its SQL connection pool over a long
|
||||
@@ -209,9 +215,65 @@ There are a few important things to know when using periodic tokens:
|
||||
* A token with both a period and an explicit max TTL will act like a periodic
|
||||
token but will be revoked when the explicit max TTL is reached
|
||||
|
||||
### CIDR-Bound Tokens
|
||||
## CIDR-Bound Tokens
|
||||
|
||||
Some tokens are able to be bound to CIDR(s) that restrict the range of client
|
||||
IPs allowed to use them. These affect all tokens except for non-expiring root
|
||||
tokens (those with a TTL of zero). If a root token has an expiration, it also
|
||||
is affected by CIDR-binding.
|
||||
|
||||
## Token Types in Detail
|
||||
|
||||
There are currently two types of tokens.
|
||||
|
||||
### Service Tokens
|
||||
|
||||
Service tokens are what users will generally think of as "normal" Vault tokens.
|
||||
They support all features, such as renewal, revocation, creating child tokens,
|
||||
and more. The are correspondingly heavyweight to create and track.
|
||||
|
||||
### Batch Tokens
|
||||
|
||||
Batch tokens are are encrypted blobs that carry enough information for them to
|
||||
be used for Vault actions, but they require no storage on disk to track them.
|
||||
As a result they are extremely lightweight and scalable, but lack most of the
|
||||
flexibility and features of service tokens.
|
||||
|
||||
### Token Type Comparison
|
||||
|
||||
This reference chart describes the difference in behavior between service and
|
||||
batch tokens.
|
||||
|
||||
| | Service Tokens | Batch Tokens |
|
||||
|---|---:|---:|
|
||||
| Can Be Root Tokens | Yes | No |
|
||||
| Can Create Child Tokens | Yes | No |
|
||||
| Can be Renewable | Yes | No |
|
||||
| Can be Periodic | Yes | No |
|
||||
| Can have Explicit Max TTL | Yes | No (always uses a fixed TTL) |
|
||||
| Has Accessors | Yes | No |
|
||||
| Has Cubbyhole | Yes | No |
|
||||
| Revoked with Parent (if not orphan) | Yes | Stops Working |
|
||||
| Dynamic Secrets Lease Assignment | Self | Parent (if not orphan) |
|
||||
| Can be Used Across Performance Replication Clusters | No | Yes (if orphan) |
|
||||
| Creation Scales with Performance Standby Node Count | No | Yes |
|
||||
| Cost | Heavyweight; multiple storage writes per token creation | Lightweight; no storage cost for token creation |
|
||||
|
||||
### Service vs. Batch Token Lease Handling
|
||||
|
||||
#### Service Tokens
|
||||
|
||||
Leases created by service tokens (including child tokens' leases) are tracked
|
||||
along with the service token and revoked when the token expires.
|
||||
|
||||
#### Batch Tokens
|
||||
|
||||
Leases created by batch tokens are constrained to the remaining TTL of the
|
||||
batch tokens and, if the batch token is not an orphan, are tracked by the
|
||||
parent. They are revoked when the batch token's TTL expires, or when the batch
|
||||
token's parent is revoked (at which point the batch token is also denied access
|
||||
to Vault).
|
||||
|
||||
As a corollary, batch tokens can be used across performance replication
|
||||
clusters, but only if they are orphan, since non-orphan tokens will not be able
|
||||
to ensure the validity of the parent token.
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-concepts") %>>
|
||||
<a href="/docs/concepts/index.html">Basic Concepts</a>
|
||||
<a href="/docs/concepts/index.html">Concepts</a>
|
||||
<ul class="nav">
|
||||
<li<%= sidebar_current("docs-concepts-devserver") %>>
|
||||
<a href="/docs/concepts/dev-server.html">"Dev" Server</a>
|
||||
@@ -63,6 +63,32 @@
|
||||
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html">Tokens</a>
|
||||
<ul class="nav">
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#token-types">Token Types</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#the-token-store">The Token Store</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#root-tokens">Root Tokens</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#token-hierarchies-and-orphan-tokens">Token Hierarchies and Orphan Tokens</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#token-accessors">Token Accessors</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#token-time-to-live-periodic-tokens-and-explicit-max-ttls">Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#cidr-bound-tokens">CIDR-Bound Tokens</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||
<a href="/docs/concepts/tokens.html#token-types-in-detail">Token Types in Detail</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-concepts-response-wrapping") %>>
|
||||
|
||||
Reference in New Issue
Block a user