Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forbid ssh key signing with specified extensions when role allowed_extensions is not set #12847

Merged
merged 3 commits into from Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 49 additions & 2 deletions builtin/logical/ssh/backend_test.go
Expand Up @@ -124,7 +124,6 @@ SjOQL/GkH1nkRcDS9++aAAAAAmNhAQID

dockerImageTagSupportsRSA1 = "8.1_p1-r0-ls20"
dockerImageTagSupportsNoRSA1 = "8.4_p1-r3-ls48"

)

func prepareTestContainer(t *testing.T, tag, caPublicKeyPEM string) (func(), string) {
Expand Down Expand Up @@ -1414,6 +1413,53 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) {
}
}

func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) {
cluster, userpassToken := getSshCaTestCluster(t, testUserName)
defer cluster.Cleanup()
client := cluster.Cores[0].Client

// Get auth accessor for identity template.
auths, err := client.Sys().ListAuth()
if err != nil {
t.Fatal(err)
}
userpassAccessor := auths["userpass/"].Accessor

// Write SSH role to test with any extension. We also provide a templated default extension,
stevendpclark marked this conversation as resolved.
Show resolved Hide resolved
// to verify that it's not actually being evaluated
_, err = client.Logical().Write("ssh/roles/test_allow_all_extensions", map[string]interface{}{
"key_type": "ca",
"allow_user_certificates": true,
"allowed_users": "tuber",
"default_user": "tuber",
"allowed_extensions": "",
"default_extensions_template": false,
"default_extensions": map[string]interface{}{
"login@foobar.com": "{{identity.entity.aliases." + userpassAccessor + ".name}}",
},
})
if err != nil {
t.Fatal(err)
}

// Issue SSH certificate with default extensions templating disabled, and no user-provided extensions
client.SetToken(userpassToken)
userProvidedAnyExtensionPermissions := map[string]string{
"login@foobar.com": "not_userpassname",
}
_, err = client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{
"public_key": publicKey4096,
"extensions": userProvidedAnyExtensionPermissions,
})
if err == nil {
t.Fatal("Expected failure we should not have allowed specifying custom extensions")
}

if !strings.Contains(err.Error(), "are not on allowed list") {
t.Fatalf("Expected failure to contain 'are not on allowed list' but was %s", err)
}
}

func TestBackend_DefExtTemplatingDisabled(t *testing.T) {
cluster, userpassToken := getSshCaTestCluster(t, testUserName)
defer cluster.Cleanup()
Expand All @@ -1433,6 +1479,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) {
"allow_user_certificates": true,
"allowed_users": "tuber",
"default_user": "tuber",
"allowed_extensions": "*",
"default_extensions_template": false,
"default_extensions": map[string]interface{}{
"login@foobar.com": "{{identity.entity.aliases." + userpassAccessor + ".name}}",
Expand All @@ -1444,7 +1491,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) {

sshKeyID := "vault-userpass-"+testUserName+"-9bd0f01b7dfc50a13aa5e5cd11aea19276968755c8f1f9c98965d04147f30ed0"

// Issue SSH certificate with default extensions templating disabled, and no user-provided extensions
// Issue SSH certificate with default extensions templating disabled, and no user-provided extensions
client.SetToken(userpassToken)
defaultExtensionPermissions := map[string]string{
"login@foobar.com": "{{identity.entity.aliases." + userpassAccessor + ".name}}",
Expand Down
3 changes: 2 additions & 1 deletion builtin/logical/ssh/path_roles.go
Expand Up @@ -245,7 +245,8 @@ func pathRoles(b *backend) *framework.Path {
Description: `
[Not applicable for Dynamic type] [Not applicable for OTP type] [Optional for CA type]
A comma-separated list of extensions that certificates can have when signed.
To allow any extensions, set this to an empty string.
An empty list means that no extension overrides are allowed by an end-user; explicitly
specify '*' to allow any extensions to be set.
`,
},
"default_critical_options": {
Expand Down
13 changes: 12 additions & 1 deletion builtin/logical/ssh/path_sign.go
Expand Up @@ -362,8 +362,19 @@ func (b *backend) calculateExtensions(data *framework.FieldData, req *logical.Re

if len(unparsedExtensions) > 0 {
extensions := convertMapToStringValue(unparsedExtensions)
if role.AllowedExtensions == "*" {
// Allowed extensions was configured to allow all
return extensions, nil
}
notAllowed := []string{}
if role.AllowedExtensions == "" {
stevendpclark marked this conversation as resolved.
Show resolved Hide resolved
// If we don't have any explicit allowed_extensions configured fail closed if the user provided us any.
for k := range extensions {
notAllowed = append(notAllowed, k)
}
return nil, fmt.Errorf("extensions %v are not on allowed list", notAllowed)
}
if role.AllowedExtensions != "" {
notAllowed := []string{}
allowedExtensions := strings.Split(role.AllowedExtensions, ",")

for extensionKey, _ := range extensions {
Expand Down
5 changes: 5 additions & 0 deletions changelog/12847.txt
@@ -0,0 +1,5 @@
```release-note:breaking-change
secrets/ssh: Roles with empty allowed_extensions will now forbid end-users
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has to be a single line, though I'm not positive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's okay as 11775.txt (https://github.com/hashicorp/vault/blob/main/changelog/11775.txt) is multiple lines and seems to have generated ok: https://github.com/hashicorp/vault/blob/main/CHANGELOG.md#180

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good to know.

specifying extensions when requesting ssh key signing. Update roles setting
allowed_extensions to '*' to permit any extension to be specified by an end-user.
```