diff --git a/builtin/logical/ssh/path_config_add_host_key.go b/builtin/logical/ssh/path_config_add_host_key.go index bb43aa588c..849d8ee8ce 100644 --- a/builtin/logical/ssh/path_config_add_host_key.go +++ b/builtin/logical/ssh/path_config_add_host_key.go @@ -34,17 +34,18 @@ func pathConfigAddHostKey(b *backend) *framework.Path { } func (b *backend) pathAddHostKeyWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + log.SetFlags(log.LstdFlags | log.Lshortfile) log.Printf("Vishal: ssh.pathAddHostKeyWrite\n") username := d.Get("username").(string) ip := d.Get("ip").(string) //TODO: parse ip into ipv4 address and validate it key := d.Get("key").(string) - log.Printf("Vishal: ssh.pathAddHostKeyWrite username:%#v ip:%#v key:%#v\n", username, ip, key) + //log.Printf("Vishal: ssh.pathAddHostKeyWrite username:%#v ip:%#v key:%#v\n", username, ip, key) - entry, err := logical.StorageEntryJSON("hosts/"+ip+"/"+username, &sshAddHostKey{ - Username: username, - IP: ip, - Key: key, + hostKeyPath := "hosts/" + ip + "/" + username + log.Printf("Vishal: hostKeyPath: %#v\n", hostKeyPath) + entry, err := logical.StorageEntryJSON("hosts/"+ip+"/"+username, &sshHostKey{ + Key: key, }) if err != nil { return nil, err @@ -55,10 +56,8 @@ func (b *backend) pathAddHostKeyWrite(req *logical.Request, d *framework.FieldDa return nil, nil } -type sshAddHostKey struct { - Username string - IP string - Key string +type sshHostKey struct { + Key string } const pathConfigAddHostKeySyn = ` diff --git a/builtin/logical/ssh/path_config_lease.go b/builtin/logical/ssh/path_config_lease.go index 304c02347e..874eabe095 100644 --- a/builtin/logical/ssh/path_config_lease.go +++ b/builtin/logical/ssh/path_config_lease.go @@ -70,6 +70,23 @@ type configLease struct { LeaseMax time.Duration } +func (b *backend) Lease(s logical.Storage) (*configLease, error) { + entry, err := s.Get("config/lease") + if err != nil { + return nil, err + } + if entry == nil { + return nil, nil + } + + var result configLease + if err := entry.DecodeJSON(&result); err != nil { + return nil, err + } + + return &result, nil +} + const pathConfigLeaseHelpSyn = ` Configure the default lease information for SSH one time keys. ` diff --git a/builtin/logical/ssh/path_roles.go b/builtin/logical/ssh/path_roles.go index 32b61d0362..9e53e3575d 100644 --- a/builtin/logical/ssh/path_roles.go +++ b/builtin/logical/ssh/path_roles.go @@ -1,6 +1,9 @@ package ssh import ( + "bytes" + "encoding/json" + "fmt" "log" "github.com/hashicorp/vault/logical" @@ -33,18 +36,45 @@ func pathRoles(b *backend) *framework.Path { } } -func (b *backend) pathRoleRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { +func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { log.Printf("Vishal: ssh.pathRoleRead\n") - return nil, nil + entry, err := req.Storage.Get("policy/" + d.Get("name").(string)) + if err != nil { + return nil, err + } + if entry == nil { + return nil, nil + } + return &logical.Response{ + Data: map[string]interface{}{ + "policy": string(entry.Value), + }, + }, nil } -func (b *backend) pathRoleWrite(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { +func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { log.Printf("Vishal: ssh.pathRoleWrite\n") + var buf bytes.Buffer + if err := json.Compact(&buf, []byte(d.Get("policy").(string))); err != nil { + return logical.ErrorResponse(fmt.Sprintf("Error compacting policy: %s", err)), nil + } + + err := req.Storage.Put(&logical.StorageEntry{ + Key: "policy/" + d.Get("name").(string), + Value: buf.Bytes(), + }) + if err != nil { + return nil, err + } return nil, nil } -func (b *backend) pathRoleDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { +func (b *backend) pathRoleDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { log.Printf("Vishal: ssh.pathRoleDelete\n") + err := req.Storage.Delete("policy/" + d.Get("name").(string)) + if err != nil { + return nil, err + } return nil, nil } diff --git a/builtin/logical/ssh/secret_one_time_key.go b/builtin/logical/ssh/secret_one_time_key.go index 96e73f3780..bfcac5c632 100644 --- a/builtin/logical/ssh/secret_one_time_key.go +++ b/builtin/logical/ssh/secret_one_time_key.go @@ -8,12 +8,12 @@ import ( "github.com/hashicorp/vault/logical/framework" ) -const SecretOneTimeKeyType = "one_time_key" +const SecretSshHostKeyType = "secret_ssh_host_key_type" func secretOneTimeKey(b *backend) *framework.Secret { log.Printf("Vishal: ssh.secretPrivateKey\n") return &framework.Secret{ - Type: SecretOneTimeKeyType, + Type: SecretSshHostKeyType, Fields: map[string]*framework.FieldSchema{ "username": &framework.FieldSchema{ Type: framework.TypeString, @@ -23,19 +23,29 @@ func secretOneTimeKey(b *backend) *framework.Secret { Type: framework.TypeString, Description: "ip address of host", }, - "one_time_key": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "SSH one-time-key for host", - }, }, DefaultDuration: 1 * time.Hour, DefaultGracePeriod: 10 * time.Minute, - Renew: framework.LeaseExtend(1*time.Hour, 0, false), + Renew: b.secretPrivateKeyRenew, Revoke: b.secretPrivateKeyRevoke, } } +func (b *backend) secretPrivateKeyRenew(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + log.Printf("Vishal: ssh.secretPrivateKeyRenew\n") + lease, err := b.Lease(req.Storage) + if err != nil { + return nil, err + } + if lease == nil { + lease = &configLease{Lease: 1 * time.Hour} + } + f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, false) + return f(req, d) +} + func (b *backend) secretPrivateKeyRevoke(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { log.Printf("Vishal: ssh.secretPrivateKeyRevoke\n") + //TODO: implement here return nil, nil } diff --git a/builtin/logical/ssh/ssh_connect.go b/builtin/logical/ssh/ssh_connect.go index cc300fc52d..cede80194e 100644 --- a/builtin/logical/ssh/ssh_connect.go +++ b/builtin/logical/ssh/ssh_connect.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "log" + "strings" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -34,24 +35,45 @@ func sshConnect(b *backend) *framework.Path { func (b *backend) sshConnectWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - log.Printf("Vishal: ssh.sshConnectWrite username:%#v address:%#v\n", d.Get("username").(string), d.Get("address").(string)) + username := d.Get("username").(string) + ipAddr := d.Get("address").(string) + log.Printf("Vishal: ssh.sshConnectWrite username:%#v address:%#v\n", username, ipAddr) - //username := d.Get("username").(string) - //ip := d.Get("ip").(string) - //key := d.Get("key").(string) - //log.Printf("Vishal: ssh.pathAddHostKeyWrite username:%#v ip:%#v key:%#v\n", username, ip, key) - localCmdString := ` - rm -f vault_ssh_otk.pem vault_ssh_otk.pem.pub; - ssh-keygen -f vault_ssh_otk.pem -t rsa -N ''; - chmod 400 vault_ssh_otk.pem; - scp -i vault_ssh_shared.pem vault_ssh_otk.pem.pub vishal@localhost:/home/vishal - echo done! - ` - err := exec_command(localCmdString) + hostKeyPath := "hosts/" + ipAddr + "/" + username + entry, err := req.Storage.Get(hostKeyPath) + if err != nil { + return nil, err + } + + if entry == nil { + return nil, fmt.Errorf("Host key is not configured. Please configure them at the config/addhostkey endpoint") + } + var hostKey sshHostKey + if err := entry.DecodeJSON(&hostKey); err != nil { + return nil, fmt.Errorf("Error reading the host key: %s", err) + } + log.Printf("Vishal: host key previously configured: \n---------------\n%#v\n--------------\n", hostKey.Key) + + //TODO: save th entry in a file + //TODO: read the hosts path and get the key + //TODO: Input validation for the commands below + + rmCmd := "rm -f " + "vault_ssh_otk.pem" + " " + "vault_ssh_otk.pem.pub" + ";" + sshKeygenCmd := "ssh-keygen -f " + "vault_ssh_otk.pem" + " -t rsa -N ''" + ";" + chmodCmd := "chmod 400 " + "vault_ssh_otk.pem" + ";" + scpCmd := "scp -i " + "vault_ssh_shared.pem" + " " + "vault_ssh_otk.pem.pub" + " " + username + "@" + ipAddr + ":~;" + + localCmdString := strings.Join([]string{ + rmCmd, + sshKeygenCmd, + chmodCmd, + scpCmd, + }, "") + err = exec_command(localCmdString) if err != nil { fmt.Errorf("Running command failed " + err.Error()) } - session := createSSHPublicKeysSession("vishal", "127.0.0.1") + session := createSSHPublicKeysSession(username, ipAddr) var buf bytes.Buffer session.Stdout = &buf if err := installSshOtkInTarget(session); err != nil { @@ -61,11 +83,16 @@ func (b *backend) sshConnectWrite( fmt.Println(buf.String()) keyBytes, err := ioutil.ReadFile("vault_ssh_otk.pem") oneTimeKey := string(keyBytes) - return &logical.Response{ + log.Printf("Vishal: Returning:%s\n", oneTimeKey) + return b.Secret(SecretSshHostKeyType).Response(map[string]interface{}{ + "key": oneTimeKey, + }, nil), nil + /*return &logical.Response{ Data: map[string]interface{}{ "key": oneTimeKey, }, }, nil + */ } const sshConnectHelpSyn = ` @@ -73,5 +100,5 @@ sshConnectionHelpSyn ` const sshConnectHelpDesc = ` -sshConnectionHelpDesc +rshConnectionHelpDesc ` diff --git a/builtin/logical/ssh/ssh_util.go b/builtin/logical/ssh/ssh_util.go index 691f132d9f..67b50babb9 100644 --- a/builtin/logical/ssh/ssh_util.go +++ b/builtin/logical/ssh/ssh_util.go @@ -3,7 +3,9 @@ package ssh import ( "fmt" "io/ioutil" + "log" "os/exec" + "strings" "golang.org/x/crypto/ssh" ) @@ -17,12 +19,19 @@ func exec_command(cmdString string) error { } func installSshOtkInTarget(session *ssh.Session) error { - remoteCmdString := ` - grep -vFf vault_ssh_otk.pem.pub ~/.ssh/authorized_keys > ./temp_authorized_keys - cat ./temp_authorized_keys > ~/.ssh/authorized_keys - cat ./vault_ssh_otk.pem.pub >> ~/.ssh/authorized_keys - rm -f ./temp_authorized_keys ./vault_ssh_otk.pem.pub - ` + log.Printf("Vishal: ssh.installSshOtkInTarget\n") + + grepCmd := "grep -vFf " + "vault_ssh_otk.pem.pub" + " " + "~/.ssh/authorized_keys" + " > " + "./temp_authorized_keys" + ";" + catCmdRemoveDuplicate := "cat " + "./temp_authorized_keys" + " > " + "~/.ssh/authorized_keys" + ";" + catCmdAppendNew := "cat " + "./vault_ssh_otk.pem.pub" + " >> " + "~/.ssh/authorized_keys" + ";" + rmCmd := "rm -f " + "./temp_authorized_keys" + " " + "./vault_ssh_otk.pem.pub" + ";" + remoteCmdString := strings.Join([]string{ + grepCmd, + catCmdRemoveDuplicate, + catCmdAppendNew, + rmCmd, + }, "") + if err := session.Run(remoteCmdString); err != nil { return err } diff --git a/command/ssh.go b/command/ssh.go index a7623766e2..664f701f8f 100644 --- a/command/ssh.go +++ b/command/ssh.go @@ -47,7 +47,9 @@ func (c *SshCommand) Run(args []string) int { sshEnv := os.Environ() - sshCmdArgs := []string{"ssh", "-i", "vault_ssh_otk_" + args[0] + ".pem", "vishal@localhost"} + sshNew := "ssh -i " + "vault_ssh_otk_" + args[0] + ".pem " + args[0] + log.Printf("Vishal: sshNew:%#v\n", sshNew) + sshCmdArgs := []string{"ssh", "-i", "vault_ssh_otk_" + args[0] + ".pem", args[0]} defer os.Remove("vault_ssh_otk_" + args[0] + ".pem") if err := syscall.Exec(sshBinary, sshCmdArgs, sshEnv); err != nil {