Add key generation.

This commit is contained in:
Brendan Burns 2015-05-28 11:45:08 -07:00 committed by CJ Cullen
parent 30a89968a4
commit 5115fd5703
13 changed files with 162 additions and 5 deletions

View File

@ -357,6 +357,11 @@ func (s *APIServer) Run(_ []string) error {
}
}
var installSSH master.InstallSSHKey
instances, supported := cloud.Instances()
if supported {
installSSH = instances.AddSSHKeyToAllInstances
}
config := &master.Config{
EtcdHelper: helper,
EventTTL: s.EventTTL,
@ -384,6 +389,7 @@ func (s *APIServer) Run(_ []string) error {
MinRequestTimeout: s.MinRequestTimeout,
SSHUser: s.SSHUser,
SSHKeyfile: s.SSHKeyfile,
InstallSSHKey: installSSH,
}
m := master.New(config)

View File

@ -230,6 +230,10 @@ func newEc2Filter(name string, value string) *ec2.Filter {
return filter
}
func (self *AWSCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
// Implementation of EC2.Instances
func (self *awsSdkEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) {
// Instances are paged

View File

@ -108,6 +108,9 @@ type Instances interface {
List(filter string) ([]string, error)
// GetNodeResources gets the resources for a particular node
GetNodeResources(name string) (*api.NodeResources, error)
// AddSSHKeyToAllInstances adds an SSH public key as a legal identity for all instances
// expected format for the key is standard ssh-keygen format: <protocol> <blob>
AddSSHKeyToAllInstances(user string, keyData []byte) error
}
// Route is a representation of an advanced routing rule.

View File

@ -144,6 +144,10 @@ func (f *FakeCloud) EnsureTCPLoadBalancerDeleted(name, region string) error {
return f.Err
}
func (f *FakeCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
// NodeAddresses is a test-spy implementation of Instances.NodeAddresses.
// It adds an entry "node-addresses" into the internal method call record.
func (f *FakeCloud) NodeAddresses(instance string) ([]api.NodeAddress, error) {

View File

@ -22,6 +22,7 @@ import (
"io/ioutil"
"net"
"net/http"
"os"
"path"
"strconv"
"strings"
@ -474,6 +475,39 @@ func (gce *GCECloud) getInstanceByName(name string) (*compute.Instance, error) {
return res, nil
}
func (gce *GCECloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
project, err := gce.service.Projects.Get(gce.projectID).Do()
if err != nil {
return err
}
hostname, err := os.Hostname()
if err != nil {
return err
}
found := false
for _, item := range project.CommonInstanceMetadata.Items {
if item.Key == "sshKeys" {
item.Value = fmt.Sprintf("%s\n%s:%s %s@%s", item.Value, user, string(keyData), user, hostname)
found = true
break
}
}
if !found {
// This is super unlikely, so log.
glog.Infof("Failed to find sshKeys metadata, creating a new item")
project.CommonInstanceMetadata.Items = append(project.CommonInstanceMetadata.Items,
&compute.MetadataItems{
Key: "sshKeys",
Value: fmt.Sprint("%s:%s %s@%s", user, string(keyData), user, hostname),
})
}
op, err := gce.service.Projects.SetCommonInstanceMetadata(gce.projectID, project.CommonInstanceMetadata).Do()
if err != nil {
return err
}
return gce.waitForGlobalOp(op)
}
// NodeAddresses is an implementation of Instances.NodeAddresses.
func (gce *GCECloud) NodeAddresses(_ string) ([]api.NodeAddress, error) {
externalIP, err := gce.metadataAccess(EXTERNAL_IP_METADATA_URL)

View File

@ -78,6 +78,10 @@ func newMesosCloud(configReader io.Reader) (*MesosCloud, error) {
}
}
func (c *MesosCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
// Instances returns a copy of the Mesos cloud Instances implementation.
// Mesos natively provides minimal cloud-type resources. More robust cloud
// support requires a combination of Mesos and cloud-specific knowledge.

View File

@ -317,6 +317,10 @@ func getAddressByName(api *gophercloud.ServiceClient, name string) (string, erro
return s, nil
}
func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) {
glog.V(4).Infof("NodeAddresses(%v) called", name)

View File

@ -18,6 +18,7 @@ package ovirt_cloud
import (
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
@ -273,3 +274,7 @@ func (v *OVirtCloud) List(filter string) ([]string, error) {
func (v *OVirtCloud) GetNodeResources(name string) (*api.NodeResources, error) {
return nil, nil
}
func (v *OVirtCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}

View File

@ -376,6 +376,10 @@ func (i *Instances) InstanceID(name string) (string, error) {
return "", nil
}
func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) {
glog.V(2).Infof("GetNodeResources(%v) called", name)

View File

@ -131,6 +131,10 @@ func (v *VagrantCloud) getInstanceByAddress(address string) (*SaltMinion, error)
return nil, fmt.Errorf("unable to find instance for address: %s", address)
}
func (v *VagrantCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return errors.New("unimplemented")
}
// NodeAddresses returns the NodeAddresses of a particular machine instance.
func (v *VagrantCloud) NodeAddresses(instance string) ([]api.NodeAddress, error) {
// Due to vagrant not running with a dedicated DNS setup, we return the IP address of a minion as its hostname at this time

View File

@ -18,7 +18,9 @@ package master
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/pprof"
@ -147,10 +149,13 @@ type Config struct {
ServiceNodePortRange util.PortRange
// Used for secure proxy. If empty, don't use secure proxy.
SSHUser string
SSHKeyfile string
SSHUser string
SSHKeyfile string
InstallSSHKey InstallSSHKey
}
type InstallSSHKey func(user string, data []byte) error
// Master contains state for a Kubernetes cluster master/api server.
type Master struct {
// "Inputs", Copied from Config
@ -204,7 +209,8 @@ type Master struct {
InsecureHandler http.Handler
// Used for secure proxy
tunnels util.SSHTunnelList
tunnels util.SSHTunnelList
installSSHKey InstallSSHKey
}
// NewEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version
@ -486,6 +492,16 @@ func (m *Master) init(c *Config) {
var proxyDialer func(net, addr string) (net.Conn, error)
if len(c.SSHUser) > 0 {
glog.Infof("Setting up proxy: %s %s", c.SSHUser, c.SSHKeyfile)
exists, err := util.FileExists(c.SSHKeyfile)
if err != nil {
glog.Errorf("Error detecting if key exists: %v", err)
} else if !exists {
glog.Infof("Key doesn't exist, attempting to create")
err := m.generateSSHKey(c.SSHUser, c.SSHKeyfile)
if err != nil {
glog.Errorf("Failed to create key pair: %v", err)
}
}
m.setupSecureProxy(c.SSHUser, c.SSHKeyfile)
proxyDialer = m.Dial
}
@ -801,3 +817,21 @@ func (m *Master) setupSecureProxy(user, keyfile string) {
}
}()
}
func (m *Master) generateSSHKey(user, keyfile string) error {
if m.installSSHKey == nil {
return errors.New("ssh install function is null")
}
private, public, err := util.GenerateKey(2048)
if err != nil {
return err
}
ioutil.WriteFile(keyfile, util.EncodePrivateKey(private), 0600)
data, err := util.EncodeSSHKey(public)
if err != nil {
return err
}
fmt.Printf("FOO: %s", data)
return m.installSSHKey(user, data)
}

View File

@ -18,10 +18,14 @@ package util
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"math/rand"
mathrand "math/rand"
"net"
"os"
@ -260,7 +264,7 @@ func (l SSHTunnelList) Dial(network, addr string) (net.Conn, error) {
if len(l) == 0 {
return nil, fmt.Errorf("Empty tunnel list.")
}
return l[rand.Int()%len(l)].Tunnel.Dial(network, addr)
return l[mathrand.Int()%len(l)].Tunnel.Dial(network, addr)
}
func (l SSHTunnelList) Has(addr string) bool {
@ -271,3 +275,38 @@ func (l SSHTunnelList) Has(addr string) bool {
}
return false
}
func EncodePrivateKey(private *rsa.PrivateKey) []byte {
return pem.EncodeToMemory(&pem.Block{
Bytes: x509.MarshalPKCS1PrivateKey(private),
Type: "RSA PRIVATE KEY",
})
}
func EncodePublicKey(public *rsa.PublicKey) ([]byte, error) {
publicBytes, err := x509.MarshalPKIXPublicKey(public)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Bytes: publicBytes,
Type: "PUBLIC KEY",
}), nil
}
func EncodeSSHKey(public *rsa.PublicKey) ([]byte, error) {
publicKey, err := ssh.NewPublicKey(public)
if err != nil {
return nil, err
}
return ssh.MarshalAuthorizedKey(publicKey), nil
}
func GenerateKey(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
private, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, nil, err
}
return private, &private.PublicKey, nil
}

View File

@ -515,4 +515,16 @@ func ShortenString(str string, n int) string {
} else {
return str[:n]
}
func FileExists(filename string) (bool, error) {
file, err := os.Open(filename)
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
return false, nil
} else {
return false, err
}
}
return true, nil
}