Switch to secure signing algorithm for SSH secrets engine (#14006)

* Explicitly call out SSH algorithm_signer default

Related: #11608

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

* Use rsa-sha2-256 as the default SSH CA hash algo

As mentioned in the OpenSSH 8.2 release notes, OpenSSH will no longer be
accepting ssh-rsa signatures by default as these use the insecure SHA-1
algorithm.

For roles in which an explicit signature type wasn't specified, we
should change the default from SHA-1 to SHA-256 for security and
compatibility with modern OpenSSH releases.

See also: https://www.openssh.com/txt/release-8.2

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

* Update docs mentioning new algorithm change

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

* Add changelog entry

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

* Fix missing parenthesis, clarify new default value

* Add to side bar

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
Alexander Scheel 2022-02-18 09:44:01 -06:00 committed by GitHub
parent dbedb23f5a
commit f0dc3a553f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 122 additions and 11 deletions

View File

@ -720,7 +720,23 @@ func TestSSHBackend_CA(t *testing.T) {
testCAPublicKey, testCAPublicKey,
testCAPrivateKey, testCAPrivateKey,
"", "",
true, false,
},
{
"RSAKey_DefaultAlgoSigner_ImageSupportsRSA1",
dockerImageTagSupportsRSA1,
testCAPublicKey,
testCAPrivateKey,
"default",
false,
},
{
"RSAKey_DefaultAlgoSigner_ImageSupportsNoRSA1",
dockerImageTagSupportsNoRSA1,
testCAPublicKey,
testCAPrivateKey,
"default",
false,
}, },
{ {
"RSAKey_RSA1AlgoSigner_ImageSupportsRSA1", "RSAKey_RSA1AlgoSigner_ImageSupportsRSA1",
@ -730,6 +746,14 @@ func TestSSHBackend_CA(t *testing.T) {
ssh.SigAlgoRSA, ssh.SigAlgoRSA,
false, false,
}, },
{
"RSAKey_RSA1AlgoSigner_ImageSupportsNoRSA1",
dockerImageTagSupportsNoRSA1,
testCAPublicKey,
testCAPrivateKey,
ssh.SigAlgoRSA,
true,
},
{ {
"RSAKey_RSASHA2256AlgoSigner_ImageSupportsRSA1", "RSAKey_RSASHA2256AlgoSigner_ImageSupportsRSA1",
dockerImageTagSupportsRSA1, dockerImageTagSupportsRSA1,

View File

@ -22,10 +22,13 @@ const (
// KeyTypeCA is an key of type CA // KeyTypeCA is an key of type CA
KeyTypeCA = "ca" KeyTypeCA = "ca"
// DefaultAlgorithmSigner is the default RSA signing algorithm
DefaultAlgorithmSigner = "default"
// Present version of the sshRole struct; when adding a new field or are // Present version of the sshRole struct; when adding a new field or are
// needing to perform a migration, increment this struct and read the note // needing to perform a migration, increment this struct and read the note
// in checkUpgrade(...). // in checkUpgrade(...).
roleEntryVersion = 1 roleEntryVersion = 2
) )
// Structure that represents a role in SSH backend. This is a common role structure // Structure that represents a role in SSH backend. This is a common role structure
@ -354,7 +357,7 @@ func pathRoles(b *backend) *framework.Path {
Type: framework.TypeString, Type: framework.TypeString,
Description: ` Description: `
When supplied, this value specifies a signing algorithm for the key. Possible values: When supplied, this value specifies a signing algorithm for the key. Possible values:
ssh-rsa, rsa-sha2-256, rsa-sha2-512. ssh-rsa, rsa-sha2-256, rsa-sha2-512, default, or the empty string.
`, `,
DisplayAttrs: &framework.DisplayAttributes{ DisplayAttrs: &framework.DisplayAttributes{
Name: "Signing Algorithm", Name: "Signing Algorithm",
@ -496,15 +499,17 @@ func (b *backend) pathRoleWrite(ctx context.Context, req *logical.Request, d *fr
Version: roleEntryVersion, Version: roleEntryVersion,
} }
} else if keyType == KeyTypeCA { } else if keyType == KeyTypeCA {
algorithmSigner := "" algorithmSigner := DefaultAlgorithmSigner
algorithmSignerRaw, ok := d.GetOk("algorithm_signer") algorithmSignerRaw, ok := d.GetOk("algorithm_signer")
if ok { if ok {
algorithmSigner = algorithmSignerRaw.(string) algorithmSigner = algorithmSignerRaw.(string)
switch algorithmSigner { switch algorithmSigner {
case ssh.SigAlgoRSA, ssh.SigAlgoRSASHA2256, ssh.SigAlgoRSASHA2512: case ssh.SigAlgoRSA, ssh.SigAlgoRSASHA2256, ssh.SigAlgoRSASHA2512:
case "": case "", DefaultAlgorithmSigner:
// This case is valid, and the sign operation will use the signer's // This case is valid, and the sign operation will use the signer's
// default algorithm. // default algorithm. Explicitly reset the value to the default value
// rather than use the more vague implicit empty string.
algorithmSigner = DefaultAlgorithmSigner
default: default:
return nil, fmt.Errorf("unknown algorithm signer %q", algorithmSigner) return nil, fmt.Errorf("unknown algorithm signer %q", algorithmSigner)
} }
@ -631,6 +636,42 @@ func (b *backend) checkUpgrade(ctx context.Context, s logical.Storage, n string,
modified = true modified = true
} }
// Role version 2 migrates an empty AlgorithmSigner to an explicit ssh-rsa
// value WHEN the SSH CA key is a RSA key.
if result.Version < 2 {
// In order to perform the version 2 upgrade, we need knowledge of the
// signing key type as we want to make ssh-rsa an explicitly notated
// algorithm choice.
var publicKey ssh.PublicKey
publicKeyEntry, err := caKey(ctx, s, caPublicKey)
if err != nil {
b.Logger().Debug(fmt.Sprintf("failed to load public key entry while attempting to migrate: %v", err))
goto SKIPVERSION2
}
if publicKeyEntry == nil || publicKeyEntry.Key == "" {
b.Logger().Debug(fmt.Sprintf("got empty public key entry while attempting to migrate"))
goto SKIPVERSION2
}
publicKey, err = parsePublicSSHKey(publicKeyEntry.Key)
if err == nil {
// Move an empty signing algorithm to an explicit ssh-rsa (SHA-1)
// if this key is of type RSA. This isn't a secure default but
// exists for backwards compatibility with existing versions of
// Vault. By making it explicit, operators can see that this is
// the value and move it to a newer algorithm in the future.
if publicKey.Type() == ssh.KeyAlgoRSA && result.AlgorithmSigner == "" {
result.AlgorithmSigner = ssh.SigAlgoRSA
}
result.Version = 2
modified = true
}
SKIPVERSION2:
err = nil
}
// Add new migrations just before here. // Add new migrations just before here.
// //
// Condition copied from PKI builtin. // Condition copied from PKI builtin.

View File

@ -560,6 +560,14 @@ func (b *creationBundle) sign() (retCert *ssh.Certificate, retErr error) {
certificateBytes := out[:len(out)-4] certificateBytes := out[:len(out)-4]
algo := b.Role.AlgorithmSigner algo := b.Role.AlgorithmSigner
// Handle the new default algorithm selection process correctly.
if algo == DefaultAlgorithmSigner && sshAlgorithmSigner.PublicKey().Type() == ssh.KeyAlgoRSA {
algo = ssh.SigAlgoRSASHA2256
} else if algo == DefaultAlgorithmSigner {
algo = ""
}
sig, err := sshAlgorithmSigner.SignWithAlgorithm(rand.Reader, certificateBytes, algo) sig, err := sshAlgorithmSigner.SignWithAlgorithm(rand.Reader, certificateBytes, algo)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate signed SSH key: sign error: %w", err) return nil, fmt.Errorf("failed to generate signed SSH key: sign error: %w", err)

3
changelog/14006.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/ssh: Use secure default for algorithm signer (rsa-sha2-256) with RSA SSH CA keys on new roles
```

View File

@ -215,11 +215,21 @@ This endpoint creates or updates a named role.
the CA type. To specify multiple sizes, either use a comma-separated list or an the CA type. To specify multiple sizes, either use a comma-separated list or an
array of allowed key widths. array of allowed key widths.
- `algorithm_signer` `(string: "")` - Algorithm to sign keys with. Valid - `algorithm_signer` `(string: "default")` - Algorithm to sign keys with. Valid
values are `ssh-rsa`, `rsa-sha2-256`, and `rsa-sha2-512`. This value may be left values are `ssh-rsa`, `rsa-sha2-256`, `rsa-sha2-512`, or `default`. This
blank to use the signer's default algorithm, and must be left blank for CA key types value may also be left blank to use the signer's default algorithm, and must
other than RSA. Note that `ssh-rsa` is now considered insecure and is not be left blank or have value `default` for CA key types other than RSA.
supported by current OpenSSH versions.
~> **Note**: The value of `default` may change over time as vulnerabilities
in algorithms are discovered. The present value for RSA keys is equivalent
to `rsa-sha2-256`.
~> **Warning**: The `algorithm_signer` value `ssh-rsa` uses the SHA-1 hash
algorithm. This algorithm is now considered insecure and is not supported by
current OpenSSH versions. As a result, Vault has made the new default
`rsa-sha2-256` for RSA CA keys. It is strongly encouraged for all users to
migrate to `rsa-sha2-256` or `default` if the role was created with an
explicit `algorithm_signer=rsa-sha` parameter or has been migrated to such.
### Sample Payload ### Sample Payload

View File

@ -254,6 +254,7 @@ accidentally SSHing into an unmanaged or malicious machine.
```text ```text
$ vault write ssh-host-signer/roles/hostrole \ $ vault write ssh-host-signer/roles/hostrole \
key_type=ca \ key_type=ca \
algorithm_signer=rsa-sha2-256 \
ttl=87600h \ ttl=87600h \
allow_host_certificates=true \ allow_host_certificates=true \
allowed_domains="localdomain,example.com" \ allowed_domains="localdomain,example.com" \

View File

@ -0,0 +1,20 @@
---
layout: docs
page_title: Upgrading to Vault 1.10.x - Guides
description: |-
This page contains the list of deprecations and important or breaking changes
for Vault 1.10.x. Please read it carefully.
---
# Overview
This page contains the list of deprecations and important or breaking changes
for Vault 1.10.x compared to 1.9. Please read it carefully.
## SSH Secrets Engine
The new default value of `algorithm_signer` for SSH CA roles has been changed
to `rsa-sha2-256` from `ssh-rsa`. Existing roles will be migrated to
explicitly specify the `algorithm_signer=ssh-rsa` for RSA keys if they used
the implicit (empty) default, but newly created roles will use the new default
value (preferring a literal `default` which presently uses `rsa-sha2-256`).

View File

@ -1494,6 +1494,10 @@
"title": "Upgrade Plugins", "title": "Upgrade Plugins",
"path": "upgrading/plugins" "path": "upgrading/plugins"
}, },
{
"title": "Upgrade to 1.10.x",
"path": "upgrading/upgrade-to-1.10.x"
},
{ {
"title": "Upgrade to 1.9.x", "title": "Upgrade to 1.9.x",
"path": "upgrading/upgrade-to-1.9.x" "path": "upgrading/upgrade-to-1.9.x"