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 411c8c6b30910b74829479da2d94800f6b921b98..45940498db84b585c157f1a88a7d51dfdd7e5dcc 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 dc76a146bbf06d7e5a7497f7741177f672c017a9..54af82b31b822ebb95fd894ef88dd54f47c243cd 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 379e17a779ac85f4bc1ce5c55612f3af701ef31c..28c6851e9c261aeb491164cc50120c28e07d9f38 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 9fc5c30961d47a5e4a4d7cc77d15bea851832172..bdb3042337365034a80e2f5a05467140fbb6a8e2 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 fe6cdfee9e8b3ce54f676c2b8eaaa0757ea79a55..f075783e88df6dee28254f656e495648341f69ad 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 09121fc8ffa714ec32c0edcce599b1b493373fdd..64fc7f72d9e9394eed789b7784a709defb736109 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 e501c586581282e1b32dfae1e12bc3104f24ed46..df3c908b40a618a89fbcefdbe9a306395edfc4b3 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 e6707bc7810dbb4a44a87413a849770ad88256ee..028785a01c3cd50e8a30ac4ffb50906d9d583d24 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()