open-vault/builtin/logical/ssh/path_config_ca.go
2017-03-02 14:36:13 -05:00

129 lines
3.3 KiB
Go

package ssh
import (
"fmt"
"github.com/hashicorp/vault/helper/errutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"golang.org/x/crypto/ssh"
"crypto/rsa"
"encoding/pem"
"crypto/rand"
"crypto/x509"
)
func pathConfigCA(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/ca",
Fields: map[string]*framework.FieldSchema{
"private_key": &framework.FieldSchema{
Type: framework.TypeString,
Description: `Private half of the SSH key that will be used to sign certificates.`,
},
"public_key": &framework.FieldSchema{
Type: framework.TypeString,
Description: `Public half of the SSH key that will be used to sign certificates.`,
},
"generate_signing_key": &framework.FieldSchema{
Type: framework.TypeBool,
Description: `Generate SSH key pair internally rather than use the private_key and public_key fields.`,
Default: true,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathCAWrite,
},
HelpSynopsis: `Set the SSH private key used for signing certificates.`,
HelpDescription: `This sets the CA information used for certificates generated by this
by this mount. The fields must be in the standard private and public SSH format.
For security reasons, the private key cannot be retrieved later.`,
}
}
func (b *backend) pathCAWrite(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
var publicKey, privateKey string
var err error
if data.Get("generate_signing_key").(bool) {
publicKey, privateKey, err = generateSSHKeyPair()
if err != nil {
return nil, err
}
} else {
publicKey, privateKey, err = parseSSHKeyPair(data)
if err != nil {
return nil, err
}
}
err = req.Storage.Put(&logical.StorageEntry{
Key: "public_key",
Value: []byte(publicKey),
})
if err != nil {
return nil, err
}
bundle := signingBundle{
Certificate: privateKey,
}
entry, err := logical.StorageEntryJSON("config/ca_bundle", bundle)
if err != nil {
return nil, err
}
err = req.Storage.Put(entry)
return nil, err
}
func generateSSHKeyPair() (string, string, error) {
privateSeed, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return "", "", err
}
privateBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: x509.MarshalPKCS1PrivateKey(privateSeed),
}
public, err := ssh.NewPublicKey(&privateSeed.PublicKey)
if err != nil {
return "", "", err
}
return string(ssh.MarshalAuthorizedKey(public)), string(pem.EncodeToMemory(privateBlock)), nil
}
func parseSSHKeyPair(data *framework.FieldData) (string, string, error) {
publicKey := data.Get("public_key").(string)
if publicKey == "" {
return "", "", errutil.UserError{Err: `missing public_key`}
}
privateKey := data.Get("private_key").(string)
if privateKey == "" {
return "", "", errutil.UserError{Err: `missing public_key`}
}
_, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return "", "", errutil.UserError{Err: fmt.Sprintf(`Unable to parse "private_key" as an SSH private key: %s`, err)}
}
_, err = parsePublicSSHKey(publicKey)
if err != nil {
return "", "", errutil.UserError{Err: fmt.Sprintf(`Unable to parse "public_key" as an SSH public key: %s`, err)}
}
return publicKey, privateKey, nil
}