diff --git a/README.md b/README.md index 26ee7b9..3016159 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Dumbiest HTTP proxy ever. * Zero-configuration * Supports CONNECT method and forwarding of HTTPS connections * Supports `Basic` proxy authentication +* Supports TLS operation mode (HTTP(S) proxy over TLS) ## Installation @@ -72,6 +73,10 @@ $ ~/go/bin/dumbproxy -h auth parameters (default "none://") -bind-address string HTTP proxy listen address (default ":8080") + -cert string + enable TLS and use certificate + -key string + key for TLS certificate -timeout duration timeout for network operations (default 10s) -verbosity int diff --git a/main.go b/main.go index 93f1758..f3ef5c7 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ type CLIArgs struct { auth string verbosity int timeout time.Duration + cert, key string } @@ -36,6 +37,8 @@ func parse_args() CLIArgs { flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity " + "(10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical)") flag.DurationVar(&args.timeout, "timeout", 10 * time.Second, "timeout for network operations") + flag.StringVar(&args.cert, "cert", "", "enable TLS and use certificate") + flag.StringVar(&args.key, "key", "", "key for TLS certificate") flag.Parse() return args } @@ -52,14 +55,30 @@ func run() int { proxyLogger := NewCondLogger(log.New(logWriter, "PROXY : ", log.LstdFlags | log.Lshortfile), args.verbosity) - mainLogger.Info("Starting proxy server...") + auth, err := NewAuth(args.auth) if err != nil { mainLogger.Critical("Failed to instantiate auth provider: %v", err) return 3 } - handler := NewProxyHandler(args.timeout, auth, proxyLogger) - err = http.ListenAndServe(args.bind_address, handler) + + var server http.Server + server.Addr = args.bind_address + server.Handler = NewProxyHandler(args.timeout, auth, proxyLogger) + server.ErrorLog = log.New(logWriter, "HTTPSRV : ", log.LstdFlags | log.Lshortfile) + + mainLogger.Info("Starting proxy server...") + if args.cert != "" { + cfg, err1 := makeServerTLSConfig(args.cert, args.key, "") + if err1 != nil { + mainLogger.Critical("TLS config construction failed: %v", err) + return 3 + } + server.TLSConfig = cfg + err = server.ListenAndServeTLS("", "") + } else { + err = server.ListenAndServe() + } mainLogger.Critical("Server terminated with a reason: %v", err) mainLogger.Info("Shutting down...") return 0 diff --git a/utils.go b/utils.go index a39e7cc..919663c 100644 --- a/utils.go +++ b/utils.go @@ -9,6 +9,9 @@ import ( "errors" "net/http" "bufio" + "crypto/tls" + "crypto/x509" + "io/ioutil" ) const COPY_BUF = 128 * 1024 @@ -108,3 +111,25 @@ func copyBody(wr io.Writer, body io.Reader) { } } } + +func makeServerTLSConfig(certfile, keyfile, cafile string) (*tls.Config, error) { + var cfg tls.Config + cert, err := tls.LoadX509KeyPair(certfile, keyfile) + if err != nil { + return nil, err + } + cfg.Certificates = []tls.Certificate{cert} + if cafile != "" { + roots := x509.NewCertPool() + certs, err := ioutil.ReadFile(cafile) + if err != nil { + return nil, err + } + if ok := roots.AppendCertsFromPEM(certs); !ok { + return nil, errors.New("Failed to load CA certificates") + } + cfg.ClientCAs = roots + cfg.ClientAuth = tls.VerifyClientCertIfGiven + } + return &cfg, nil +}