Skip to content

Commit 338744a

Browse files
Merge pull request docker-archive-public#3571 from nathanleclaire/disable_nonswarm_ext_attribute_client_for_server_cert
Disable extended key usage as a client for non-Swarm-master server certs
2 parents 42a08de + c0b721b commit 338744a

File tree

9 files changed

+84
-31
lines changed

9 files changed

+84
-31
lines changed

libmachine/cert/bootstrap.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,19 @@ func BootstrapCertificates(authOptions *auth.Options) error {
6666
return errors.New("The client key already exists. Please remove it or specify a different key/cert.")
6767
}
6868

69-
if err := GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caPrivateKeyPath, org, bits); err != nil {
69+
// Used to generate the client certificate.
70+
certOptions := &Options{
71+
Hosts: []string{""},
72+
CertFile: clientCertPath,
73+
KeyFile: clientKeyPath,
74+
CAFile: caCertPath,
75+
CAKeyFile: caPrivateKeyPath,
76+
Org: org,
77+
Bits: bits,
78+
SwarmMaster: false,
79+
}
80+
81+
if err := GenerateCert(certOptions); err != nil {
7082
return fmt.Errorf("Generating client certificate failed: %s", err)
7183
}
7284
}

libmachine/cert/cert.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,16 @@ import (
2121

2222
var defaultGenerator = NewX509CertGenerator()
2323

24+
type Options struct {
25+
Hosts []string
26+
CertFile, KeyFile, CAFile, CAKeyFile, Org string
27+
Bits int
28+
SwarmMaster bool
29+
}
30+
2431
type Generator interface {
2532
GenerateCACertificate(certFile, keyFile, org string, bits int) error
26-
GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error
33+
GenerateCert(opts *Options) error
2734
ReadTLSConfig(addr string, authOptions *auth.Options) (*tls.Config, error)
2835
ValidateCertificate(addr string, authOptions *auth.Options) (bool, error)
2936
}
@@ -38,8 +45,8 @@ func GenerateCACertificate(certFile, keyFile, org string, bits int) error {
3845
return defaultGenerator.GenerateCACertificate(certFile, keyFile, org, bits)
3946
}
4047

41-
func GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
42-
return defaultGenerator.GenerateCert(hosts, certFile, keyFile, caFile, caKeyFile, org, bits)
48+
func GenerateCert(opts *Options) error {
49+
return defaultGenerator.GenerateCert(opts)
4350
}
4451

4552
func ValidateCertificate(addr string, authOptions *auth.Options) (bool, error) {
@@ -150,18 +157,24 @@ func (xcg *X509CertGenerator) GenerateCACertificate(certFile, keyFile, org strin
150157
// certificate authority files and stores the result in the certificate
151158
// file and key provided. The provided host names are set to the
152159
// appropriate certificate fields.
153-
func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
154-
template, err := xcg.newCertificate(org)
160+
func (xcg *X509CertGenerator) GenerateCert(opts *Options) error {
161+
template, err := xcg.newCertificate(opts.Org)
155162
if err != nil {
156163
return err
157164
}
158165
// client
159-
if len(hosts) == 1 && hosts[0] == "" {
166+
if len(opts.Hosts) == 1 && opts.Hosts[0] == "" {
160167
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
161168
template.KeyUsage = x509.KeyUsageDigitalSignature
162169
} else { // server
163-
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
164-
for _, h := range hosts {
170+
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
171+
if opts.SwarmMaster {
172+
// Extend the Swarm master's server certificate
173+
// permissions to also be able to connect to downstream
174+
// nodes as a client.
175+
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
176+
}
177+
for _, h := range opts.Hosts {
165178
if ip := net.ParseIP(h); ip != nil {
166179
template.IPAddresses = append(template.IPAddresses, ip)
167180
} else {
@@ -170,12 +183,12 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
170183
}
171184
}
172185

173-
tlsCert, err := tls.LoadX509KeyPair(caFile, caKeyFile)
186+
tlsCert, err := tls.LoadX509KeyPair(opts.CAFile, opts.CAKeyFile)
174187
if err != nil {
175188
return err
176189
}
177190

178-
priv, err := rsa.GenerateKey(rand.Reader, bits)
191+
priv, err := rsa.GenerateKey(rand.Reader, opts.Bits)
179192
if err != nil {
180193
return err
181194
}
@@ -190,15 +203,15 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
190203
return err
191204
}
192205

193-
certOut, err := os.Create(certFile)
206+
certOut, err := os.Create(opts.CertFile)
194207
if err != nil {
195208
return err
196209
}
197210

198211
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
199212
certOut.Close()
200213

201-
keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
214+
keyOut, err := os.OpenFile(opts.KeyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
202215
if err != nil {
203216
return err
204217
}
@@ -212,28 +225,28 @@ func (xcg *X509CertGenerator) GenerateCert(hosts []string, certFile, keyFile, ca
212225
// ReadTLSConfig reads the tls config for a machine.
213226
func (xcg *X509CertGenerator) ReadTLSConfig(addr string, authOptions *auth.Options) (*tls.Config, error) {
214227
caCertPath := authOptions.CaCertPath
215-
serverCertPath := authOptions.ServerCertPath
216-
serverKeyPath := authOptions.ServerKeyPath
228+
clientCertPath := authOptions.ClientCertPath
229+
clientKeyPath := authOptions.ClientKeyPath
217230

218231
log.Debugf("Reading CA certificate from %s", caCertPath)
219232
caCert, err := ioutil.ReadFile(caCertPath)
220233
if err != nil {
221234
return nil, err
222235
}
223236

224-
log.Debugf("Reading server certificate from %s", serverCertPath)
225-
serverCert, err := ioutil.ReadFile(serverCertPath)
237+
log.Debugf("Reading client certificate from %s", clientCertPath)
238+
clientCert, err := ioutil.ReadFile(clientCertPath)
226239
if err != nil {
227240
return nil, err
228241
}
229242

230-
log.Debugf("Reading server key from %s", serverKeyPath)
231-
serverKey, err := ioutil.ReadFile(serverKeyPath)
243+
log.Debugf("Reading client key from %s", clientKeyPath)
244+
clientKey, err := ioutil.ReadFile(clientKeyPath)
232245
if err != nil {
233246
return nil, err
234247
}
235248

236-
return xcg.getTLSConfig(caCert, serverCert, serverKey, false)
249+
return xcg.getTLSConfig(caCert, clientCert, clientKey, false)
237250
}
238251

239252
// ValidateCertificate validate the certificate installed on the vm.

libmachine/cert/cert_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,18 @@ func TestGenerateCert(t *testing.T) {
5656
t.Fatal(err)
5757
}
5858

59-
if err := GenerateCert([]string{}, certPath, keyPath, caCertPath, caKeyPath, testOrg, bits); err != nil {
59+
opts := &Options{
60+
Hosts: []string{},
61+
CertFile: certPath,
62+
CAKeyFile: caKeyPath,
63+
CAFile: caCertPath,
64+
KeyFile: keyPath,
65+
Org: testOrg,
66+
Bits: bits,
67+
SwarmMaster: false,
68+
}
69+
70+
if err := GenerateCert(opts); err != nil {
6071
t.Fatal(err)
6172
}
6273

libmachine/check/check_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (fcg FakeCertGenerator) GenerateCACertificate(certFile, keyFile, org string
2424
return nil
2525
}
2626

27-
func (fcg FakeCertGenerator) GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
27+
func (fcg FakeCertGenerator) GenerateCert(opts *cert.Options) error {
2828
return nil
2929
}
3030

libmachine/provision/boot2docker.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ func (provisioner *Boot2DockerProvisioner) GetAuthOptions() auth.Options {
128128
return provisioner.AuthOptions
129129
}
130130

131+
func (provisioner *Boot2DockerProvisioner) GetSwarmOptions() swarm.Options {
132+
return provisioner.SwarmOptions
133+
}
134+
131135
func (provisioner *Boot2DockerProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) {
132136
var (
133137
engineCfg bytes.Buffer

libmachine/provision/fake_provisioner.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ func (fp *FakeProvisioner) GetAuthOptions() auth.Options {
4343
return auth.Options{}
4444
}
4545

46+
func (fp *FakeProvisioner) GetSwarmOptions() swarm.Options {
47+
return swarm.Options{}
48+
}
49+
4650
func (fp *FakeProvisioner) Package(name string, action pkgaction.PackageAction) error {
4751
return nil
4852
}

libmachine/provision/generic.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ func (provisioner *GenericProvisioner) GetAuthOptions() auth.Options {
7676
return provisioner.AuthOptions
7777
}
7878

79+
func (provisioner *GenericProvisioner) GetSwarmOptions() swarm.Options {
80+
return provisioner.SwarmOptions
81+
}
82+
7983
func (provisioner *GenericProvisioner) SetOsReleaseInfo(info *OsRelease) {
8084
provisioner.OsReleaseInfo = info
8185
}

libmachine/provision/provisioner.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ type Provisioner interface {
4646
// Return the auth options used to configure remote connection for the daemon.
4747
GetAuthOptions() auth.Options
4848

49+
// Get the swarm options associated with this host.
50+
GetSwarmOptions() swarm.Options
51+
4952
// Run a package action e.g. install
5053
Package(name string, action pkgaction.PackageAction) error
5154

libmachine/provision/utils.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func ConfigureAuth(p Provisioner) error {
6464
driver := p.GetDriver()
6565
machineName := driver.GetMachineName()
6666
authOptions := p.GetAuthOptions()
67+
swarmOptions := p.GetSwarmOptions()
6768
org := mcnutils.GetUsername() + "." + machineName
6869
bits := 2048
6970

@@ -98,15 +99,16 @@ func ConfigureAuth(p Provisioner) error {
9899

99100
// TODO: Switch to passing just authOptions to this func
100101
// instead of all these individual fields
101-
err = cert.GenerateCert(
102-
hosts,
103-
authOptions.ServerCertPath,
104-
authOptions.ServerKeyPath,
105-
authOptions.CaCertPath,
106-
authOptions.CaPrivateKeyPath,
107-
org,
108-
bits,
109-
)
102+
err = cert.GenerateCert(&cert.Options{
103+
Hosts: hosts,
104+
CertFile: authOptions.ServerCertPath,
105+
KeyFile: authOptions.ServerKeyPath,
106+
CAFile: authOptions.CaCertPath,
107+
CAKeyFile: authOptions.CaPrivateKeyPath,
108+
Org: org,
109+
Bits: bits,
110+
SwarmMaster: swarmOptions.Master,
111+
})
110112

111113
if err != nil {
112114
return fmt.Errorf("error generating server cert: %s", err)

0 commit comments

Comments
 (0)