mirror of https://github.com/docker/docs.git
TLS support.
TLS authentication support between the CLI and Swarm but also between Swarm and the Docker nodes. Closes #148. Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
c6815ee1fe
commit
39fe73a231
13
README.md
13
README.md
|
@ -51,6 +51,19 @@ $ swarm list --token=6856663cdefdec325839a4b7e1de38e8
|
||||||
http://<node_ip:2375>
|
http://<node_ip:2375>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TLS
|
||||||
|
|
||||||
|
Swarm supports TLS authentication between the CLI and Swarm but also between Swarm and the Docker nodes.
|
||||||
|
|
||||||
|
In order to enable TLS, the same command line options as Docker can be specified:
|
||||||
|
|
||||||
|
`swarm manage --tlsverify --tlscacert=<CACERT> --tlscert=<CERT> --tlskey=<KEY> [...]`
|
||||||
|
|
||||||
|
Please refer to the [Docker documentation](https://docs.docker.com/articles/https/) for more information on how
|
||||||
|
to set up TLS authentication on Docker and generating the certificates.
|
||||||
|
|
||||||
|
Note that Swarm certificates must be generated with`extendedKeyUsage = clientAuth,serverAuth`.
|
||||||
|
|
||||||
## Participating
|
## Participating
|
||||||
|
|
||||||
We welcome pull requests and patches; come say hi on IRC, #docker-swarm on freenode.
|
We welcome pull requests and patches; come say hi on IRC, #docker-swarm on freenode.
|
||||||
|
|
16
api/api.go
16
api/api.go
|
@ -2,9 +2,11 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -362,7 +364,7 @@ func createRouter(c *context, enableCors bool) (*mux.Router, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenAndServe(c *cluster.Cluster, s *scheduler.Scheduler, addr, version string, enableCors bool) error {
|
func ListenAndServe(c *cluster.Cluster, s *scheduler.Scheduler, addr, version string, enableCors bool, tlsConfig *tls.Config) error {
|
||||||
context := &context{
|
context := &context{
|
||||||
cluster: c,
|
cluster: c,
|
||||||
scheduler: s,
|
scheduler: s,
|
||||||
|
@ -374,9 +376,19 @@ func ListenAndServe(c *cluster.Cluster, s *scheduler.Scheduler, addr, version st
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: r,
|
Handler: r,
|
||||||
}
|
}
|
||||||
return server.ListenAndServe()
|
|
||||||
|
l, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if tlsConfig != nil {
|
||||||
|
tlsConfig.NextProtos = []string{"http/1.1"}
|
||||||
|
l = tls.NewListener(l, tlsConfig)
|
||||||
|
}
|
||||||
|
return server.Serve(l)
|
||||||
}
|
}
|
||||||
|
|
25
main.go
25
main.go
|
@ -54,6 +54,26 @@ func main() {
|
||||||
Name: "api-enable-cors, cors",
|
Name: "api-enable-cors, cors",
|
||||||
Usage: "enable CORS headers in the remote API",
|
Usage: "enable CORS headers in the remote API",
|
||||||
}
|
}
|
||||||
|
flTls := cli.BoolFlag{
|
||||||
|
Name: "tls",
|
||||||
|
Usage: "Use TLS; implied by --tlsverify=true",
|
||||||
|
}
|
||||||
|
flTlsCaCert := cli.StringFlag{
|
||||||
|
Name: "tlscacert",
|
||||||
|
Usage: "Trust only remotes providing a certificate signed by the CA given here",
|
||||||
|
}
|
||||||
|
flTlsCert := cli.StringFlag{
|
||||||
|
Name: "tlscert",
|
||||||
|
Usage: "Path to TLS certificate file",
|
||||||
|
}
|
||||||
|
flTlsKey := cli.StringFlag{
|
||||||
|
Name: "tlskey",
|
||||||
|
Usage: "Path to TLS key file",
|
||||||
|
}
|
||||||
|
flTlsVerify := cli.BoolFlag{
|
||||||
|
Name: "tlsverify",
|
||||||
|
Usage: "Use TLS and verify the remote",
|
||||||
|
}
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
|
@ -91,7 +111,10 @@ func main() {
|
||||||
Name: "manage",
|
Name: "manage",
|
||||||
ShortName: "m",
|
ShortName: "m",
|
||||||
Usage: "manage a docker cluster",
|
Usage: "manage a docker cluster",
|
||||||
Flags: []cli.Flag{flToken, flAddr, flHeartBeat, flEnableCors},
|
Flags: []cli.Flag{
|
||||||
|
flToken, flAddr, flHeartBeat,
|
||||||
|
flTls, flTlsCaCert, flTlsCert, flTlsKey, flTlsVerify,
|
||||||
|
flEnableCors},
|
||||||
Action: manage,
|
Action: manage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
53
manage.go
53
manage.go
|
@ -1,6 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -22,7 +26,52 @@ func (h *logHandler) Handle(e *cluster.Event) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the TLS certificates/keys and, if verify is true, the CA.
|
||||||
|
func loadTlsConfig(ca, cert, key string, verify bool) (*tls.Config, error) {
|
||||||
|
c, err := tls.LoadX509KeyPair(cert, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
|
||||||
|
cert, key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{c},
|
||||||
|
MinVersion: tls.VersionTLS10,
|
||||||
|
}
|
||||||
|
|
||||||
|
if verify {
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
file, err := ioutil.ReadFile(ca)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
|
||||||
|
}
|
||||||
|
certPool.AppendCertsFromPEM(file)
|
||||||
|
config.RootCAs = certPool
|
||||||
|
} else {
|
||||||
|
// If --tlsverify is not supplied, disable CA validation.
|
||||||
|
config.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
func manage(c *cli.Context) {
|
func manage(c *cli.Context) {
|
||||||
|
var (
|
||||||
|
tlsConfig *tls.Config = nil
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// If either --tls or --tlsverify are specified, load the certificates.
|
||||||
|
if c.Bool("tls") || c.Bool("tlsverify") {
|
||||||
|
tlsConfig, err = loadTlsConfig(
|
||||||
|
c.String("tlscacert"),
|
||||||
|
c.String("tlscert"),
|
||||||
|
c.String("tlskey"),
|
||||||
|
c.Bool("tlsverify"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
refresh := func(c *cluster.Cluster, nodes []string) {
|
refresh := func(c *cluster.Cluster, nodes []string) {
|
||||||
for _, addr := range nodes {
|
for _, addr := range nodes {
|
||||||
|
@ -32,7 +81,7 @@ func manage(c *cli.Context) {
|
||||||
}
|
}
|
||||||
if c.Node(addr) == nil {
|
if c.Node(addr) == nil {
|
||||||
n := cluster.NewNode(addr)
|
n := cluster.NewNode(addr)
|
||||||
if err := n.Connect(nil); err != nil {
|
if err := n.Connect(tlsConfig); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -82,5 +131,5 @@ func manage(c *cli.Context) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Fatal(api.ListenAndServe(cluster, s, c.String("addr"), c.App.Version, c.Bool("cors")))
|
log.Fatal(api.ListenAndServe(cluster, s, c.String("addr"), c.App.Version, c.Bool("cors"), tlsConfig))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue