blob: 4c33fc80892dc7e9c85713e245df9286458840ba [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ssh
import (
"context"
"strings"
"testing"
"github.com/hashicorp/vault/sdk/logical"
)
func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
var err error
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
err = b.Setup(context.Background(), config)
if err != nil {
t.Fatal(err)
}
// Store at an older path
err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
Key: caPrivateKeyStoragePathDeprecated,
Value: []byte(testCAPrivateKey),
})
if err != nil {
t.Fatal(err)
}
// Reading it should return the key as well as upgrade the storage path
privateKeyEntry, err := caKey(context.Background(), config.StorageView, caPrivateKey)
if err != nil {
t.Fatal(err)
}
if privateKeyEntry == nil || privateKeyEntry.Key == "" {
t.Fatalf("failed to read the stored private key")
}
entry, err := config.StorageView.Get(context.Background(), caPrivateKeyStoragePathDeprecated)
if err != nil {
t.Fatal(err)
}
if entry != nil {
t.Fatalf("bad: expected a nil entry after upgrade")
}
entry, err = config.StorageView.Get(context.Background(), caPrivateKeyStoragePath)
if err != nil {
t.Fatal(err)
}
if entry == nil {
t.Fatalf("bad: expected a non-nil entry after upgrade")
}
// Store at an older path
err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
Key: caPublicKeyStoragePathDeprecated,
Value: []byte(testCAPublicKey),
})
if err != nil {
t.Fatal(err)
}
// Reading it should return the key as well as upgrade the storage path
publicKeyEntry, err := caKey(context.Background(), config.StorageView, caPublicKey)
if err != nil {
t.Fatal(err)
}
if publicKeyEntry == nil || publicKeyEntry.Key == "" {
t.Fatalf("failed to read the stored public key")
}
entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePathDeprecated)
if err != nil {
t.Fatal(err)
}
if entry != nil {
t.Fatalf("bad: expected a nil entry after upgrade")
}
entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePath)
if err != nil {
t.Fatal(err)
}
if entry == nil {
t.Fatalf("bad: expected a non-nil entry after upgrade")
}
}
func TestSSH_ConfigCAUpdateDelete(t *testing.T) {
var resp *logical.Response
var err error
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatalf("Cannot create backend: %s", err)
}
caReq := &logical.Request{
Path: "config/ca",
Operation: logical.UpdateOperation,
Storage: config.StorageView,
}
// Auto-generate the keys
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
// Fail to overwrite it
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil {
t.Fatal(err)
}
if !resp.IsError() {
t.Fatalf("expected an error, got %#v", *resp)
}
caReq.Operation = logical.DeleteOperation
// Delete the configured keys
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
caReq.Operation = logical.UpdateOperation
caReq.Data = map[string]interface{}{
"public_key": testCAPublicKey,
"private_key": testCAPrivateKey,
}
// Successfully create a new one
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
// Fail to overwrite it
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil {
t.Fatal(err)
}
if !resp.IsError() {
t.Fatalf("expected an error, got %#v", *resp)
}
caReq.Operation = logical.DeleteOperation
// Delete the configured keys
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
caReq.Operation = logical.UpdateOperation
caReq.Data = nil
// Successfully create a new one
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
// Delete the configured keys
caReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v, resp:%v", err, resp)
}
}
func createDeleteHelper(t *testing.T, b logical.Backend, config *logical.BackendConfig, index int, keyType string, keyBits int) {
// Check that we can create a new key of the specified type
caReq := &logical.Request{
Path: "config/ca",
Operation: logical.UpdateOperation,
Storage: config.StorageView,
}
caReq.Data = map[string]interface{}{
"generate_signing_key": true,
"key_type": keyType,
"key_bits": keyBits,
}
resp, err := b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}
if !strings.Contains(resp.Data["public_key"].(string), caReq.Data["key_type"].(string)) {
t.Fatalf("bad case %v: expected public key of type %v but was %v", index, caReq.Data["key_type"], resp.Data["public_key"])
}
issueOptions := map[string]interface{}{
"public_key": testCAPublicKeyEd25519,
}
issueReq := &logical.Request{
Path: "sign/ca-issuance",
Operation: logical.UpdateOperation,
Storage: config.StorageView,
Data: issueOptions,
}
resp, err = b.HandleRequest(context.Background(), issueReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}
// Delete the configured keys
caReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}
}
func TestSSH_ConfigCAKeyTypes(t *testing.T) {
var err error
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatalf("Cannot create backend: %s", err)
}
cases := []struct {
keyType string
keyBits int
}{
{"ssh-rsa", 2048},
{"ssh-rsa", 4096},
{"ssh-rsa", 0},
{"rsa", 2048},
{"rsa", 4096},
{"ecdsa-sha2-nistp256", 0},
{"ecdsa-sha2-nistp384", 0},
{"ecdsa-sha2-nistp521", 0},
{"ec", 256},
{"ec", 384},
{"ec", 521},
{"ec", 0},
{"ssh-ed25519", 0},
{"ed25519", 0},
}
// Create a role for ssh signing.
roleOptions := map[string]interface{}{
"allow_user_certificates": true,
"allowed_users": "*",
"key_type": "ca",
"ttl": "30s",
"not_before_duration": "2h",
}
roleReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "roles/ca-issuance",
Data: roleOptions,
Storage: config.StorageView,
}
_, err = b.HandleRequest(context.Background(), roleReq)
if err != nil {
t.Fatalf("Cannot create role to issue against: %s", err)
}
for index, scenario := range cases {
createDeleteHelper(t, b, config, index, scenario.keyType, scenario.keyBits)
}
}