Files
docker-machine/host.go
Evan Hazlett 4534944f6a use tls for auth
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2015-01-15 22:56:24 -05:00

323 lines
7.2 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"path/filepath"
"regexp"
log "github.com/Sirupsen/logrus"
"github.com/docker/machine/drivers"
"github.com/docker/machine/utils"
)
var (
validHostNameChars = `[a-zA-Z0-9_]`
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
)
type Host struct {
Name string `json:"-"`
DriverName string
Driver drivers.Driver
CaCertPath string
ServerCertPath string
ServerKeyPath string
PrivateKeyPath string
ClientCertPath string
storePath string
}
type hostConfig struct {
DriverName string
}
func waitForDocker(addr string) error {
for {
conn, err := net.DialTimeout("tcp", addr, time.Second*5)
if err != nil {
time.Sleep(time.Second * 5)
continue
}
conn.Close()
break
}
return nil
}
func NewHost(name, driverName, storePath, caCert, privateKey string) (*Host, error) {
driver, err := drivers.NewDriver(driverName, name, storePath, caCert, privateKey)
if err != nil {
return nil, err
}
return &Host{
Name: name,
DriverName: driverName,
Driver: driver,
CaCertPath: caCert,
PrivateKeyPath: privateKey,
storePath: storePath,
}, nil
}
func LoadHost(name string, storePath string) (*Host, error) {
if _, err := os.Stat(storePath); os.IsNotExist(err) {
return nil, fmt.Errorf("Host %q does not exist", name)
}
host := &Host{Name: name, storePath: storePath}
if err := host.LoadConfig(); err != nil {
return nil, err
}
return host, nil
}
func ValidateHostName(name string) (string, error) {
if !validHostNamePattern.MatchString(name) {
return name, fmt.Errorf("Invalid host name %q, it must match %s", name, validHostNamePattern)
}
return name, nil
}
func (h *Host) GenerateCertificates(serverIPs []string) error {
var (
caPathExists bool
privateKeyExists bool
org = "docker-machine"
bits = 2048
)
caCertPath := filepath.Join(h.storePath, "ca.pem")
privateKeyPath := filepath.Join(h.storePath, "private.pem")
if _, err := os.Stat(h.CaCertPath); os.IsNotExist(err) {
caPathExists = false
} else {
caPathExists = true
}
if _, err := os.Stat(h.PrivateKeyPath); os.IsNotExist(err) {
privateKeyExists = false
} else {
privateKeyExists = true
}
if !caPathExists && !privateKeyExists {
log.Debugf("generating self-signed CA cert: %s", caCertPath)
if err := utils.GenerateCACert(caCertPath, privateKeyPath, org, bits); err != nil {
return fmt.Errorf("error generating self-signed CA cert: %s", err)
}
} else {
if err := utils.CopyFile(h.CaCertPath, caCertPath); err != nil {
return fmt.Errorf("unable to copy CA cert: %s", err)
}
if err := utils.CopyFile(h.PrivateKeyPath, privateKeyPath); err != nil {
return fmt.Errorf("unable to copy private key: %s", err)
}
}
serverCertPath := filepath.Join(h.storePath, "server.pem")
serverKeyPath := filepath.Join(h.storePath, "server-key.pem")
log.Debugf("generating server cert: %s", serverCertPath)
if err := utils.GenerateCert(serverIPs, serverCertPath, serverKeyPath, caCertPath, privateKeyPath, org, bits); err != nil {
return fmt.Errorf("error generating server cert: %s", err)
}
clientCertPath := filepath.Join(h.storePath, "client.pem")
clientKeyPath := filepath.Join(h.storePath, "client-key.pem")
log.Debugf("generating client cert: %s", clientCertPath)
if err := utils.GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, privateKeyPath, org, bits); err != nil {
return fmt.Errorf("error generating client cert: %s", err)
}
return nil
}
func (h *Host) ConfigureAuth() error {
d := h.Driver
ip, err := d.GetIP()
if err != nil {
return err
}
log.Debugf("generating certificates for %s", ip)
if err := h.GenerateCertificates([]string{ip}); err != nil {
return err
}
serverCertPath := filepath.Join(h.storePath, "server.pem")
caCertPath := filepath.Join(h.storePath, "ca.pem")
serverKeyPath := filepath.Join(h.storePath, "server-key.pem")
cmd, err := d.GetSSHCommand("sudo stop docker")
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = d.GetSSHCommand("sudo mkdir -p /etc/docker")
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
// upload certs and configure TLS auth
caCert, err := ioutil.ReadFile(caCertPath)
if err != nil {
return err
}
serverCert, err := ioutil.ReadFile(serverCertPath)
if err != nil {
return err
}
serverKey, err := ioutil.ReadFile(serverKeyPath)
if err != nil {
return err
}
cmd, err = d.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a /etc/docker/ca.pem", string(caCert)))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = d.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a /etc/docker/server-key.pem", string(serverKey)))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = d.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a /etc/docker/server.pem", string(serverCert)))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = d.GetSSHCommand(`echo 'export DOCKER_OPTS=" \
--tlsverify \
--tlscacert=/etc/docker/ca.pem \
--tlskey=/etc/docker/server-key.pem \
--tlscert=/etc/docker/server.pem \
--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376"' | sudo tee -a /etc/default/docker`)
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = d.GetSSHCommand("sudo start docker")
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func (h *Host) Create(name string) error {
if err := h.Driver.Create(); err != nil {
return err
}
if err := h.SaveConfig(); err != nil {
return err
}
return nil
}
func (h *Host) Start() error {
return h.Driver.Start()
}
func (h *Host) Stop() error {
return h.Driver.Stop()
}
func (h *Host) Upgrade() error {
return h.Driver.Upgrade()
}
func (h *Host) Remove(force bool) error {
if err := h.Driver.Remove(); err != nil {
if !force {
return err
}
}
return h.removeStorePath()
}
func (h *Host) removeStorePath() error {
file, err := os.Stat(h.storePath)
if err != nil {
return err
}
if !file.IsDir() {
return fmt.Errorf("%q is not a directory", h.storePath)
}
return os.RemoveAll(h.storePath)
}
func (h *Host) GetURL() (string, error) {
return h.Driver.GetURL()
}
func (h *Host) LoadConfig() error {
data, err := ioutil.ReadFile(filepath.Join(h.storePath, "config.json"))
if err != nil {
return err
}
// First pass: find the driver name and load the driver
var config hostConfig
if err := json.Unmarshal(data, &config); err != nil {
return err
}
driver, err := drivers.NewDriver(config.DriverName, h.Name, h.storePath, h.CaCertPath, h.PrivateKeyPath)
if err != nil {
return err
}
h.Driver = driver
// Second pass: unmarshal driver config into correct driver
if err := json.Unmarshal(data, &h); err != nil {
return err
}
return nil
}
func (h *Host) SaveConfig() error {
data, err := json.Marshal(h)
if err != nil {
return err
}
if err := ioutil.WriteFile(filepath.Join(h.storePath, "config.json"), data, 0600); err != nil {
return err
}
return nil
}