1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #9031 from cpuguy83/cleanup_api_server_creation

Cleanup api server creation
This commit is contained in:
Alexandr Morozov 2014-11-12 15:37:34 -08:00
commit e12572f265

View file

@ -3,8 +3,7 @@ package server
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"crypto/tls"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"expvar" "expvar"
@ -19,6 +18,9 @@ import (
"strings" "strings"
"syscall" "syscall"
"crypto/tls"
"crypto/x509"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"github.com/docker/libcontainer/user" "github.com/docker/libcontainer/user"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -39,6 +41,18 @@ var (
activationLock chan struct{} activationLock chan struct{}
) )
type HttpServer struct {
srv *http.Server
l net.Listener
}
func (s *HttpServer) Serve() error {
return s.srv.Serve(s.l)
}
func (s *HttpServer) Close() error {
return s.l.Close()
}
type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { 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
return nil return nil
} }
// ServeFD creates an http.Server and sets it up to serve given a socket activated // serveFd creates an http.Server and sets it up to serve given a socket activated
// argument. // argument.
func ServeFd(addr string, handle http.Handler) error { func serveFd(addr string, job *engine.Job) error {
r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
if err != nil {
return err
}
ls, e := systemd.ListenFD(addr) ls, e := systemd.ListenFD(addr)
if e != nil { if e != nil {
return e return e
@ -1354,7 +1373,7 @@ func ServeFd(addr string, handle http.Handler) error {
for i := range ls { for i := range ls {
listener := ls[i] listener := ls[i]
go func() { go func() {
httpSrv := http.Server{Handler: handle} httpSrv := http.Server{Handler: r}
chErrors <- httpSrv.Serve(listener) chErrors <- httpSrv.Serve(listener)
}() }()
} }
@ -1386,6 +1405,41 @@ func lookupGidByName(nameOrGid string) (int, error) {
return -1, fmt.Errorf("Group %s not found", nameOrGid) return -1, fmt.Errorf("Group %s not found", nameOrGid)
} }
func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
tlsCert, 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)
}
tlsConfig := &tls.Config{
NextProtos: []string{"http/1.1"},
Certificates: []tls.Certificate{tlsCert},
// Avoid fallback on insecure SSL protocols
MinVersion: tls.VersionTLS10,
}
if ca != "" {
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)
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
tlsConfig.ClientCAs = certPool
}
return tls.NewListener(l, tlsConfig), nil
}
func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
if bufferRequests {
return listenbuffer.NewListenBuffer(proto, addr, activationLock)
}
return net.Listen(proto, addr)
}
func changeGroup(addr string, nameOrGid string) error { func changeGroup(addr string, nameOrGid string) error {
gid, err := lookupGidByName(nameOrGid) gid, err := lookupGidByName(nameOrGid)
if err != nil { if err != nil {
@ -1396,99 +1450,95 @@ func changeGroup(addr string, nameOrGid string) error {
return os.Chown(addr, 0, gid) return os.Chown(addr, 0, gid)
} }
// ListenAndServe sets up the required http.Server and gets it listening for func setSocketGroup(addr, group string) error {
// each addr passed in and does protocol specific checking. if group == "" {
func ListenAndServe(proto, addr string, job *engine.Job) error { return nil
var l net.Listener }
if err := changeGroup(addr, group); err != nil {
if group != "docker" {
return err
}
log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
}
return nil
}
func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
if err != nil { if err != nil {
return err return nil, err
} }
if proto == "fd" { if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
return ServeFd(addr, r) return nil, err
} }
mask := syscall.Umask(0777)
defer syscall.Umask(mask)
if proto == "unix" { l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
return err
}
}
var oldmask int
if proto == "unix" {
oldmask = syscall.Umask(0777)
}
if job.GetenvBool("BufferRequests") {
l, err = listenbuffer.NewListenBuffer(proto, addr, activationLock)
} else {
l, err = net.Listen(proto, addr)
}
if proto == "unix" {
syscall.Umask(oldmask)
}
if err != nil { if err != nil {
return err return nil, err
} }
if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) { if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
tlsCert := job.Getenv("TlsCert") return nil, err
tlsKey := job.Getenv("TlsKey") }
cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)
if err != nil { if err := os.Chmod(addr, 0660); err != nil {
return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", return nil, err
tlsCert, tlsKey, err) }
}
tlsConfig := &tls.Config{ return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
NextProtos: []string{"http/1.1"}, }
Certificates: []tls.Certificate{cert},
// Avoid fallback on insecure SSL protocols func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
MinVersion: tls.VersionTLS10, if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
} log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
if err != nil {
return nil, err
}
l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
if err != nil {
return nil, err
}
if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
var tlsCa string
if job.GetenvBool("TlsVerify") { if job.GetenvBool("TlsVerify") {
certPool := x509.NewCertPool() tlsCa = job.Getenv("TlsCa")
file, err := ioutil.ReadFile(job.Getenv("TlsCa")) }
if err != nil { l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
return fmt.Errorf("Couldn't read CA certificate: %s", err) if err != nil {
} return nil, err
certPool.AppendCertsFromPEM(file)
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
tlsConfig.ClientCAs = certPool
} }
l = tls.NewListener(l, tlsConfig)
} }
return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
}
// NewServer sets up the required Server and does protocol specific checking.
func NewServer(proto, addr string, job *engine.Job) (Server, error) {
// Basic error and sanity checking // Basic error and sanity checking
switch proto { switch proto {
case "fd":
return nil, serveFd(addr, job)
case "tcp": case "tcp":
if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") { return setupTcpHttp(addr, job)
log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
case "unix": case "unix":
socketGroup := job.Getenv("SocketGroup") return setupUnixHttp(addr, job)
if socketGroup != "" {
if err := changeGroup(addr, socketGroup); err != nil {
if socketGroup == "docker" {
// if the user hasn't explicitly specified the group ownership, don't fail on errors.
log.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
} else {
return err
}
}
}
if err := os.Chmod(addr, 0660); err != nil {
return err
}
default: default:
return fmt.Errorf("Invalid protocol format.") return nil, fmt.Errorf("Invalid protocol format.")
} }
}
httpSrv := http.Server{Addr: addr, Handler: r} type Server interface {
return httpSrv.Serve(l) Serve() error
Close() error
} }
// ServeApi loops through all of the protocols sent in to docker and spawns // ServeApi loops through all of the protocols sent in to docker and spawns
@ -1510,7 +1560,12 @@ func ServeApi(job *engine.Job) engine.Status {
} }
go func() { go func() {
log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job) srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job)
if err != nil {
chErrors <- err
return
}
chErrors <- srv.Serve()
}() }()
} }