Remove dynamic keys from SSH Secrets Engine (#18874)

* Remove dynamic keys from SSH Secrets Engine

This removes the functionality of Vault creating keys and adding them to
the authorized keys file on hosts.

This functionality has been deprecated since Vault version 0.7.2.

The preferred alternative is to use the SSH CA method, which also allows
key generation but places limits on TTL and doesn't require Vault reach
out to provision each key on the specified host, making it much more
secure.

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Remove dynamic ssh references from documentation

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Add changelog entry

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Remove dynamic key secret type entirely

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Clarify changelog language

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Add removal notice to the website

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

---------

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
Alexander Scheel
2023-01-31 16:02:22 -05:00
committed by GitHub
parent cc26d0dcb8
commit 8e7f2076a2
15 changed files with 67 additions and 1439 deletions

View File

@@ -26,17 +26,15 @@ import (
)
const (
testIP = "127.0.0.1"
testUserName = "vaultssh"
testMultiUserName = "vaultssh,otherssh"
testAdminUser = "vaultssh"
testCaKeyType = "ca"
testOTPKeyType = "otp"
testDynamicKeyType = "dynamic"
testCIDRList = "127.0.0.1/32"
testAtRoleName = "test@RoleName"
testDynamicRoleName = "testDynamicRoleName"
testOTPRoleName = "testOTPRoleName"
testIP = "127.0.0.1"
testUserName = "vaultssh"
testMultiUserName = "vaultssh,otherssh"
testAdminUser = "vaultssh"
testCaKeyType = "ca"
testOTPKeyType = "otp"
testCIDRList = "127.0.0.1/32"
testAtRoleName = "test@RoleName"
testOTPRoleName = "testOTPRoleName"
// testKeyName is the name of the entry that will be written to SSHMOUNTPOINT/ssh/keys
testKeyName = "testKeyName"
// testSharedPrivateKey is the value of the entry that will be written to SSHMOUNTPOINT/ssh/keys
@@ -537,36 +535,22 @@ func TestSSHBackend_Lookup(t *testing.T) {
"default_user": testUserName,
"cidr_list": testCIDRList,
}
testDynamicRoleData := map[string]interface{}{
"key_type": testDynamicKeyType,
"key": testKeyName,
"admin_user": testAdminUser,
"default_user": testAdminUser,
"cidr_list": testCIDRList,
}
data := map[string]interface{}{
"ip": testIP,
}
resp1 := []string(nil)
resp2 := []string{testOTPRoleName}
resp3 := []string{testDynamicRoleName, testOTPRoleName}
resp4 := []string{testDynamicRoleName}
resp5 := []string{testAtRoleName}
resp3 := []string{testAtRoleName}
logicaltest.Test(t, logicaltest.TestCase{
LogicalFactory: newTestingFactory(t),
Steps: []logicaltest.TestStep{
testLookupRead(t, data, resp1),
testRoleWrite(t, testOTPRoleName, testOTPRoleData),
testLookupRead(t, data, resp2),
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testRoleWrite(t, testDynamicRoleName, testDynamicRoleData),
testLookupRead(t, data, resp3),
testRoleDelete(t, testOTPRoleName),
testLookupRead(t, data, resp4),
testRoleDelete(t, testDynamicRoleName),
testLookupRead(t, data, resp1),
testRoleWrite(t, testAtRoleName, testDynamicRoleData),
testLookupRead(t, data, resp5),
testRoleWrite(t, testAtRoleName, testOTPRoleData),
testLookupRead(t, data, resp3),
testRoleDelete(t, testAtRoleName),
testLookupRead(t, data, resp1),
},
@@ -615,39 +599,6 @@ func TestSSHBackend_RoleList(t *testing.T) {
})
}
func TestSSHBackend_DynamicKeyCreate(t *testing.T) {
cleanup, sshAddress := prepareTestContainer(t, "", "")
defer cleanup()
host, port, err := net.SplitHostPort(sshAddress)
if err != nil {
t.Fatal(err)
}
testDynamicRoleData := map[string]interface{}{
"key_type": testDynamicKeyType,
"key": testKeyName,
"admin_user": testAdminUser,
"default_user": testAdminUser,
"cidr_list": host + "/32",
"port": port,
}
data := map[string]interface{}{
"username": testUserName,
"ip": host,
}
logicaltest.Test(t, logicaltest.TestCase{
LogicalFactory: newTestingFactory(t),
Steps: []logicaltest.TestStep{
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testRoleWrite(t, testDynamicRoleName, testDynamicRoleData),
testCredsWrite(t, testDynamicRoleName, data, false, sshAddress),
testRoleWrite(t, testAtRoleName, testDynamicRoleData),
testCredsWrite(t, testAtRoleName, data, false, sshAddress),
},
})
}
func TestSSHBackend_OTPRoleCrud(t *testing.T) {
testOTPRoleData := map[string]interface{}{
"key_type": testOTPKeyType,
@@ -675,50 +626,6 @@ func TestSSHBackend_OTPRoleCrud(t *testing.T) {
})
}
func TestSSHBackend_DynamicRoleCrud(t *testing.T) {
testDynamicRoleData := map[string]interface{}{
"key_type": testDynamicKeyType,
"key": testKeyName,
"admin_user": testAdminUser,
"default_user": testAdminUser,
"cidr_list": testCIDRList,
}
respDynamicRoleData := map[string]interface{}{
"cidr_list": testCIDRList,
"port": 22,
"install_script": DefaultPublicKeyInstallScript,
"key_bits": 1024,
"key": testKeyName,
"admin_user": testUserName,
"default_user": testUserName,
"key_type": testDynamicKeyType,
}
logicaltest.Test(t, logicaltest.TestCase{
LogicalFactory: newTestingFactory(t),
Steps: []logicaltest.TestStep{
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testRoleWrite(t, testDynamicRoleName, testDynamicRoleData),
testRoleRead(t, testDynamicRoleName, respDynamicRoleData),
testRoleDelete(t, testDynamicRoleName),
testRoleRead(t, testDynamicRoleName, nil),
testRoleWrite(t, testAtRoleName, testDynamicRoleData),
testRoleRead(t, testAtRoleName, respDynamicRoleData),
testRoleDelete(t, testAtRoleName),
testRoleRead(t, testAtRoleName, nil),
},
})
}
func TestSSHBackend_NamedKeysCrud(t *testing.T) {
logicaltest.Test(t, logicaltest.TestCase{
LogicalFactory: newTestingFactory(t),
Steps: []logicaltest.TestStep{
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testNamedKeysDelete(t),
},
})
}
func TestSSHBackend_OTPCreate(t *testing.T) {
cleanup, sshAddress := prepareTestContainer(t, "", "")
defer func() {
@@ -772,24 +679,14 @@ func TestSSHBackend_ConfigZeroAddressCRUD(t *testing.T) {
"default_user": testUserName,
"cidr_list": testCIDRList,
}
testDynamicRoleData := map[string]interface{}{
"key_type": testDynamicKeyType,
"key": testKeyName,
"admin_user": testAdminUser,
"default_user": testAdminUser,
"cidr_list": testCIDRList,
}
req1 := map[string]interface{}{
"roles": testOTPRoleName,
}
resp1 := map[string]interface{}{
"roles": []string{testOTPRoleName},
}
req2 := map[string]interface{}{
"roles": fmt.Sprintf("%s,%s", testOTPRoleName, testDynamicRoleName),
}
resp2 := map[string]interface{}{
"roles": []string{testOTPRoleName, testDynamicRoleName},
"roles": []string{testOTPRoleName},
}
resp3 := map[string]interface{}{
"roles": []string{},
@@ -801,11 +698,7 @@ func TestSSHBackend_ConfigZeroAddressCRUD(t *testing.T) {
testRoleWrite(t, testOTPRoleName, testOTPRoleData),
testConfigZeroAddressWrite(t, req1),
testConfigZeroAddressRead(t, resp1),
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testRoleWrite(t, testDynamicRoleName, testDynamicRoleData),
testConfigZeroAddressWrite(t, req2),
testConfigZeroAddressRead(t, resp2),
testRoleDelete(t, testDynamicRoleName),
testConfigZeroAddressRead(t, resp1),
testRoleDelete(t, testOTPRoleName),
testConfigZeroAddressRead(t, resp3),
@@ -839,43 +732,6 @@ func TestSSHBackend_CredsForZeroAddressRoles_otp(t *testing.T) {
})
}
func TestSSHBackend_CredsForZeroAddressRoles_dynamic(t *testing.T) {
cleanup, sshAddress := prepareTestContainer(t, "", "")
defer cleanup()
host, port, err := net.SplitHostPort(sshAddress)
if err != nil {
t.Fatal(err)
}
dynamicRoleData := map[string]interface{}{
"key_type": testDynamicKeyType,
"key": testKeyName,
"admin_user": testAdminUser,
"default_user": testAdminUser,
"port": port,
}
data := map[string]interface{}{
"username": testUserName,
"ip": host,
}
req2 := map[string]interface{}{
"roles": testDynamicRoleName,
}
logicaltest.Test(t, logicaltest.TestCase{
LogicalFactory: newTestingFactory(t),
Steps: []logicaltest.TestStep{
testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
testRoleWrite(t, testDynamicRoleName, dynamicRoleData),
testCredsWrite(t, testDynamicRoleName, data, true, sshAddress),
testConfigZeroAddressWrite(t, req2),
testCredsWrite(t, testDynamicRoleName, data, false, sshAddress),
testConfigZeroAddressDelete(t),
testCredsWrite(t, testDynamicRoleName, data, true, sshAddress),
},
})
}
func TestSSHBackend_CA(t *testing.T) {
testCases := []struct {
name string
@@ -2414,23 +2270,6 @@ func testVerifyWrite(t *testing.T, data map[string]interface{}, expected map[str
}
}
func testNamedKeysWrite(t *testing.T, name, key string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: fmt.Sprintf("keys/%s", name),
Data: map[string]interface{}{
"key": key,
},
}
}
func testNamedKeysDelete(t *testing.T) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.DeleteOperation,
Path: fmt.Sprintf("keys/%s", testKeyName),
}
}
func testLookupRead(t *testing.T, data map[string]interface{}, expected []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
@@ -2495,10 +2334,6 @@ func testRoleRead(t *testing.T, roleName string, expected map[string]interface{}
if d.KeyType != expected["key_type"] || d.DefaultUser != expected["default_user"] || d.CIDRList != expected["cidr_list"] {
return fmt.Errorf("data mismatch. bad: %#v", resp)
}
case "dynamic":
if d.AdminUser != expected["admin_user"] || d.CIDRList != expected["cidr_list"] || d.KeyName != expected["key"] || d.KeyType != expected["key_type"] {
return fmt.Errorf("data mismatch. bad: %#v", resp)
}
default:
return fmt.Errorf("unknown key type. bad: %#v", resp)
}
@@ -2539,7 +2374,7 @@ func testCredsWrite(t *testing.T, roleName string, data map[string]interface{},
}
return nil
}
if roleName == testDynamicRoleName || roleName == testAtRoleName {
if roleName == testAtRoleName {
var d struct {
Key string `mapstructure:"key"`
}