Added: Ssh CLI command and API, config lease impl, sshConnect path to backend, http handler for Ssh connect
This commit is contained in:
parent
08c921c75e
commit
3ed73d98c2
|
@ -0,0 +1,39 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *Sys) Ssh(target string) (*OneTimeKey, error) {
|
||||
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/ssh/connect"))
|
||||
input := strings.Split(target, "@")
|
||||
username := input[0]
|
||||
ipAddr := input[1]
|
||||
ip4Addr, err := net.ResolveIPAddr("ip4", ipAddr)
|
||||
log.Printf("Vishal: ssh.Ssh ipAddr_resolved: %#v\n", ip4Addr.String())
|
||||
body := map[string]interface{}{
|
||||
"username": username,
|
||||
"address": ip4Addr.String(),
|
||||
}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result OneTimeKey
|
||||
err = resp.DecodeJSON(&result)
|
||||
log.Printf("Vishal: api.Sys.Ssh: result:%#v\n", result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type OneTimeKey struct {
|
||||
Key string
|
||||
}
|
|
@ -27,6 +27,7 @@ func Backend() *framework.Backend {
|
|||
pathConfigAddHostKey(&b),
|
||||
pathConfigRemoveHostKey(&b),
|
||||
pathRoles(&b),
|
||||
sshConnect(&b),
|
||||
},
|
||||
|
||||
Secrets: []*framework.Secret{
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
|
@ -34,9 +36,40 @@ func pathConfigLease(b *backend) *framework.Path {
|
|||
|
||||
func (b *backend) pathLeaseWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.pathLeaseWrite\n")
|
||||
leaseRaw := d.Get("lease").(string)
|
||||
leaseMaxRaw := d.Get("lease_max").(string)
|
||||
|
||||
lease, err := time.ParseDuration(leaseRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid lease: %s", err)), nil
|
||||
}
|
||||
leaseMax, err := time.ParseDuration(leaseMaxRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid lease: %s", err)), nil
|
||||
}
|
||||
|
||||
// Store it
|
||||
entry, err := logical.StorageEntryJSON("config/lease", &configLease{
|
||||
Lease: lease,
|
||||
LeaseMax: leaseMax,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type configLease struct {
|
||||
Lease time.Duration
|
||||
LeaseMax time.Duration
|
||||
}
|
||||
|
||||
const pathConfigLeaseHelpSyn = `
|
||||
Configure the default lease information for SSH one time keys.
|
||||
`
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func sshConnect(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.sshConnect\n")
|
||||
return &framework.Path{
|
||||
Pattern: "connect",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"username": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "username at SSH host",
|
||||
},
|
||||
"address": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "IPv4 address of SSH host",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.sshConnectWrite,
|
||||
},
|
||||
HelpSynopsis: sshConnectHelpSyn,
|
||||
HelpDescription: sshConnectHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"key": "createdKey",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
const sshConnectHelpSyn = `
|
||||
sshConnectionHelpSyn
|
||||
`
|
||||
|
||||
const sshConnectHelpDesc = `
|
||||
sshConnectionHelpDesc
|
||||
`
|
|
@ -73,6 +73,12 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
|||
}, nil
|
||||
},
|
||||
|
||||
"ssh": func() (cli.Command, error) {
|
||||
return &command.SshCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"help": func() (cli.Command, error) {
|
||||
return &command.HelpCommand{
|
||||
Meta: meta,
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SshCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *SshCommand) Run(args []string) int {
|
||||
log.Printf("Vishal: SshCommand.Run: args:%#v len(args):%d\n", args, len(args))
|
||||
flags := c.Meta.FlagSet("ssh", FlagSetDefault)
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||
return 2
|
||||
}
|
||||
//if len(args) < 3, fail
|
||||
log.Printf("Vishal: sshCommand.Run: args[0]: %#v\n", args[0])
|
||||
sshOneTimeKey, err := client.Sys().Ssh(args[0])
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error getting one-time-key for establishing SSH session", err))
|
||||
return 2
|
||||
}
|
||||
|
||||
log.Printf("Vishal: client.Sys().Ssh() returned! OTK:%#v\n", sshOneTimeKey)
|
||||
//if sshOneTimeKey is empty, fail
|
||||
//Establish a session directly from client to the target using the one time key received without making the vault server the middle guy:w
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *SshCommand) Synopsis() string {
|
||||
return "Initiate a SSH session"
|
||||
}
|
||||
|
||||
func (c *SshCommand) Help() string {
|
||||
helpText := `
|
||||
SshCommand Help String
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
|
@ -44,6 +44,7 @@ func Handler(core *vault.Core) http.Handler {
|
|||
mux.Handle("/v1/sys/key-status", handleSysKeyStatus(core))
|
||||
mux.Handle("/v1/sys/rekey/init", handleSysRekeyInit(core))
|
||||
mux.Handle("/v1/sys/rekey/update", handleSysRekeyUpdate(core))
|
||||
mux.Handle("/v1/ssh/connect", handleSysSsh(core))
|
||||
mux.Handle("/v1/", handleLogical(core))
|
||||
|
||||
// Wrap the handler in another handler to trigger all help paths.
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func handleSysSsh(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
return
|
||||
}
|
||||
log.Printf("Vishal: http.sys_ssh.handleSysSsh req:%#v\n", r)
|
||||
var req SshRequest
|
||||
if err := parseRequest(r, &req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, ok := request(core, w, r, requestAuth(r, &logical.Request{
|
||||
Operation: logical.WriteOperation,
|
||||
Path: "ssh/connect",
|
||||
Data: map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"address": req.Address,
|
||||
},
|
||||
}))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
respondOk(w, resp.Data)
|
||||
})
|
||||
}
|
||||
|
||||
type SshRequest struct {
|
||||
Username string `json: "username"`
|
||||
Address string `json: "address"`
|
||||
}
|
Loading…
Reference in New Issue