git » remoteu2f » next » tree

[next] / remoteu2f-proxy / main.go

// remoteu2f-proxy is the http+grpc server for remoteu2f.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	// Enable profiling via HTTP.
	// We will only use this if --debug_addr is given.
	_ "net/http/pprof"

	"github.com/golang/glog"
)

// Command-line flags.
var (
	baseURL = flag.String("base_url", "",
		"base URL to send the clients (e.g. https://domain:8800); "+
			"must be https and not have a trailing '/'")

	httpAddr = flag.String("http_addr", ":8800",
		"address where to listen for HTTP requests")
	grpcAddr = flag.String("grpc_addr", ":8801",
		"address where to listen for GPRC requests")
	debugAddr = flag.String("debug_addr", "",
		"address where to listen for debug/trace/profile HTTP requests")

	tlsCert = flag.String("tls_cert", "cert.pem",
		"file containing the TLS certificate to use")
	tlsKey = flag.String("tls_key", "key.pem",
		"file containing the TLS key to use")
	grpcCert = flag.String("tls_cert_grpc", "",
		"if set, use this certificate for GRPC instead of --tls_cert")
	grpcKey = flag.String("tls_key_grpc", "",
		"if set, use this key for GRPC instead of --tls_key")

	tokenFile = flag.String("token_file", "tokens",
		"file containing the valid client access tokens")
)

func validateBaseURL(s string) error {
	u, err := url.Parse(*baseURL)
	if err != nil {
		return fmt.Errorf("malformed url: %v", err)
	}
	if u.Scheme != "https" {
		return fmt.Errorf("scheme MUST be 'https'")
	}
	if u.Path != "" {
		return fmt.Errorf("path MUST be empty (not even a trailing '/')")
	}

	return nil
}

func getValidTokens(path string) (map[string]bool, error) {
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	tokens := map[string]bool{}
	for _, t := range strings.Split(string(contents), "\n") {
		// Remove empty lines, and make sure all tokens have a minimum lenght.
		t = strings.TrimSpace(t)
		if t == "" {
			continue
		}
		if len(t) < 10 {
			return nil, fmt.Errorf(
				"token %q too short (minimum lenght: 10)", t)
		}

		tokens[t] = true
	}

	return tokens, nil
}

func main() {
	flag.Parse()

	// We have strict requirements on baseURL because we use it as the appID.
	if err := validateBaseURL(*baseURL); err != nil {
		glog.Fatalf("invalid --base_url: %s", err)
	}

	validTokens, err := getValidTokens(*tokenFile)
	if err != nil {
		glog.Fatalf("error reading token file: %s", err)
	}

	s := NewServer()
	s.BaseURL = *baseURL
	s.HTTPAddr = *httpAddr
	s.GRPCAddr = *grpcAddr
	s.ValidTokens = validTokens

	s.HTTPCert = *tlsCert
	s.HTTPKey = *tlsKey
	s.GRPCCert = *tlsCert
	s.GRPCKey = *tlsKey
	if *grpcCert != "" {
		s.GRPCCert = *grpcCert
	}
	if *grpcKey != "" {
		s.GRPCKey = *grpcKey
	}

	if *debugAddr != "" {
		// Launch the default HTTP server at the given address.
		// This is the one pprof and grpc register automatically against.
		go func() {
			glog.Infof("Debug HTTP listening on %s", *debugAddr)
			err := http.ListenAndServe(*debugAddr, nil)
			glog.Fatalf("Debug HTTP exiting: %s", err)
		}()
	}

	s.ListenAndServe()
}