mirror of https://github.com/portainer/portainer
				
				
				
			refactor(api): create a new structure for the Go api (#94)
* refactor(api): create a new structure for the Go api * refactor(api): update the way keyFile parameter is managedpull/97/head
							parent
							
								
									06c2635e82
								
							
						
					
					
						commit
						b0ebbdf68c
					
				| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
package main // import "github.com/cloudinovasi/ui-for-docker"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"gopkg.in/alecthomas/kingpin.v2"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// main is the entry point of the program
 | 
			
		||||
func main() {
 | 
			
		||||
	kingpin.Version("1.5.0")
 | 
			
		||||
	var (
 | 
			
		||||
		endpoint  = kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String()
 | 
			
		||||
		addr      = kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String()
 | 
			
		||||
		assets    = kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String()
 | 
			
		||||
		data      = kingpin.Flag("data", "Path to the data").Default(".").Short('d').String()
 | 
			
		||||
		swarm     = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool()
 | 
			
		||||
		tlsverify = kingpin.Flag("tlsverify", "TLS support").Default("false").Bool()
 | 
			
		||||
		tlscacert = kingpin.Flag("tlscacert", "Path to the CA").Default("/certs/ca.pem").String()
 | 
			
		||||
		tlscert   = kingpin.Flag("tlscert", "Path to the TLS certificate file").Default("/certs/cert.pem").String()
 | 
			
		||||
		tlskey    = kingpin.Flag("tlskey", "Path to the TLS key").Default("/certs/key.pem").String()
 | 
			
		||||
		labels    = pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l'))
 | 
			
		||||
	)
 | 
			
		||||
	kingpin.Parse()
 | 
			
		||||
 | 
			
		||||
	configuration := newConfig(*swarm, *labels)
 | 
			
		||||
	tlsFlags := newTLSFlags(*tlsverify, *tlscacert, *tlscert, *tlskey)
 | 
			
		||||
 | 
			
		||||
	handler := newHandler(*assets, *data, *endpoint, configuration, tlsFlags)
 | 
			
		||||
	if err := http.ListenAndServe(*addr, handler); err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Config defines the configuration available under the /config endpoint
 | 
			
		||||
type Config struct {
 | 
			
		||||
	Swarm        bool     `json:"swarm"`
 | 
			
		||||
	HiddenLabels pairList `json:"hiddenLabels"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newConfig creates a new Config from command flags
 | 
			
		||||
func newConfig(swarm bool, labels pairList) Config {
 | 
			
		||||
	return Config{
 | 
			
		||||
		Swarm:        swarm,
 | 
			
		||||
		HiddenLabels: labels,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// configurationHandler defines a handler function used to encode the configuration in JSON
 | 
			
		||||
func configurationHandler(w http.ResponseWriter, r *http.Request, c Config) {
 | 
			
		||||
	json.NewEncoder(w).Encode(c)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gorilla/csrf"
 | 
			
		||||
	"github.com/gorilla/securecookie"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const keyFile = "authKey.dat"
 | 
			
		||||
 | 
			
		||||
// newAuthKey reuses an existing CSRF authkey if present or generates a new one
 | 
			
		||||
func newAuthKey(path string) []byte {
 | 
			
		||||
	var authKey []byte
 | 
			
		||||
	authKeyPath := path + "/" + keyFile
 | 
			
		||||
	data, err := ioutil.ReadFile(authKeyPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Print("Unable to find an existing CSRF auth key. Generating a new key.")
 | 
			
		||||
		authKey = securecookie.GenerateRandomKey(32)
 | 
			
		||||
		err := ioutil.WriteFile(authKeyPath, authKey, 0644)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal("Unable to persist CSRF auth key.")
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		authKey = data
 | 
			
		||||
	}
 | 
			
		||||
	return authKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newCSRF initializes a new CSRF handler
 | 
			
		||||
func newCSRFHandler(keyPath string) func(h http.Handler) http.Handler {
 | 
			
		||||
	authKey := newAuthKey(keyPath)
 | 
			
		||||
	return csrf.Protect(
 | 
			
		||||
		authKey,
 | 
			
		||||
		csrf.HttpOnly(false),
 | 
			
		||||
		csrf.Secure(false),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newCSRFWrapper wraps a http.Handler to add the CSRF token
 | 
			
		||||
func newCSRFWrapper(h http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("X-CSRF-Token", csrf.Token(r))
 | 
			
		||||
		h.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"gopkg.in/alecthomas/kingpin.v2"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TLSFlags defines all the flags associated to the SSL configuration
 | 
			
		||||
type TLSFlags struct {
 | 
			
		||||
	tls      bool
 | 
			
		||||
	caPath   string
 | 
			
		||||
	certPath string
 | 
			
		||||
	keyPath  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pair defines a key/value pair
 | 
			
		||||
type pair struct {
 | 
			
		||||
	Name  string `json:"name"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pairList defines an array of Label
 | 
			
		||||
type pairList []pair
 | 
			
		||||
 | 
			
		||||
// Set implementation for Labels
 | 
			
		||||
func (l *pairList) Set(value string) error {
 | 
			
		||||
	parts := strings.SplitN(value, "=", 2)
 | 
			
		||||
	if len(parts) != 2 {
 | 
			
		||||
		return fmt.Errorf("expected NAME=VALUE got '%s'", value)
 | 
			
		||||
	}
 | 
			
		||||
	p := new(pair)
 | 
			
		||||
	p.Name = parts[0]
 | 
			
		||||
	p.Value = parts[1]
 | 
			
		||||
	*l = append(*l, *p)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implementation for Labels
 | 
			
		||||
func (l *pairList) String() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsCumulative implementation for Labels
 | 
			
		||||
func (l *pairList) IsCumulative() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabelParser defines a custom parser for Labels flags
 | 
			
		||||
func pairs(s kingpin.Settings) (target *[]pair) {
 | 
			
		||||
	target = new([]pair)
 | 
			
		||||
	s.SetValue((*pairList)(target))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTLSFlags creates a new TLSFlags from command flags
 | 
			
		||||
func newTLSFlags(tls bool, cacert string, cert string, key string) TLSFlags {
 | 
			
		||||
	return TLSFlags{
 | 
			
		||||
		tls:      tls,
 | 
			
		||||
		caPath:   cacert,
 | 
			
		||||
		certPath: cert,
 | 
			
		||||
		keyPath:  key,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httputil"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// newHandler creates a new http.Handler with CSRF protection
 | 
			
		||||
func newHandler(dir string, d string, e string, c Config, tlsFlags TLSFlags) http.Handler {
 | 
			
		||||
	var (
 | 
			
		||||
		mux         = http.NewServeMux()
 | 
			
		||||
		fileHandler = http.FileServer(http.Dir(dir))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	u, perr := url.Parse(e)
 | 
			
		||||
	if perr != nil {
 | 
			
		||||
		log.Fatal(perr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := newAPIHandler(u, tlsFlags)
 | 
			
		||||
	CSRFHandler := newCSRFHandler(d)
 | 
			
		||||
 | 
			
		||||
	mux.Handle("/dockerapi/", http.StripPrefix("/dockerapi", handler))
 | 
			
		||||
	mux.Handle("/", fileHandler)
 | 
			
		||||
	mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		configurationHandler(w, r, c)
 | 
			
		||||
	})
 | 
			
		||||
	return CSRFHandler(newCSRFWrapper(mux))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newAPIHandler initializes a new http.Handler based on the URL scheme
 | 
			
		||||
func newAPIHandler(u *url.URL, tlsFlags TLSFlags) http.Handler {
 | 
			
		||||
	var handler http.Handler
 | 
			
		||||
	if u.Scheme == "tcp" {
 | 
			
		||||
		if tlsFlags.tls {
 | 
			
		||||
			handler = newTCPHandlerWithTLS(u, tlsFlags)
 | 
			
		||||
		} else {
 | 
			
		||||
			handler = newTCPHandler(u)
 | 
			
		||||
		}
 | 
			
		||||
	} else if u.Scheme == "unix" {
 | 
			
		||||
		socketPath := u.Path
 | 
			
		||||
		if _, err := os.Stat(socketPath); err != nil {
 | 
			
		||||
			if os.IsNotExist(err) {
 | 
			
		||||
				log.Fatalf("Unix socket %s does not exist", socketPath)
 | 
			
		||||
			}
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		handler = newUnixHandler(socketPath)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Fatalf("Bad Docker enpoint: %s. Only unix:// and tcp:// are supported.", u)
 | 
			
		||||
	}
 | 
			
		||||
	return handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newUnixHandler initializes a new UnixHandler
 | 
			
		||||
func newUnixHandler(e string) http.Handler {
 | 
			
		||||
	return &unixHandler{e}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTCPHandler initializes a HTTP reverse proxy
 | 
			
		||||
func newTCPHandler(u *url.URL) http.Handler {
 | 
			
		||||
	u.Scheme = "http"
 | 
			
		||||
	return httputil.NewSingleHostReverseProxy(u)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTCPHandlerWithL initializes a HTTPS reverse proxy with a TLS configuration
 | 
			
		||||
func newTCPHandlerWithTLS(u *url.URL, tlsFlags TLSFlags) http.Handler {
 | 
			
		||||
	u.Scheme = "https"
 | 
			
		||||
	var tlsConfig = newTLSConfig(tlsFlags)
 | 
			
		||||
	proxy := httputil.NewSingleHostReverseProxy(u)
 | 
			
		||||
	proxy.Transport = &http.Transport{
 | 
			
		||||
		TLSClientConfig: tlsConfig,
 | 
			
		||||
	}
 | 
			
		||||
	return proxy
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// newTLSConfig initializes a tls.Config from the TLS flags
 | 
			
		||||
func newTLSConfig(tlsFlags TLSFlags) *tls.Config {
 | 
			
		||||
	cert, err := tls.LoadX509KeyPair(tlsFlags.certPath, tlsFlags.keyPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	caCert, err := ioutil.ReadFile(tlsFlags.caPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	caCertPool := x509.NewCertPool()
 | 
			
		||||
	caCertPool.AppendCertsFromPEM(caCert)
 | 
			
		||||
	tlsConfig := &tls.Config{
 | 
			
		||||
		Certificates: []tls.Certificate{cert},
 | 
			
		||||
		RootCAs:      caCertPool,
 | 
			
		||||
	}
 | 
			
		||||
	return tlsConfig
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httputil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// unixHandler defines a handler holding the path to a socket under UNIX
 | 
			
		||||
type unixHandler struct {
 | 
			
		||||
	path string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServeHTTP implementation for unixHandler
 | 
			
		||||
func (h *unixHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	conn, err := net.Dial("unix", h.path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c := httputil.NewClientConn(conn, nil)
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	res, err := c.Do(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer res.Body.Close()
 | 
			
		||||
 | 
			
		||||
	copyHeader(w.Header(), res.Header)
 | 
			
		||||
	if _, err := io.Copy(w, res.Body); err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyHeader(dst, src http.Header) {
 | 
			
		||||
	for k, vv := range src {
 | 
			
		||||
		for _, v := range vv {
 | 
			
		||||
			dst.Add(k, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										243
									
								
								dockerui.go
								
								
								
								
							
							
						
						
									
										243
									
								
								dockerui.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,243 +0,0 @@
 | 
			
		|||
package main // import "github.com/cloudinovasi/ui-for-docker"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httputil"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"github.com/gorilla/csrf"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/gorilla/securecookie"
 | 
			
		||||
	"gopkg.in/alecthomas/kingpin.v2"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	endpoint 	= kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String()
 | 
			
		||||
	addr 		 	= kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String()
 | 
			
		||||
	assets   	= kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String()
 | 
			
		||||
	data		 	= kingpin.Flag("data", "Path to the data").Default(".").Short('d').String()
 | 
			
		||||
	swarm	   	= kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool()
 | 
			
		||||
	tlsverify	= kingpin.Flag("tlsverify", "TLS support").Default("false").Bool()
 | 
			
		||||
	tlscacert = kingpin.Flag("tlscacert", "Path to the CA").Default("/certs/ca.pem").String()
 | 
			
		||||
	tlscert   = kingpin.Flag("tlscert", "Path to the TLS certificate file").Default("/certs/cert.pem").String()
 | 
			
		||||
	tlskey    = kingpin.Flag("tlskey", "Path to the TLS key").Default("/certs/key.pem").String()
 | 
			
		||||
	labels    = LabelParser(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l'))
 | 
			
		||||
	authKey  []byte
 | 
			
		||||
	authKeyFile = "authKey.dat"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UnixHandler struct {
 | 
			
		||||
	path string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TlsFlags struct {
 | 
			
		||||
	tls bool
 | 
			
		||||
	caPath string
 | 
			
		||||
	certPath string
 | 
			
		||||
	keyPath string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	Swarm bool `json:"swarm"`
 | 
			
		||||
	HiddenLabels Labels `json:"hiddenLabels"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Label struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Labels []Label
 | 
			
		||||
 | 
			
		||||
func (l *Labels) Set(value string) error {
 | 
			
		||||
  parts := strings.SplitN(value, "=", 2)
 | 
			
		||||
  if len(parts) != 2 {
 | 
			
		||||
    return fmt.Errorf("expected HEADER=VALUE got '%s'", value)
 | 
			
		||||
  }
 | 
			
		||||
	label := new(Label)
 | 
			
		||||
  label.Name = parts[0]
 | 
			
		||||
	label.Value = parts[1]
 | 
			
		||||
	*l = append(*l, *label)
 | 
			
		||||
  return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *Labels) String() string {
 | 
			
		||||
  return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *Labels) IsCumulative() bool {
 | 
			
		||||
  return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LabelParser(s kingpin.Settings) (target *[]Label) {
 | 
			
		||||
	target = new([]Label)
 | 
			
		||||
  s.SetValue((*Labels)(target))
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *UnixHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	conn, err := net.Dial("unix", h.path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c := httputil.NewClientConn(conn, nil)
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	res, err := c.Do(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer res.Body.Close()
 | 
			
		||||
 | 
			
		||||
	copyHeader(w.Header(), res.Header)
 | 
			
		||||
	if _, err := io.Copy(w, res.Body); err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyHeader(dst, src http.Header) {
 | 
			
		||||
	for k, vv := range src {
 | 
			
		||||
		for _, v := range vv {
 | 
			
		||||
			dst.Add(k, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func configurationHandler(w http.ResponseWriter, r *http.Request, c Config) {
 | 
			
		||||
    json.NewEncoder(w).Encode(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createTcpHandler(u *url.URL) http.Handler {
 | 
			
		||||
	u.Scheme = "http";
 | 
			
		||||
	return httputil.NewSingleHostReverseProxy(u)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createTlsConfig(tlsFlags TlsFlags) *tls.Config {
 | 
			
		||||
	cert, err := tls.LoadX509KeyPair(tlsFlags.certPath, tlsFlags.keyPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	caCert, err := ioutil.ReadFile(tlsFlags.caPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	caCertPool := x509.NewCertPool()
 | 
			
		||||
	caCertPool.AppendCertsFromPEM(caCert)
 | 
			
		||||
	tlsConfig := &tls.Config{
 | 
			
		||||
		Certificates: []tls.Certificate{cert},
 | 
			
		||||
		RootCAs:      caCertPool,
 | 
			
		||||
	}
 | 
			
		||||
	return tlsConfig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createTcpHandlerWithTLS(u *url.URL, tlsFlags TlsFlags) http.Handler {
 | 
			
		||||
	u.Scheme = "https";
 | 
			
		||||
	var tlsConfig = createTlsConfig(tlsFlags)
 | 
			
		||||
	proxy := httputil.NewSingleHostReverseProxy(u)
 | 
			
		||||
	proxy.Transport = &http.Transport{
 | 
			
		||||
		TLSClientConfig: tlsConfig,
 | 
			
		||||
	}
 | 
			
		||||
	return proxy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createUnixHandler(e string) http.Handler {
 | 
			
		||||
	return &UnixHandler{e}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createHandler(dir string, d string, e string, c Config, tlsFlags TlsFlags) http.Handler {
 | 
			
		||||
	var (
 | 
			
		||||
		mux         = http.NewServeMux()
 | 
			
		||||
		fileHandler = http.FileServer(http.Dir(dir))
 | 
			
		||||
		h           http.Handler
 | 
			
		||||
	)
 | 
			
		||||
	u, perr := url.Parse(e)
 | 
			
		||||
	if perr != nil {
 | 
			
		||||
		log.Fatal(perr)
 | 
			
		||||
	}
 | 
			
		||||
	if u.Scheme == "tcp" {
 | 
			
		||||
		if tlsFlags.tls {
 | 
			
		||||
			h = createTcpHandlerWithTLS(u, tlsFlags)
 | 
			
		||||
		} else {
 | 
			
		||||
			h = createTcpHandler(u)
 | 
			
		||||
		}
 | 
			
		||||
	} else if u.Scheme == "unix" {
 | 
			
		||||
		var socketPath = u.Path
 | 
			
		||||
		if _, err := os.Stat(socketPath); err != nil {
 | 
			
		||||
			if os.IsNotExist(err) {
 | 
			
		||||
				log.Fatalf("unix socket %s does not exist", socketPath)
 | 
			
		||||
			}
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		h = createUnixHandler(socketPath)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Fatalf("Bad Docker enpoint: %s. Only unix:// and tcp:// are supported.", e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Use existing csrf authKey if present or generate a new one.
 | 
			
		||||
	var authKeyPath = d + "/" + authKeyFile
 | 
			
		||||
	dat, err := ioutil.ReadFile(authKeyPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		authKey = securecookie.GenerateRandomKey(32)
 | 
			
		||||
		err := ioutil.WriteFile(authKeyPath, authKey, 0644)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("unable to persist auth key", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		authKey = dat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CSRF := csrf.Protect(
 | 
			
		||||
		authKey,
 | 
			
		||||
		csrf.HttpOnly(false),
 | 
			
		||||
		csrf.Secure(false),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	mux.Handle("/dockerapi/", http.StripPrefix("/dockerapi", h))
 | 
			
		||||
	mux.Handle("/", fileHandler)
 | 
			
		||||
	mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
  	configurationHandler(w, r, c)
 | 
			
		||||
  })
 | 
			
		||||
	return CSRF(csrfWrapper(mux))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func csrfWrapper(h http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("X-CSRF-Token", csrf.Token(r))
 | 
			
		||||
		h.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	kingpin.Version("1.5.0")
 | 
			
		||||
	kingpin.Parse()
 | 
			
		||||
 | 
			
		||||
	configuration := Config{
 | 
			
		||||
		Swarm: *swarm,
 | 
			
		||||
		HiddenLabels: *labels,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tlsFlags := TlsFlags{
 | 
			
		||||
		tls: *tlsverify,
 | 
			
		||||
		caPath: *tlscacert,
 | 
			
		||||
		certPath: *tlscert,
 | 
			
		||||
		keyPath: *tlskey,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := createHandler(*assets, *data, *endpoint, configuration, tlsFlags)
 | 
			
		||||
	if err := http.ListenAndServe(*addr, handler); err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -254,10 +254,10 @@ module.exports = function (grunt) {
 | 
			
		|||
            },
 | 
			
		||||
            buildBinary: {
 | 
			
		||||
                command: [
 | 
			
		||||
                    'docker run --rm -v $(pwd):/src centurylink/golang-builder',
 | 
			
		||||
                    'shasum ui-for-docker > ui-for-docker-checksum.txt',
 | 
			
		||||
                    'docker run --rm -v $(pwd)/api:/src centurylink/golang-builder',
 | 
			
		||||
                    'shasum api/ui-for-docker > ui-for-docker-checksum.txt',
 | 
			
		||||
                    'mkdir -p dist',
 | 
			
		||||
                    'mv ui-for-docker dist/'
 | 
			
		||||
                    'mv api/ui-for-docker dist/'
 | 
			
		||||
                ].join(' && ')
 | 
			
		||||
            },
 | 
			
		||||
            run: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue