1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #32828 from cyli/external-ca-cert

Add the `CACert` parameter to the `ExternalCA` object
This commit is contained in:
Brian Goff 2017-04-28 10:30:57 -04:00 committed by GitHub
commit 25058d9b0c
6 changed files with 70 additions and 10 deletions

View file

@ -1837,6 +1837,9 @@ definitions:
type: "object" type: "object"
additionalProperties: additionalProperties:
type: "string" 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: EncryptionConfig:
description: "Parameters related to encryption-at-rest." description: "Parameters related to encryption-at-rest."
type: "object" type: "object"

View file

@ -126,6 +126,10 @@ type ExternalCA struct {
// Options is a set of additional key/value pairs whose interpretation // Options is a set of additional key/value pairs whose interpretation
// depends on the specified CA type. // depends on the specified CA type.
Options map[string]string `json:",omitempty"` 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. // InitRequest is the request used to init a swarm.

View file

@ -2,7 +2,9 @@ package swarm
import ( import (
"encoding/csv" "encoding/csv"
"encoding/pem"
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"time" "time"
@ -156,6 +158,15 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
case "url": case "url":
hasURL = true hasURL = true
externalCA.URL = value 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: default:
externalCA.Options[key] = value externalCA.Options[key] = value
} }

View file

@ -47,6 +47,7 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
Protocol: types.ExternalCAProtocol(strings.ToLower(ca.Protocol.String())), Protocol: types.ExternalCAProtocol(strings.ToLower(ca.Protocol.String())),
URL: ca.URL, URL: ca.URL,
Options: ca.Options, 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), Protocol: swarmapi.ExternalCA_CAProtocol(protocol),
URL: ca.URL, URL: ca.URL,
Options: ca.Options, Options: ca.Options,
CACert: []byte(ca.CACert),
}) })
} }

View file

@ -146,9 +146,6 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
} }
func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(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) d1 := s.AddDaemon(c, false, false)
c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil) c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil)
d1.UpdateSwarm(c, func(s *swarm.Spec) { d1.UpdateSwarm(c, func(s *swarm.Spec) {
@ -157,11 +154,18 @@ func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
Protocol: swarm.ExternalCAProtocolCFSSL, Protocol: swarm.ExternalCAProtocolCFSSL,
URL: "https://thishasnoca.org", URL: "https://thishasnoca.org",
}, },
{
Protocol: swarm.ExternalCAProtocolCFSSL,
URL: "https://thishasacacert.org",
CACert: "cacert",
},
} }
}) })
info, err := d1.SwarmInfo() info, err := d1.SwarmInfo()
c.Assert(err, checker.IsNil) 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) { func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *check.C) {

View file

@ -23,6 +23,7 @@ import (
"github.com/docker/docker/integration-cli/daemon" "github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/pkg/testutil" "github.com/docker/docker/pkg/testutil"
icmd "github.com/docker/docker/pkg/testutil/cmd" icmd "github.com/docker/docker/pkg/testutil/cmd"
"github.com/docker/docker/pkg/testutil/tempfile"
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipamapi"
remoteipam "github.com/docker/libnetwork/ipams/remote/api" 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) c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
// passing an external CA (this is without starting a root rotation) does not fail // 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") cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org",
c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) "--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() 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) { func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
@ -68,17 +87,34 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
return sw.Spec 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", 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) cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
c.Assert(err, checker.IsNil)
spec := getSpec() spec := getSpec()
c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour) c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second) 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) 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) cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
spec = getSpec() spec = getSpec()