Merge pull request #32828 from cyli/external-ca-cert
Add the `CACert` parameter to the `ExternalCA` object
This commit is contained in:
commit
25058d9b0c
6 changed files with 70 additions and 10 deletions
|
@ -1837,6 +1837,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"
|
||||
|
||||
|
@ -156,6 +158,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