Skip to content

[controlapi] Allow starting and aborting root CA rotations during swarm update API#2052

Merged
diogomonica merged 3 commits into
moby:masterfrom
cyli:root-rotation-cluster-update-api
Mar 30, 2017
Merged

[controlapi] Allow starting and aborting root CA rotations during swarm update API#2052
diogomonica merged 3 commits into
moby:masterfrom
cyli:root-rotation-cluster-update-api

Conversation

@cyli
Copy link
Copy Markdown
Contributor

@cyli cyli commented Mar 23, 2017

This adds the ability to specify, using the ClusterSpec, whether the swarm root CA should be rotated, and if so, what the new cert and key should be.

If no desired cert or key are provided, then a pair will be generated so long as the ForceRotate counter is bumped up.

If a desired cert is provided, the ForceRotate counter needs not be bumped, but it would not break anything if it were.

This is stacked on top of #2050 and #2051. Update: These have been merged.

Remaining TODOs:

  • I think it may be possible for the current security config, if a root rotation is in progress, to sign using a different cert and key than the current root. Need to confirm. Update: Yes, my test was not actually updating the test cases for root rotation - this has been fixed.
  • There might also be a chance for a race condition when updating the cluster - the cluster may have been updated but the security config containing the RootCA may not have been updated yet due to a delay in notifications. So the new join tokens or cross-signed certs may be generating using an outdated RootCA. Update: Fixed in [ca] Make the control API server take a security config instead of RootCA #2051
  • Validate on ClusterUpdate that at least one of the remote CAs on the list is up.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 23, 2017

Codecov Report

Merging #2052 into master will increase coverage by 0.3%.
The diff coverage is 89.4%.

@@            Coverage Diff            @@
##           master    #2052     +/-   ##
=========================================
+ Coverage   54.21%   54.51%   +0.3%     
=========================================
  Files         111      112      +1     
  Lines       19354    19497    +143     
=========================================
+ Hits        10492    10629    +137     
+ Misses       7597     7590      -7     
- Partials     1265     1278     +13

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 74a3d19...1b1c030. Read the comment docs.

@cyli cyli mentioned this pull request Mar 23, 2017
10 tasks
Comment thread api/types.proto Outdated
// ForceRotate is a counter that triggers an root CA rotation even if no relevant
// parameters have been in the spec. This will force the manager to generate a new
// certificate and key, if none have been provided.
uint64 force_rotate = 5;}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline here

Comment thread api/types.proto Outdated

// ForceRotationVersion matches the Cluster Spec's CAConfig's ForceRotation counter.
// It indicates when the current CA cert and key were generated (or updated).
uint64 force_rotation_version = 6;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just RotationVersion?

Maybe LastForcedRotation is better, actually, because that is less likely to be confused with the Version field in Meta.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we probably only need the top level one.

Comment thread api/types.proto Outdated
bytes cross_signed_ca_cert = 3 [(gogoproto.customname) = "CrossSignedCACert"];
// ForceRotationVersion matches the Cluster Spec's CAConfig's ForceRotation counter.
// It indicates when the current to-be-rotated CA cert and key were generated (or updated).
uint64 force_rotation_version = 6;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this appear in both RootRotation and one level above it? Is that intentional?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, read the comments... But I'm still surprised we would need to keep track of both. Will read the code next.

@aaronlehmann
Copy link
Copy Markdown
Collaborator

ping @aluzzardi for API review

I think this approach makes sense. It lets a client request a rotation in a declarative way, either by providing a new key/cert or asking the manager to generate one. We also discussed passing flags to controlapi's UpdateCluster, but I think doing it in a declarative way is preferable.

@cyli cyli force-pushed the root-rotation-cluster-update-api branch 3 times, most recently from 1e44ff4 to 4d986d2 Compare March 24, 2017 20:07
Copy link
Copy Markdown
Contributor

@diogomonica diogomonica left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general LGTM, but I would love to have a deeper look at validateAndUpdateCA with a high-level diagram that describes the four big blocks of that function.

Comment thread api/types.proto Outdated
repeated ExternalCA external_cas = 2 [(gogoproto.customname) = "ExternalCAs"];

// SigningCACert is the desired CA certificate to be used as the root and
// signing CA for the swarm. If not provided,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If not provided,"

Comment thread api/types.proto Outdated
// to sign certificates for the swarm
bytes signing_ca_key = 4 [(gogoproto.customname) = "SigningCAKey"];

// ForceRotate is a counter that triggers an root CA rotation even if no relevant
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*a root CA rotation

Comment thread api/types.proto

// LastForcedRotation matches the Cluster Spec's CAConfig's ForceRotation counter.
// It indicates when the current CA cert and key were generated (or updated).
uint64 last_forced_rotation = 6;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't all rotations forced? why not just last_rotation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't particularly feel strongly - the name is to make it clearer that it corresponds with the ForceRotation value in the config.

Comment thread manager/controlapi/ca_rotation.go Outdated
"github.com/docker/swarmkit/log"
)

var minRootExpiration = helpers.OneYear
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first instinct was: this is reasonable. But on further consideration, and given that fact that:

  • This is the root CA
  • We have the ability of rotating at any time if compromised

This makes me think that maybe we should help our users not shoot themselves in the foot and increase the minimum a bit more (3 years)? Just a thought though, I'm 100% ok with 1 year.

/cc @sul3n3t

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't feel strongly - happy to change it to 3 years too. I just picked some value. :)

Comment thread manager/controlapi/ca_rotation.go Outdated
// Creates and updates the api.RootRotation object and the cross-signed intermediate.
// This function assumes that a root rotation is not already in progress, and that the root
// cert and key to be rotated in or the external CAs in the cluster have already been validated
func updateRootRotationObject(ctx context.Context, securityConfig *ca.SecurityConfig, cluster *api.Cluster, newRootCA ca.RootCA, version uint64) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a method on cluster?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike that this is not a method on cluster but has side-effects on cluster.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The api module is just data, and I don't know if we should be adding this to that module, particular could create import issues since then nothing from ca would be able to import anything from api, since api would then depend in ca.

Comment thread manager/controlapi/ca_rotation.go Outdated
// whether it's a locally signing CA or an external CA. The key can be replaced with the desired key (which will
// either be the correct key or nil) without having to do any kind of root rotation, so we can abort any outstanding
// root rotations and just finish the validation.
if bytes.Equal(cluster.RootCA.CACert, newConfig.SigningCACert) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just change cluster at will?

Comment thread manager/controlapi/ca_rotation.go Outdated
return grpc.Errorf(codes.InvalidArgument, "CA certificate expires too soon")
}

// If the desired cert has the same public key and subject as current root, then the private signer is the same
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the exact same conditional as the one on line 147?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the cert has changed, it just has the same subject and public key. One such example is if the cert is renewed. In this case, leaf certs issued by the previous CA cert will still correctly validate, but the cert digest has changed, and hence join tokens need to be re-generated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So: Root CA Cert is expiring, and we self-sign a longer lived one?

Comment thread manager/controlapi/ca_rotation.go Outdated
return grpc.Errorf(codes.InvalidArgument, "the desired CA certificate cannot contain multiple certificates")
}

// If the desired cert is the same as the current root CA cert then the private signer is the same
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the situations for each of these? Why doesn't this one update the tokens?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's byte-for-byte the exact same cert, so the tokens do not need to be re-generated, since the hash is the same.

@@ -0,0 +1,623 @@
package controlapi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of tests 👍

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this table-driven.

Comment thread manager/controlapi/ca_rotation_test.go Outdated
{
rootCA: initialLocalRootCA,
oldCAConfig: api.CAConfig{ForceRotate: 1},
expectErrorString: "ForceRotate value must be nondecreasing",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about testing the same?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore, the same is a valid case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the same value test case to the valid cases.

@diogomonica
Copy link
Copy Markdown
Contributor

@aaronlehmann @cyli I commented it inline, but why not have a simpler logic of bumping the rotation version to kick a rotation, and if valid Cert/Key are provided we use them, if not, we generate new ones. Not sure I see the advantage of having two mechanisms.

@cyli cyli force-pushed the root-rotation-cluster-update-api branch 2 times, most recently from 6e8ae5e to 0bf8867 Compare March 27, 2017 18:42
@aaronlehmann
Copy link
Copy Markdown
Collaborator

@cyli: Is this still WIP?

@cyli cyli force-pushed the root-rotation-cluster-update-api branch from 0bf8867 to 60d5cdd Compare March 27, 2017 21:58
@cyli
Copy link
Copy Markdown
Contributor Author

cyli commented Mar 27, 2017

@aaronlehmann It wasn't before - just pushed the latest :) So it is now, thanks for checking!

@cyli cyli changed the title WIP: [controlapi] Allow starting and aborting root CA rotations during swarm update API [controlapi] Allow starting and aborting root CA rotations during swarm update API Mar 27, 2017
@cyli cyli force-pushed the root-rotation-cluster-update-api branch from 60d5cdd to 01833ab Compare March 27, 2017 22:04
Comment thread manager/controlapi/ca_rotation.go Outdated

// TODO(cyli): in go 1.8 this will be easier as we can use *url.URL.Port() and *url.URL.Hostname()
func getConnectionStringFromHost(hostport string) string {
colon := strings.IndexByte(hostport, ':')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use net.SplitHostPort

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! Thanks! I didn't know about that one - go 1.8's code doesn't use it either. :D

Comment thread manager/controlapi/ca_rotation.go Outdated
func getConnectionStringFromHost(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return fmt.Sprintf("%s:443", hostport) // no port, default to 443
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use net.JoinHostPort

Comment thread manager/controlapi/ca_rotation.go Outdated
}
conn, err := tls.DialWithDialer(dialer, "tcp", getConnectionStringFromHost(parsed.Host), tlsOpts)
if conn != nil {
defer conn.Close()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be necessary to defer this.

Comment thread manager/controlapi/ca_rotation.go Outdated
case api.CAConfig:
return len(b.SigningCACert) > 0 && len(b.SigningCAKey) == 0
default:
return false
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

panic here?

Comment thread manager/controlapi/ca_rotation.go Outdated
if err == nil {
return true
}
log.G(ctx).WithError(err).Debugf("external CA # %d is unreachable or invalid", i+1)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a potentially useful log message. Any downsides to making it a warning?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not particularly - this may be noisy if we have to re-validate every time, but perhaps if I reflect.DeepEqual(<oldCAConfig spec>, <newCAConfig spec> in UpdateCluster it would be fine? That'd assume nothing else goes and changes api.RootCA in the store (which is probably a fair assumption).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in controlapi, so at most you would get a warning per unreachable CA every time the cluster object is updated through the API, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Do we want to warn every time a cluster update is attempted, or just when the the user attempts to change the CAConfig?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any harm in warning every time the cluster is updated, as long as the unreachable CAs continue to be referenced in the spec. We're checking the CAs anyway, so if we discover that one is unreachable we might as well expose that information.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, although this is on attempted spec update as well, not just when the spec update succeeds. I'm ok with that though, just wanted to make sure it's clear.

// Do not copy secret key
// Do not copy secret keys
redactedSpec := cluster.Spec.Copy()
redactedSpec.CAConfig.SigningCAKey = nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have a test for this being redacted?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, it's there, never mind!

Comment thread manager/controlapi/node_test.go Outdated
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
logrus.SetOutput(ioutil.Discard)
}
// func init() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have commented out code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry, I needed this while debugging. Forgot to uncomment it, thanks!

Comment thread manager/controlapi/ca_rotation.go Outdated
JoinTokens: api.JoinTokens{
Worker: ca.GenerateJoinToken(newCARootCA),
Manager: ca.GenerateJoinToken(newCARootCA),
},
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the join tokens being regenerated because the digest of the certificate might be changing even if the subject and public key are the same?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the digest is guaranteed to change in this case, because otherwise the bytes.Equal above would return true, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's correct

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some discussion with @diogomonica, it may not be worth it to check this particular case. We're optimizing for not having to generate a new cross-signed certificate, but it might simplify the code if we don't try to do this optimization and just do a full root rotation object instead. How would you feel about removing this check?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Comment thread manager/controlapi/ca_rotation.go Outdated
"github.com/docker/swarmkit/log"
)

var minRootExpiration = 3 * helpers.OneYear
Copy link
Copy Markdown
Contributor

@diogomonica diogomonica Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I regret my comment. Maybe 1 year makes this more flexible?

@sul3n3t

"reflect"
"testing"
"time"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra blank line

Comment thread manager/controlapi/ca_rotation_test.go Outdated
"github.com/docker/swarmkit/ca"
"github.com/docker/swarmkit/ca/testutils"
"github.com/docker/swarmkit/manager/state/store"
digest "github.com/opencontainers/go-digest"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

digest shouldn't be necessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks! My auto-importer insists on adding it every time I save. :)

Comment thread manager/controlapi/ca_rotation_test.go Outdated
require.NoError(t, err, valid.description)

// ensure that the cluster was not mutated
require.True(t, reflect.DeepEqual(valid.rootCA, cluster.RootCA))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW require.Equal will do the same thing, and show a diff if there is an unexpected change.

Comment thread manager/controlapi/ca_rotation_test.go Outdated

// Because join tokens are random, we can't predict exactly what it is, so this needs to be manually checked
require.Equal(t, valid.expectJoinTokenChange, !reflect.DeepEqual(result.JoinTokens, valid.rootCA.JoinTokens),
fmtString, valid.description, result.JoinTokens, valid.rootCA.JoinTokens)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly more verbose, but I think this would be easier to read and more useful as:

if valid.expectJoinTokenChange {
        require.NotEqual(t, result.JoinTokens, valid.rootCA.JoinTokens, fmtString, valid.description, result.JoinTokens, valid.rootCA.JoinTokens)
} else {
        require.Equal(t, result.JoinTokens, valid.rootCA.JoinTokens, fmtString, valid.description, result.JoinTokens, valid.rootCA.JoinTokens)
}

Comment thread manager/controlapi/ca_rotation_test.go Outdated
if valid.expectedGeneratedCross || valid.expectGeneratedRootRotation { // both generate cross signed certs
require.NotNil(t, result.RootRotation, valid.description)
require.True(t, len(result.RootRotation.CrossSignedCACert) > 0,
"%s: if there is a root rotation object there must be a cross-signed cert", valid.description)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require.Len :)

Comment thread manager/controlapi/ca_rotation_test.go Outdated
// just assert that the value has changed.
if valid.expectGeneratedRootRotation {
require.NotNil(t, result.RootRotation, valid.description)
require.False(t, reflect.DeepEqual(valid.rootCA.RootRotation, result.RootRotation), valid.description)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require.NotEqual


require.True(t, reflect.DeepEqual(result, &valid.expectRootCA),
"should be equal: "+fmtString, valid.description, result, &valid.expectRootCA)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require.Equal

Comment thread manager/controlapi/ca_rotation_test.go Outdated
testcase.rootCA = getRootCAWithRotation(testcase.rootCA, rotationCert, rotationKey, crossSigned)
testcases[2].description = "different cert than root rotation contents, even if internal->external, results in replacing root rotation"
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the purpose of this loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair - I'm just testing that if the desired cert is different than the current root rotation cert, then the root rotation is replaced. That can just be 1 extra case, instead of 3 extra cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, unless you mean because I'm only checking the last one (testCases[2:])? That was something accidentally left over from when I was debugging. Regardless, I probably don't need the extra cases anyway.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confused about why you're looping and then doing something different for each value of the loop counter. I think I'm missing something.

Copy link
Copy Markdown
Contributor Author

@cyli cyli Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to preserve the original testcases' configurations and expected root CA values (e.g. I was too lazy to type it again). It was to test that whatever the initial RootRotation value was, because the cert is different, it will just be replaced completely by a new root rotation with differentInitialCert, and that any external CAs required by the old root rotation will no longer be required.

I think this can probably be replaced by a single case.

Comment thread manager/controlapi/ca_rotation.go Outdated
return nil, grpc.Errorf(codes.InvalidArgument, "the desired CA certificate cannot contain multiple certificates")
}

// because newRootCA was parsed without error, this should not fail either
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this saying that NewRootCA parses SigningCACert, so we expect parsing it again will succeed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - that's much clearer, thanks. I'll update the comment to say that.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference is to check the error anyway, as a future-proofing step.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, can do that as well.

@cyli cyli force-pushed the root-rotation-cluster-update-api branch 4 times, most recently from 8bd77a9 to acae0c6 Compare March 28, 2017 00:29
Comment thread manager/controlapi/ca_rotation.go Outdated
if err != nil {
return err
}
if port == "" {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is reachable - it looks like SplitHostPort returns an error in this case. I expected that if SplitHostPort returns an error, we would assume the address does not have a port.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh hm... I didn't realize a port was required. It also looks like it returns an error if there are too many colons, though, and that does not seem to be something url.Parse will return an error on: https://play.golang.org/p/lAmzyII0-z.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poking a bit more I guess go1.8's URL.Hostname() interprets "http://x:1:2" as a single host with no port, at least SplitHostPort will assume the same thing.

And I guess the dialer will fail if it's a weird hostname.

port = "443" //https
}

conn, err := tls.DialWithDialer(dialer, "tcp", net.JoinHostPort(host, port), tlsOpts)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment as to why we use TCP vs HTTP?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it to the comment for the function (at the top of this function).

@diogomonica
Copy link
Copy Markdown
Contributor

This LGTM

/cc @aluzzardi

@cyli cyli force-pushed the root-rotation-cluster-update-api branch from acae0c6 to 03c8d56 Compare March 28, 2017 22:41
if err != nil {
// It either has no port or is otherwise invalid (e.g. too many colons). If it's otherwise invalid the dialer
// will error later, so just assume it's no port and set the port to the default HTTPS port.
port = "443"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also have host = parsed.Host here. I don't know if SplitHostPort returns the argument as host if it also returns an error (and even if it does, it seems better not to rely on the other values when an error is returned).

@cyli cyli force-pushed the root-rotation-cluster-update-api branch from 03c8d56 to 621a36d Compare March 28, 2017 23:04
@aaronlehmann
Copy link
Copy Markdown
Collaborator

LGTM

@aaronlehmann
Copy link
Copy Markdown
Collaborator

Feel free to reorganize the commits.

@cyli cyli mentioned this pull request Mar 29, 2017
@cyli cyli force-pushed the root-rotation-cluster-update-api branch from 621a36d to 8f3f0a7 Compare March 29, 2017 00:47
cyli added 3 commits March 29, 2017 16:25
The cluster RootCA object will reflect the current status of the actual
cluster RootCA, and the goal is to converge towards the desired CA cert
and key.

Signed-off-by: cyli <ying.li@docker.com>
…update, which ensures that

1. A minimum of required external CAs are provided and reachable and valid.
2. If a desired signing certificate and/or signing key are provided, that they are valid.

If the CAConfig spec differs from the current root CA + current root rotation configuration,
this function will also start, update, or abort a root rotation.  It returns a new api.RootCA
object (which may be completely unchanged from the current one) to replace the existing
api.RootCA object in the cluster.

Signed-off-by: cyli <ying.li@docker.com>
…Cluster function.

Also make sure that when listing and getting clusters, the CAConfig's signing key
and the root rotation object's signing key are redacted, if present.

Signed-off-by: cyli <ying.li@docker.com>
@cyli cyli force-pushed the root-rotation-cluster-update-api branch from 8f3f0a7 to 1b1c030 Compare March 29, 2017 23:28
@aluzzardi
Copy link
Copy Markdown
Member

Just had a quick glance, overall LGTM

@diogomonica diogomonica merged commit 63faf85 into moby:master Mar 30, 2017
@cyli cyli deleted the root-rotation-cluster-update-api branch March 30, 2017 00:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants