From 64cccedbce86542fc9289ad9cca1dd758f2f21c2 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Wed, 26 Apr 2017 19:46:20 -0700 Subject: [PATCH] Propagate the swarm cluster and node TLS info provided by the swarm objects into the REST API responses. In the CLI, display only whether the nodes' TLS info matches the cluster's TLS info, or whether the node needs cert rotation. Signed-off-by: Ying Li --- api/swagger.yaml | 63 ++++++++++++++++++++++++ api/types/swarm/common.go | 13 +++++ api/types/swarm/node.go | 1 + api/types/swarm/swarm.go | 4 +- daemon/cluster/convert/node.go | 5 ++ daemon/cluster/convert/swarm.go | 11 +++++ docs/api/version-history.md | 4 ++ integration-cli/docker_api_swarm_test.go | 1 + 8 files changed, 101 insertions(+), 1 deletion(-) diff --git a/api/swagger.yaml b/api/swagger.yaml index 411c8c6b30..45940498db 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -1716,6 +1716,8 @@ definitions: type: "string" Name: type: "string" + TLSInfo: + $ref: "#/definitions/SwarmSpec" example: ID: "24ifsmvkjbyhk" Version: @@ -1756,6 +1758,47 @@ definitions: Leader: true Reachability: "reachable" Addr: "172.17.0.2:2377" + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" SwarmSpec: description: "User modifiable swarm configuration." type: "object" @@ -1903,6 +1946,11 @@ definitions: format: "dateTime" Spec: $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" TaskSpec: description: "User modifiable task configuration." type: "object" @@ -7205,6 +7253,21 @@ paths: UpdatedAt: "2016-08-15T16:32:09.623207604Z" Version: Index: 51 + RootRotationInProgress: false + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" 404: description: "no such swarm" schema: diff --git a/api/types/swarm/common.go b/api/types/swarm/common.go index dc76a146bb..54af82b31b 100644 --- a/api/types/swarm/common.go +++ b/api/types/swarm/common.go @@ -25,3 +25,16 @@ type Driver struct { Name string `json:",omitempty"` Options map[string]string `json:",omitempty"` } + +// TLSInfo represents the TLS information about what CA certificate is trusted, +// and who the issuer for a TLS certificate is +type TLSInfo struct { + // TrustRoot is the trusted CA root certificate in PEM format + TrustRoot string `json:",omitempty"` + + // CertIssuer is the raw subject bytes of the issuer + CertIssuerSubject []byte `json:",omitempty"` + + // CertIssuerPublicKey is the raw public key bytes of the issuer + CertIssuerPublicKey []byte `json:",omitempty"` +} diff --git a/api/types/swarm/node.go b/api/types/swarm/node.go index 379e17a779..28c6851e9c 100644 --- a/api/types/swarm/node.go +++ b/api/types/swarm/node.go @@ -52,6 +52,7 @@ type NodeDescription struct { Platform Platform `json:",omitempty"` Resources Resources `json:",omitempty"` Engine EngineDescription `json:",omitempty"` + TLSInfo TLSInfo `json:",omitempty"` } // Platform represents the platform (Arch/OS). diff --git a/api/types/swarm/swarm.go b/api/types/swarm/swarm.go index 9fc5c30961..bdb3042337 100644 --- a/api/types/swarm/swarm.go +++ b/api/types/swarm/swarm.go @@ -7,7 +7,9 @@ import "time" type ClusterInfo struct { ID string Meta - Spec Spec + Spec Spec + TLSInfo TLSInfo + RootRotationInProgress bool } // Swarm represents a swarm. diff --git a/daemon/cluster/convert/node.go b/daemon/cluster/convert/node.go index fe6cdfee9e..f075783e88 100644 --- a/daemon/cluster/convert/node.go +++ b/daemon/cluster/convert/node.go @@ -50,6 +50,11 @@ func NodeFromGRPC(n swarmapi.Node) types.Node { node.Description.Engine.Plugins = append(node.Description.Engine.Plugins, types.PluginDescription{Type: plugin.Type, Name: plugin.Name}) } } + if n.Description.TLSInfo != nil { + node.Description.TLSInfo.TrustRoot = string(n.Description.TLSInfo.TrustRoot) + node.Description.TLSInfo.CertIssuerPublicKey = n.Description.TLSInfo.CertIssuerPublicKey + node.Description.TLSInfo.CertIssuerSubject = n.Description.TLSInfo.CertIssuerSubject + } } //Manager diff --git a/daemon/cluster/convert/swarm.go b/daemon/cluster/convert/swarm.go index 09121fc8ff..64fc7f72d9 100644 --- a/daemon/cluster/convert/swarm.go +++ b/daemon/cluster/convert/swarm.go @@ -7,6 +7,7 @@ import ( types "github.com/docker/docker/api/types/swarm" swarmapi "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/ca" gogotypes "github.com/gogo/protobuf/types" ) @@ -30,6 +31,10 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm { AutoLockManagers: c.Spec.EncryptionConfig.AutoLockManagers, }, }, + TLSInfo: types.TLSInfo{ + TrustRoot: string(c.RootCA.CACert), + }, + RootRotationInProgress: c.RootCA.RootRotation != nil, }, JoinTokens: types.JoinTokens{ Worker: c.RootCA.JoinTokens.Worker, @@ -37,6 +42,12 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm { }, } + issuerInfo, err := ca.IssuerFromAPIRootCA(&c.RootCA) + if err == nil && issuerInfo != nil { + swarm.TLSInfo.CertIssuerSubject = issuerInfo.Subject + swarm.TLSInfo.CertIssuerPublicKey = issuerInfo.PublicKey + } + heartbeatPeriod, _ := gogotypes.DurationFromProto(c.Spec.Dispatcher.HeartbeatPeriod) swarm.Spec.Dispatcher.HeartbeatPeriod = heartbeatPeriod diff --git a/docs/api/version-history.md b/docs/api/version-history.md index e501c58658..df3c908b40 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -18,7 +18,11 @@ keywords: "API, Docker, rcli, REST, documentation" [Docker Engine API v1.30](https://docs.docker.com/engine/api/v1.30/) documentation * `GET /info` now returns the list of supported logging drivers, including plugins. +* `GET /info` and `GET /swarm` now returns the cluster-wide swarm CA info if the node is in a swarm: the cluster root CA certificate, and the cluster TLS + leaf certificate issuer's subject and public key. * `POST /build/` now (when not silent) produces an `Aux` message in the JSON output stream with payload `types.BuildResult` for each image produced. The final such message will reference the image resulting from the build. +* `GET /nodes` and `GET /nodes/{id}` now returns additional information about swarm TLS info if the node is part of a swarm: the trusted root CA, and the + issuer's subject and public key. ## v1.29 API changes diff --git a/integration-cli/docker_api_swarm_test.go b/integration-cli/docker_api_swarm_test.go index e6707bc781..028785a01c 100644 --- a/integration-cli/docker_api_swarm_test.go +++ b/integration-cli/docker_api_swarm_test.go @@ -32,6 +32,7 @@ func (s *DockerSwarmSuite) TestAPISwarmInit(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(info.ControlAvailable, checker.True) c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) + c.Assert(info.Cluster.RootRotationInProgress, checker.False) d2 := s.AddDaemon(c, true, false) info, err = d2.SwarmInfo()