mirror of https://github.com/perkeep/perkeep.git
Move TLS setup to config file, not flags. Use jsonconfig for root config.
This commit is contained in:
parent
0d3f7305cb
commit
17a804b7c1
|
@ -1,6 +1,10 @@
|
||||||
{ "_for-emacs": "-*- mode: js2;-*-",
|
{ "_for-emacs": "-*- mode: js2;-*-",
|
||||||
"baseURL": ["_env", "http://localhost:${CAMLI_PORT}"],
|
"baseURL": ["_env", "http://localhost:${CAMLI_PORT}"],
|
||||||
"password": ["_env", "${CAMLI_PASSWORD}"],
|
"password": ["_env", "${CAMLI_PASSWORD}"],
|
||||||
|
|
||||||
|
"TLSCertFile": ["_env", "${CAMLI_TLS_CRT_FILE}"],
|
||||||
|
"TLSKeyFile": ["_env", "${CAMLI_TLS_KEY_FILE}"],
|
||||||
|
|
||||||
"prefixes": {
|
"prefixes": {
|
||||||
"/": {
|
"/": {
|
||||||
"handler": "filesystem",
|
"handler": "filesystem",
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
{ "_for-emacs": "-*- mode: js2;-*-",
|
||||||
|
"baseURL": ["_env", "http://localhost:${CAMLI_PORT}"],
|
||||||
|
"password": ["_env", "${CAMLI_PASSWORD}"],
|
||||||
|
"prefixes": {
|
||||||
|
"/": {
|
||||||
|
"handler": "filesystem",
|
||||||
|
"handlerArgs": {
|
||||||
|
"path": ["_env", "${CAMLI_ROOT}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda
|
||||||
|
Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6
|
||||||
|
oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw
|
||||||
|
nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe
|
||||||
|
KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/
|
||||||
|
BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw
|
||||||
|
EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD
|
||||||
|
g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W
|
||||||
|
JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl
|
||||||
|
kJrLdIhBajcJRYu/YGltHQRaXuVt
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL
|
||||||
|
Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S
|
||||||
|
jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC
|
||||||
|
AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI
|
||||||
|
YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH
|
||||||
|
aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7
|
||||||
|
HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs
|
||||||
|
jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz
|
||||||
|
mHRJOWk0jBtXfRft1McH2H51CpXAyw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -5,12 +5,15 @@ use FindBin qw($Bin);
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
die "Usage: dev-blobserver [--wipe] <portnumber> -- [other_blobserver_opts]";
|
die "Usage: dev-blobserver [--wipe] [--tls] <portnumber> -- [other_blobserver_opts]";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $opt_wipe;
|
my $opt_wipe;
|
||||||
GetOptions("wipe" => \$opt_wipe)
|
my $opt_tls;
|
||||||
or usage();
|
GetOptions(
|
||||||
|
"wipe" => \$opt_wipe,
|
||||||
|
"tls" => \$opt_tls,
|
||||||
|
) or usage();
|
||||||
|
|
||||||
my $port = shift || "3179";
|
my $port = shift || "3179";
|
||||||
usage() unless $port =~ /^\d+$/;
|
usage() unless $port =~ /^\d+$/;
|
||||||
|
@ -31,7 +34,10 @@ print "Starting blobserver on http://localhost:$port/ in $root\n";
|
||||||
$ENV{CAMLI_PASSWORD} = "pass$port";
|
$ENV{CAMLI_PASSWORD} = "pass$port";
|
||||||
$ENV{CAMLI_PORT} = $port;
|
$ENV{CAMLI_PORT} = $port;
|
||||||
$ENV{CAMLI_ROOT} = $root;
|
$ENV{CAMLI_ROOT} = $root;
|
||||||
|
$ENV{CAMLI_TLS_CRT_FILE} = $opt_tls ? "$Bin/config/dev-tls.crt" : "";
|
||||||
|
$ENV{CAMLI_TLS_KEY_FILE} = $opt_tls ? "$Bin/config/dev-tls.key" : "";
|
||||||
exec("$FindBin::Bin/server/go/camlistored/camlistored",
|
exec("$FindBin::Bin/server/go/camlistored/camlistored",
|
||||||
"-configfile=$Bin/config/dev-blobserver-config.json",
|
"-configfile=$Bin/config/dev-blobserver-config.json",
|
||||||
"-listen=:$port",
|
"-listen=:$port",
|
||||||
@ARGV);
|
@ARGV);
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,25 @@ import (
|
||||||
|
|
||||||
type Obj map[string]interface{}
|
type Obj map[string]interface{}
|
||||||
|
|
||||||
|
func (jc Obj) RequiredObject(key string) Obj {
|
||||||
|
jc.noteKnownKey(key)
|
||||||
|
ei, ok := jc[key]
|
||||||
|
if !ok {
|
||||||
|
jc.appendError(fmt.Errorf("Missing required config key %q (object)", key))
|
||||||
|
return make(Obj)
|
||||||
|
}
|
||||||
|
m, ok := ei.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
jc.appendError(fmt.Errorf("Expected config key %q to be an object, not %T", key, ei))
|
||||||
|
return make(Obj)
|
||||||
|
}
|
||||||
|
return Obj(m)
|
||||||
|
}
|
||||||
|
|
||||||
func (jc Obj) RequiredString(key string) string {
|
func (jc Obj) RequiredString(key string) string {
|
||||||
jc.noteKnownKey(key)
|
jc.noteKnownKey(key)
|
||||||
ei, ok := jc[key]
|
ei, ok := jc[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
jc.appendError(fmt.Errorf("Missing required config key %q (string)", key))
|
jc.appendError(fmt.Errorf("Missing required config key %q (string)", key))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,17 +32,15 @@ import (
|
||||||
|
|
||||||
var Listen = flag.String("listen", "0.0.0.0:2856", "host:port to listen on, or :0 to auto-select")
|
var Listen = flag.String("listen", "0.0.0.0:2856", "host:port to listen on, or :0 to auto-select")
|
||||||
|
|
||||||
var flagSelfUrlBase = flag.String("self-base-url", "", "If empty, automatic. Else of form https://foo.com (no trailing slash)")
|
|
||||||
var flagTLS = flag.Bool("tls", false, "Use TLS")
|
|
||||||
var flagCertFile = flag.String("tls-crt", "", "If using TLS, path to cert (public key) file.")
|
|
||||||
var flagKeyFile = flag.String("tls-key", "", "If using TLS, path to private key file.")
|
|
||||||
|
|
||||||
type HandlerPicker func(req *http.Request) (http.HandlerFunc, bool)
|
type HandlerPicker func(req *http.Request) (http.HandlerFunc, bool)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
premux []HandlerPicker
|
premux []HandlerPicker
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
|
|
||||||
|
enableTLS bool
|
||||||
|
tlsCertFile, tlsKeyFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Server {
|
func New() *Server {
|
||||||
|
@ -52,15 +50,21 @@ func New() *Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetTLS(certFile, keyFile string) {
|
||||||
|
s.enableTLS = true
|
||||||
|
s.tlsCertFile = certFile
|
||||||
|
s.tlsKeyFile = keyFile
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) BaseURL() string {
|
func (s *Server) BaseURL() string {
|
||||||
if *flagSelfUrlBase != "" {
|
scheme := "http"
|
||||||
// TODO: be automatic for TLS certs? find host name of cert inside it.
|
if s.enableTLS {
|
||||||
return *flagSelfUrlBase
|
scheme = "https"
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(*Listen, ":") {
|
if strings.HasPrefix(*Listen, ":") {
|
||||||
return "http://127.0.0.1" + *Listen
|
return scheme + "://127.0.0.1" + *Listen
|
||||||
}
|
}
|
||||||
return "http://" + strings.Replace(*Listen, "0.0.0.0:", "127.0.0.1:", 1)
|
return scheme + "://" + strings.Replace(*Listen, "0.0.0.0:", "127.0.0.1:", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register conditional handler-picker functions which get run before
|
// Register conditional handler-picker functions which get run before
|
||||||
|
@ -91,7 +95,7 @@ func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
func (s *Server) Serve() {
|
func (s *Server) Serve() {
|
||||||
if os.Getenv("TESTING_PORT_WRITE_FD") == "" { // Don't make noise during unit tests
|
if os.Getenv("TESTING_PORT_WRITE_FD") == "" { // Don't make noise during unit tests
|
||||||
log.Printf("Starting to listen on http://%v/\n", *Listen)
|
log.Printf("Starting to listen on %s\n", s.BaseURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
var err os.Error
|
var err os.Error
|
||||||
|
@ -100,14 +104,14 @@ func (s *Server) Serve() {
|
||||||
log.Fatalf("Failed to listen on %s: %v", *Listen, err)
|
log.Fatalf("Failed to listen on %s: %v", *Listen, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flagTLS {
|
if s.enableTLS {
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
Rand: rand.Reader,
|
Rand: rand.Reader,
|
||||||
Time: time.Seconds,
|
Time: time.Seconds,
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"http/1.1"},
|
||||||
}
|
}
|
||||||
config.Certificates = make([]tls.Certificate, 1)
|
config.Certificates = make([]tls.Certificate, 1)
|
||||||
config.Certificates[0], err = tls.LoadX509KeyPair(*flagCertFile, *flagKeyFile)
|
config.Certificates[0], err = tls.LoadX509KeyPair(s.tlsCertFile, s.tlsKeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load TLS cert: %v", err)
|
log.Fatalf("Failed to load TLS cert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,8 +204,8 @@ func configFileMain() {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
dj := json.NewDecoder(f)
|
dj := json.NewDecoder(f)
|
||||||
config := make(map[string]interface{})
|
rootjson := make(map[string]interface{})
|
||||||
if err = dj.Decode(&config); err != nil {
|
if err = dj.Decode(&rootjson); err != nil {
|
||||||
extra := ""
|
extra := ""
|
||||||
if serr, ok := err.(*json.SyntaxError); ok {
|
if serr, ok := err.(*json.SyntaxError); ok {
|
||||||
if _, serr := f.Seek(0, os.SEEK_SET); serr != nil {
|
if _, serr := f.Seek(0, os.SEEK_SET); serr != nil {
|
||||||
|
@ -218,26 +218,34 @@ func configFileMain() {
|
||||||
exitFailure("error parsing JSON object in config file %s%s\n%v",
|
exitFailure("error parsing JSON object in config file %s%s\n%v",
|
||||||
osutil.UserServerConfigPath(), extra, err)
|
osutil.UserServerConfigPath(), extra, err)
|
||||||
}
|
}
|
||||||
if err := jsonconfig.EvaluateExpressions(config); err != nil {
|
if err := jsonconfig.EvaluateExpressions(rootjson); err != nil {
|
||||||
exitFailure("error expanding JSON config expressions in %s: %v", configPath, err)
|
exitFailure("error expanding JSON config expressions in %s: %v", configPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := webserver.New()
|
ws := webserver.New()
|
||||||
baseURL := ws.BaseURL()
|
baseURL := ws.BaseURL()
|
||||||
|
|
||||||
if password, ok := config["password"].(string); ok {
|
// Root configuration
|
||||||
auth.AccessPassword = password
|
config := jsonconfig.Obj(rootjson)
|
||||||
|
|
||||||
|
{
|
||||||
|
cert, key := config.OptionalString("TLSCertFile", ""), config.OptionalString("TLSKeyFile", "")
|
||||||
|
if (cert != "") != (key != "") {
|
||||||
|
exitFailure("TLSCertFile and TLSKeyFile must both be either present or absent")
|
||||||
|
}
|
||||||
|
if cert != "" {
|
||||||
|
ws.SetTLS(cert, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if url, ok := config["baseURL"].(string); ok {
|
auth.AccessPassword = config.OptionalString("password", "")
|
||||||
|
if url := config.OptionalString("baseURL", ""); url != "" {
|
||||||
baseURL = url
|
baseURL = url
|
||||||
}
|
}
|
||||||
|
prefixes := config.RequiredObject("prefixes")
|
||||||
prefixes, ok := config["prefixes"].(map[string]interface{})
|
if err := config.Validate(); err != nil {
|
||||||
if !ok {
|
exitFailure("configuration error in root object's keys in %s: %v", configPath, err)
|
||||||
exitFailure("No top-level \"prefixes\": {...} in %s", osutil.UserServerConfigPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createdHandlers := make(map[string]interface{})
|
createdHandlers := make(map[string]interface{})
|
||||||
|
|
||||||
for prefix, vei := range prefixes {
|
for prefix, vei := range prefixes {
|
||||||
|
|
Loading…
Reference in New Issue