use tls for auth

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
Evan Hazlett
2015-01-09 09:52:24 -08:00
parent 6ea17eee0d
commit 4534944f6a
10 changed files with 370 additions and 268 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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)

View File

@@ -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
View 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
View 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
}