mirror of
https://github.com/docker/docs.git
synced 2026-03-27 14:28:47 +07:00
41
commands.go
41
commands.go
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
@@ -149,7 +150,7 @@ var Commands = []cli.Command{
|
||||
|
||||
func cmdActive(c *cli.Context) {
|
||||
name := c.Args().First()
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
|
||||
if name == "" {
|
||||
host, err := store.GetActive()
|
||||
@@ -182,25 +183,7 @@ func cmdCreate(c *cli.Context) {
|
||||
log.Fatal("You must specify a machine name")
|
||||
}
|
||||
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
|
||||
exists, err := store.Exists(name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
log.Fatal("There's already a machine with the same name")
|
||||
}
|
||||
|
||||
keyExists, err := drivers.PublicKeyExists()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !keyExists {
|
||||
log.Fatalf("Identity authentication public key doesn't exist at %q. Create your public key by running the \"docker\" command.", drivers.PublicKeyPath())
|
||||
}
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
|
||||
host, err := store.Create(name, driver, c)
|
||||
if err != nil {
|
||||
@@ -220,7 +203,15 @@ func cmdCreate(c *cli.Context) {
|
||||
log.Fatalf("error setting active host: %v", err)
|
||||
}
|
||||
|
||||
log.Infof("%q has been created and is now the active machine. To point Docker at this machine, run: export DOCKER_HOST=$(machine url) DOCKER_AUTH=identity", name)
|
||||
log.Infof("%q has been created and is now the active machine", name)
|
||||
// TODO @ehazlett - this will change but at least show how to connect for now
|
||||
log.Info("To connect, pass these args to Docker: ")
|
||||
storeDir := c.GlobalString("storage-path")
|
||||
caCert := filepath.Join(storeDir, name, "ca.pem")
|
||||
clientCert := filepath.Join(storeDir, name, "client.pem")
|
||||
clientKey := filepath.Join(storeDir, name, "client-key.pem")
|
||||
log.Infof("--auth=cert --auth-ca=%s --auth-cert=%s --auth-key=%s -H $(machine url)",
|
||||
caCert, clientCert, clientKey)
|
||||
}
|
||||
|
||||
func cmdInspect(c *cli.Context) {
|
||||
@@ -249,7 +240,7 @@ func cmdKill(c *cli.Context) {
|
||||
|
||||
func cmdLs(c *cli.Context) {
|
||||
quiet := c.Bool("quiet")
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
|
||||
hostList, err := store.List()
|
||||
if err != nil {
|
||||
@@ -320,7 +311,7 @@ func cmdRm(c *cli.Context) {
|
||||
|
||||
isError := false
|
||||
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
for _, host := range c.Args() {
|
||||
if err := store.Remove(host, force); err != nil {
|
||||
log.Errorf("Error removing machine %s: %s", host, err)
|
||||
@@ -334,7 +325,7 @@ func cmdRm(c *cli.Context) {
|
||||
|
||||
func cmdSsh(c *cli.Context) {
|
||||
name := c.Args().First()
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
|
||||
if name == "" {
|
||||
host, err := store.GetActive()
|
||||
@@ -412,7 +403,7 @@ func cmdNotFound(c *cli.Context, command string) {
|
||||
|
||||
func getHost(c *cli.Context) *Host {
|
||||
name := c.Args().First()
|
||||
store := NewStore(c.GlobalString("storage-path"))
|
||||
store := NewStore(c.GlobalString("storage-path"), c.GlobalString("auth-ca"), c.GlobalString("auth-key"))
|
||||
|
||||
if name == "" {
|
||||
host, err := store.GetActive()
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
const (
|
||||
driverName = "amazonec2"
|
||||
defaultRegion = "us-east-1"
|
||||
defaultAMI = "ami-a00461c8"
|
||||
defaultAMI = "ami-4ae27e22"
|
||||
defaultInstanceType = "t2.micro"
|
||||
defaultRootSize = 16
|
||||
ipRange = "0.0.0.0/0"
|
||||
@@ -31,6 +30,7 @@ const (
|
||||
|
||||
type Driver struct {
|
||||
Id string
|
||||
MachineName string
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
SessionToken string
|
||||
@@ -48,6 +48,8 @@ type Driver struct {
|
||||
VpcId string
|
||||
SubnetId string
|
||||
Zone string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
storePath string
|
||||
keyPath string
|
||||
}
|
||||
@@ -134,9 +136,9 @@ func GetCreateFlags() []cli.Flag {
|
||||
}
|
||||
}
|
||||
|
||||
func NewDriver(machineName string, storePath string) (drivers.Driver, error) {
|
||||
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
|
||||
id := generateId()
|
||||
return &Driver{Id: id, MachineName: machineName, storePath: storePath}, nil
|
||||
return &Driver{Id: id, MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil
|
||||
}
|
||||
|
||||
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
@@ -263,72 +265,11 @@ func (d *Driver) Create() error {
|
||||
cmd, err = d.GetSSHCommand("if [ ! -e /usr/bin/docker ]; then curl get.docker.io | sudo sh -; fi")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = d.GetSSHCommand("sudo stop docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("HACK: Downloading version of Docker with identity auth...")
|
||||
|
||||
cmd, err = d.GetSSHCommand("sudo curl -sS -o /usr/bin/docker https://ehazlett.s3.amazonaws.com/public/docker/linux/docker-1.4.1-136b351e-identity")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Updating /etc/default/docker to use identity auth...")
|
||||
|
||||
cmd, err = d.GetSSHCommand("echo 'export DOCKER_OPTS=\"--auth=identity --host=tcp://0.0.0.0:2376 --host=unix:///var/run/docker.sock --auth-authorized-dir=/root/.docker/authorized-keys.d\"' | sudo tee -a /etc/default/docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// HACK: create dir for ubuntu user to access
|
||||
log.Debugf("Adding key to authorized-keys.d...")
|
||||
|
||||
cmd, err = d.GetSSHCommand("sudo mkdir -p /root/.docker && sudo chown -R ubuntu /root/.docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Open(filepath.Join(os.Getenv("HOME"), ".docker/public-key.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
cmdString := fmt.Sprintf("sudo mkdir -p %q && sudo tee -a %q", "/root/.docker/authorized-keys.d", "/root/.docker/authorized-keys.d/docker-host.json")
|
||||
cmd, err = d.GetSSHCommand(cmdString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdin = f
|
||||
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
|
||||
|
||||
@@ -19,15 +19,19 @@ import (
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
AccessToken string
|
||||
DropletID int
|
||||
Image string
|
||||
IPAddress string
|
||||
MachineName string
|
||||
Region string
|
||||
SSHKeyID int
|
||||
Size string
|
||||
storePath string
|
||||
AccessToken string
|
||||
DropletID int
|
||||
DropletName string
|
||||
Image string
|
||||
MachineName string
|
||||
IPAddress string
|
||||
Region string
|
||||
SSHKeyID int
|
||||
Size string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
DriverKeyPath string
|
||||
storePath string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -67,8 +71,8 @@ func GetCreateFlags() []cli.Flag {
|
||||
}
|
||||
}
|
||||
|
||||
func NewDriver(machineName string, storePath string) (drivers.Driver, error) {
|
||||
return &Driver{MachineName: machineName, storePath: storePath}, nil
|
||||
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
|
||||
return &Driver{MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil
|
||||
}
|
||||
|
||||
func (d *Driver) DriverName() string {
|
||||
@@ -160,48 +164,6 @@ func (d *Driver) Create() error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("HACK: Downloading version of Docker with identity auth...")
|
||||
|
||||
cmd, err = d.GetSSHCommand("stop docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = d.GetSSHCommand("curl -sS https://ehazlett.s3.amazonaws.com/public/docker/linux/docker-1.4.1-136b351e-identity > /usr/bin/docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Updating /etc/default/docker to use identity auth...")
|
||||
|
||||
cmd, err = d.GetSSHCommand("echo 'export DOCKER_OPTS=\"--auth=identity --host=tcp://0.0.0.0:2376 --host=unix:///var/run/docker.sock\"' >> /etc/default/docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Adding key to authorized-keys.d...")
|
||||
|
||||
if err := drivers.AddPublicKeyToAuthorizedHosts(d, "/.docker/authorized-keys.d"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = d.GetSSHCommand("start docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ type Driver interface {
|
||||
// - RegisterCreateFlags: a function that takes the FlagSet for
|
||||
// "docker hosts create" and returns an object to pass to SetConfigFromFlags
|
||||
type RegisteredDriver struct {
|
||||
New func(machineName string, storePath string) (Driver, error)
|
||||
New func(machineName string, storePath string, caCert string, privateKey string) (Driver, error)
|
||||
GetCreateFlags func() []cli.Flag
|
||||
}
|
||||
|
||||
@@ -92,12 +92,12 @@ func Register(name string, registeredDriver *RegisteredDriver) error {
|
||||
}
|
||||
|
||||
// NewDriver creates a new driver of type "name"
|
||||
func NewDriver(name string, machineName string, storePath string) (Driver, error) {
|
||||
func NewDriver(name string, machineName string, storePath string, caCert string, privateKey string) (Driver, error) {
|
||||
driver, exists := drivers[name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("hosts: Unknown driver %q", name)
|
||||
}
|
||||
return driver.New(machineName, storePath)
|
||||
return driver.New(machineName, storePath, caCert, privateKey)
|
||||
}
|
||||
|
||||
// GetCreateFlags runs GetCreateFlags for all of the drivers and
|
||||
|
||||
@@ -32,6 +32,8 @@ type Driver struct {
|
||||
Memory int
|
||||
DiskSize int
|
||||
Boot2DockerURL string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
storePath string
|
||||
}
|
||||
|
||||
@@ -71,8 +73,8 @@ func GetCreateFlags() []cli.Flag {
|
||||
}
|
||||
}
|
||||
|
||||
func NewDriver(machineName string, storePath string) (drivers.Driver, error) {
|
||||
return &Driver{MachineName: machineName, storePath: storePath}, nil
|
||||
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
|
||||
return &Driver{MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil
|
||||
}
|
||||
|
||||
func (d *Driver) DriverName() string {
|
||||
|
||||
247
host.go
247
host.go
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -11,11 +9,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -24,10 +21,15 @@ var (
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
Name string `json:"-"`
|
||||
DriverName string
|
||||
Driver drivers.Driver
|
||||
storePath string
|
||||
Name string `json:"-"`
|
||||
DriverName string
|
||||
Driver drivers.Driver
|
||||
CaCertPath string
|
||||
ServerCertPath string
|
||||
ServerKeyPath string
|
||||
PrivateKeyPath string
|
||||
ClientCertPath string
|
||||
storePath string
|
||||
}
|
||||
|
||||
type hostConfig struct {
|
||||
@@ -47,16 +49,18 @@ func waitForDocker(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHost(name, driverName, storePath string) (*Host, error) {
|
||||
driver, err := drivers.NewDriver(driverName, name, storePath)
|
||||
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,
|
||||
storePath: storePath,
|
||||
Name: name,
|
||||
DriverName: driverName,
|
||||
Driver: driver,
|
||||
CaCertPath: caCert,
|
||||
PrivateKeyPath: privateKey,
|
||||
storePath: storePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -79,139 +83,156 @@ func ValidateHostName(name string) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func loadTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||
if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil {
|
||||
return nil, err
|
||||
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
|
||||
}
|
||||
|
||||
trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
|
||||
if err == libtrust.ErrKeyFileDoesNotExist {
|
||||
trustKey, err = libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating key: %s", err)
|
||||
}
|
||||
|
||||
if err := libtrust.SaveKey(trustKeyPath, trustKey); err != nil {
|
||||
return nil, fmt.Errorf("error saving key file: %s", err)
|
||||
|
||||
}
|
||||
|
||||
dir, file := filepath.Split(trustKeyPath)
|
||||
if err := libtrust.SavePublicKey(filepath.Join(dir, "public-"+file), trustKey.PublicKey()); err != nil {
|
||||
return nil, fmt.Errorf("error saving public key file: %s", err)
|
||||
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("error loading key file: %s", err)
|
||||
|
||||
if _, err := os.Stat(h.PrivateKeyPath); os.IsNotExist(err) {
|
||||
privateKeyExists = false
|
||||
} else {
|
||||
privateKeyExists = true
|
||||
}
|
||||
return trustKey, nil
|
||||
|
||||
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) addHostToKnownHosts() error {
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
func (h *Host) ConfigureAuth() error {
|
||||
d := h.Driver
|
||||
|
||||
trustKeyPath := filepath.Join(drivers.GetDockerDir(), "key.json")
|
||||
knownHostsPath := filepath.Join(drivers.GetDockerDir(), "known-hosts.json")
|
||||
|
||||
driverUrl, err := h.GetURL()
|
||||
ip, err := d.GetIP()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get machine url: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
u, err := url.Parse(driverUrl)
|
||||
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 fmt.Errorf("unable to parse machine url")
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if u.Scheme == "unix" {
|
||||
return nil
|
||||
}
|
||||
|
||||
addr := u.Host
|
||||
proto := "tcp"
|
||||
|
||||
trustKey, err := loadTrustKey(trustKeyPath)
|
||||
cmd, err = d.GetSSHCommand("sudo mkdir -p /etc/docker")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load trust key: %s", err)
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
knownHosts, err := libtrust.LoadKeySetFile(knownHostsPath)
|
||||
// upload certs and configure TLS auth
|
||||
caCert, err := ioutil.ReadFile(caCertPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load trusted hosts file: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
allowedHosts, err := libtrust.FilterByHosts(knownHosts, addr, false)
|
||||
serverCert, err := ioutil.ReadFile(serverCertPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error filtering hosts: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
certPool, err := libtrust.GenerateCACertPool(trustKey, allowedHosts)
|
||||
serverKey, err := ioutil.ReadFile(serverKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not create CA pool: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig.ServerName = "docker"
|
||||
tlsConfig.RootCAs = certPool
|
||||
|
||||
x509Cert, err := libtrust.GenerateSelfSignedClientCert(trustKey)
|
||||
cmd, err = d.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a /etc/docker/ca.pem", string(caCert)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate generation error: %s", err)
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig.Certificates = []tls.Certificate{{
|
||||
Certificate: [][]byte{x509Cert.Raw},
|
||||
PrivateKey: trustKey.CryptoPrivateKey(),
|
||||
Leaf: x509Cert,
|
||||
}}
|
||||
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
|
||||
log.Debugf("waiting for Docker to become available on %s", addr)
|
||||
if err := waitForDocker(addr); err != nil {
|
||||
return fmt.Errorf("unable to connect to Docker daemon: %s", err)
|
||||
}
|
||||
testConn, err := tls.Dial(proto, addr, tlsConfig)
|
||||
cmd, err = d.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a /etc/docker/server-key.pem", string(serverKey)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("tls Handshake error: %s", err)
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
DNSName: tlsConfig.ServerName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
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
|
||||
}
|
||||
|
||||
certs := testConn.ConnectionState().PeerCertificates
|
||||
for i, cert := range certs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
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
|
||||
}
|
||||
|
||||
if _, err := certs[0].Verify(opts); err != nil {
|
||||
if _, ok := err.(x509.UnknownAuthorityError); ok {
|
||||
pubKey, err := libtrust.FromCryptoPublicKey(certs[0].PublicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error extracting public key from cert: %s", err)
|
||||
}
|
||||
|
||||
pubKey.AddExtendedField("hosts", []string{addr})
|
||||
|
||||
log.Debugf("Adding machine to known hosts: %s", addr)
|
||||
|
||||
if err := libtrust.AddKeySetFile(knownHostsPath, pubKey); err != nil {
|
||||
return fmt.Errorf("error adding machine to known hosts: %s", err)
|
||||
}
|
||||
}
|
||||
cmd, err = d.GetSSHCommand("sudo start docker")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
testConn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -224,10 +245,6 @@ func (h *Host) Create(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.addHostToKnownHosts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -279,7 +296,7 @@ func (h *Host) LoadConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
driver, err := drivers.NewDriver(config.DriverName, h.Name, h.storePath)
|
||||
driver, err := drivers.NewDriver(config.DriverName, h.Name, h.storePath, h.CaCertPath, h.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
10
main.go
10
main.go
@@ -32,6 +32,16 @@ func main() {
|
||||
Name: "storage-path",
|
||||
Usage: "Configures storage path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_AUTH_CA",
|
||||
Name: "auth-ca",
|
||||
Usage: "CA to verify remotes against",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_AUTH_PRIVATE_KEY",
|
||||
Name: "auth-key",
|
||||
Usage: "Private key to generate certificates",
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
|
||||
14
store.go
14
store.go
@@ -12,15 +12,17 @@ import (
|
||||
|
||||
// Store persists hosts on the filesystem
|
||||
type Store struct {
|
||||
Path string
|
||||
Path string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
}
|
||||
|
||||
func NewStore(rootPath string) *Store {
|
||||
func NewStore(rootPath string, caCert string, privateKey string) *Store {
|
||||
if rootPath == "" {
|
||||
rootPath = filepath.Join(drivers.GetHomeDir(), ".docker", "machines")
|
||||
}
|
||||
|
||||
return &Store{Path: rootPath}
|
||||
return &Store{Path: rootPath, CaCertPath: caCert, PrivateKeyPath: privateKey}
|
||||
}
|
||||
|
||||
func (s *Store) Create(name string, driverName string, flags drivers.DriverOptions) (*Host, error) {
|
||||
@@ -34,7 +36,7 @@ func (s *Store) Create(name string, driverName string, flags drivers.DriverOptio
|
||||
|
||||
hostPath := filepath.Join(s.Path, name)
|
||||
|
||||
host, err := NewHost(name, driverName, hostPath)
|
||||
host, err := NewHost(name, driverName, hostPath, s.CaCertPath, s.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return host, err
|
||||
}
|
||||
@@ -56,6 +58,10 @@ func (s *Store) Create(name string, driverName string, flags drivers.DriverOptio
|
||||
return host, err
|
||||
}
|
||||
|
||||
if err := host.ConfigureAuth(); err != nil {
|
||||
return host, err
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
|
||||
147
utils/certs.go
Normal file
147
utils/certs.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newCertificate(org string) (*x509.Certificate, error) {
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(time.Hour * 24 * 1080)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{org},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
BasicConstraintsValid: true,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// GenerateCACert generates a new certificate authority from the specified org
|
||||
// and bit size and stores the resulting certificate and key file
|
||||
// in the arguments.
|
||||
func GenerateCACert(certFile, keyFile, org string, bits int) error {
|
||||
template, err := newCertificate(org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certOut, err := os.Create(certFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
|
||||
keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
keyOut.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateCert generates a new certificate signed using the provided
|
||||
// certificate authority files and stores the result in the certificate
|
||||
// file and key provided. The provided host names are set to the
|
||||
// appropriate certificate fields.
|
||||
func GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
|
||||
template, err := newCertificate(org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// client
|
||||
if len(hosts) == 1 && hosts[0] == "" {
|
||||
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
||||
template.KeyUsage = x509.KeyUsageDigitalSignature
|
||||
} else { // server
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tlsCert, err := tls.LoadX509KeyPair(caFile, caKeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
x509Cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, x509Cert, &priv.PublicKey, tlsCert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certOut, err := os.Create(certFile)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
|
||||
keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
keyOut.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
26
utils/utils.go
Normal file
26
utils/utils.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func CopyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user