Evaluate ssh validprincipals user template before splitting (#16622)
The SSH secrets engine previously split the `validPrincipals` field on comma, then if user templating is enabled, evaluated the templates on each substring. This meant the identity template was only ever allowed to return a single principal. There are use cases where it would be helpful for identity metadata to contain a list of valid principals and for the identity template to be able to inject all of those as valid principals. This change inverts the order of processing. First the template is evaluated, and then the resulting string is split on commas. This allows the identity template to return a single comma-separated string with multiple permitted principals. There is a potential security implication here, that if a user is allowed to update their own identity metadata, they may be able to elevate privileges where previously this was not possible. Fixes #11038
This commit is contained in:
parent
2d6c6c01c4
commit
d710f8e8dc
|
@ -28,6 +28,7 @@ import (
|
||||||
const (
|
const (
|
||||||
testIP = "127.0.0.1"
|
testIP = "127.0.0.1"
|
||||||
testUserName = "vaultssh"
|
testUserName = "vaultssh"
|
||||||
|
testMultiUserName = "vaultssh,otherssh"
|
||||||
testAdminUser = "vaultssh"
|
testAdminUser = "vaultssh"
|
||||||
testCaKeyType = "ca"
|
testCaKeyType = "ca"
|
||||||
testOTPKeyType = "otp"
|
testOTPKeyType = "otp"
|
||||||
|
@ -356,6 +357,15 @@ func TestBackend_AllowedUsersTemplate(t *testing.T) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackend_MultipleAllowedUsersTemplate(t *testing.T) {
|
||||||
|
testAllowedUsersTemplate(t,
|
||||||
|
"{{ identity.entity.metadata.ssh_username }}",
|
||||||
|
testUserName, map[string]string{
|
||||||
|
"ssh_username": testMultiUserName,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBackend_AllowedUsersTemplate_WithStaticPrefix(t *testing.T) {
|
func TestBackend_AllowedUsersTemplate_WithStaticPrefix(t *testing.T) {
|
||||||
testAllowedUsersTemplate(t,
|
testAllowedUsersTemplate(t,
|
||||||
"ssh-{{ identity.entity.metadata.ssh_username }}",
|
"ssh-{{ identity.entity.metadata.ssh_username }}",
|
||||||
|
|
|
@ -176,18 +176,14 @@ func (b *backend) calculateValidPrincipals(data *framework.FieldData, req *logic
|
||||||
parsedPrincipals := strutil.RemoveDuplicates(strutil.ParseStringSlice(validPrincipals, ","), false)
|
parsedPrincipals := strutil.RemoveDuplicates(strutil.ParseStringSlice(validPrincipals, ","), false)
|
||||||
// Build list of allowed Principals from template and static principalsAllowedByRole
|
// Build list of allowed Principals from template and static principalsAllowedByRole
|
||||||
var allowedPrincipals []string
|
var allowedPrincipals []string
|
||||||
for _, principal := range strutil.RemoveDuplicates(strutil.ParseStringSlice(principalsAllowedByRole, ","), false) {
|
|
||||||
if enableTemplating {
|
if enableTemplating {
|
||||||
rendered, err := b.renderPrincipal(principal, req)
|
rendered, err := b.renderPrincipal(principalsAllowedByRole, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Template returned a principal
|
allowedPrincipals = strutil.RemoveDuplicates(strutil.ParseStringSlice(rendered, ","), false)
|
||||||
allowedPrincipals = append(allowedPrincipals, rendered)
|
|
||||||
} else {
|
} else {
|
||||||
// Static principal
|
allowedPrincipals = strutil.RemoveDuplicates(strutil.ParseStringSlice(principalsAllowedByRole, ","), false)
|
||||||
allowedPrincipals = append(allowedPrincipals, principal)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
secrets/ssh: Evaluate ssh validprincipals user template before splitting
|
||||||
|
```
|
Loading…
Reference in New Issue