Skip to content

Commit

Permalink
Merge branch 'c-manual-protect' into i-docker-reconnect
Browse files Browse the repository at this point in the history
  • Loading branch information
Karl Gutwin committed Jul 6, 2015
2 parents 11959f0 + c4931fa commit 6470d47
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 11 deletions.
13 changes: 13 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type Config struct {
dnsAddr string
domain Domain
dockerHost string
tlsVerify bool
tlsCaCert string
tlsCert string
tlsKey string
verbose bool
httpAddr string
ttl int
Expand All @@ -37,13 +41,22 @@ func NewConfig() *Config {
if len(dockerHost) == 0 {
dockerHost = "unix:///var/run/docker.sock"
}
tlsVerify := len(os.Getenv("DOCKER_TLS_VERIFY")) != 0
dockerCerts := os.Getenv("DOCKER_CERT_PATH")
if len(dockerCerts) == 0 {
dockerCerts = os.Getenv("HOME") + "/.docker"
}

return &Config{
nameserver: "8.8.8.8:53",
dnsAddr: ":53",
domain: NewDomain("docker"),
dockerHost: dockerHost,
httpAddr: ":80",
tlsVerify: tlsVerify,
tlsCaCert: dockerCerts + "/ca.pem",
tlsCert: dockerCerts + "/cert.pem",
tlsKey: dockerCerts + "/key.pem",
}

}
3 changes: 2 additions & 1 deletion dnsserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ type Service struct {
Ip net.IP
Ttl int
Aliases []string
Manual bool
}

func NewService() (s *Service) {
s = &Service{Ttl: -1}
s = &Service{Ttl: -1, Manual: false}
return
}

Expand Down
20 changes: 15 additions & 5 deletions docker.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"crypto/tls"
"errors"
"log"
"net"
Expand All @@ -18,8 +19,8 @@ type DockerManager struct {
docker *dockerclient.DockerClient
}

func NewDockerManager(c *Config, list ServiceListProvider) (*DockerManager, error) {
docker, err := dockerclient.NewDockerClient(c.dockerHost, nil)
func NewDockerManager(c *Config, list ServiceListProvider, tlsConfig *tls.Config) (*DockerManager, error) {
docker, err := dockerclient.NewDockerClient(c.dockerHost, tlsConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -70,7 +71,10 @@ func (d *DockerManager) Update() error {
log.Println(err)
continue
}
d.list.AddService(container.Id, *service)
s, err := d.list.GetService(container.Id)
if err != nil || !s.Manual {
d.list.AddService(container.Id, *service)
}
}

return nil
Expand Down Expand Up @@ -108,18 +112,24 @@ func (d *DockerManager) getService(id string) (*Service, error) {
func (d *DockerManager) eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) {
//log.Printf("Received event: %#v %#v\n", *event, args)

s, s_err := d.list.GetService(event.Id)

switch event.Status {
case "die", "stop", "kill":
// Errors can be ignored here because there can be no-op events.
d.list.RemoveService(event.Id)
if s_err == nil && !s.Manual {
d.list.RemoveService(event.Id)
}
case "start", "restart":
service, err := d.getService(event.Id)
if err != nil {
ec <- err
return
}

d.list.AddService(event.Id, *service)
if s_err != nil || !s.Manual {
d.list.AddService(event.Id, *service)
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func (s *HTTPServer) addService(w http.ResponseWriter, req *http.Request) {
return
}

service.Manual = true
s.list.AddService(id, *service)
}

Expand Down Expand Up @@ -160,6 +161,7 @@ func (s *HTTPServer) updateService(w http.ResponseWriter, req *http.Request) {
}
}

service.Manual = true
// todo: this probably needs to be moved. consider stop event in the
// middle of sending PATCH. container would not be removed.
s.list.AddService(id, service)
Expand Down
8 changes: 4 additions & 4 deletions http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func TestServiceRequests(t *testing.T) {
{"GET", "/services/foo", "", "", 404},
{"PUT", "/services/foo", `{"name": "foo"}`, "", 500},
{"PUT", "/services/foo", `{"name": "foo", "image": "bar", "ip": "127.0.0.1", "aliases": ["foo.docker"]}`, "", 200},
{"GET", "/services/foo", "", `{"Name":"foo","Image":"bar","Ip":"127.0.0.1","Ttl":-1,"Aliases":["foo.docker"]}`, 200},
{"GET", "/services/foo", "", `{"Name":"foo","Image":"bar","Ip":"127.0.0.1","Ttl":-1,"Aliases":["foo.docker"],"Manual":true}`, 200},
{"PUT", "/services/boo", `{"name": "baz", "image": "bar", "ip": "127.0.0.2"}`, "", 200},
{"GET", "/services", "", `{"boo":{"Name":"baz","Image":"bar","Ip":"127.0.0.2","Ttl":-1,"Aliases":null},"foo":{"Name":"foo","Image":"bar","Ip":"127.0.0.1","Ttl":-1,"Aliases":["foo.docker"]}}`, 200},
{"GET", "/services", "", `{"boo":{"Name":"baz","Image":"bar","Ip":"127.0.0.2","Ttl":-1,"Aliases":null,"Manual":true},"foo":{"Name":"foo","Image":"bar","Ip":"127.0.0.1","Ttl":-1,"Aliases":["foo.docker"],"Manual":true}}`, 200},
{"PATCH", "/services/boo", `{"name": "bar", "ttl": 20, "image": "bar"}`, "", 200},
{"GET", "/services/boo", "", `{"Name":"bar","Image":"bar","Ip":"127.0.0.2","Ttl":20,"Aliases":null}`, 200},
{"GET", "/services/boo", "", `{"Name":"bar","Image":"bar","Ip":"127.0.0.2","Ttl":20,"Aliases":null,"Manual":true}`, 200},
{"DELETE", "/services/foo", ``, "", 200},
{"GET", "/services", "", `{"boo":{"Name":"bar","Image":"bar","Ip":"127.0.0.2","Ttl":20,"Aliases":null}}`, 200},
{"GET", "/services", "", `{"boo":{"Name":"bar","Image":"bar","Ip":"127.0.0.2","Ttl":20,"Aliases":null,"Manual":true}}`, 200},
}

for _, input := range tests {
Expand Down
28 changes: 27 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package main

import (
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
)
Expand All @@ -20,6 +23,10 @@ func main() {
domain := flag.String("domain", config.domain.String(), "Domain that is appended to all requests")
environment := flag.String("environment", "", "Optional context before domain suffix")
flag.StringVar(&config.dockerHost, "docker", config.dockerHost, "Path to the docker socket")
flag.BoolVar(&config.tlsVerify, "tlsverify", false, "Enable mTLS when connecting to docker")
flag.StringVar(&config.tlsCaCert, "tlscacert", config.tlsCaCert, "Path to CA certificate")
flag.StringVar(&config.tlsCert, "tlscert", config.tlsCert, "Path to client certificate")
flag.StringVar(&config.tlsKey, "tlskey", config.tlsKey, "Path to client certificate private key")
flag.BoolVar(&config.verbose, "verbose", true, "Verbose output")
flag.IntVar(&config.ttl, "ttl", config.ttl, "TTL for matched requests")

Expand All @@ -45,7 +52,26 @@ func main() {

dnsServer := NewDNSServer(config)

docker, err := NewDockerManager(config, dnsServer)
var tlsConfig *tls.Config = nil
if config.tlsVerify {
clientCert, err := tls.LoadX509KeyPair(config.tlsCert, config.tlsKey)
if err != nil {
log.Fatal(err)
}
tlsConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{clientCert},
}
pemData, err := ioutil.ReadFile(config.tlsCaCert)
if err == nil {
rootCert := x509.NewCertPool()
rootCert.AppendCertsFromPEM(pemData)
tlsConfig.RootCAs = rootCert
} else {
log.Print(err)
}
}
docker, err := NewDockerManager(config, dnsServer, tlsConfig)
if err != nil {
log.Fatal(err)
}
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ Additional configuration options to dnsdock command:
-nameserver="8.8.8.8:53": DNS server for unmatched requests
-ttl=0: TTL for matched requests
-verbose=true: Verbose output
-tlsverify=false: enable mutual TLS between dnsdock and Docker
-tlscacert="$HOME/.docker/ca.pem": Path to CA certificate
-tlscert="$HOME/.docker/cert.pem": Path to client certificate
-tlskey="$HOME/.docker/key.pem": Path to client certificate private key
```

If you also want to let the host machine discover the containers add `nameserver 172.17.42.1` to your `/etc/resolv.conf`.
Expand All @@ -94,6 +98,23 @@ If you also want to let the host machine discover the containers add `nameserver

Mounting docker daemon's unix socket may not work with default configuration on these platforms. Please use [selinux-dockersock](https://github.com/dpw/selinux-dockersock) to fix this. More information in [#11](https://github.com/tonistiigi/dnsdock/issues/11).

#### TLS Authentication

Instead of connecting to the Docker daemon's UNIX socket, you may prefer to connect via a TLS-protected TCP socket (for example, if you are running Swarm). The `-tlsverify` option enables TLS, and the three additional options (`-tlscacert`, `-tlscert` and `-tlskey`) must also be specified. Alternatively, you may set the `DOCKER_TLS_VERIFY` environment variable to a non-empty value and the `DOCKER_CERTS` to a directory containing files named `ca.pem`, `cert.pem` and `key.pem`.

You may build this into your own container with this example Dockerfile:

```
FROM tonistiigi/dnsdock
ENV DOCKER_TLS_VERIFY 1
ENV DOCKER_CERTS /certs
CMD ["-docker=tcp://172.17.42.1:2376"]
```

Use a volume (`-v /path/to/certs:/certs`) to give the container access to the certificate files, or build the certificates into the image if you have access to a secure private image registry.

#### HTTP Server

For easy overview and manual control dnsdock also includes HTTP server that lets you configure the server using a JSON API.
Expand Down

0 comments on commit 6470d47

Please sign in to comment.