Merge pull request #813 from paulbellamy/proxy-ssl
First pass at Proxy SSL Support
This commit is contained in:
commit
3586ddcd63
8 files changed
+266
-20
No files matched your search
@@ -47,3 +47,7 @@ weaveexec/weavewait
|
||||
weaveexec/sigproxy
|
||||
weaveexec/docker*.tgz
|
||||
Vagrantfile.local
|
||||
test/tls/*.pem
|
||||
test/tls/*.srl
|
||||
test/tls/*.csr
|
||||
test/tls/*.cnf
|
||||
+31
-8
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,6 +14,12 @@ import (
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCaFile = "ca.pem"
|
||||
defaultKeyFile = "key.pem"
|
||||
defaultCertFile = "cert.pem"
|
||||
)
|
||||
|
||||
var (
|
||||
containerCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/create$")
|
||||
containerStartRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/(re)?start$")
|
||||
@@ -24,13 +31,14 @@ type Proxy struct {
|
||||
version string
|
||||
client *docker.Client
|
||||
dockerAddr string
|
||||
listenAddr string
|
||||
withDNS bool
|
||||
dockerBridgeIP string
|
||||
listenAddr string
|
||||
tlsConfig *TLSConfig
|
||||
withDNS bool
|
||||
withIPAM bool
|
||||
}
|
||||
|
||||
func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (*Proxy, error) {
|
||||
func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool, tlsConfig *TLSConfig) (*Proxy, error) {
|
||||
u, err := url.Parse(dockerAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -44,6 +52,10 @@ func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (*
|
||||
}
|
||||
}
|
||||
|
||||
if err := tlsConfig.loadCerts(); err != nil {
|
||||
Error.Fatalf("Could not configure tls for proxy: %s", err)
|
||||
}
|
||||
|
||||
client, err := docker.NewClient(dockerAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -65,8 +77,9 @@ func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (*
|
||||
client: client,
|
||||
dockerAddr: dockerAddr,
|
||||
listenAddr: listenAddr,
|
||||
withDNS: withDNS,
|
||||
dockerBridgeIP: string(dockerBridgeIP),
|
||||
tlsConfig: tlsConfig,
|
||||
withDNS: withDNS,
|
||||
withIPAM: withIPAM,
|
||||
}, nil
|
||||
}
|
||||
@@ -112,8 +125,18 @@ func (proxy *Proxy) Status() string {
|
||||
}
|
||||
|
||||
func (proxy *Proxy) ListenAndServe() error {
|
||||
return (&http.Server{
|
||||
Addr: proxy.listenAddr,
|
||||
Handler: proxy,
|
||||
}).ListenAndServe()
|
||||
listener, err := net.Listen("tcp", proxy.listenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if proxy.tlsConfig.enabled() {
|
||||
listener = tls.NewListener(listener, proxy.tlsConfig.Config)
|
||||
Info.Println("TLS Enabled")
|
||||
}
|
||||
|
||||
Info.Printf("Listening on %s", proxy.listenAddr)
|
||||
Info.Printf("Proxying %s", proxy.dockerAddr)
|
||||
|
||||
return (&http.Server{Handler: proxy}).Serve(listener)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
)
|
||||
|
||||
type TLSConfig struct {
|
||||
Enabled, Verify bool
|
||||
Cert, Key, CACert string
|
||||
*tls.Config
|
||||
}
|
||||
|
||||
func (c *TLSConfig) enabled() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.Enabled || c.Verify
|
||||
}
|
||||
|
||||
func (c *TLSConfig) loadCerts() error {
|
||||
if !c.enabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dockerCertPath := os.Getenv("DOCKER_CERT_PATH")
|
||||
if dockerCertPath == "" {
|
||||
dockerCertPath = filepath.Join(homedir.Get(), ".docker")
|
||||
}
|
||||
|
||||
if c.CACert == "" {
|
||||
c.CACert = filepath.Join(dockerCertPath, defaultCaFile)
|
||||
}
|
||||
if c.Cert == "" {
|
||||
c.Cert = filepath.Join(dockerCertPath, defaultCertFile)
|
||||
}
|
||||
if c.Key == "" {
|
||||
c.Key = filepath.Join(dockerCertPath, defaultKeyFile)
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"http/1.1"},
|
||||
// Avoid fallback on insecure SSL protocols
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
|
||||
if c.Verify {
|
||||
certPool := x509.NewCertPool()
|
||||
file, err := ioutil.ReadFile(c.CACert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read CA cert %s: %s", c.CACert, err)
|
||||
}
|
||||
certPool.AppendCertsFromPEM(file)
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
tlsConfig.InsecureSkipVerify = false
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
|
||||
_, errCert := os.Stat(c.Cert)
|
||||
_, errKey := os.Stat(c.Key)
|
||||
if errCert == nil && errKey == nil {
|
||||
cert, err := tls.LoadX509KeyPair(c.Cert, c.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't load X509 key pair: %q. Make sure the key is encrypted", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
c.Config = tlsConfig
|
||||
return nil
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Boot the proxy with TLS-enabled Docker support"
|
||||
|
||||
PWD=$($SSH $HOST1 pwd)
|
||||
|
||||
weave_on $HOST1 launch
|
||||
weave_on $HOST1 launch-proxy \
|
||||
--tls \
|
||||
--tlscacert $PWD/tls/$HOST1.ca.pem \
|
||||
--tlscert $PWD/tls/$HOST1.server.pem \
|
||||
--tlskey $PWD/tls/server-key.pem
|
||||
|
||||
assert_raises "proxy docker_on $HOST1 --tls --tlscacert ./tls/$HOST1.ca.pem ps"
|
||||
|
||||
end_suite
|
||||
@@ -15,3 +15,10 @@ for HOST in $HOSTS; do
|
||||
run_on $HOST chmod a+x $DOCKER_NS ./weave
|
||||
run_on $HOST sudo service docker restart
|
||||
done
|
||||
|
||||
echo Setting up Docker TLS on hosts
|
||||
(cd ./tls && make)
|
||||
chmod 0600 tls/*
|
||||
for HOST in $HOSTS; do
|
||||
rsync -az -e "$SSH" ./tls/ $HOST:~/tls/
|
||||
done
|
||||
@@ -0,0 +1,41 @@
|
||||
SERVERS = 192.168.48.11 192.168.48.12
|
||||
SERVER_CAS = $(SERVERS:=.ca.pem)
|
||||
SERVER_CSRS = $(SERVERS:=.server.csr)
|
||||
SERVER_PEMS = $(SERVERS:=.server.pem)
|
||||
CLIENT_PEMS = $(SERVERS:=.client.pem)
|
||||
|
||||
.DEFAULT: all
|
||||
.PHONY: all clean $(SERVERS)
|
||||
|
||||
SHELL=/bin/bash
|
||||
|
||||
all: ca-key.pem server-key.pem client-key.pem $(SERVER_CAS) $(SERVER_CSRS) $(SERVER_PEMS) client.csr $(CLIENT_PEMS)
|
||||
|
||||
ca-key.pem server-key.pem client-key.pem:
|
||||
openssl genrsa -out $@ 1024
|
||||
|
||||
$(SERVER_CAS): ca-key.pem
|
||||
openssl req -subj "/CN=$(@:.ca.pem=)" -new -x509 -days 3650 -key ca-key.pem -sha256 -out $@
|
||||
|
||||
$(SERVER_CSRS): server-key.pem
|
||||
openssl req -subj "/CN=$(@:.server.csr=)" -new -key server-key.pem -out $@
|
||||
|
||||
$(SERVER_PEMS): %.server.pem: %.server.csr ca-key.pem %.ca.pem
|
||||
openssl x509 -req -days 3650 -in $(@:.pem=.csr) \
|
||||
-CA $(@:.server.pem=.ca.pem) -CAkey ca-key.pem -CAcreateserial -out $@
|
||||
|
||||
client.csr: client-key.pem
|
||||
openssl req -subj '/CN=client' -new -key client-key.pem -out $@
|
||||
|
||||
$(CLIENT_PEMS): %.client.pem: client.csr ca-key.pem %.ca.pem client-extfile.cnf
|
||||
openssl x509 -req -days 3650 -in client.csr \
|
||||
-CA $(@:.client.pem=.ca.pem) -CAkey ca-key.pem -CAcreateserial \
|
||||
-extfile client-extfile.cnf \
|
||||
-out $@
|
||||
|
||||
client-extfile.cnf:
|
||||
IPS=$$(ip -4 addr ls | sed -n -e 's|\s*inet \([^/]*\).*|IP:\1|p' | tr '\n' ','); \
|
||||
echo -e "extendedKeyUsage = clientAuth\nsubjectAltName = $${IPS%,}" > client-extfile.cnf
|
||||
|
||||
clean:
|
||||
rm -f *.pem *.csr *.srl *.cnf
|
||||
@@ -749,6 +749,53 @@ get_ipam_cidr() {
|
||||
fi
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# weave proxy helpers
|
||||
######################################################################
|
||||
|
||||
|
||||
# TODO: Handle relative paths for args
|
||||
# TODO: Handle args with spaces
|
||||
proxy_args() {
|
||||
PROXY_VOLUMES=""
|
||||
PROXY_ARGS=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--tlscacert)
|
||||
shift
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/ca.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem"
|
||||
;;
|
||||
--tlscacert=*)
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/ca.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem"
|
||||
;;
|
||||
--tlscert)
|
||||
shift
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/cert.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem"
|
||||
;;
|
||||
--tlscert=*)
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/cert.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem"
|
||||
;;
|
||||
--tlskey)
|
||||
shift
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/key.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem"
|
||||
;;
|
||||
--tlskey=*)
|
||||
PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/key.pem"
|
||||
PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem"
|
||||
;;
|
||||
*)
|
||||
PROXY_ARGS="$PROXY_ARGS $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
|
||||
[ $(id -u) = 0 ] || {
|
||||
@@ -882,14 +929,16 @@ case "$COMMAND" in
|
||||
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
|
||||
# additional parameters, such as resource limits, to docker
|
||||
# when launching the weaveproxy container.
|
||||
proxy_args "$@"
|
||||
PROXY_CONTAINER=$(docker run --privileged --net=host -d \
|
||||
--name=$PROXY_CONTAINER_NAME \
|
||||
-p $PROXY_PORT:$PROXY_PORT/tcp \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v /proc:/hostproc -e PROCFS=/hostproc \
|
||||
$PROXY_VOLUMES \
|
||||
--entrypoint=/home/weave/weaveproxy \
|
||||
$WEAVEPROXY_DOCKER_ARGS \
|
||||
$EXEC_IMAGE "$@")
|
||||
$EXEC_IMAGE $PROXY_ARGS)
|
||||
|
||||
wait_for_status_ip 127.0.0.1 $PROXY_PORT
|
||||
echo $PROXY_CONTAINER
|
||||
|
||||
+37
-11
@@ -16,13 +16,29 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
var target, listen string
|
||||
var withDNS, withIPAM, debug, justVersion bool
|
||||
var (
|
||||
debug bool
|
||||
justVersion bool
|
||||
listen string
|
||||
target string
|
||||
tlsCACert string
|
||||
tlsCert string
|
||||
tlsEnabled bool
|
||||
tlsKey string
|
||||
tlsVerify bool
|
||||
withDNS bool
|
||||
withIPAM bool
|
||||
)
|
||||
|
||||
getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information")
|
||||
getopt.BoolVarLong(&justVersion, "version", 0, "print version and exit")
|
||||
getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget))
|
||||
getopt.StringVar(&listen, 'L', fmt.Sprintf("address on which to listen (default %s)", defaultListen))
|
||||
getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget))
|
||||
getopt.StringVarLong(&tlsCACert, "tlscacert", 0, "Trust certs signed only by this CA")
|
||||
getopt.StringVarLong(&tlsCert, "tlscert", 0, "Path to TLS certificate file")
|
||||
getopt.BoolVarLong(&tlsEnabled, "tls", 0, "Use TLS; implied by --tlsverify")
|
||||
getopt.StringVarLong(&tlsKey, "tlskey", 0, "Path to TLS key file")
|
||||
getopt.BoolVarLong(&tlsVerify, "tlsverify", 0, "Use TLS and verify the remote")
|
||||
getopt.BoolVarLong(&withDNS, "with-dns", 'w', "instruct created containers to use weaveDNS as their nameserver")
|
||||
getopt.BoolVarLong(&withIPAM, "with-ipam", 'i', "automatically allocate addresses for containers without a WEAVE_CIDR")
|
||||
getopt.Parse()
|
||||
@@ -32,6 +48,10 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if debug {
|
||||
InitDefaultLogging(true)
|
||||
}
|
||||
|
||||
if target == "" {
|
||||
target = defaultTarget
|
||||
}
|
||||
@@ -40,18 +60,24 @@ func main() {
|
||||
listen = defaultListen
|
||||
}
|
||||
|
||||
if debug {
|
||||
InitDefaultLogging(true)
|
||||
}
|
||||
|
||||
p, err := proxy.NewProxy(version, target, listen, withDNS, withIPAM)
|
||||
p, err := proxy.NewProxy(
|
||||
version,
|
||||
target,
|
||||
listen,
|
||||
withDNS,
|
||||
withIPAM,
|
||||
&proxy.TLSConfig{
|
||||
Enabled: tlsEnabled,
|
||||
Verify: tlsVerify,
|
||||
Cert: tlsCert,
|
||||
Key: tlsKey,
|
||||
CACert: tlsCACert,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
Error.Fatalf("Could not start proxy: %s", err)
|
||||
}
|
||||
|
||||
Info.Printf("Listening on %s", listen)
|
||||
Info.Printf("Proxying %s", target)
|
||||
|
||||
if err := p.ListenAndServe(); err != nil {
|
||||
Error.Fatalf("Could not listen on %s: %s", listen, err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user