mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1230 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1230 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package ssh
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"os/user"
 | 
						|
	"reflect"
 | 
						|
	"strconv"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"golang.org/x/crypto/ssh"
 | 
						|
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/hashicorp/vault/api"
 | 
						|
	logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
 | 
						|
	"github.com/hashicorp/vault/sdk/logical"
 | 
						|
	"github.com/hashicorp/vault/vault"
 | 
						|
	"github.com/mitchellh/mapstructure"
 | 
						|
)
 | 
						|
 | 
						|
// Before the following tests are run, a username going by the name 'vaultssh' has
 | 
						|
// to be created and its ~/.ssh/authorized_keys file should contain the below key.
 | 
						|
//
 | 
						|
// ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9i+hFxZHGo6KblVme4zrAcJstR6I0PTJozW286X4WyvPnkMYDQ5mnhEYC7UWCvjoTWbPEXPX7NjhRtwQTGD67bV+lrxgfyzK1JZbUXK4PwgKJvQD+XyyWYMzDgGSQY61KUSqCxymSm/9NZkPU3ElaQ9xQuTzPpztM4ROfb8f2Yv6/ZESZsTo0MTAkp8Pcy+WkioI/uJ1H7zqs0EA4OMY4aDJRu0UtP4rTVeYNEAuRXdX+eH4aW3KMvhzpFTjMbaJHJXlEeUm2SaX5TNQyTOvghCeQILfYIL/Ca2ij8iwCmulwdV6eQGfd4VDu40PvSnmfoaE38o6HaPnX0kUcnKiT
 | 
						|
 | 
						|
const (
 | 
						|
	testIP               = "127.0.0.1"
 | 
						|
	testUserName         = "vaultssh"
 | 
						|
	testAdminUser        = "vaultssh"
 | 
						|
	testOTPKeyType       = "otp"
 | 
						|
	testDynamicKeyType   = "dynamic"
 | 
						|
	testCIDRList         = "127.0.0.1/32"
 | 
						|
	testAtRoleName       = "test@RoleName"
 | 
						|
	testDynamicRoleName  = "testDynamicRoleName"
 | 
						|
	testOTPRoleName      = "testOTPRoleName"
 | 
						|
	testKeyName          = "testKeyName"
 | 
						|
	testSharedPrivateKey = `
 | 
						|
-----BEGIN RSA PRIVATE KEY-----
 | 
						|
MIIEogIBAAKCAQEAvYvoRcWRxqOim5VZnuM6wHCbLUeiND0yaM1tvOl+Fsrz55DG
 | 
						|
A0OZp4RGAu1Fgr46E1mzxFz1+zY4UbcEExg+u21fpa8YH8sytSWW1FyuD8ICib0A
 | 
						|
/l8slmDMw4BkkGOtSlEqgscpkpv/TWZD1NxJWkPcULk8z6c7TOETn2/H9mL+v2RE
 | 
						|
mbE6NDEwJKfD3MvlpIqCP7idR+86rNBAODjGOGgyUbtFLT+K01XmDRALkV3V/nh+
 | 
						|
GltyjL4c6RU4zG2iRyV5RHlJtkml+UzUMkzr4IQnkCC32CC/wmtoo/IsAprpcHVe
 | 
						|
nkBn3eFQ7uND70p5n6GhN/KOh2j519JFHJyokwIDAQABAoIBAHX7VOvBC3kCN9/x
 | 
						|
+aPdup84OE7Z7MvpX6w+WlUhXVugnmsAAVDczhKoUc/WktLLx2huCGhsmKvyVuH+
 | 
						|
MioUiE+vx75gm3qGx5xbtmOfALVMRLopjCnJYf6EaFA0ZeQ+NwowNW7Lu0PHmAU8
 | 
						|
Z3JiX8IwxTz14DU82buDyewO7v+cEr97AnERe3PUcSTDoUXNaoNxjNpEJkKREY6h
 | 
						|
4hAY676RT/GsRcQ8tqe/rnCqPHNd7JGqL+207FK4tJw7daoBjQyijWuB7K5chSal
 | 
						|
oPInylM6b13ASXuOAOT/2uSUBWmFVCZPDCmnZxy2SdnJGbsJAMl7Ma3MUlaGvVI+
 | 
						|
Tfh1aQkCgYEA4JlNOabTb3z42wz6mz+Nz3JRwbawD+PJXOk5JsSnV7DtPtfgkK9y
 | 
						|
6FTQdhnozGWShAvJvc+C4QAihs9AlHXoaBY5bEU7R/8UK/pSqwzam+MmxmhVDV7G
 | 
						|
IMQPV0FteoXTaJSikhZ88mETTegI2mik+zleBpVxvfdhE5TR+lq8Br0CgYEA2AwJ
 | 
						|
CUD5CYUSj09PluR0HHqamWOrJkKPFPwa+5eiTTCzfBBxImYZh7nXnWuoviXC0sg2
 | 
						|
AuvCW+uZ48ygv/D8gcz3j1JfbErKZJuV+TotK9rRtNIF5Ub7qysP7UjyI7zCssVM
 | 
						|
kuDd9LfRXaB/qGAHNkcDA8NxmHW3gpln4CFdSY8CgYANs4xwfercHEWaJ1qKagAe
 | 
						|
rZyrMpffAEhicJ/Z65lB0jtG4CiE6w8ZeUMWUVJQVcnwYD+4YpZbX4S7sJ0B8Ydy
 | 
						|
AhkSr86D/92dKTIt2STk6aCN7gNyQ1vW198PtaAWH1/cO2UHgHOy3ZUt5X/Uwxl9
 | 
						|
cex4flln+1Viumts2GgsCQKBgCJH7psgSyPekK5auFdKEr5+Gc/jB8I/Z3K9+g4X
 | 
						|
5nH3G1PBTCJYLw7hRzw8W/8oALzvddqKzEFHphiGXK94Lqjt/A4q1OdbCrhiE68D
 | 
						|
My21P/dAKB1UYRSs9Y8CNyHCjuZM9jSMJ8vv6vG/SOJPsnVDWVAckAbQDvlTHC9t
 | 
						|
O98zAoGAcbW6uFDkrv0XMCpB9Su3KaNXOR0wzag+WIFQRXCcoTvxVi9iYfUReQPi
 | 
						|
oOyBJU/HMVvBfv4g+OVFLVgSwwm6owwsouZ0+D/LasbuHqYyqYqdyPJQYzWA2Y+F
 | 
						|
+B6f4RoPdSXj24JHPg/ioRxjaj094UXJxua2yfkcecGNEuBQHSs=
 | 
						|
-----END RSA PRIVATE KEY-----
 | 
						|
`
 | 
						|
	// Public half of `privateKey`, identical to how it would be fed in from a file
 | 
						|
	publicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDArgK0ilRRfk8E7HIsjz5l3BuxmwpDd8DHRCVfOhbZ4gOSVxjEOOqBwWGjygdboBIZwFXmwDlU6sWX0hBJAgpQz0Cjvbjxtq/NjkvATrYPgnrXUhTaEn2eQO0PsqRNSFH46SK/oJfTp0q8/WgojxWJ2L7FUV8PO8uIk49DzqAqPV7WXU63vFsjx+3WQOX/ILeQvHCvaqs3dWjjzEoDudRWCOdUqcHEOshV9azIzPrXlQVzRV3QAKl6u7pC+/Secorpwt6IHpMKoVPGiR0tMMuNOVH8zrAKzIxPGfy2WmNDpJopbXMTvSOGAqNcp49O4SKOQl9Fzfq2HEevJamKLrMB dummy@example.com
 | 
						|
`
 | 
						|
	publicKey2 = `AAAAB3NzaC1yc2EAAAADAQABAAABAQDArgK0ilRRfk8E7HIsjz5l3BuxmwpDd8DHRCVfOhbZ4gOSVxjEOOqBwWGjygdboBIZwFXmwDlU6sWX0hBJAgpQz0Cjvbjxtq/NjkvATrYPgnrXUhTaEn2eQO0PsqRNSFH46SK/oJfTp0q8/WgojxWJ2L7FUV8PO8uIk49DzqAqPV7WXU63vFsjx+3WQOX/ILeQvHCvaqs3dWjjzEoDudRWCOdUqcHEOshV9azIzPrXlQVzRV3QAKl6u7pC+/Secorpwt6IHpMKoVPGiR0tMMuNOVH8zrAKzIxPGfy2WmNDpJopbXMTvSOGAqNcp49O4SKOQl9Fzfq2HEevJamKLrMB
 | 
						|
`
 | 
						|
 | 
						|
	publicKey4096 = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC54Oj4YCFDYxYv69Q9KfU6rWYtUB1eByQdUW0nXFi/vr98QUIV77sEeUVhaQzZcuCojAi/GrloW7ta0Z2DaEv5jOQMAnGpXBcqLJsz3KdrHbpvl93MPNdmNaGPU0GnUEsjBVuDVn9HdIUa8CNrxShvPu7/VqoaRHKLqphGgzFb37vi4qvnQ+5VYAO/TzyVYMD6qJX6I/9Pw8d74jCfEdOh2yGKkP7rXWOghreyIl8H2zTJKg9KoZuPq9F5M8nNt7Oi3rf+DwQiYvamzIqlDP4s5oFVTZW0E9lwWvYDpyiJnUrkQqksebBK/rcyfiFG3onb4qLo2WVWXeK3si8IhGik/TEzprScyAWIf9RviT8O+l5hTA2/c+ctn3MVCLRNfez2lKpdxCoprv1MbIcySGWblTJEcY6RA+aauVJpu7FMtRxHHtZKtMpep8cLu8GKbiP6Ifq2JXBtXtNxDeIgo2MkNoMh/NHAsACJniE/dqV/+u9HvhvgrTbJ69ell0nE4ivzA7O4kZgbR/4MHlLgLFvaqC8RrWRLY6BdFagPIMxghWha7Qw16zqoIjRnolvRzUWvSXanJVg8Z6ua1VxwgirNaAH1ivmJhUh2+4lNxCX6jmZyR3zjJsWY03gjJTairvI762opjjalF8fH6Xrs15mB14JiAlNbk6+5REQcvXlGqw== dummy@example.com`
 | 
						|
 | 
						|
	privateKey = `-----BEGIN RSA PRIVATE KEY-----
 | 
						|
MIIEowIBAAKCAQEAwK4CtIpUUX5PBOxyLI8+ZdwbsZsKQ3fAx0QlXzoW2eIDklcY
 | 
						|
xDjqgcFho8oHW6ASGcBV5sA5VOrFl9IQSQIKUM9Ao7248bavzY5LwE62D4J611IU
 | 
						|
2hJ9nkDtD7KkTUhR+Okiv6CX06dKvP1oKI8Vidi+xVFfDzvLiJOPQ86gKj1e1l1O
 | 
						|
t7xbI8ft1kDl/yC3kLxwr2qrN3Vo48xKA7nUVgjnVKnBxDrIVfWsyMz615UFc0Vd
 | 
						|
0ACperu6Qvv0nnKK6cLeiB6TCqFTxokdLTDLjTlR/M6wCsyMTxn8tlpjQ6SaKW1z
 | 
						|
E70jhgKjXKePTuEijkJfRc36thxHryWpii6zAQIDAQABAoIBAA/DrPD8iF2KigiL
 | 
						|
F+RRa/eFhLaJStOuTpV/G9eotwnolgY5Hguf5H/tRIHUG7oBZLm6pMyWWZp7AuOj
 | 
						|
CjYO9q0Z5939vc349nVI+SWoyviF4msPiik1bhWulja8lPjFu/8zg+ZNy15Dx7ei
 | 
						|
vAzleAupMiKOv8pNSB/KguQ3WZ9a9bcQcoFQ2Foru6mXpLJ03kghVRlkqvQ7t5cA
 | 
						|
n11d2Hiipq9mleESr0c+MUPKLBX/neaWfGA4xgJTjIYjZi6avmYc/Ox3sQ9aLq2J
 | 
						|
tH0D4HVUZvaU28hn+jhbs64rRFbu++qQMe3vNvi/Q/iqcYU4b6tgDNzm/JFRTS/W
 | 
						|
njiz4mkCgYEA44CnQVmonN6qQ0AgNNlBY5+RX3wwBJZ1AaxpzwDRylAt2vlVUA0n
 | 
						|
YY4RW4J4+RMRKwHwjxK5RRmHjsIJx+nrpqihW3fte3ev5F2A9Wha4dzzEHxBY6IL
 | 
						|
362T/x2f+vYk6tV+uTZSUPHsuELH26mitbBVFNB/00nbMNdEc2bO5FMCgYEA2NCw
 | 
						|
ubt+g2bRkkT/Qf8gIM8ZDpZbARt6onqxVcWkQFT16ZjbsBWUrH1Xi7alv9+lwYLJ
 | 
						|
ckY/XDX4KeU19HabeAbpyy6G9Q2uBSWZlJbjl7QNhdLeuzV82U1/r8fy6Uu3gQnU
 | 
						|
WSFx2GesRpSmZpqNKMs5ksqteZ9Yjg1EIgXdINsCgYBIn9REt1NtKGOf7kOZu1T1
 | 
						|
cYXdvm4xuLoHW7u3OiK+e9P3mCqU0G4m5UxDMyZdFKohWZAqjCaamWi9uNGYgOMa
 | 
						|
I7DG20TzaiS7OOIm9TY17eul8pSJMrypnealxRZB7fug/6Bhjaa/cktIEwFr7P4l
 | 
						|
E/JFH73+fBA9yipu0H3xQwKBgHmiwrLAZF6VrVcxDD9bQQwHA5iyc4Wwg+Fpkdl7
 | 
						|
0wUgZQHTdtRXlxwaCaZhJqX5c4WXuSo6DMvPn1TpuZZXgCsbPch2ZtJOBWXvzTSW
 | 
						|
XkK6iaedQMWoYU2L8+mK9FU73EwxVodWgwcUSosiVCRV6oGLWdZnjGEiK00uVh38
 | 
						|
Si1nAoGBAL47wWinv1cDTnh5mm0mybz3oI2a6V9aIYCloQ/EFcvtahyR/gyB8qNF
 | 
						|
lObH9Faf0WGdnACZvTz22U9gWhw79S0SpDV31tC5Kl8dXHFiZ09vYUKkYmSd/kms
 | 
						|
SeKWrUkryx46LVf6NMhkyYmRqCEjBwfOozzezi5WbiJy6nn54GQt
 | 
						|
-----END RSA PRIVATE KEY-----
 | 
						|
`
 | 
						|
)
 | 
						|
 | 
						|
func TestBackend_allowed_users(t *testing.T) {
 | 
						|
	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)
 | 
						|
	}
 | 
						|
 | 
						|
	roleData := map[string]interface{}{
 | 
						|
		"key_type":      "otp",
 | 
						|
		"default_user":  "ubuntu",
 | 
						|
		"cidr_list":     "52.207.235.245/16",
 | 
						|
		"allowed_users": "test",
 | 
						|
	}
 | 
						|
 | 
						|
	roleReq := &logical.Request{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      "roles/role1",
 | 
						|
		Storage:   config.StorageView,
 | 
						|
		Data:      roleData,
 | 
						|
	}
 | 
						|
 | 
						|
	resp, err := b.HandleRequest(context.Background(), roleReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp != nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
 | 
						|
	credsData := map[string]interface{}{
 | 
						|
		"ip":       "52.207.235.245",
 | 
						|
		"username": "ubuntu",
 | 
						|
	}
 | 
						|
	credsReq := &logical.Request{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Storage:   config.StorageView,
 | 
						|
		Path:      "creds/role1",
 | 
						|
		Data:      credsData,
 | 
						|
	}
 | 
						|
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp == nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
	if resp.Data["key"] == "" ||
 | 
						|
		resp.Data["key_type"] != "otp" ||
 | 
						|
		resp.Data["ip"] != "52.207.235.245" ||
 | 
						|
		resp.Data["username"] != "ubuntu" {
 | 
						|
		t.Fatalf("failed to create credential: resp:%#v", resp)
 | 
						|
	}
 | 
						|
 | 
						|
	credsData["username"] = "test"
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp == nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
	if resp.Data["key"] == "" ||
 | 
						|
		resp.Data["key_type"] != "otp" ||
 | 
						|
		resp.Data["ip"] != "52.207.235.245" ||
 | 
						|
		resp.Data["username"] != "test" {
 | 
						|
		t.Fatalf("failed to create credential: resp:%#v", resp)
 | 
						|
	}
 | 
						|
 | 
						|
	credsData["username"] = "random"
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || resp == nil || (resp != nil && !resp.IsError()) {
 | 
						|
		t.Fatalf("expected failure: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
 | 
						|
	delete(roleData, "allowed_users")
 | 
						|
	resp, err = b.HandleRequest(context.Background(), roleReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp != nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
 | 
						|
	credsData["username"] = "ubuntu"
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp == nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
	if resp.Data["key"] == "" ||
 | 
						|
		resp.Data["key_type"] != "otp" ||
 | 
						|
		resp.Data["ip"] != "52.207.235.245" ||
 | 
						|
		resp.Data["username"] != "ubuntu" {
 | 
						|
		t.Fatalf("failed to create credential: resp:%#v", resp)
 | 
						|
	}
 | 
						|
 | 
						|
	credsData["username"] = "test"
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || resp == nil || (resp != nil && !resp.IsError()) {
 | 
						|
		t.Fatalf("expected failure: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
 | 
						|
	roleData["allowed_users"] = "*"
 | 
						|
	resp, err = b.HandleRequest(context.Background(), roleReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp != nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
 | 
						|
	resp, err = b.HandleRequest(context.Background(), credsReq)
 | 
						|
	if err != nil || (resp != nil && resp.IsError()) || resp == nil {
 | 
						|
		t.Fatalf("failed to create role: resp:%#v err:%s", resp, err)
 | 
						|
	}
 | 
						|
	if resp.Data["key"] == "" ||
 | 
						|
		resp.Data["key_type"] != "otp" ||
 | 
						|
		resp.Data["ip"] != "52.207.235.245" ||
 | 
						|
		resp.Data["username"] != "test" {
 | 
						|
		t.Fatalf("failed to create credential: resp:%#v", resp)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testingFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
 | 
						|
	_, err := vault.StartSSHHostTestServer()
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Sprintf("error starting mock server:%s", err))
 | 
						|
	}
 | 
						|
	defaultLeaseTTLVal := 2 * time.Minute
 | 
						|
	maxLeaseTTLVal := 10 * time.Minute
 | 
						|
	return Factory(context.Background(), &logical.BackendConfig{
 | 
						|
		Logger:      nil,
 | 
						|
		StorageView: &logical.InmemStorage{},
 | 
						|
		System: &logical.StaticSystemView{
 | 
						|
			DefaultLeaseTTLVal: defaultLeaseTTLVal,
 | 
						|
			MaxLeaseTTLVal:     maxLeaseTTLVal,
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_Lookup(t *testing.T) {
 | 
						|
	testOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"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}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		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),
 | 
						|
			testRoleDelete(t, testAtRoleName),
 | 
						|
			testLookupRead(t, data, resp1),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_RoleList(t *testing.T) {
 | 
						|
	testOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"default_user": testUserName,
 | 
						|
		"cidr_list":    testCIDRList,
 | 
						|
	}
 | 
						|
	resp1 := map[string]interface{}{}
 | 
						|
	resp2 := map[string]interface{}{
 | 
						|
		"keys": []string{testOTPRoleName},
 | 
						|
		"key_info": map[string]interface{}{
 | 
						|
			testOTPRoleName: map[string]interface{}{
 | 
						|
				"key_type": testOTPKeyType,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	resp3 := map[string]interface{}{
 | 
						|
		"keys": []string{testAtRoleName, testOTPRoleName},
 | 
						|
		"key_info": map[string]interface{}{
 | 
						|
			testOTPRoleName: map[string]interface{}{
 | 
						|
				"key_type": testOTPKeyType,
 | 
						|
			},
 | 
						|
			testAtRoleName: map[string]interface{}{
 | 
						|
				"key_type": testOTPKeyType,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testRoleList(t, resp1),
 | 
						|
			testRoleWrite(t, testOTPRoleName, testOTPRoleData),
 | 
						|
			testRoleList(t, resp2),
 | 
						|
			testRoleWrite(t, testAtRoleName, testOTPRoleData),
 | 
						|
			testRoleList(t, resp3),
 | 
						|
			testRoleDelete(t, testAtRoleName),
 | 
						|
			testRoleList(t, resp2),
 | 
						|
			testRoleDelete(t, testOTPRoleName),
 | 
						|
			testRoleList(t, resp1),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_DynamicKeyCreate(t *testing.T) {
 | 
						|
	testDynamicRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testDynamicKeyType,
 | 
						|
		"key":          testKeyName,
 | 
						|
		"admin_user":   testAdminUser,
 | 
						|
		"default_user": testAdminUser,
 | 
						|
		"cidr_list":    testCIDRList,
 | 
						|
	}
 | 
						|
	data := map[string]interface{}{
 | 
						|
		"username": testUserName,
 | 
						|
		"ip":       testIP,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		PreCheck:       testAccUserPrecheckFunc(t),
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
 | 
						|
			testRoleWrite(t, testDynamicRoleName, testDynamicRoleData),
 | 
						|
			testCredsWrite(t, testDynamicRoleName, data, false),
 | 
						|
			testRoleWrite(t, testAtRoleName, testDynamicRoleData),
 | 
						|
			testCredsWrite(t, testAtRoleName, data, false),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_OTPRoleCrud(t *testing.T) {
 | 
						|
	testOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"default_user": testUserName,
 | 
						|
		"cidr_list":    testCIDRList,
 | 
						|
	}
 | 
						|
	respOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"port":         22,
 | 
						|
		"default_user": testUserName,
 | 
						|
		"cidr_list":    testCIDRList,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testRoleWrite(t, testOTPRoleName, testOTPRoleData),
 | 
						|
			testRoleRead(t, testOTPRoleName, respOTPRoleData),
 | 
						|
			testRoleDelete(t, testOTPRoleName),
 | 
						|
			testRoleRead(t, testOTPRoleName, nil),
 | 
						|
			testRoleWrite(t, testAtRoleName, testOTPRoleData),
 | 
						|
			testRoleRead(t, testAtRoleName, respOTPRoleData),
 | 
						|
			testRoleDelete(t, testAtRoleName),
 | 
						|
			testRoleRead(t, testAtRoleName, nil),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
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{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		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{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
 | 
						|
			testNamedKeysDelete(t),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_OTPCreate(t *testing.T) {
 | 
						|
	testOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"default_user": testUserName,
 | 
						|
		"cidr_list":    testCIDRList,
 | 
						|
	}
 | 
						|
	data := map[string]interface{}{
 | 
						|
		"username": testUserName,
 | 
						|
		"ip":       testIP,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testRoleWrite(t, testOTPRoleName, testOTPRoleData),
 | 
						|
			testCredsWrite(t, testOTPRoleName, data, false),
 | 
						|
			testRoleWrite(t, testAtRoleName, testOTPRoleData),
 | 
						|
			testCredsWrite(t, testAtRoleName, data, false),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_VerifyEcho(t *testing.T) {
 | 
						|
	verifyData := map[string]interface{}{
 | 
						|
		"otp": api.VerifyEchoRequest,
 | 
						|
	}
 | 
						|
	expectedData := map[string]interface{}{
 | 
						|
		"message": api.VerifyEchoResponse,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testVerifyWrite(t, verifyData, expectedData),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_ConfigZeroAddressCRUD(t *testing.T) {
 | 
						|
	testOTPRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"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},
 | 
						|
	}
 | 
						|
	resp3 := map[string]interface{}{
 | 
						|
		"roles": []string{},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			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),
 | 
						|
			testConfigZeroAddressDelete(t),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_CredsForZeroAddressRoles_otp(t *testing.T) {
 | 
						|
	otpRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testOTPKeyType,
 | 
						|
		"default_user": testUserName,
 | 
						|
	}
 | 
						|
	data := map[string]interface{}{
 | 
						|
		"username": testUserName,
 | 
						|
		"ip":       testIP,
 | 
						|
	}
 | 
						|
	req1 := map[string]interface{}{
 | 
						|
		"roles": testOTPRoleName,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testRoleWrite(t, testOTPRoleName, otpRoleData),
 | 
						|
			testCredsWrite(t, testOTPRoleName, data, true),
 | 
						|
			testConfigZeroAddressWrite(t, req1),
 | 
						|
			testCredsWrite(t, testOTPRoleName, data, false),
 | 
						|
			testConfigZeroAddressDelete(t),
 | 
						|
			testCredsWrite(t, testOTPRoleName, data, true),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSSHBackend_CredsForZeroAddressRoles_dynamic(t *testing.T) {
 | 
						|
	dynamicRoleData := map[string]interface{}{
 | 
						|
		"key_type":     testDynamicKeyType,
 | 
						|
		"key":          testKeyName,
 | 
						|
		"admin_user":   testAdminUser,
 | 
						|
		"default_user": testAdminUser,
 | 
						|
	}
 | 
						|
	data := map[string]interface{}{
 | 
						|
		"username": testUserName,
 | 
						|
		"ip":       testIP,
 | 
						|
	}
 | 
						|
	req2 := map[string]interface{}{
 | 
						|
		"roles": testDynamicRoleName,
 | 
						|
	}
 | 
						|
	logicaltest.Test(t, logicaltest.TestCase{
 | 
						|
		PreCheck:       testAccUserPrecheckFunc(t),
 | 
						|
		AcceptanceTest: true,
 | 
						|
		LogicalFactory: testingFactory,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			testNamedKeysWrite(t, testKeyName, testSharedPrivateKey),
 | 
						|
			testRoleWrite(t, testDynamicRoleName, dynamicRoleData),
 | 
						|
			testCredsWrite(t, testDynamicRoleName, data, true),
 | 
						|
			testConfigZeroAddressWrite(t, req2),
 | 
						|
			testCredsWrite(t, testDynamicRoleName, data, false),
 | 
						|
			testConfigZeroAddressDelete(t),
 | 
						|
			testCredsWrite(t, testDynamicRoleName, data, true),
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_AbleToRetrievePublicKey(t *testing.T) {
 | 
						|
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation:       logical.ReadOperation,
 | 
						|
				Path:            "public_key",
 | 
						|
				Unauthenticated: true,
 | 
						|
 | 
						|
				Check: func(resp *logical.Response) error {
 | 
						|
 | 
						|
					key := string(resp.Data["http_raw_body"].([]byte))
 | 
						|
 | 
						|
					if key != publicKey {
 | 
						|
						return fmt.Errorf("public_key incorrect. Expected %v, actual %v", publicKey, key)
 | 
						|
					}
 | 
						|
 | 
						|
					return nil
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_AbleToAutoGenerateSigningKeys(t *testing.T) {
 | 
						|
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation: logical.UpdateOperation,
 | 
						|
				Path:      "config/ca",
 | 
						|
			},
 | 
						|
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation:       logical.ReadOperation,
 | 
						|
				Path:            "public_key",
 | 
						|
				Unauthenticated: true,
 | 
						|
 | 
						|
				Check: func(resp *logical.Response) error {
 | 
						|
 | 
						|
					key := string(resp.Data["http_raw_body"].([]byte))
 | 
						|
 | 
						|
					if key == "" {
 | 
						|
						return fmt.Errorf("public_key empty. Expected not empty, actual %s", key)
 | 
						|
					}
 | 
						|
 | 
						|
					return nil
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_ValidPrincipalsValidatedForHostCertificates(t *testing.T) {
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
 | 
						|
			createRoleStep("testing", map[string]interface{}{
 | 
						|
				"key_type":                "ca",
 | 
						|
				"allow_host_certificates": true,
 | 
						|
				"allowed_domains":         "example.com,example.org",
 | 
						|
				"allow_subdomains":        true,
 | 
						|
				"default_critical_options": map[string]interface{}{
 | 
						|
					"option": "value",
 | 
						|
				},
 | 
						|
				"default_extensions": map[string]interface{}{
 | 
						|
					"extension": "extended",
 | 
						|
				},
 | 
						|
			}),
 | 
						|
 | 
						|
			signCertificateStep("testing", "vault-root-22608f5ef173aabf700797cb95c5641e792698ec6380e8e1eb55523e39aa5e51", ssh.HostCert, []string{"dummy.example.org", "second.example.com"}, map[string]string{
 | 
						|
				"option": "value",
 | 
						|
			}, map[string]string{
 | 
						|
				"extension": "extended",
 | 
						|
			},
 | 
						|
				2*time.Hour, map[string]interface{}{
 | 
						|
					"public_key":       publicKey2,
 | 
						|
					"ttl":              "2h",
 | 
						|
					"cert_type":        "host",
 | 
						|
					"valid_principals": "dummy.example.org,second.example.com",
 | 
						|
				}),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_OptionsOverrideDefaults(t *testing.T) {
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
 | 
						|
			createRoleStep("testing", map[string]interface{}{
 | 
						|
				"key_type":                 "ca",
 | 
						|
				"allowed_users":            "tuber",
 | 
						|
				"default_user":             "tuber",
 | 
						|
				"allow_user_certificates":  true,
 | 
						|
				"allowed_critical_options": "option,secondary",
 | 
						|
				"allowed_extensions":       "extension,additional",
 | 
						|
				"default_critical_options": map[string]interface{}{
 | 
						|
					"option": "value",
 | 
						|
				},
 | 
						|
				"default_extensions": map[string]interface{}{
 | 
						|
					"extension": "extended",
 | 
						|
				},
 | 
						|
			}),
 | 
						|
 | 
						|
			signCertificateStep("testing", "vault-root-22608f5ef173aabf700797cb95c5641e792698ec6380e8e1eb55523e39aa5e51", ssh.UserCert, []string{"tuber"}, map[string]string{
 | 
						|
				"secondary": "value",
 | 
						|
			}, map[string]string{
 | 
						|
				"additional": "value",
 | 
						|
			}, 2*time.Hour, map[string]interface{}{
 | 
						|
				"public_key": publicKey2,
 | 
						|
				"ttl":        "2h",
 | 
						|
				"critical_options": map[string]interface{}{
 | 
						|
					"secondary": "value",
 | 
						|
				},
 | 
						|
				"extensions": map[string]interface{}{
 | 
						|
					"additional": "value",
 | 
						|
				},
 | 
						|
			}),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_AllowedUserKeyLengths(t *testing.T) {
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
			createRoleStep("weakkey", map[string]interface{}{
 | 
						|
				"key_type":                "ca",
 | 
						|
				"allow_user_certificates": true,
 | 
						|
				"allowed_user_key_lengths": map[string]interface{}{
 | 
						|
					"rsa": json.Number(strconv.FormatInt(4096, 10)),
 | 
						|
				},
 | 
						|
			}),
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation: logical.UpdateOperation,
 | 
						|
				Path:      "sign/weakkey",
 | 
						|
				Data: map[string]interface{}{
 | 
						|
					"public_key": publicKey,
 | 
						|
				},
 | 
						|
				ErrorOk: true,
 | 
						|
				Check: func(resp *logical.Response) error {
 | 
						|
					if resp.Data["error"] != "public_key failed to meet the key requirements: key is of an invalid size: 2048" {
 | 
						|
						return errors.New("a smaller key (2048) was allowed, when the minimum was set for 4096")
 | 
						|
					}
 | 
						|
					return nil
 | 
						|
				},
 | 
						|
			},
 | 
						|
			createRoleStep("stdkey", map[string]interface{}{
 | 
						|
				"key_type":                "ca",
 | 
						|
				"allow_user_certificates": true,
 | 
						|
				"allowed_user_key_lengths": map[string]interface{}{
 | 
						|
					"rsa": json.Number(strconv.FormatInt(2048, 10)),
 | 
						|
				},
 | 
						|
			}),
 | 
						|
			// Pass with 2048 key
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation: logical.UpdateOperation,
 | 
						|
				Path:      "sign/stdkey",
 | 
						|
				Data: map[string]interface{}{
 | 
						|
					"public_key": publicKey,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			// Fail with 4096 key
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation: logical.UpdateOperation,
 | 
						|
				Path:      "sign/stdkey",
 | 
						|
				Data: map[string]interface{}{
 | 
						|
					"public_key": publicKey4096,
 | 
						|
				},
 | 
						|
				ErrorOk: true,
 | 
						|
				Check: func(resp *logical.Response) error {
 | 
						|
					if resp.Data["error"] != "public_key failed to meet the key requirements: key is of an invalid size: 4096" {
 | 
						|
						return errors.New("a larger key (4096) was allowed, when the size was set for 2048")
 | 
						|
					}
 | 
						|
					return nil
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_CustomKeyIDFormat(t *testing.T) {
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
 | 
						|
			createRoleStep("customrole", map[string]interface{}{
 | 
						|
				"key_type":                 "ca",
 | 
						|
				"key_id_format":            "{{role_name}}-{{token_display_name}}-{{public_key_hash}}",
 | 
						|
				"allowed_users":            "tuber",
 | 
						|
				"default_user":             "tuber",
 | 
						|
				"allow_user_certificates":  true,
 | 
						|
				"allowed_critical_options": "option,secondary",
 | 
						|
				"allowed_extensions":       "extension,additional",
 | 
						|
				"default_critical_options": map[string]interface{}{
 | 
						|
					"option": "value",
 | 
						|
				},
 | 
						|
				"default_extensions": map[string]interface{}{
 | 
						|
					"extension": "extended",
 | 
						|
				},
 | 
						|
			}),
 | 
						|
 | 
						|
			signCertificateStep("customrole", "customrole-root-22608f5ef173aabf700797cb95c5641e792698ec6380e8e1eb55523e39aa5e51", ssh.UserCert, []string{"tuber"}, map[string]string{
 | 
						|
				"secondary": "value",
 | 
						|
			}, map[string]string{
 | 
						|
				"additional": "value",
 | 
						|
			}, 2*time.Hour, map[string]interface{}{
 | 
						|
				"public_key": publicKey2,
 | 
						|
				"ttl":        "2h",
 | 
						|
				"critical_options": map[string]interface{}{
 | 
						|
					"secondary": "value",
 | 
						|
				},
 | 
						|
				"extensions": map[string]interface{}{
 | 
						|
					"additional": "value",
 | 
						|
				},
 | 
						|
			}),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func TestBackend_DisallowUserProvidedKeyIDs(t *testing.T) {
 | 
						|
	config := logical.TestBackendConfig()
 | 
						|
 | 
						|
	b, err := Factory(context.Background(), config)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Cannot create backend: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	testCase := logicaltest.TestCase{
 | 
						|
		LogicalBackend: b,
 | 
						|
		Steps: []logicaltest.TestStep{
 | 
						|
			configCaStep(),
 | 
						|
 | 
						|
			createRoleStep("testing", map[string]interface{}{
 | 
						|
				"key_type":                "ca",
 | 
						|
				"allow_user_key_ids":      false,
 | 
						|
				"allow_user_certificates": true,
 | 
						|
			}),
 | 
						|
			logicaltest.TestStep{
 | 
						|
				Operation: logical.UpdateOperation,
 | 
						|
				Path:      "sign/testing",
 | 
						|
				Data: map[string]interface{}{
 | 
						|
					"public_key": publicKey2,
 | 
						|
					"key_id":     "override",
 | 
						|
				},
 | 
						|
				ErrorOk: true,
 | 
						|
				Check: func(resp *logical.Response) error {
 | 
						|
					if resp.Data["error"] != "setting key_id is not allowed by role" {
 | 
						|
						return errors.New("custom user key id was allowed even when 'allow_user_key_ids' is false")
 | 
						|
					}
 | 
						|
					return nil
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	logicaltest.Test(t, testCase)
 | 
						|
}
 | 
						|
 | 
						|
func configCaStep() logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      "config/ca",
 | 
						|
		Data: map[string]interface{}{
 | 
						|
			"public_key":  publicKey,
 | 
						|
			"private_key": privateKey,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func createRoleStep(name string, parameters map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.CreateOperation,
 | 
						|
		Path:      "roles/" + name,
 | 
						|
		Data:      parameters,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func signCertificateStep(
 | 
						|
	role, keyID string, certType int, validPrincipals []string,
 | 
						|
	criticalOptionPermissions, extensionPermissions map[string]string,
 | 
						|
	ttl time.Duration,
 | 
						|
	requestParameters map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      "sign/" + role,
 | 
						|
		Data:      requestParameters,
 | 
						|
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
 | 
						|
			serialNumber := resp.Data["serial_number"].(string)
 | 
						|
			if serialNumber == "" {
 | 
						|
				return errors.New("no serial number in response")
 | 
						|
			}
 | 
						|
 | 
						|
			signedKey := strings.TrimSpace(resp.Data["signed_key"].(string))
 | 
						|
			if signedKey == "" {
 | 
						|
				return errors.New("no signed key in response")
 | 
						|
			}
 | 
						|
 | 
						|
			key, _ := base64.StdEncoding.DecodeString(strings.Split(signedKey, " ")[1])
 | 
						|
 | 
						|
			parsedKey, err := ssh.ParsePublicKey(key)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			return validateSSHCertificate(parsedKey.(*ssh.Certificate), keyID, certType, validPrincipals, criticalOptionPermissions, extensionPermissions, ttl)
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func validateSSHCertificate(cert *ssh.Certificate, keyID string, certType int, validPrincipals []string, criticalOptionPermissions, extensionPermissions map[string]string,
 | 
						|
	ttl time.Duration) error {
 | 
						|
 | 
						|
	if cert.KeyId != keyID {
 | 
						|
		return fmt.Errorf("incorrect KeyId: %v, wanted %v", cert.KeyId, keyID)
 | 
						|
	}
 | 
						|
 | 
						|
	if cert.CertType != uint32(certType) {
 | 
						|
		return fmt.Errorf("incorrect CertType: %v", cert.CertType)
 | 
						|
	}
 | 
						|
 | 
						|
	if time.Unix(int64(cert.ValidAfter), 0).After(time.Now()) {
 | 
						|
		return fmt.Errorf("incorrect ValidAfter: %v", cert.ValidAfter)
 | 
						|
	}
 | 
						|
 | 
						|
	if time.Unix(int64(cert.ValidBefore), 0).Before(time.Now()) {
 | 
						|
		return fmt.Errorf("incorrect ValidBefore: %v", cert.ValidBefore)
 | 
						|
	}
 | 
						|
 | 
						|
	actualTTL := time.Unix(int64(cert.ValidBefore), 0).Add(-30 * time.Second).Sub(time.Unix(int64(cert.ValidAfter), 0))
 | 
						|
	if actualTTL != ttl {
 | 
						|
		return fmt.Errorf("incorrect ttl: expected: %v, actualL %v", ttl, actualTTL)
 | 
						|
	}
 | 
						|
 | 
						|
	if !reflect.DeepEqual(cert.ValidPrincipals, validPrincipals) {
 | 
						|
		return fmt.Errorf("incorrect ValidPrincipals: expected: %#v actual: %#v", validPrincipals, cert.ValidPrincipals)
 | 
						|
	}
 | 
						|
 | 
						|
	publicSigningKey, err := getSigningPublicKey()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if !reflect.DeepEqual(cert.SignatureKey, publicSigningKey) {
 | 
						|
		return fmt.Errorf("incorrect SignatureKey: %v", cert.SignatureKey)
 | 
						|
	}
 | 
						|
 | 
						|
	if cert.Signature == nil {
 | 
						|
		return fmt.Errorf("incorrect Signature: %v", cert.Signature)
 | 
						|
	}
 | 
						|
 | 
						|
	if !reflect.DeepEqual(cert.Permissions.Extensions, extensionPermissions) {
 | 
						|
		return fmt.Errorf("incorrect Permissions.Extensions: Expected: %v, Actual: %v", extensionPermissions, cert.Permissions.Extensions)
 | 
						|
	}
 | 
						|
 | 
						|
	if !reflect.DeepEqual(cert.Permissions.CriticalOptions, criticalOptionPermissions) {
 | 
						|
		return fmt.Errorf("incorrect Permissions.CriticalOptions: %v", cert.Permissions.CriticalOptions)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func getSigningPublicKey() (ssh.PublicKey, error) {
 | 
						|
	key, err := base64.StdEncoding.DecodeString(strings.Split(publicKey, " ")[1])
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	parsedKey, err := ssh.ParsePublicKey(key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return parsedKey, nil
 | 
						|
}
 | 
						|
 | 
						|
func testConfigZeroAddressDelete(t *testing.T) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.DeleteOperation,
 | 
						|
		Path:      "config/zeroaddress",
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testConfigZeroAddressWrite(t *testing.T, data map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      "config/zeroaddress",
 | 
						|
		Data:      data,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testConfigZeroAddressRead(t *testing.T, expected map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.ReadOperation,
 | 
						|
		Path:      "config/zeroaddress",
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			var d zeroAddressRoles
 | 
						|
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			var ex zeroAddressRoles
 | 
						|
			if err := mapstructure.Decode(expected, &ex); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			if !reflect.DeepEqual(d, ex) {
 | 
						|
				return fmt.Errorf("Response mismatch:\nActual:%#v\nExpected:%#v", d, ex)
 | 
						|
			}
 | 
						|
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testVerifyWrite(t *testing.T, data map[string]interface{}, expected map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      fmt.Sprintf("verify"),
 | 
						|
		Data:      data,
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			var ac api.SSHVerifyResponse
 | 
						|
			if err := mapstructure.Decode(resp.Data, &ac); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			var ex api.SSHVerifyResponse
 | 
						|
			if err := mapstructure.Decode(expected, &ex); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			if !reflect.DeepEqual(ac, ex) {
 | 
						|
				return fmt.Errorf("invalid response")
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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,
 | 
						|
		Path:      "lookup",
 | 
						|
		Data:      data,
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			if resp.Data == nil || resp.Data["roles"] == nil {
 | 
						|
				return fmt.Errorf("missing roles information")
 | 
						|
			}
 | 
						|
			if !reflect.DeepEqual(resp.Data["roles"].([]string), expected) {
 | 
						|
				return fmt.Errorf("Invalid response: \nactual:%#v\nexpected:%#v", resp.Data["roles"].([]string), expected)
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testRoleWrite(t *testing.T, name string, data map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      "roles/" + name,
 | 
						|
		Data:      data,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testRoleList(t *testing.T, expected map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.ListOperation,
 | 
						|
		Path:      "roles",
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			if resp == nil {
 | 
						|
				return fmt.Errorf("nil response")
 | 
						|
			}
 | 
						|
			if resp.Data == nil {
 | 
						|
				return fmt.Errorf("nil data")
 | 
						|
			}
 | 
						|
			if !reflect.DeepEqual(resp.Data, expected) {
 | 
						|
				return fmt.Errorf("Invalid response:\nactual:%#v\nexpected is %#v", resp.Data, expected)
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testRoleRead(t *testing.T, roleName string, expected map[string]interface{}) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.ReadOperation,
 | 
						|
		Path:      "roles/" + roleName,
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			if resp == nil {
 | 
						|
				if expected == nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				return fmt.Errorf("bad: %#v", resp)
 | 
						|
			}
 | 
						|
			var d sshRole
 | 
						|
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
 | 
						|
				return fmt.Errorf("error decoding response:%s", err)
 | 
						|
			}
 | 
						|
			switch d.KeyType {
 | 
						|
			case "otp":
 | 
						|
				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)
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testRoleDelete(t *testing.T, name string) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.DeleteOperation,
 | 
						|
		Path:      "roles/" + name,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testCredsWrite(t *testing.T, roleName string, data map[string]interface{}, expectError bool) logicaltest.TestStep {
 | 
						|
	return logicaltest.TestStep{
 | 
						|
		Operation: logical.UpdateOperation,
 | 
						|
		Path:      fmt.Sprintf("creds/%s", roleName),
 | 
						|
		Data:      data,
 | 
						|
		ErrorOk:   true,
 | 
						|
		Check: func(resp *logical.Response) error {
 | 
						|
			if resp == nil {
 | 
						|
				return fmt.Errorf("response is nil")
 | 
						|
			}
 | 
						|
			if resp.Data == nil {
 | 
						|
				return fmt.Errorf("data is nil")
 | 
						|
			}
 | 
						|
			if expectError {
 | 
						|
				var e struct {
 | 
						|
					Error string `mapstructure:"error"`
 | 
						|
				}
 | 
						|
				if err := mapstructure.Decode(resp.Data, &e); err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				if len(e.Error) == 0 {
 | 
						|
					return fmt.Errorf("expected error, but write succeeded")
 | 
						|
				}
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
			if roleName == testDynamicRoleName {
 | 
						|
				var d struct {
 | 
						|
					Key string `mapstructure:"key"`
 | 
						|
				}
 | 
						|
				if err := mapstructure.Decode(resp.Data, &d); err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				if d.Key == "" {
 | 
						|
					return fmt.Errorf("generated key is an empty string")
 | 
						|
				}
 | 
						|
				// Checking only for a parsable key
 | 
						|
				_, err := ssh.ParsePrivateKey([]byte(d.Key))
 | 
						|
				if err != nil {
 | 
						|
					return fmt.Errorf("generated key is invalid")
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if resp.Data["key_type"] != KeyTypeOTP {
 | 
						|
					return fmt.Errorf("incorrect key_type")
 | 
						|
				}
 | 
						|
				if resp.Data["key"] == nil {
 | 
						|
					return fmt.Errorf("invalid key")
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testAccUserPrecheckFunc(t *testing.T) func() {
 | 
						|
	return func() {
 | 
						|
		if _, err := user.Lookup(testUserName); err != nil {
 | 
						|
			t.Skipf("Acceptance test skipped unless user %q is present", testUserName)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |