Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
praveenkumar committed Sep 27, 2021
1 parent 388eb44 commit bf15463
Show file tree
Hide file tree
Showing 29 changed files with 2,348 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
out/
30 changes: 30 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
SHELL := /bin/bash

# Go and compilation related variables
BUILD_DIR ?= out
BINARY_NAME ?= proxy

# Add default target
.PHONY: default
default: install

.PHONY: install
install: $(SOURCES)
go build -o $(BINARY_NAME)

$(BUILD_DIR)/macos-amd64/$(BINARY_NAME):
GOARCH=amd64 GOOS=darwin go build -o $(BUILD_DIR)/macos-amd64/$(BINARY_NAME)

$(BUILD_DIR)/linux-amd64/$(BINARY_NAME):
GOOS=linux GOARCH=amd64 go build -o $(BUILD_DIR)/linux-amd64/$(BINARY_NAME)

$(BUILD_DIR)/windows-amd64/$(BINARY_NAME).exe:
GOARCH=amd64 GOOS=windows go build -o $(BUILD_DIR)/windows-amd64/$(BINARY_NAME).exe

.PHONY: cross ## Cross compiles all binaries
cross: $(BUILD_DIR)/macos-amd64/$(BINARY_NAME) $(BUILD_DIR)/linux-amd64/$(BINARY_NAME) $(BUILD_DIR)/windows-amd64/$(BINARY_NAME).exe

.PHONY: clean
clean:
rm -fr $(BUILD_DIR)
rm -fr $(BINARY_NAME)
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,52 @@
# proxy
Run http/https proxy using go
Run http/https proxy server.

```
$ ./proxy -h
Usage of ./pro:
-ca-cert-path string
CA cert path for TLS
-ca-key-path string
CA key path for TLS
-host string
host (domain/IP) for alternate dns in certificate
-port int
Port to serve non tls proxy (default 8080)
-ssl-port int
SSL port to serve tls proxy (default 8443)
```

If a user already have CA cert/key then use `--ca-cert-path`
and `--ca-key-path` otherwise it will generate and provide
the details about created cert/key.

Example
------

```
$ ./proxy
2021/09/27 14:25:42 CA cert file /tmp/proxy-ca290921658/proxy-cert.pem
2021/09/27 14:25:42 CA key file /tmp/proxy-ca290921658/proxy-key.pem
```

```
// HTTP proxy
$ curl -x http://localhost:8080 -Ik gandi.net
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Content-Length: 0
Date: Mon, 27 Sep 2021 08:56:58 GMT
Location: http://www.gandi.net/
Server: Varnish
// HTTPS proxy
$ curl --proxy-cacert /tmp/proxy-ca290921658/proxy-cert.pem -x https://localhost:8443 -Ik https://gandi.net
HTTP/1.0 200 OK
HTTP/1.1 301 Moved Permanently
Date: Mon, 27 Sep 2021 08:57:42 GMT
Server: Varnish
Location: https://www.gandi.net/
Content-Length: 0
Connection: keep-alive
```
97 changes: 97 additions & 0 deletions cert_generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package main

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net"
"os"
"path/filepath"
"time"
)

// https://gist.github.com/samuel/8b500ddd3f6118d052b5e6bc16bc4c09
func publicKey(priv interface{}) interface{} {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &k.PublicKey
case *ecdsa.PrivateKey:
return &k.PublicKey
default:
return nil
}
}

func pemBlockForKey(priv interface{}) *pem.Block {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
case *ecdsa.PrivateKey:
b, err := x509.MarshalECPrivateKey(k)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
os.Exit(2)
}
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
default:
return nil
}
}

func generateCA(hosts []string) (string, string, error) {
tmpDir, err := ioutil.TempDir("", "proxy-ca")
if err != nil {
return "", "", err
}

priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return "", "", err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Example Co."},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180),

KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
}
}

derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil {
return "", "", err
}
cert, key := &bytes.Buffer{}, &bytes.Buffer{}
pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
pem.Encode(key, pemBlockForKey(priv))

keyFilePath := filepath.Join(tmpDir, "proxy-key.pem")
certFilePath := filepath.Join(tmpDir, "proxy-cert.pem")
if err := ioutil.WriteFile(keyFilePath, key.Bytes(), 0600); err != nil {
return "", "", err
}
if err := ioutil.WriteFile(certFilePath, cert.Bytes(), 0600); err != nil {
return "", "", err
}
return certFilePath, keyFilePath, nil
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/praveenkumar/proxy

go 1.16

require github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY=
github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
89 changes: 89 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package main

import (
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"

"github.com/elazarl/goproxy"
)

func serverHTTP(addr string, handler http.Handler, err chan<- error) {
err <- http.ListenAndServe(addr, handler)
}

func serverHTTPS(addr, caCert, caKey string, handler http.Handler, err chan<- error) {
err <- http.ListenAndServeTLS(addr, caCert, caKey, handler)
}

func main() {
// Create the flags to get parameter around ca certs
var port, sslPort int
var caCertPath, caKeyPath, host string
flag.IntVar(&port, "port", 8080, "Port to serve non tls proxy")
flag.IntVar(&sslPort, "ssl-port", 8443, "SSL port to serve tls proxy")

flag.StringVar(&caCertPath, "ca-cert-path", "", "CA cert path for TLS")
flag.StringVar(&caKeyPath, "ca-key-path", "", "CA key path for TLS")
flag.StringVar(&host, "host", "", "host (domain/IP) for alternate dns in certificate")

flag.Parse()

if (caKeyPath != "" && caCertPath == "") || (caKeyPath == "" && caCertPath != "") {
log.Fatalf("ca-cert-path and ca-key-path should be used together")
}

// Create error channel
errCh := make(chan error, 1)

if caKeyPath == "" && caCertPath == "" {
var err error
hosts := []string{"localhost", "127.0.0.1", "192.168.122.1"}
if host != "" {
hosts = append(hosts, host)
}
caCertPath, caKeyPath, err = generateCA(hosts)
if err != nil {
log.Fatal("Failed to generate proxy CA")
}
log.Printf("CA cert file %s\n", caCertPath)
log.Printf("CA key file %s\n", caKeyPath)
}
{
// Get the system certificate pools
caCertPool, err := x509.SystemCertPool()
if err != nil {
log.Fatalf("Not able to get system certificate pool %v", err)
}
caCert, err := ioutil.ReadFile(caCertPath)
if err != nil {
log.Fatalf("Error reading %s file %v", caCertPath, err)
}
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
log.Fatal("Failed to append proxy CA to system CAs")
}

proxy := goproxy.NewProxyHttpServer()
proxy.Tr = &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
RootCAs: caCertPool,
},
}
proxy.Verbose = true
p := fmt.Sprintf(":%d", sslPort)
go serverHTTPS(p, caCertPath, caKeyPath, proxy, errCh)
}

p := fmt.Sprintf(":%d", port)
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = true

go serverHTTP(p, proxy, errCh)
log.Fatal(<-errCh)
}
2 changes: 2 additions & 0 deletions vendor/github.com/elazarl/goproxy/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions vendor/github.com/elazarl/goproxy/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bf15463

Please sign in to comment.