mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Input validations, help strings, default_user support
This commit is contained in:
@@ -2,7 +2,6 @@ package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/aws-sdk-go/aws"
|
||||
"github.com/hashicorp/aws-sdk-go/gen/iam"
|
||||
@@ -33,8 +32,6 @@ func pathUser(b *backend) *framework.Path {
|
||||
func (b *backend) pathUserRead(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
policyName := d.Get("name").(string)
|
||||
log.Printf("Vishal: policyName: %#v\n", policyName)
|
||||
log.Printf("Vishal: data d: %#v\n", d)
|
||||
|
||||
// Read the policy
|
||||
policy, err := req.Storage.Get("policy/" + policyName)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
@@ -13,7 +12,6 @@ func Factory(map[string]string) (logical.Backend, error) {
|
||||
}
|
||||
|
||||
func Backend() *framework.Backend {
|
||||
log.Printf("Vishal: ssh.Backend\n")
|
||||
var b backend
|
||||
b.Backend = &framework.Backend{
|
||||
Help: strings.TrimSpace(backendHelp),
|
||||
@@ -42,8 +40,12 @@ type backend struct {
|
||||
}
|
||||
|
||||
const backendHelp = `
|
||||
The ssh backend enables secure connections to remote hosts.
|
||||
The SSH backend dynamically generates SSH private keys for remote hosts.
|
||||
The key generated has a configurable lease set and are automatically
|
||||
revoked at the end of the lease.
|
||||
|
||||
After mounting this backend, configure it using the endpoints within
|
||||
the "config/" path.
|
||||
After mounting this backend, configure the lease using the 'config/lease'
|
||||
endpoint. The shared SSH key belonging to any infrastructure should be
|
||||
registered with the 'roles/' endpoint before dynamic keys for remote hosts
|
||||
can be generated.
|
||||
`
|
||||
|
||||
@@ -2,7 +2,6 @@ package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
@@ -10,7 +9,6 @@ import (
|
||||
)
|
||||
|
||||
func pathConfigLease(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.pathConfigLease\n")
|
||||
return &framework.Path{
|
||||
Pattern: "config/lease",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -25,7 +23,7 @@ func pathConfigLease(b *backend) *framework.Path {
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathLeaseWrite,
|
||||
logical.WriteOperation: b.pathConfigLeaseWrite,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathConfigLeaseHelpSyn,
|
||||
@@ -33,10 +31,15 @@ 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")
|
||||
func (b *backend) pathConfigLeaseWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
leaseRaw := d.Get("lease").(string)
|
||||
leaseMaxRaw := d.Get("lease_max").(string)
|
||||
if leaseRaw == "" {
|
||||
return logical.ErrorResponse("Invalid 'lease'"), nil
|
||||
}
|
||||
if leaseMaxRaw == "" {
|
||||
return logical.ErrorResponse("Invalid 'lease_max'"), nil
|
||||
}
|
||||
|
||||
lease, err := time.ParseDuration(leaseRaw)
|
||||
if err != nil {
|
||||
@@ -49,7 +52,6 @@ func (b *backend) pathLeaseWrite(req *logical.Request, d *framework.FieldData) (
|
||||
"Invalid lease: %s", err)), nil
|
||||
}
|
||||
|
||||
// Store it
|
||||
entry, err := logical.StorageEntryJSON("config/lease", &configLease{
|
||||
Lease: lease,
|
||||
LeaseMax: leaseMax,
|
||||
@@ -64,11 +66,6 @@ func (b *backend) pathLeaseWrite(req *logical.Request, d *framework.FieldData) (
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type configLease struct {
|
||||
Lease time.Duration
|
||||
LeaseMax time.Duration
|
||||
}
|
||||
|
||||
func (b *backend) Lease(s logical.Storage) (*configLease, error) {
|
||||
entry, err := s.Get("config/lease")
|
||||
if err != nil {
|
||||
@@ -86,12 +83,17 @@ func (b *backend) Lease(s logical.Storage) (*configLease, error) {
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
type configLease struct {
|
||||
Lease time.Duration
|
||||
LeaseMax time.Duration
|
||||
}
|
||||
|
||||
const pathConfigLeaseHelpSyn = `
|
||||
Configure the default lease information for SSH one time keys.
|
||||
`
|
||||
|
||||
const pathConfigLeaseHelpDesc = `
|
||||
This configures the default lease information used for SSH one time keys
|
||||
This configures the default lease information used for SSH keys
|
||||
generated by this backend. The lease specifies the duration that a
|
||||
credential will be valid for, as well as the maximum session for
|
||||
a set of credentials.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
@@ -8,7 +9,6 @@ import (
|
||||
)
|
||||
|
||||
func pathKeys(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.pathConfigAddHostKey\n")
|
||||
return &framework.Path{
|
||||
Pattern: "keys/(?P<name>\\w+)",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -26,14 +26,13 @@ func pathKeys(b *backend) *framework.Path {
|
||||
logical.WriteOperation: b.pathKeysWrite,
|
||||
logical.DeleteOperation: b.pathKeysDelete,
|
||||
},
|
||||
HelpSynopsis: pathConfigAddHostKeySyn,
|
||||
HelpDescription: pathConfigAddHostKeyDesc,
|
||||
HelpSynopsis: pathKeysSyn,
|
||||
HelpDescription: pathKeysDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) pathKeysRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
keyName := d.Get("name").(string)
|
||||
log.Printf("Vishal: ssh.pathKeysRead: keyName: %#v\n", keyName)
|
||||
keyPath := "keys/" + keyName
|
||||
entry, err := req.Storage.Get(keyPath)
|
||||
if err != nil {
|
||||
@@ -52,7 +51,6 @@ func (b *backend) pathKeysRead(req *logical.Request, d *framework.FieldData) (*l
|
||||
|
||||
func (b *backend) pathKeysDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
keyName := d.Get("name").(string)
|
||||
log.Printf("Vishal: ssh.pathKeysDelete: keyName: %#v\n", keyName)
|
||||
keyPath := "keys/" + keyName
|
||||
err := req.Storage.Delete(keyPath)
|
||||
if err != nil {
|
||||
@@ -63,13 +61,16 @@ func (b *backend) pathKeysDelete(req *logical.Request, d *framework.FieldData) (
|
||||
|
||||
func (b *backend) pathKeysWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
log.Printf("Vishal: ssh.pathKeysWrite\n")
|
||||
|
||||
keyName := d.Get("name").(string)
|
||||
keyString := d.Get("key").(string)
|
||||
|
||||
if keyString == "" {
|
||||
return nil, fmt.Errorf("Invalid 'key'")
|
||||
}
|
||||
|
||||
keyPath := "keys/" + keyName
|
||||
|
||||
log.Printf("Vishal: ssh.path_keys.pathKeysWrite: keyPath: %#v\n", keyPath)
|
||||
entry, err := logical.StorageEntryJSON(keyPath, &sshHostKey{
|
||||
Key: keyString,
|
||||
})
|
||||
@@ -86,10 +87,11 @@ type sshHostKey struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
const pathConfigAddHostKeySyn = `
|
||||
pathConfigAddHostKeySyn
|
||||
const pathKeysSyn = `
|
||||
Register a shared key which can be used to install dynamic key in remote machine.
|
||||
`
|
||||
|
||||
const pathConfigAddHostKeyDesc = `
|
||||
pathConfigAddHostKeyDesc
|
||||
const pathKeysDesc = `
|
||||
The shared key registered will be used to install and uninstall dynamic keys in remote machine.
|
||||
This key should have "root" privileges which enables installing keys for unprivileged usernames.
|
||||
`
|
||||
|
||||
@@ -2,7 +2,6 @@ package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func pathLookup(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.pathLookup\n")
|
||||
return &framework.Path{
|
||||
Pattern: "lookup",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -29,20 +27,29 @@ func pathLookup(b *backend) *framework.Path {
|
||||
}
|
||||
|
||||
func (b *backend) pathLookupWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
ip := d.Get("ip").(string)
|
||||
//ip := "127.0.0.1"
|
||||
ipAddr := net.ParseIP(ip)
|
||||
if ipAddr == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Invalid IP '%s'", ip)), nil
|
||||
ipAddr := d.Get("ip").(string)
|
||||
if ipAddr == "" {
|
||||
return logical.ErrorResponse("Missing 'ip'"), nil
|
||||
}
|
||||
keys, _ := req.Storage.List("policy/")
|
||||
ip := net.ParseIP(ipAddr)
|
||||
if ip == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Invalid IP '%s'", ip.String())), nil
|
||||
}
|
||||
|
||||
keys, err := req.Storage.List("policy/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil, fmt.Errorf("No roles registered for IP '%s'", ip.String())
|
||||
}
|
||||
|
||||
var matchingRoles []string
|
||||
for _, item := range keys {
|
||||
if contains, _ := containsIP(req.Storage, item, ip); contains {
|
||||
if contains, _ := containsIP(req.Storage, item, ip.String()); contains {
|
||||
matchingRoles = append(matchingRoles, item)
|
||||
}
|
||||
}
|
||||
log.Printf("Vishal: req.Path: %#v \n Keys:%#v\n", req.Path, keys)
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"roles": matchingRoles,
|
||||
@@ -51,6 +58,9 @@ func (b *backend) pathLookupWrite(req *logical.Request, d *framework.FieldData)
|
||||
}
|
||||
|
||||
func containsIP(s logical.Storage, roleName string, ip string) (bool, error) {
|
||||
if roleName == "" || ip == "" {
|
||||
return false, fmt.Errorf("invalid parameters")
|
||||
}
|
||||
roleEntry, err := s.Get("policy/" + roleName)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error retrieving role '%s'", err)
|
||||
@@ -64,8 +74,10 @@ func containsIP(s logical.Storage, roleName string, ip string) (bool, error) {
|
||||
}
|
||||
ipMatched := false
|
||||
for _, item := range strings.Split(role.CIDR, ",") {
|
||||
log.Println(item)
|
||||
_, cidrIPNet, _ := net.ParseCIDR(item)
|
||||
_, cidrIPNet, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(fmt.Sprintf("Invalid cidr entry '%s'", item))
|
||||
}
|
||||
ipMatched = cidrIPNet.Contains(net.ParseIP(ip))
|
||||
if ipMatched {
|
||||
break
|
||||
@@ -75,9 +87,10 @@ func containsIP(s logical.Storage, roleName string, ip string) (bool, error) {
|
||||
}
|
||||
|
||||
const pathLookupSyn = `
|
||||
pathLookupSyn
|
||||
Lists 'roles' that can be used to create a dynamic key.
|
||||
`
|
||||
|
||||
const pathLookupDesc = `
|
||||
pathLoookupDesc
|
||||
CIDR blocks will be associated with multiple 'roles'.
|
||||
This endpoint lists all the 'roles' that are associated with the supplied IP address.
|
||||
`
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func pathRoleCreate(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.sshConnect\n")
|
||||
return &framework.Path{
|
||||
Pattern: "creds/(?P<name>\\w+)",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -33,19 +31,22 @@ func pathRoleCreate(b *backend) *framework.Path {
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathRoleCreateWrite,
|
||||
},
|
||||
HelpSynopsis: sshConnectHelpSyn,
|
||||
HelpDescription: sshConnectHelpDesc,
|
||||
HelpSynopsis: pathRoleCreateHelpSyn,
|
||||
HelpDescription: pathRoleCreateHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleCreateWrite(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.pathRoleCreateWrite\n")
|
||||
|
||||
//fetch the parameters
|
||||
roleName := d.Get("name").(string)
|
||||
username := d.Get("username").(string)
|
||||
ipRaw := d.Get("ip").(string)
|
||||
if roleName == "" {
|
||||
return logical.ErrorResponse("Invalid 'name'"), nil
|
||||
}
|
||||
if ipRaw == "" {
|
||||
return logical.ErrorResponse("Invalid 'ip'"), nil
|
||||
}
|
||||
|
||||
//find the role to be used for installing dynamic key
|
||||
rolePath := "policy/" + roleName
|
||||
@@ -61,6 +62,10 @@ func (b *backend) pathRoleCreateWrite(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
username = role.DefaultUser
|
||||
}
|
||||
|
||||
//validate the IP address
|
||||
ipAddr := net.ParseIP(ipRaw)
|
||||
if ipAddr == nil {
|
||||
@@ -70,8 +75,10 @@ func (b *backend) pathRoleCreateWrite(
|
||||
|
||||
ipMatched := false
|
||||
for _, item := range strings.Split(role.CIDR, ",") {
|
||||
log.Println(item)
|
||||
_, cidrIPNet, _ := net.ParseCIDR(item)
|
||||
_, cidrIPNet, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Invalid cidr entry '%s'", item)), nil
|
||||
}
|
||||
ipMatched = cidrIPNet.Contains(ipAddr)
|
||||
if ipMatched {
|
||||
break
|
||||
@@ -96,40 +103,30 @@ func (b *backend) pathRoleCreateWrite(
|
||||
hostKeyFileName := "./vault_ssh_" + username + "_" + ip + "_shared.pem"
|
||||
err = ioutil.WriteFile(hostKeyFileName, []byte(hostKey.Key), 0600)
|
||||
|
||||
otkPrivateKeyFileName := "vault_ssh_" + username + "_" + ip + "_otk.pem"
|
||||
otkPublicKeyFileName := otkPrivateKeyFileName + ".pub"
|
||||
dynamicPrivateKeyFileName := "vault_ssh_" + username + "_" + ip + "_otk.pem"
|
||||
dynamicPublicKeyFileName := dynamicPrivateKeyFileName + ".pub"
|
||||
|
||||
//commands to be run on vault server
|
||||
removeFile(otkPrivateKeyFileName)
|
||||
removeFile(otkPublicKeyFileName)
|
||||
//delete the temporary files if they are already present
|
||||
err = removeFile(dynamicPrivateKeyFileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(fmt.Sprintf("Error removing dynamic private key file: '%s'", err))
|
||||
}
|
||||
err = removeFile(dynamicPublicKeyFileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(fmt.Sprintf("Error removing dynamic private key file: '%s'", err))
|
||||
}
|
||||
|
||||
//generate RSA key pair
|
||||
dynamicPublicKey, dynamicPrivateKey, _ := generateRSAKeys()
|
||||
ioutil.WriteFile(otkPrivateKeyFileName, []byte(dynamicPrivateKey), 0600)
|
||||
ioutil.WriteFile(otkPublicKeyFileName, []byte(dynamicPublicKey), 0644)
|
||||
|
||||
uploadFileScp(otkPublicKeyFileName, username, ip, hostKey.Key)
|
||||
/*
|
||||
otkPublicKeyFileNameBase := filepath.Base(otkPublicKeyFileName)
|
||||
otkPublicKeyFile, _ := os.Open(otkPublicKeyFileName)
|
||||
otkPublicKeyStat, err := otkPublicKeyFile.Stat()
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("File does not exist")
|
||||
}
|
||||
session := createSSHPublicKeysSession(username, ip, hostKey.Key)
|
||||
if session == nil {
|
||||
return nil, fmt.Errorf("Invalid session object")
|
||||
}
|
||||
go func() {
|
||||
w, _ := session.StdinPipe()
|
||||
fmt.Fprintln(w, "C0644", otkPublicKeyStat.Size(), otkPublicKeyFileNameBase)
|
||||
io.Copy(w, otkPublicKeyFile)
|
||||
fmt.Fprint(w, "\x00")
|
||||
w.Close()
|
||||
}()
|
||||
if err := session.Run(fmt.Sprintf("scp -vt %s", otkPublicKeyFileNameBase)); err != nil {
|
||||
panic("Failed to run: " + err.Error())
|
||||
}
|
||||
session.Close()
|
||||
*/
|
||||
//save the public key pair to a file
|
||||
ioutil.WriteFile(dynamicPublicKeyFileName, []byte(dynamicPublicKey), 0644)
|
||||
|
||||
//send the public key to target machine
|
||||
err = uploadFileScp(dynamicPublicKeyFileName, username, ip, hostKey.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//connect to target machine
|
||||
session, err := createSSHPublicKeysSession(username, ip, hostKey.Key)
|
||||
@@ -142,14 +139,14 @@ func (b *backend) pathRoleCreateWrite(
|
||||
var buf bytes.Buffer
|
||||
session.Stdout = &buf
|
||||
|
||||
authKeysFileName := "~/.ssh/authorized_keys"
|
||||
tempKeysFileName := "~/temp_authorized_keys"
|
||||
authKeysFileName := "/home/" + username + "/.ssh/authorized_keys"
|
||||
tempKeysFileName := "/home/" + username + "/temp_authorized_keys"
|
||||
|
||||
//commands to be run on target machine
|
||||
grepCmd := "grep -vFf " + otkPublicKeyFileName + " " + authKeysFileName + " > " + tempKeysFileName + ";"
|
||||
grepCmd := "grep -vFf " + dynamicPublicKeyFileName + " " + authKeysFileName + " > " + tempKeysFileName + ";"
|
||||
catCmdRemoveDuplicate := "cat " + tempKeysFileName + " > " + authKeysFileName + ";"
|
||||
catCmdAppendNew := "cat " + otkPublicKeyFileName + " >> " + authKeysFileName + ";"
|
||||
removeCmd := "rm -f " + tempKeysFileName + " " + otkPublicKeyFileName + ";"
|
||||
catCmdAppendNew := "cat " + dynamicPublicKeyFileName + " >> " + authKeysFileName + ";"
|
||||
removeCmd := "rm -f " + tempKeysFileName + " " + dynamicPublicKeyFileName + ";"
|
||||
remoteCmdString := strings.Join([]string{
|
||||
grepCmd,
|
||||
catCmdRemoveDuplicate,
|
||||
@@ -178,10 +175,17 @@ type sshCIDR struct {
|
||||
CIDR []string
|
||||
}
|
||||
|
||||
const sshConnectHelpSyn = `
|
||||
sshConnectionHelpSyn
|
||||
const pathRoleCreateHelpSyn = `
|
||||
Creates a dynamic key for the target machine.
|
||||
`
|
||||
|
||||
const sshConnectHelpDesc = `
|
||||
rshConnectionHelpDesc
|
||||
const pathRoleCreateHelpDesc = `
|
||||
This path will generates a new key for establishing SSH session with
|
||||
target host. Previously registered shared key belonging to target
|
||||
infrastructure will be used to install the new key at the target. If
|
||||
this backend is mounted at 'ssh', then "ssh/creds/role" would generate
|
||||
a dynamic key for 'web' role.
|
||||
|
||||
The dynamic keys will have a lease associated with them. The access
|
||||
keys can be revoked by using the lease ID.
|
||||
`
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathRoles(b *backend) *framework.Path {
|
||||
log.Printf("Vishal: ssh.pathRoles\n")
|
||||
return &framework.Path{
|
||||
Pattern: "roles/(?P<name>\\w+)",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -46,18 +47,38 @@ func pathRoles(b *backend) *framework.Path {
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.pathRoleWrite\n")
|
||||
|
||||
roleName := d.Get("name").(string)
|
||||
keyName := d.Get("key").(string)
|
||||
adminUser := d.Get("admin_user").(string)
|
||||
defaultUser := d.Get("default_user").(string)
|
||||
cidr := d.Get("cidr").(string)
|
||||
|
||||
log.Printf("Vishal: name[%s] key[%s] admin_user[%s] default_user[%s] cidr[%s]\n", roleName, keyName, adminUser, defaultUser, cidr)
|
||||
//input validations
|
||||
if roleName == "" {
|
||||
return logical.ErrorResponse("Invalid 'roleName'"), nil
|
||||
}
|
||||
if keyName == "" {
|
||||
return logical.ErrorResponse("Invalid 'key'"), nil
|
||||
}
|
||||
if adminUser == "" {
|
||||
return logical.ErrorResponse("Invalid 'admin_user'"), nil
|
||||
}
|
||||
if cidr == "" {
|
||||
return logical.ErrorResponse("Invalid 'cidr'"), nil
|
||||
}
|
||||
for _, item := range strings.Split(cidr, ",") {
|
||||
_, _, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Invalid cidr entry '%s'", item)), nil
|
||||
}
|
||||
}
|
||||
|
||||
rolePath := "policy/" + roleName
|
||||
|
||||
if defaultUser == "" {
|
||||
defaultUser = adminUser
|
||||
}
|
||||
|
||||
entry, err := logical.StorageEntryJSON(rolePath, sshRole{
|
||||
KeyName: keyName,
|
||||
AdminUser: adminUser,
|
||||
@@ -69,7 +90,6 @@ func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("Vishal: entryJSON:%s\n", entry.Value)
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,7 +98,6 @@ func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.pathRoleRead\n")
|
||||
roleName := d.Get("name").(string)
|
||||
rolePath := "policy/" + roleName
|
||||
entry, err := req.Storage.Get(rolePath)
|
||||
@@ -96,7 +115,6 @@ func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*l
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.pathRoleDelete\n")
|
||||
roleName := d.Get("name").(string)
|
||||
rolePath := "policy/" + roleName
|
||||
err := req.Storage.Delete(rolePath)
|
||||
@@ -114,9 +132,13 @@ type sshRole struct {
|
||||
}
|
||||
|
||||
const pathRoleHelpSyn = `
|
||||
Manage the roles that can be created with this backend.
|
||||
Manage the 'roles' that can be created with this backend.
|
||||
`
|
||||
|
||||
const pathRoleHelpDesc = `
|
||||
This path lets you manage the roles that can be created with this backend.
|
||||
This path allows you to manage the roles that are used to create dynamic keys.
|
||||
These roles will be having privileged access to all the hosts mentioned by CIDR blocks.
|
||||
For example, if the backend is mounted at "ssh" and the role is created at "ssh/roles/web", then a user could request for a new key at "ssh/creds/web" for the supplied username and IP address.
|
||||
|
||||
The 'cidr' field takes comma seperated CIDR blocks. The 'admin_user' should have root access in all the hosts represented by the 'cidr' field. When the user requests key for an IP, the key will be installed for the user mentioned by 'default_user' field. The 'key' field takes a named key which can be configured by 'ssh/keys/' endpoint.
|
||||
`
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -15,7 +13,6 @@ import (
|
||||
const SecretOneTimeKeyType = "secret_one_type_key_type"
|
||||
|
||||
func secretSshKey(b *backend) *framework.Secret {
|
||||
log.Printf("Vishal: ssh.secretPrivateKey\n")
|
||||
return &framework.Secret{
|
||||
Type: SecretOneTimeKeyType,
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
@@ -28,15 +25,14 @@ func secretSshKey(b *backend) *framework.Secret {
|
||||
Description: "ip address of host",
|
||||
},
|
||||
},
|
||||
DefaultDuration: 10 * time.Second, //TODO: change this
|
||||
DefaultGracePeriod: 10 * time.Second, //TODO: change this
|
||||
DefaultDuration: 5 * time.Second, //TODO: change this
|
||||
DefaultGracePeriod: 1 * time.Second, //TODO: change this
|
||||
Renew: b.secretSshKeyRenew,
|
||||
Revoke: b.secretSshKeyRevoke,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) secretSshKeyRenew(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
|
||||
@@ -49,8 +45,6 @@ func (b *backend) secretSshKeyRenew(req *logical.Request, d *framework.FieldData
|
||||
}
|
||||
|
||||
func (b *backend) secretSshKeyRevoke(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("Vishal: ssh.secretPrivateKeyRevoke req: %#v\n", req)
|
||||
//fetch the values from secret
|
||||
usernameRaw, ok := req.Secret.InternalData["username"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing internal data")
|
||||
@@ -83,7 +77,6 @@ func (b *backend) secretSshKeyRevoke(req *logical.Request, d *framework.FieldDat
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing internal data")
|
||||
}
|
||||
log.Printf("Vishal: username:%s ip:%s keyName:%s\n", username, ip, hostKeyName)
|
||||
|
||||
//fetch the host key using the key name
|
||||
hostKeyPath := "keys/" + hostKeyName
|
||||
@@ -101,24 +94,23 @@ func (b *backend) secretSshKeyRevoke(req *logical.Request, d *framework.FieldDat
|
||||
err = ioutil.WriteFile(hostKeyFileName, []byte(hostKey.Key), 0400)
|
||||
|
||||
//write dynamicPublicKey to file and use it as argument to scp command
|
||||
otkPrivateKeyFileName := "vault_ssh_" + username + "_" + ip + "_otk.pem"
|
||||
otkPublicKeyFileName := otkPrivateKeyFileName + ".pub"
|
||||
err = ioutil.WriteFile(otkPublicKeyFileName, []byte(dynamicPublicKey), 0400)
|
||||
dynamicPrivateKeyFileName := "vault_ssh_" + username + "_" + ip + "_otk.pem"
|
||||
dynamicPublicKeyFileName := dynamicPrivateKeyFileName + ".pub"
|
||||
err = ioutil.WriteFile(dynamicPublicKeyFileName, []byte(dynamicPublicKey), 0400)
|
||||
|
||||
//transfer the dynamic public key to target machine and use it to remove the entry from authorized_keys file
|
||||
scpCmd := "scp -i " + hostKeyFileName + " " + otkPublicKeyFileName + " " + username + "@" + ip + ":~;"
|
||||
err = exec_command(scpCmd)
|
||||
err = uploadFileScp(dynamicPublicKeyFileName, username, ip, hostKey.Key)
|
||||
if err != nil {
|
||||
fmt.Errorf("Running command scp failed " + err.Error())
|
||||
return nil, fmt.Errorf("Public key transfer failed: %s", err)
|
||||
}
|
||||
|
||||
authKeysFileName := "~/.ssh/authorized_keys"
|
||||
tempKeysFileName := "~/temp_authorized_keys"
|
||||
|
||||
//commands to be run on target machine
|
||||
grepCmd := "grep -vFf " + otkPublicKeyFileName + " " + authKeysFileName + " > " + tempKeysFileName + ";"
|
||||
grepCmd := "grep -vFf " + dynamicPublicKeyFileName + " " + authKeysFileName + " > " + tempKeysFileName + ";"
|
||||
catCmdRemoveDuplicate := "cat " + tempKeysFileName + " > " + authKeysFileName + ";"
|
||||
rmCmd := "rm -f " + tempKeysFileName + " " + otkPublicKeyFileName + ";"
|
||||
rmCmd := "rm -f " + tempKeysFileName + " " + dynamicPublicKeyFileName + ";"
|
||||
remoteCmdString := strings.Join([]string{
|
||||
grepCmd,
|
||||
catCmdRemoveDuplicate,
|
||||
@@ -134,12 +126,10 @@ func (b *backend) secretSshKeyRevoke(req *logical.Request, d *framework.FieldDat
|
||||
return nil, fmt.Errorf("Invalid session object")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
session.Stdout = &buf
|
||||
//run the commands in target machine
|
||||
if err := session.Run(remoteCmdString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session.Close()
|
||||
fmt.Println(buf.String())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -16,6 +15,11 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
/*
|
||||
Executes the command represented by the input.
|
||||
Multiple commands can be run by concatinating strings with ';'.
|
||||
Currently, it is supported only for linux platforms and user bash shell.
|
||||
*/
|
||||
func exec_command(cmdString string) error {
|
||||
cmd := exec.Command("/bin/bash", "-c", cmdString)
|
||||
if _, err := cmd.Output(); err != nil {
|
||||
@@ -24,11 +28,16 @@ func exec_command(cmdString string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Transfers the file to the target machine by establishing an SSH session with the target.
|
||||
Uses the public key authentication method and hence the parameter 'key' takes in the private key.
|
||||
The fileName parameter takes an absolute path.
|
||||
*/
|
||||
func uploadFileScp(fileName, username, ip, key string) error {
|
||||
nameBase := filepath.Base(fileName)
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to open file")
|
||||
return err
|
||||
}
|
||||
stat, err := file.Stat()
|
||||
if os.IsNotExist(err) {
|
||||
@@ -36,7 +45,7 @@ func uploadFileScp(fileName, username, ip, key string) error {
|
||||
}
|
||||
session, err := createSSHPublicKeysSession(username, ip, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create SSH Session using public keys: %s", err)
|
||||
return err
|
||||
}
|
||||
if session == nil {
|
||||
return fmt.Errorf("Invalid session object")
|
||||
@@ -50,12 +59,19 @@ func uploadFileScp(fileName, username, ip, key string) error {
|
||||
w.Close()
|
||||
}()
|
||||
if err := session.Run(fmt.Sprintf("scp -vt %s", nameBase)); err != nil {
|
||||
return fmt.Errorf("Failed to run: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSSHPublicKeysSession(username string, ipAddr string, hostKey string) (*ssh.Session, error) {
|
||||
/*
|
||||
Creates a SSH session object which can be used to run commands in the target machine.
|
||||
The session will use public key authentication method with port 22.
|
||||
*/
|
||||
func createSSHPublicKeysSession(username, ipAddr, hostKey string) (*ssh.Session, error) {
|
||||
if username == "" || ipAddr == "" || hostKey == "" {
|
||||
return nil, fmt.Errorf("Invalid parameters")
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey([]byte(hostKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Parsing Private Key failed: %s", err)
|
||||
@@ -70,7 +86,7 @@ func createSSHPublicKeysSession(username string, ipAddr string, hostKey string)
|
||||
|
||||
client, err := ssh.Dial("tcp", ipAddr+":22", config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Dial Failed: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if client == nil {
|
||||
return nil, fmt.Errorf("Invalid client object: %s", err)
|
||||
@@ -78,35 +94,45 @@ func createSSHPublicKeysSession(username string, ipAddr string, hostKey string)
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Creating new client session failed: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func removeFile(fileName string) {
|
||||
/*
|
||||
Deletes the file in the current directory.
|
||||
The parameter is just the name of the file and not a path.
|
||||
*/
|
||||
func removeFile(fileName string) error {
|
||||
if fileName == "" {
|
||||
return fmt.Errorf("Invalid file name")
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Printf("Error fetching working directory:%s", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
absFileName := wd + "/" + fileName
|
||||
|
||||
if _, err := os.Stat(absFileName); err == nil {
|
||||
err := os.Remove(absFileName)
|
||||
if err != nil {
|
||||
log.Printf(fmt.Sprintf("Failed: %s", err))
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateRSAKeys() (string, string, error) {
|
||||
/*
|
||||
Creates a new RSA key pair with key length of 2048.
|
||||
The private key will be of pem format and the public key will be of OpenSSH format.
|
||||
*/
|
||||
func generateRSAKeys() (publicKeyRsa string, privateKeyRsa string, err error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error generating RSA key-pair: %s", err)
|
||||
}
|
||||
|
||||
privateKeyRsa := string(pem.EncodeToMemory(&pem.Block{
|
||||
privateKeyRsa = string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}))
|
||||
@@ -115,10 +141,6 @@ func generateRSAKeys() (string, string, error) {
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error generating RSA key-pair: %s", err)
|
||||
}
|
||||
publicKeyRsa := "ssh-rsa " + base64.StdEncoding.EncodeToString(sshPublicKey.Marshal())
|
||||
|
||||
//ioutil.WriteFile("testkey.pem", []byte(privateKeyRsa), 0600)
|
||||
//ioutil.WriteFile("testkey.pub", []byte(publicKeyRsa), 0600)
|
||||
|
||||
return publicKeyRsa, privateKeyRsa, nil
|
||||
publicKeyRsa = "ssh-rsa " + base64.StdEncoding.EncodeToString(sshPublicKey.Marshal())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package command
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -19,7 +18,6 @@ type WriteCommand struct {
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Run(args []string) int {
|
||||
log.Printf("Vishal: writeCommand\n")
|
||||
var format string
|
||||
var force bool
|
||||
flags := c.Meta.FlagSet("write", FlagSetDefault)
|
||||
@@ -57,7 +55,6 @@ func (c *WriteCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
log.Printf("Vishal: write Path: %#v\n", path)
|
||||
secret, err := client.Logical().Write(path, data)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
|
||||
@@ -3,7 +3,6 @@ package http
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -22,7 +21,6 @@ const AuthHeaderName = "X-Vault-Token"
|
||||
// its own to mount the Vault API within another web server.
|
||||
func Handler(core *vault.Core) http.Handler {
|
||||
// Create the muxer to handle the actual endpoints
|
||||
log.Printf("Vishal: http.handler.Handler\n")
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/v1/sys/init", handleSysInit(core))
|
||||
mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core))
|
||||
@@ -77,7 +75,6 @@ func parseRequest(r *http.Request, out interface{}) error {
|
||||
// request is a helper to perform a request and properly exit in the
|
||||
// case of an error.
|
||||
func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *logical.Request) (*logical.Response, bool) {
|
||||
log.Printf("Vishal: http.handler.request: \n")
|
||||
resp, err := core.HandleRequest(r)
|
||||
if err == vault.ErrStandby {
|
||||
respondStandby(core, w, rawReq.URL)
|
||||
|
||||
@@ -2,7 +2,6 @@ package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
|
||||
func handleLogical(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Vishal: handleLogical called\n")
|
||||
// Determine the path...
|
||||
if !strings.HasPrefix(r.URL.Path, "/v1/") {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
@@ -59,7 +57,6 @@ func handleLogical(core *vault.Core) http.Handler {
|
||||
// Make the internal request. We attach the connection info
|
||||
// as well in case this is an authentication request that requires
|
||||
// it. Vault core handles stripping this if we need to.
|
||||
log.Printf("Vishal: http.logical.handleLogical: requesting\n")
|
||||
resp, ok := request(core, w, r, requestAuth(r, &logical.Request{
|
||||
Operation: op,
|
||||
Path: path,
|
||||
|
||||
@@ -345,7 +345,6 @@ func (c *Core) Shutdown() error {
|
||||
|
||||
// HandleRequest is used to handle a new incoming request
|
||||
func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err error) {
|
||||
log.Printf("Vishal: vault.core.HandleRequest: req.Path:%#v\n", req.Path)
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -150,7 +149,6 @@ func (r *Router) Route(req *logical.Request) (*logical.Response, error) {
|
||||
r.l.RLock()
|
||||
mount, raw, ok := r.root.LongestPrefix(req.Path)
|
||||
if !ok {
|
||||
log.Printf("Vishal: vault.router.Route: here\n")
|
||||
// Re-check for a backend by appending a slash. This lets "foo" mean
|
||||
// "foo/" at the root level which is almost always what we want.
|
||||
req.Path += "/"
|
||||
|
||||
Reference in New Issue
Block a user