Disable extended key usage as a client for non-Swarm-master server certs

Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
This commit is contained in:
Nathan LeClaire 2016-06-24 10:52:24 -07:00
parent bca4af7130
commit c0b721b5f9
9 changed files with 84 additions and 31 deletions

View File

@ -66,7 +66,19 @@ func BootstrapCertificates(authOptions *auth.Options) error {
return errors.New("The client key already exists. Please remove it or specify a different key/cert.")
}
if err := GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caPrivateKeyPath, org, bits); err != nil {
// Used to generate the client certificate.
certOptions := &Options{
Hosts: []string{""},
CertFile: clientCertPath,
KeyFile: clientKeyPath,
CAFile: caCertPath,
CAKeyFile: caPrivateKeyPath,
Org: org,
Bits: bits,
SwarmMaster: false,
}
if err := GenerateCert(certOptions); err != nil {
return fmt.Errorf("Generating client certificate failed: %s", err)
}
}

View File

@ -21,9 +21,16 @@ import (
var defaultGenerator = NewX509CertGenerator()
type Options struct {
Hosts []string
CertFile, KeyFile, CAFile, CAKeyFile, Org string
Bits int
SwarmMaster bool
}
type Generator interface {
GenerateCACertificate(certFile, keyFile, org string, bits int) error
GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error
GenerateCert(opts *Options) error
ReadTLSConfig(addr string, authOptions *auth.Options) (*tls.Config, error)
ValidateCertificate(addr string, authOptions *auth.Options) (bool, error)
}
@ -38,8 +45,8 @@ func GenerateCACertificate(certFile, keyFile, org string, bits int) error {
return defaultGenerator.GenerateCACertificate(certFile, keyFile, org, bits)
}
func GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
return defaultGenerator.GenerateCert(hosts, certFile, keyFile, caFile, caKeyFile, org, bits)
func GenerateCert(opts *Options) error {
return defaultGenerator.GenerateCert(opts)
}
func ValidateCertificate(addr string, authOptions *auth.Options) (bool, error) {
@ -150,18 +157,24 @@ func (xcg *X509CertGenerator) GenerateCACertificate(certFile, keyFile, org strin
// 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 (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
template, err := xcg.newCertificate(org)
func (xcg *X509CertGenerator) GenerateCert(opts *Options) error {
template, err := xcg.newCertificate(opts.Org)
if err != nil {
return err
}
// client
if len(hosts) == 1 && hosts[0] == "" {
if len(opts.Hosts) == 1 && opts.Hosts[0] == "" {
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
template.KeyUsage = x509.KeyUsageDigitalSignature
} else { // server
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
for _, h := range hosts {
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
if opts.SwarmMaster {
// Extend the Swarm master's server certificate
// permissions to also be able to connect to downstream
// nodes as a client.
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
for _, h := range opts.Hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
@ -170,12 +183,12 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
}
}
tlsCert, err := tls.LoadX509KeyPair(caFile, caKeyFile)
tlsCert, err := tls.LoadX509KeyPair(opts.CAFile, opts.CAKeyFile)
if err != nil {
return err
}
priv, err := rsa.GenerateKey(rand.Reader, bits)
priv, err := rsa.GenerateKey(rand.Reader, opts.Bits)
if err != nil {
return err
}
@ -190,7 +203,7 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
return err
}
certOut, err := os.Create(certFile)
certOut, err := os.Create(opts.CertFile)
if err != nil {
return err
}
@ -198,7 +211,7 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
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)
keyOut, err := os.OpenFile(opts.KeyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
@ -212,8 +225,8 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
// ReadTLSConfig reads the tls config for a machine.
func (xcg *X509CertGenerator) ReadTLSConfig(addr string, authOptions *auth.Options) (*tls.Config, error) {
caCertPath := authOptions.CaCertPath
serverCertPath := authOptions.ServerCertPath
serverKeyPath := authOptions.ServerKeyPath
clientCertPath := authOptions.ClientCertPath
clientKeyPath := authOptions.ClientKeyPath
log.Debugf("Reading CA certificate from %s", caCertPath)
caCert, err := ioutil.ReadFile(caCertPath)
@ -221,19 +234,19 @@ func (xcg *X509CertGenerator) ReadTLSConfig(addr string, authOptions *auth.Optio
return nil, err
}
log.Debugf("Reading server certificate from %s", serverCertPath)
serverCert, err := ioutil.ReadFile(serverCertPath)
log.Debugf("Reading client certificate from %s", clientCertPath)
clientCert, err := ioutil.ReadFile(clientCertPath)
if err != nil {
return nil, err
}
log.Debugf("Reading server key from %s", serverKeyPath)
serverKey, err := ioutil.ReadFile(serverKeyPath)
log.Debugf("Reading client key from %s", clientKeyPath)
clientKey, err := ioutil.ReadFile(clientKeyPath)
if err != nil {
return nil, err
}
return xcg.getTLSConfig(caCert, serverCert, serverKey, false)
return xcg.getTLSConfig(caCert, clientCert, clientKey, false)
}
// ValidateCertificate validate the certificate installed on the vm.

View File

@ -56,7 +56,18 @@ func TestGenerateCert(t *testing.T) {
t.Fatal(err)
}
if err := GenerateCert([]string{}, certPath, keyPath, caCertPath, caKeyPath, testOrg, bits); err != nil {
opts := &Options{
Hosts: []string{},
CertFile: certPath,
CAKeyFile: caKeyPath,
CAFile: caCertPath,
KeyFile: keyPath,
Org: testOrg,
Bits: bits,
SwarmMaster: false,
}
if err := GenerateCert(opts); err != nil {
t.Fatal(err)
}

View File

@ -24,7 +24,7 @@ func (fcg FakeCertGenerator) GenerateCACertificate(certFile, keyFile, org string
return nil
}
func (fcg FakeCertGenerator) GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
func (fcg FakeCertGenerator) GenerateCert(opts *cert.Options) error {
return nil
}

View File

@ -128,6 +128,10 @@ func (provisioner *Boot2DockerProvisioner) GetAuthOptions() auth.Options {
return provisioner.AuthOptions
}
func (provisioner *Boot2DockerProvisioner) GetSwarmOptions() swarm.Options {
return provisioner.SwarmOptions
}
func (provisioner *Boot2DockerProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) {
var (
engineCfg bytes.Buffer

View File

@ -43,6 +43,10 @@ func (fp *FakeProvisioner) GetAuthOptions() auth.Options {
return auth.Options{}
}
func (fp *FakeProvisioner) GetSwarmOptions() swarm.Options {
return swarm.Options{}
}
func (fp *FakeProvisioner) Package(name string, action pkgaction.PackageAction) error {
return nil
}

View File

@ -76,6 +76,10 @@ func (provisioner *GenericProvisioner) GetAuthOptions() auth.Options {
return provisioner.AuthOptions
}
func (provisioner *GenericProvisioner) GetSwarmOptions() swarm.Options {
return provisioner.SwarmOptions
}
func (provisioner *GenericProvisioner) SetOsReleaseInfo(info *OsRelease) {
provisioner.OsReleaseInfo = info
}

View File

@ -46,6 +46,9 @@ type Provisioner interface {
// Return the auth options used to configure remote connection for the daemon.
GetAuthOptions() auth.Options
// Get the swarm options associated with this host.
GetSwarmOptions() swarm.Options
// Run a package action e.g. install
Package(name string, action pkgaction.PackageAction) error

View File

@ -64,6 +64,7 @@ func ConfigureAuth(p Provisioner) error {
driver := p.GetDriver()
machineName := driver.GetMachineName()
authOptions := p.GetAuthOptions()
swarmOptions := p.GetSwarmOptions()
org := mcnutils.GetUsername() + "." + machineName
bits := 2048
@ -98,15 +99,16 @@ func ConfigureAuth(p Provisioner) error {
// TODO: Switch to passing just authOptions to this func
// instead of all these individual fields
err = cert.GenerateCert(
hosts,
authOptions.ServerCertPath,
authOptions.ServerKeyPath,
authOptions.CaCertPath,
authOptions.CaPrivateKeyPath,
org,
bits,
)
err = cert.GenerateCert(&cert.Options{
Hosts: hosts,
CertFile: authOptions.ServerCertPath,
KeyFile: authOptions.ServerKeyPath,
CAFile: authOptions.CaCertPath,
CAKeyFile: authOptions.CaPrivateKeyPath,
Org: org,
Bits: bits,
SwarmMaster: swarmOptions.Master,
})
if err != nil {
return fmt.Errorf("error generating server cert: %s", err)