Skip to content

add reverse proxy mode #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/plugins/capture/capture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func newTestResponseWriter() *testResponseWriter {
}

func TestListenHTTP(t *testing.T) {
px = proxy.NewProxy()
px = proxy.NewProxy(listenHTTPAddr, "")

go func(t *testing.T) {
if err := px.Start(listenHTTPAddr); err != nil {
if err := px.Start(); err != nil {
t.Fatal(err)
}
}(t)
Expand Down
4 changes: 2 additions & 2 deletions lib/plugins/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ func newTestResponseWriter() *testResponseWriter {
}

func TestListenHTTP(t *testing.T) {
px = proxy.NewProxy()
px = proxy.NewProxy(listenHTTPAddr, "")

go func(t *testing.T) {
if err := px.Start(listenHTTPAddr); err != nil {
if err := px.Start(); err != nil {
t.Fatal(err)
}
}(t)
Expand Down
48 changes: 31 additions & 17 deletions lib/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ type Proxy struct {
interceptors []Interceptor
// Logger functions.
loggers []Logger
// The proxy's listen address
listenAddr string
// Destination server, if not empty, the proxy acts as a reverse proxy
dstAddr string
}

// ProxiedRequest struct provides properties for executing a *http.Request and
Expand All @@ -108,9 +112,14 @@ type ProxiedRequest struct {
Response *http.Response
}

// NewProxy creates and returns a Proxy reference.
func NewProxy() *Proxy {
return &Proxy{}
// NewProxy creates and returns a Proxy reference. the proxy will listen on the
// given address. if dstAddr is not empty, the proxy will be a reverse proxy,
// and forward request data to dstAddr
func NewProxy(listenAddr, dstServer string) *Proxy {
return &Proxy{
listenAddr: listenAddr,
dstAddr: dstServer,
}
}

// Reset clears the list of interfaces.
Expand Down Expand Up @@ -183,8 +192,13 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
out.URL.Scheme = "https"
}

if p.dstAddr != "" {
out.Host = p.dstAddr
}

// Making sure the Host header is present.
out.URL.Host = out.Host
log.Println("Host: ", out.Host);
out.Header.Add("Host", out.Host)

out.Proto = "HTTP/1.1"
Expand Down Expand Up @@ -280,14 +294,14 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

// Start creates an HTTP proxy server that listens on the given address.
func (p *Proxy) Start(addr string) error {
func (p *Proxy) Start() error {
p.srv = http.Server{
Addr: addr,
Addr: p.listenAddr,
Handler: p,
}
p.rt = &http.Transport{}

log.Printf("Listening for incoming HTTP client requests on %s.\n", addr)
log.Printf("Listening for incoming HTTP client requests on %s.\n", p.listenAddr)
if err := p.srv.ListenAndServe(); err != nil {
return err
}
Expand All @@ -309,22 +323,22 @@ func certificateLookup(clientHello *tls.ClientHelloInfo) (*tls.Certificate, erro
}

// StartTLS creates an HTTPs proxy server that listens on the given address.
func (p *Proxy) StartTLS(addr string) error {
func (p *Proxy) StartTLS() error {
p.srv = http.Server{
Addr: addr,
Addr: p.listenAddr,
Handler: p,
TLSConfig: &tls.Config{
GetCertificate: certificateLookup,
InsecureSkipVerify: false,
InsecureSkipVerify: true,
},
}
p.rt = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
InsecureSkipVerify: true,
},
}

log.Printf("Listening for incoming HTTPs client requests on %s.\n", addr)
log.Printf("Listening for incoming HTTPs client requests on %s.\n", p.listenAddr)

gencert.SetRootCACert(os.Getenv(EnvSSLCert))
gencert.SetRootCAKey(os.Getenv(EnvSSLKey))
Expand All @@ -336,25 +350,25 @@ func (p *Proxy) StartTLS(addr string) error {
}

// StartUnix creates an HTTP proxy server that listens on the proxy unix socket and forwards to proxied unix socket.
func (p *Proxy) StartUnix(proxy string, proxied string) error {
func (p *Proxy) StartUnix() error {
p.srv = http.Server{
Addr: "http+unix://proxy",
Handler: p,
}
u := &httpunix.Transport{}
u.RegisterLocation("proxied", proxied)
u.RegisterLocation("proxied", p.dstAddr)
p.rt = u
p.AddDirector(UnixDirector{"http+unix://proxied"})

log.Printf("Listening for incoming HTTP client requests on %s\n", proxy)
log.Printf("Listening for incoming HTTP client requests on %s\n", p.listenAddr)

os.Remove(proxy)
l, err := net.Listen("unix", proxy)
os.Remove(p.listenAddr)
l, err := net.Listen("unix", p.listenAddr)
if err != nil {
return err
}
defer l.Close()
defer os.Remove(proxy)
defer os.Remove(p.listenAddr)
return p.srv.Serve(l)
}

Expand Down
12 changes: 6 additions & 6 deletions lib/proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ func newTestResponseWriter() *testResponseWriter {
}

func TestListenHTTP(t *testing.T) {
proxy = NewProxy()
proxy = NewProxy(listenHTTPAddr, "")

go func(t *testing.T) {
if err := proxy.Start(listenHTTPAddr); err != nil {
if err := proxy.Start(); err != nil {
t.Fatal(err)
}
}(t)
Expand All @@ -137,10 +137,10 @@ func TestListenHTTP(t *testing.T) {
}

func TestListenHTTPs(t *testing.T) {
sslProxy = NewProxy()
sslProxy = NewProxy(listenHTTPsAddr, "")

go func(t *testing.T) {
if err := sslProxy.StartTLS(listenHTTPsAddr); err != nil {
if err := sslProxy.StartTLS(); err != nil {
t.Fatal(err)
}
}(t)
Expand Down Expand Up @@ -182,10 +182,10 @@ func TestUnixServer(t *testing.T) {
}

func TestListenUnix(t *testing.T) {
unixProxy = NewProxy()
unixProxy = NewProxy(listenUnixPath, serverUnixPath)

go func(t *testing.T) {
if err := unixProxy.StartUnix(listenUnixPath, serverUnixPath); err != nil {
if err := unixProxy.StartUnix(); err != nil {
t.Fatal(err)
}
}(t)
Expand Down
95 changes: 72 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ import (
"github.com/malfunkt/hyperfox/lib/plugins/logger"
"github.com/malfunkt/hyperfox/lib/proxy"
"upper.io/db.v3"
"strings"
)

const version = "1.9.8"

const (
defaultAddress = `0.0.0.0`
defaultPort = uint(1080)
defaultSSLPort = uint(10443)
defaultSSLPort = uint(0)
proxyUnixSocket = `/tmp/hyperfox`
)

Expand All @@ -51,6 +52,12 @@ var (
flagSSLCertFile = flag.String("c", "", "Path to root CA certificate.")
flagSSLKeyFile = flag.String("k", "", "Path to root CA key.")
flagUnixSocket = flag.String("S", "", "Path to socket.")
flagHTTPProxy = flag.String("httpProxy", "",
"start http reverse proxy, format example: 10.0.0.2:80-192.168.0.2:80, " +
"part before dash is listen address, part after dash is destination address.")
flagHTTPSProxy = flag.String("httpsProxy", "",
"start https reverse proxy, format example: 10.0.0.2:443-192.168.0.2:443, " +
"part before dash is listen address, part after dash is destination address.")
)

var (
Expand All @@ -60,7 +67,7 @@ var (

func main() {
// Banner.
log.Printf("Hyperfox v%s // https://hyperfox.org\n", version)
log.Printf("Hyperfox v%s // https://hyperfox.org", version)
log.Printf("By José Carlos Nieto.\n\n")

// Parsing command line flags.
Expand All @@ -80,13 +87,7 @@ func main() {
}

// Is SSL enabled?
var sslEnabled bool
if *flagSSLPort > 0 && *flagSSLCertFile != "" {
sslEnabled = true
}

// User requested SSL mode.
if sslEnabled {
if *flagSSLPort > 0 || *flagHTTPSProxy != "" {
if *flagSSLCertFile == "" {
flag.Usage()
log.Fatal("Missing root CA certificate")
Expand All @@ -101,16 +102,9 @@ func main() {
os.Setenv(proxy.EnvSSLKey, *flagSSLKeyFile)
}

// Creating proxy.
p := proxy.NewProxy()

// Attaching logger.
p.AddLogger(logger.Stdout{})

// Attaching capture tool.
res := make(chan capture.Response, 256)

p.AddBodyWriteCloser(capture.New(res))
capt := capture.New(res)

// Saving captured data with a goroutine.
go func() {
Expand Down Expand Up @@ -139,18 +133,49 @@ func main() {
wg.Add(1)
go func() {
defer wg.Done()
if err := p.Start(fmt.Sprintf("%s:%d", *flagAddress, *flagPort)); err != nil {
log.Fatalf("Failed to bind on the given interface (HTTP): ", err)
addr := fmt.Sprintf("%s:%d", *flagAddress, *flagPort)
if err := startHTTPProxy(addr, "", capt); err != nil {
log.Fatalf("Failed to route mode http proxy, bind address: %s, error: %s", addr, err)
}
}()
}

// start http reverse proxy
if *flagHTTPProxy != "" {
addrs := strings.Split(*flagHTTPProxy, "-")
if len(addrs) != 2 {
log.Fatalf("'%s' is not valid http proxy address", flagHTTPProxy)
}
wg.Add(1)
go func() {
defer wg.Done()
if err := startHTTPProxy(addrs[0], addrs[1], capt); err != nil {
log.Fatalf("Failed to http reverse proxy, address: %s, error: %s", *flagHTTPProxy, err)
}
}()
}

if *flagSSLPort > 0 {
wg.Add(1)
go func() {
defer wg.Done()
addr := fmt.Sprintf("%s:%d", *flagAddress, *flagSSLPort)
if err := startHTTPSProxy(addr, "", capt); err != nil {
log.Fatalf("Failed to route mode https proxy, bind address: %s, error: %s", addr, err)
}
}()
}

if sslEnabled {
if *flagHTTPSProxy != "" {
addrs := strings.Split(*flagHTTPSProxy, "-")
if len(addrs) != 2 {
log.Fatalf("'%s' is not valid https proxy address", *flagHTTPSProxy)
}
wg.Add(1)
go func() {
defer wg.Done()
if err := p.StartTLS(fmt.Sprintf("%s:%d", *flagAddress, *flagSSLPort)); err != nil {
log.Fatalf("Failed to bind on the given interface (HTTPS): ", err)
if err := startHTTPSProxy(addrs[0], addrs[1], capt); err != nil {
log.Fatalf("Failed to https reverse proxy, address: %s, error: %s", *flagHTTPSProxy, err)
}
}()
}
Expand All @@ -159,11 +184,35 @@ func main() {
wg.Add(1)
go func() {
defer wg.Done()
if err := p.StartUnix(proxyUnixSocket, *flagUnixSocket); err != nil {
if err := startUnixSocketProxy(proxyUnixSocket, *flagUnixSocket, capt); err != nil {
log.Fatalf("Failed to bind on %s: %s", proxyUnixSocket, err)
}
}()
}

wg.Wait()
}

func startHTTPProxy(listenAddr, dstAddr string, capt *capture.Capture) error {
p := proxy.NewProxy(listenAddr, dstAddr)
// Attaching logger.
p.AddLogger(logger.Stdout{})
p.AddBodyWriteCloser(capt)
return p.Start()
}

func startHTTPSProxy(listenAddr, dstAddr string, capt *capture.Capture) error {
p := proxy.NewProxy(listenAddr, dstAddr)
// Attaching logger.
p.AddLogger(logger.Stdout{})
p.AddBodyWriteCloser(capt)
return p.StartTLS()
}

func startUnixSocketProxy(listenAddr, dstAddr string, capt *capture.Capture) error {
p := proxy.NewProxy(listenAddr, dstAddr)
// Attaching logger.
p.AddLogger(logger.Stdout{})
p.AddBodyWriteCloser(capt)
return p.StartUnix()
}