mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 12:07:54 +00:00 
			
		
		
		
	* Make reception of an empty valid principals configurable based on a role flag. Adds allow_empty_principals, which if true allows valid_principals on credential generation calls to be empty. * changelog * Allow empty principals on unrelated unit test * whitespace
		
			
				
	
	
		
			279 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) HashiCorp, Inc.
 | 
						|
// SPDX-License-Identifier: BUSL-1.1
 | 
						|
 | 
						|
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",
 | 
						|
		"allow_empty_principals":  true,
 | 
						|
	}
 | 
						|
	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)
 | 
						|
	}
 | 
						|
}
 |