diff --git a/api/client/swarm/init.go b/api/client/swarm/init.go index 8ea11de5b6..83ffb47847 100644 --- a/api/client/swarm/init.go +++ b/api/client/swarm/init.go @@ -12,6 +12,13 @@ import ( "github.com/spf13/pflag" ) +const ( + generatedSecretEntropyBytes = 16 + generatedSecretBase = 36 + // floor(log(2^128-1, 36)) + 1 + maxGeneratedSecretLength = 25 +) + type initOptions struct { swarmOptions listenAddr NodeAddrOption @@ -46,6 +53,12 @@ func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions client := dockerCli.Client() ctx := context.Background() + // If no secret was specified, we create a random one + if !flags.Changed("secret") { + opts.secret = generateRandomSecret() + fmt.Fprintf(dockerCli.Out(), "No --secret provided. Generated random secret:\n\t%s\n\n", opts.secret) + } + req := swarm.InitRequest{ ListenAddr: opts.listenAddr.String(), ForceNewCluster: opts.forceNewCluster, @@ -57,7 +70,26 @@ func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions return err } - fmt.Fprintf(dockerCli.Out(), "Swarm initialized: current node (%s) is now a manager.\n", nodeID) + fmt.Fprintf(dockerCli.Out(), "Swarm initialized: current node (%s) is now a manager.\n\n", nodeID) + + // Fetch CAHash and Address from the API + info, err := client.Info(ctx) + if err != nil { + return err + } + + node, _, err := client.NodeInspectWithRaw(ctx, nodeID) + if err != nil { + return err + } + + if node.ManagerStatus != nil && info.Swarm.CACertHash != "" { + var secretArgs string + if opts.secret != "" { + secretArgs = "--secret " + opts.secret + } + fmt.Fprintf(dockerCli.Out(), "To add a worker to this swarm, run the following command:\n\tdocker swarm join %s \\\n\t--ca-hash %s \\\n\t%s\n", secretArgs, info.Swarm.CACertHash, node.ManagerStatus.Addr) + } return nil } diff --git a/api/client/swarm/opts.go b/api/client/swarm/opts.go index 72d6a7a827..6b2c688102 100644 --- a/api/client/swarm/opts.go +++ b/api/client/swarm/opts.go @@ -237,7 +237,7 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) { func addSwarmFlags(flags *pflag.FlagSet, opts *swarmOptions) { flags.Var(&opts.autoAccept, flagAutoAccept, "Auto acceptance policy (worker, manager or none)") - flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to accept nodes into cluster") + flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to join a cluster") flags.Int64Var(&opts.taskHistoryLimit, flagTaskHistoryLimit, 10, "Task history retention limit") flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period") flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates") diff --git a/api/client/swarm/secret.go b/api/client/swarm/secret.go new file mode 100644 index 0000000000..e98f1d6626 --- /dev/null +++ b/api/client/swarm/secret.go @@ -0,0 +1,19 @@ +package swarm + +import ( + cryptorand "crypto/rand" + "fmt" + "math/big" +) + +func generateRandomSecret() string { + var secretBytes [generatedSecretEntropyBytes]byte + + if _, err := cryptorand.Read(secretBytes[:]); err != nil { + panic(fmt.Errorf("failed to read random bytes: %v", err)) + } + + var nn big.Int + nn.SetBytes(secretBytes[:]) + return fmt.Sprintf("%0[1]*s", maxGeneratedSecretLength, nn.Text(generatedSecretBase)) +} diff --git a/docs/reference/commandline/swarm_init.md b/docs/reference/commandline/swarm_init.md index c2e32f37f6..5dfc07d6ad 100644 --- a/docs/reference/commandline/swarm_init.md +++ b/docs/reference/commandline/swarm_init.md @@ -30,12 +30,24 @@ in the newly created one node Swarm cluster. ```bash $ docker swarm init --listen-addr 192.168.99.121:2377 +No --secret provided. Generated random secret: + 4ao565v9jsuogtq5t8s379ulb + Swarm initialized: current node (1ujecd0j9n3ro9i6628smdmth) is now a manager. + +To add a worker to this swarm, run the following command: + docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb \ + --ca-hash sha256:07ce22bd1a7619f2adc0d63bd110479a170e7c4e69df05b67a1aa2705c88ef09 \ + 192.168.99.121:2377 $ docker node ls ID HOSTNAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS LEADER 1ujecd0j9n3ro9i6628smdmth * manager1 Accepted Ready Active Reachable Yes ``` +If a secret for joining new nodes is not provided with `--secret`, `docker swarm init` will +generate a random one and print it to the terminal (as seen in the example above). To initialize +a swarm with no secret, use `--secret ""`. + ### `--auto-accept value` This flag controls node acceptance into the cluster. By default, `worker` nodes are @@ -48,7 +60,6 @@ For example, the following initializes a cluster with auto-acceptance of workers ```bash $ docker swarm init --listen-addr 192.168.99.121:2377 --auto-accept worker -Swarm initialized: current node (1m8cdsylxbf3lk8qriqt07hx1) is now a manager. ``` ### `--external-ca value` diff --git a/docs/reference/commandline/swarm_join.md b/docs/reference/commandline/swarm_join.md index ac02674686..c1955c513c 100644 --- a/docs/reference/commandline/swarm_join.md +++ b/docs/reference/commandline/swarm_join.md @@ -28,7 +28,7 @@ targeted by this command becomes a `manager`. If it is not specified, it becomes ### Join a node to swarm as a manager ```bash -$ docker swarm join --manager --listen-addr 192.168.99.122:2377 192.168.99.121:2377 +$ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb --manager --listen-addr 192.168.99.122:2377 192.168.99.121:2377 This node joined a Swarm as a manager. $ docker node ls ID HOSTNAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS LEADER @@ -39,7 +39,7 @@ dvfxp4zseq4s0rih1selh0d20 manager1 Accepted Ready Active Reachab ### Join a node to swarm as a worker ```bash -$ docker swarm join --listen-addr 192.168.99.123:2377 192.168.99.121:2377 +$ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb --listen-addr 192.168.99.123:2377 192.168.99.121:2377 This node joined a Swarm as a worker. $ docker node ls ID HOSTNAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS LEADER diff --git a/docs/swarm/swarm-tutorial/add-nodes.md b/docs/swarm/swarm-tutorial/add-nodes.md index ca31493506..dee1d9db09 100644 --- a/docs/swarm/swarm-tutorial/add-nodes.md +++ b/docs/swarm/swarm-tutorial/add-nodes.md @@ -23,16 +23,17 @@ This tutorial uses the name `worker1`. the existing swarm: ``` - docker swarm join : + docker swarm join --secret : ``` - Replace `` with the address of the manager node and `` - with the port where the manager listens. + Replace `` with the secret that was printed by `docker swarm init` in the + previous step. Replace `` with the address of the manager node + and `` with the port where the manager listens. In the tutorial, the following command joins `worker1` to the swarm on `manager1`: ``` - $ docker swarm join 192.168.99.100:2377 + $ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb 192.168.99.100:2377 This node joined a Swarm as a worker. ``` @@ -40,11 +41,12 @@ the existing swarm: 3. Open a terminal and ssh into the machine where you want to run a second worker node. This tutorial uses the name `worker2`. -4. Run `docker swarm join :` to create a worker node joined to +4. Run `docker swarm join --secret :` to create a worker node joined to the existing Swarm. - Replace `` with the address of the manager node and `` - with the port where the manager listens. + Replace `` with the secret that was printed by `docker swarm init` in the + previous step. Replace `` with the address of the manager node + and `` with the port where the manager listens. 5. Open a terminal and ssh into the machine where the manager node runs and run the `docker node ls` command to see the worker nodes: diff --git a/docs/swarm/swarm-tutorial/create-swarm.md b/docs/swarm/swarm-tutorial/create-swarm.md index 92566d3648..d48aba43d5 100644 --- a/docs/swarm/swarm-tutorial/create-swarm.md +++ b/docs/swarm/swarm-tutorial/create-swarm.md @@ -30,8 +30,15 @@ node. For example, the tutorial uses a machine named `manager1`. ``` $ docker swarm init --listen-addr 192.168.99.100:2377 + No --secret provided. Generated random secret: + 4ao565v9jsuogtq5t8s379ulb Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager. + + To add a worker to this swarm, run the following command: + docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb \ + --ca-hash sha256:07ce22bd1a7619f2adc0d63bd110479a170e7c4e69df05b67a1aa2705c88ef09 \ + 192.168.99.100:2377 ``` The `--listen-addr` flag configures the manager node to listen on port diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go index 83b09d43fc..dffa2f4834 100644 --- a/integration-cli/docker_cli_swarm_test.go +++ b/integration-cli/docker_cli_swarm_test.go @@ -106,7 +106,7 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) { c.Assert(d.Leave(true), checker.IsNil) - out, err = d.Cmd("swarm", "init", "--auto-accept", "none") + out, err = d.Cmd("swarm", "init", "--auto-accept", "none", "--secret", "") c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) spec = getSpec()