Add the CACert parameter to the ExternalCA object in order to match
swarmkit's API type. Make sure this parameter gets propagated to swarmkit, and also add an extra option to the CLI when providing external CAs to parse the CA cert from a file. Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
parent
e1101b1295
commit
b0401a71f7
6 changed files with 70 additions and 10 deletions
|
@ -1835,6 +1835,9 @@ definitions:
|
|||
type: "object"
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
CACert:
|
||||
description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)."
|
||||
type: "string"
|
||||
EncryptionConfig:
|
||||
description: "Parameters related to encryption-at-rest."
|
||||
type: "object"
|
||||
|
|
|
@ -126,6 +126,10 @@ type ExternalCA struct {
|
|||
// Options is a set of additional key/value pairs whose interpretation
|
||||
// depends on the specified CA type.
|
||||
Options map[string]string `json:",omitempty"`
|
||||
|
||||
// CACert specifies which root CA is used by this external CA. This certificate must
|
||||
// be in PEM format.
|
||||
CACert string
|
||||
}
|
||||
|
||||
// InitRequest is the request used to init a swarm.
|
||||
|
|
|
@ -2,7 +2,9 @@ package swarm
|
|||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -155,6 +157,15 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
|
|||
case "url":
|
||||
hasURL = true
|
||||
externalCA.URL = value
|
||||
case "cacert":
|
||||
cacontents, err := ioutil.ReadFile(value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to read CA cert for external CA")
|
||||
}
|
||||
if pemBlock, _ := pem.Decode(cacontents); pemBlock == nil {
|
||||
return nil, errors.New("CA cert for external CA must be in PEM format")
|
||||
}
|
||||
externalCA.CACert = string(cacontents)
|
||||
default:
|
||||
externalCA.Options[key] = value
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
|
|||
Protocol: types.ExternalCAProtocol(strings.ToLower(ca.Protocol.String())),
|
||||
URL: ca.URL,
|
||||
Options: ca.Options,
|
||||
CACert: string(ca.CACert),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -112,6 +113,7 @@ func MergeSwarmSpecToGRPC(s types.Spec, spec swarmapi.ClusterSpec) (swarmapi.Clu
|
|||
Protocol: swarmapi.ExternalCA_CAProtocol(protocol),
|
||||
URL: ca.URL,
|
||||
Options: ca.Options,
|
||||
CACert: []byte(ca.CACert),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -146,9 +146,6 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
|
||||
// TODO: when root rotation is in, convert to a series of root rotation tests instead.
|
||||
// currently just makes sure that we don't have to provide a CA certificate when
|
||||
// providing an external CA
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil)
|
||||
d1.UpdateSwarm(c, func(s *swarm.Spec) {
|
||||
|
@ -157,11 +154,18 @@ func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
|
|||
Protocol: swarm.ExternalCAProtocolCFSSL,
|
||||
URL: "https://thishasnoca.org",
|
||||
},
|
||||
{
|
||||
Protocol: swarm.ExternalCAProtocolCFSSL,
|
||||
URL: "https://thishasacacert.org",
|
||||
CACert: "cacert",
|
||||
},
|
||||
}
|
||||
})
|
||||
info, err := d1.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs, checker.HasLen, 1)
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs, checker.HasLen, 2)
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, "cacert")
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *check.C) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
icmd "github.com/docker/docker/pkg/testutil/cmd"
|
||||
"github.com/docker/docker/pkg/testutil/tempfile"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
remoteipam "github.com/docker/libnetwork/ipams/remote/api"
|
||||
|
@ -53,11 +54,29 @@ func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
|
|||
c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
|
||||
|
||||
// passing an external CA (this is without starting a root rotation) does not fail
|
||||
out, err = d.Cmd("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
|
||||
cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org",
|
||||
"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
|
||||
cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
|
||||
expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
spec = getSpec()
|
||||
c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 1)
|
||||
c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
|
||||
c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
|
||||
c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
|
||||
|
||||
// passing an invalid external CA fails
|
||||
tempFile := tempfile.NewTempFile(c, "testfile", "fakecert")
|
||||
defer tempFile.Remove()
|
||||
|
||||
result := cli.Docker(cli.Args("swarm", "update",
|
||||
"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://something.org,cacert=%s", tempFile.Name())),
|
||||
cli.Daemon(d.Daemon))
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "must be in PEM format",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
||||
|
@ -68,17 +87,34 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
|||
return sw.Spec
|
||||
}
|
||||
|
||||
// passing an invalid external CA fails
|
||||
tempFile := tempfile.NewTempFile(c, "testfile", "fakecert")
|
||||
defer tempFile.Remove()
|
||||
|
||||
result := cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
|
||||
"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://somethingelse.org,cacert=%s", tempFile.Name())),
|
||||
cli.Daemon(d.Daemon))
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "must be in PEM format",
|
||||
})
|
||||
|
||||
cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
|
||||
"--external-ca", "protocol=cfssl,url=https://something.org"),
|
||||
"--external-ca", "protocol=cfssl,url=https://something.org",
|
||||
"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
|
||||
cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
|
||||
expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
spec := getSpec()
|
||||
c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
|
||||
c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
|
||||
c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 1)
|
||||
c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
|
||||
c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
|
||||
c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
|
||||
|
||||
c.Assert(d.Leave(true), checker.IsNil)
|
||||
time.Sleep(500 * time.Millisecond) // https://github.com/docker/swarmkit/issues/1421
|
||||
cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
|
||||
spec = getSpec()
|
||||
|
|
Loading…
Reference in a new issue