| ... | ... |
@@ -3,8 +3,7 @@ package server |
| 3 | 3 |
import ( |
| 4 | 4 |
"bufio" |
| 5 | 5 |
"bytes" |
| 6 |
- "crypto/tls" |
|
| 7 |
- "crypto/x509" |
|
| 6 |
+ |
|
| 8 | 7 |
"encoding/base64" |
| 9 | 8 |
"encoding/json" |
| 10 | 9 |
"expvar" |
| ... | ... |
@@ -19,6 +18,9 @@ import ( |
| 19 | 19 |
"strings" |
| 20 | 20 |
"syscall" |
| 21 | 21 |
|
| 22 |
+ "crypto/tls" |
|
| 23 |
+ "crypto/x509" |
|
| 24 |
+ |
|
| 22 | 25 |
"code.google.com/p/go.net/websocket" |
| 23 | 26 |
"github.com/docker/libcontainer/user" |
| 24 | 27 |
"github.com/gorilla/mux" |
| ... | ... |
@@ -39,6 +41,18 @@ var ( |
| 39 | 39 |
activationLock chan struct{}
|
| 40 | 40 |
) |
| 41 | 41 |
|
| 42 |
+type HttpServer struct {
|
|
| 43 |
+ srv *http.Server |
|
| 44 |
+ l net.Listener |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (s *HttpServer) Serve() error {
|
|
| 48 |
+ return s.srv.Serve(s.l) |
|
| 49 |
+} |
|
| 50 |
+func (s *HttpServer) Close() error {
|
|
| 51 |
+ return s.l.Close() |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 42 | 54 |
type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error |
| 43 | 55 |
|
| 44 | 56 |
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
| ... | ... |
@@ -1334,9 +1348,14 @@ func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.Respons |
| 1334 | 1334 |
return nil |
| 1335 | 1335 |
} |
| 1336 | 1336 |
|
| 1337 |
-// ServeFD creates an http.Server and sets it up to serve given a socket activated |
|
| 1337 |
+// serveFd creates an http.Server and sets it up to serve given a socket activated |
|
| 1338 | 1338 |
// argument. |
| 1339 |
-func ServeFd(addr string, handle http.Handler) error {
|
|
| 1339 |
+func serveFd(addr string, job *engine.Job) error {
|
|
| 1340 |
+ r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
|
|
| 1341 |
+ if err != nil {
|
|
| 1342 |
+ return err |
|
| 1343 |
+ } |
|
| 1344 |
+ |
|
| 1340 | 1345 |
ls, e := systemd.ListenFD(addr) |
| 1341 | 1346 |
if e != nil {
|
| 1342 | 1347 |
return e |
| ... | ... |
@@ -1354,7 +1373,7 @@ func ServeFd(addr string, handle http.Handler) error {
|
| 1354 | 1354 |
for i := range ls {
|
| 1355 | 1355 |
listener := ls[i] |
| 1356 | 1356 |
go func() {
|
| 1357 |
- httpSrv := http.Server{Handler: handle}
|
|
| 1357 |
+ httpSrv := http.Server{Handler: r}
|
|
| 1358 | 1358 |
chErrors <- httpSrv.Serve(listener) |
| 1359 | 1359 |
}() |
| 1360 | 1360 |
} |
| ... | ... |
@@ -1386,6 +1405,41 @@ func lookupGidByName(nameOrGid string) (int, error) {
|
| 1386 | 1386 |
return -1, fmt.Errorf("Group %s not found", nameOrGid)
|
| 1387 | 1387 |
} |
| 1388 | 1388 |
|
| 1389 |
+func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
|
|
| 1390 |
+ tlsCert, err := tls.LoadX509KeyPair(cert, key) |
|
| 1391 |
+ if err != nil {
|
|
| 1392 |
+ return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
|
|
| 1393 |
+ cert, key, err) |
|
| 1394 |
+ } |
|
| 1395 |
+ tlsConfig := &tls.Config{
|
|
| 1396 |
+ NextProtos: []string{"http/1.1"},
|
|
| 1397 |
+ Certificates: []tls.Certificate{tlsCert},
|
|
| 1398 |
+ // Avoid fallback on insecure SSL protocols |
|
| 1399 |
+ MinVersion: tls.VersionTLS10, |
|
| 1400 |
+ } |
|
| 1401 |
+ |
|
| 1402 |
+ if ca != "" {
|
|
| 1403 |
+ certPool := x509.NewCertPool() |
|
| 1404 |
+ file, err := ioutil.ReadFile(ca) |
|
| 1405 |
+ if err != nil {
|
|
| 1406 |
+ return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
|
|
| 1407 |
+ } |
|
| 1408 |
+ certPool.AppendCertsFromPEM(file) |
|
| 1409 |
+ tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert |
|
| 1410 |
+ tlsConfig.ClientCAs = certPool |
|
| 1411 |
+ } |
|
| 1412 |
+ |
|
| 1413 |
+ return tls.NewListener(l, tlsConfig), nil |
|
| 1414 |
+} |
|
| 1415 |
+ |
|
| 1416 |
+func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
|
|
| 1417 |
+ if bufferRequests {
|
|
| 1418 |
+ return listenbuffer.NewListenBuffer(proto, addr, activationLock) |
|
| 1419 |
+ } |
|
| 1420 |
+ |
|
| 1421 |
+ return net.Listen(proto, addr) |
|
| 1422 |
+} |
|
| 1423 |
+ |
|
| 1389 | 1424 |
func changeGroup(addr string, nameOrGid string) error {
|
| 1390 | 1425 |
gid, err := lookupGidByName(nameOrGid) |
| 1391 | 1426 |
if err != nil {
|
| ... | ... |
@@ -1396,99 +1450,95 @@ func changeGroup(addr string, nameOrGid string) error {
|
| 1396 | 1396 |
return os.Chown(addr, 0, gid) |
| 1397 | 1397 |
} |
| 1398 | 1398 |
|
| 1399 |
-// ListenAndServe sets up the required http.Server and gets it listening for |
|
| 1400 |
-// each addr passed in and does protocol specific checking. |
|
| 1401 |
-func ListenAndServe(proto, addr string, job *engine.Job) error {
|
|
| 1402 |
- var l net.Listener |
|
| 1399 |
+func setSocketGroup(addr, group string) error {
|
|
| 1400 |
+ if group == "" {
|
|
| 1401 |
+ return nil |
|
| 1402 |
+ } |
|
| 1403 |
+ |
|
| 1404 |
+ if err := changeGroup(addr, group); err != nil {
|
|
| 1405 |
+ if group != "docker" {
|
|
| 1406 |
+ return err |
|
| 1407 |
+ } |
|
| 1408 |
+ log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
|
|
| 1409 |
+ } |
|
| 1410 |
+ |
|
| 1411 |
+ return nil |
|
| 1412 |
+} |
|
| 1413 |
+ |
|
| 1414 |
+func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
|
|
| 1403 | 1415 |
r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
|
| 1404 | 1416 |
if err != nil {
|
| 1405 |
- return err |
|
| 1417 |
+ return nil, err |
|
| 1406 | 1418 |
} |
| 1407 | 1419 |
|
| 1408 |
- if proto == "fd" {
|
|
| 1409 |
- return ServeFd(addr, r) |
|
| 1420 |
+ if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
|
|
| 1421 |
+ return nil, err |
|
| 1410 | 1422 |
} |
| 1423 |
+ mask := syscall.Umask(0777) |
|
| 1424 |
+ defer syscall.Umask(mask) |
|
| 1411 | 1425 |
|
| 1412 |
- if proto == "unix" {
|
|
| 1413 |
- if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
|
|
| 1414 |
- return err |
|
| 1415 |
- } |
|
| 1426 |
+ l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
|
|
| 1427 |
+ if err != nil {
|
|
| 1428 |
+ return nil, err |
|
| 1416 | 1429 |
} |
| 1417 | 1430 |
|
| 1418 |
- var oldmask int |
|
| 1419 |
- if proto == "unix" {
|
|
| 1420 |
- oldmask = syscall.Umask(0777) |
|
| 1431 |
+ if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
|
|
| 1432 |
+ return nil, err |
|
| 1421 | 1433 |
} |
| 1422 | 1434 |
|
| 1423 |
- if job.GetenvBool("BufferRequests") {
|
|
| 1424 |
- l, err = listenbuffer.NewListenBuffer(proto, addr, activationLock) |
|
| 1425 |
- } else {
|
|
| 1426 |
- l, err = net.Listen(proto, addr) |
|
| 1435 |
+ if err := os.Chmod(addr, 0660); err != nil {
|
|
| 1436 |
+ return nil, err |
|
| 1427 | 1437 |
} |
| 1428 | 1438 |
|
| 1429 |
- if proto == "unix" {
|
|
| 1430 |
- syscall.Umask(oldmask) |
|
| 1439 |
+ return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
|
|
| 1440 |
+} |
|
| 1441 |
+ |
|
| 1442 |
+func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
|
|
| 1443 |
+ if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
|
|
| 1444 |
+ log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 1431 | 1445 |
} |
| 1446 |
+ |
|
| 1447 |
+ r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
|
|
| 1432 | 1448 |
if err != nil {
|
| 1433 |
- return err |
|
| 1449 |
+ return nil, err |
|
| 1434 | 1450 |
} |
| 1435 | 1451 |
|
| 1436 |
- if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) {
|
|
| 1437 |
- tlsCert := job.Getenv("TlsCert")
|
|
| 1438 |
- tlsKey := job.Getenv("TlsKey")
|
|
| 1439 |
- cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) |
|
| 1440 |
- if err != nil {
|
|
| 1441 |
- return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
|
|
| 1442 |
- tlsCert, tlsKey, err) |
|
| 1443 |
- } |
|
| 1444 |
- tlsConfig := &tls.Config{
|
|
| 1445 |
- NextProtos: []string{"http/1.1"},
|
|
| 1446 |
- Certificates: []tls.Certificate{cert},
|
|
| 1447 |
- // Avoid fallback on insecure SSL protocols |
|
| 1448 |
- MinVersion: tls.VersionTLS10, |
|
| 1449 |
- } |
|
| 1450 |
- if job.GetenvBool("TlsVerify") {
|
|
| 1451 |
- certPool := x509.NewCertPool() |
|
| 1452 |
- file, err := ioutil.ReadFile(job.Getenv("TlsCa"))
|
|
| 1453 |
- if err != nil {
|
|
| 1454 |
- return fmt.Errorf("Couldn't read CA certificate: %s", err)
|
|
| 1455 |
- } |
|
| 1456 |
- certPool.AppendCertsFromPEM(file) |
|
| 1452 |
+ l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
|
|
| 1453 |
+ if err != nil {
|
|
| 1454 |
+ return nil, err |
|
| 1455 |
+ } |
|
| 1457 | 1456 |
|
| 1458 |
- tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert |
|
| 1459 |
- tlsConfig.ClientCAs = certPool |
|
| 1457 |
+ if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
|
|
| 1458 |
+ var tlsCa string |
|
| 1459 |
+ if job.GetenvBool("TlsVerify") {
|
|
| 1460 |
+ tlsCa = job.Getenv("TlsCa")
|
|
| 1461 |
+ } |
|
| 1462 |
+ l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
|
|
| 1463 |
+ if err != nil {
|
|
| 1464 |
+ return nil, err |
|
| 1460 | 1465 |
} |
| 1461 |
- l = tls.NewListener(l, tlsConfig) |
|
| 1462 | 1466 |
} |
| 1467 |
+ return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
|
|
| 1468 |
+} |
|
| 1463 | 1469 |
|
| 1470 |
+// NewServer sets up the required Server and does protocol specific checking. |
|
| 1471 |
+func NewServer(proto, addr string, job *engine.Job) (Server, error) {
|
|
| 1464 | 1472 |
// Basic error and sanity checking |
| 1465 | 1473 |
switch proto {
|
| 1474 |
+ case "fd": |
|
| 1475 |
+ return nil, serveFd(addr, job) |
|
| 1466 | 1476 |
case "tcp": |
| 1467 |
- if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
|
|
| 1468 |
- log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 1469 |
- } |
|
| 1477 |
+ return setupTcpHttp(addr, job) |
|
| 1470 | 1478 |
case "unix": |
| 1471 |
- socketGroup := job.Getenv("SocketGroup")
|
|
| 1472 |
- if socketGroup != "" {
|
|
| 1473 |
- if err := changeGroup(addr, socketGroup); err != nil {
|
|
| 1474 |
- if socketGroup == "docker" {
|
|
| 1475 |
- // if the user hasn't explicitly specified the group ownership, don't fail on errors. |
|
| 1476 |
- log.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
|
|
| 1477 |
- } else {
|
|
| 1478 |
- return err |
|
| 1479 |
- } |
|
| 1480 |
- } |
|
| 1481 |
- |
|
| 1482 |
- } |
|
| 1483 |
- if err := os.Chmod(addr, 0660); err != nil {
|
|
| 1484 |
- return err |
|
| 1485 |
- } |
|
| 1479 |
+ return setupUnixHttp(addr, job) |
|
| 1486 | 1480 |
default: |
| 1487 |
- return fmt.Errorf("Invalid protocol format.")
|
|
| 1481 |
+ return nil, fmt.Errorf("Invalid protocol format.")
|
|
| 1488 | 1482 |
} |
| 1483 |
+} |
|
| 1489 | 1484 |
|
| 1490 |
- httpSrv := http.Server{Addr: addr, Handler: r}
|
|
| 1491 |
- return httpSrv.Serve(l) |
|
| 1485 |
+type Server interface {
|
|
| 1486 |
+ Serve() error |
|
| 1487 |
+ Close() error |
|
| 1492 | 1488 |
} |
| 1493 | 1489 |
|
| 1494 | 1490 |
// ServeApi loops through all of the protocols sent in to docker and spawns |
| ... | ... |
@@ -1510,7 +1560,12 @@ func ServeApi(job *engine.Job) engine.Status {
|
| 1510 | 1510 |
} |
| 1511 | 1511 |
go func() {
|
| 1512 | 1512 |
log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
|
| 1513 |
- chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job) |
|
| 1513 |
+ srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job) |
|
| 1514 |
+ if err != nil {
|
|
| 1515 |
+ chErrors <- err |
|
| 1516 |
+ return |
|
| 1517 |
+ } |
|
| 1518 |
+ chErrors <- srv.Serve() |
|
| 1514 | 1519 |
}() |
| 1515 | 1520 |
} |
| 1516 | 1521 |
|