Files
fproxy/fproxy/new.go
Bel LaPointe a36266b30b chain works
2019-02-17 12:12:46 -07:00

265 lines
5.9 KiB
Go

package fproxy
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"sort"
"strings"
)
type FProxy struct {
transport *http.Transport
sserver *http.Server
iserver *http.Server
httpport string
tlsport string
whitelist []string
bypass []string
secure []string
localhost []string
mykey string
mycrt string
fromcrt string
tocrt string
sockssecret string
}
func GetPort() string {
s := httptest.NewUnstartedServer(nil)
s.Close()
return ":" + strings.Split(s.Listener.Addr().String(), ":")[1]
}
func (fp *FProxy) Apply(conf map[string]string) error {
if v, ok := conf["secret"]; ok && v != "" {
fp.sockssecret = v
}
if v, ok := conf["whitelist"]; ok && v != "" {
fp.WithWhitelist(strings.Split(v, ",,"))
}
if v, ok := conf["bypass"]; ok && v != "" {
fp.WithBypass(strings.Split(v, ",,"))
}
if v, ok := conf["secure"]; ok && v != "" {
fp.WithSecure(strings.Split(v, ",,"))
}
if v, ok := conf["port"]; ok {
fp.setPorts(v)
} else {
fp.setPorts()
}
if v, ok := conf["mycrt"]; ok && v != "" {
if err := fp.WithCerts(v, conf["mykey"]); err != nil {
return err
}
}
if v, ok := conf["fromcrt"]; ok && v != "" {
if err := fp.WithFrom(v); err != nil {
return err
}
}
if v, ok := conf["tocrt"]; ok && v != "" {
if err := fp.WithTo(conf["toaddr"], v); err != nil {
return err
}
}
return nil
}
func New(ports ...string) *FProxy {
fp := &FProxy{
transport: &http.Transport{},
sserver: &http.Server{},
iserver: &http.Server{},
localhost: []string{
"[::1]",
"127.0.0.1",
"::1",
"bel.pc",
"192.168.0.",
"172.17.0.",
},
}
sort.Strings(fp.localhost)
fp.sserver.Handler = fp
fp.iserver.Handler = fp
return fp
}
func (fp *FProxy) setPorts(ports ...string) {
if len(ports) == 0 {
ports = []string{GetPort()}
}
if len(ports) > 0 {
p := string(strings.TrimPrefix(ports[0], ":")[0] + byte(1))
if p[0] == '7' {
p = "1"
}
ports = []string{ports[0], ":" + p + strings.TrimPrefix(ports[0], ":")[1:]}
}
fp.httpport = ports[0]
fp.tlsport = ports[1]
fp.sserver.Addr = fp.httpport
fp.iserver.Addr = fp.httpport
return
}
func NewClient(clientCrt, clientKey, proxyCrt, proxyPort string) (*http.Client, error) {
return (&FProxy{
httpport: proxyPort,
}).NewClient(clientCrt, clientKey, proxyCrt)
}
func (fp *FProxy) NewClient(certs ...string) (*http.Client, error) {
myport := fp.httpport
if !strings.HasPrefix(myport, ":") {
myport = ":" + myport
}
proxyCrt := fp.mycrt
clientCrt := ""
clientKey := ""
if len(certs) > 1 {
clientCrt = certs[0]
clientKey = certs[1]
if len(certs) > 2 && certs[2] != "" {
proxyCrt = certs[2]
}
}
hclient := &http.Client{}
transport := &http.Transport{
Proxy: func(r *http.Request) (*url.URL, error) {
if clientCrt == "" {
return url.Parse("http://localhost" + myport)
} else {
return url.Parse("https://localhost" + myport)
}
},
}
if proxyCrt == "" {
hclient.Transport = transport
return hclient, nil
}
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
cert, err := ioutil.ReadFile(proxyCrt)
if err != nil {
return nil, err
}
rootCAs.AppendCertsFromPEM(cert)
transport.TLSClientConfig = &tls.Config{
RootCAs: rootCAs,
}
transport.TLSClientConfig.BuildNameToCertificate()
if clientCrt == "" {
hclient.Transport = transport
return hclient, nil
} else if clientKey == "" {
return nil, errors.New("FProxy requires cert files")
}
clientCert, err := tls.LoadX509KeyPair(clientCrt, clientKey)
if err != nil {
return nil, err
}
transport.TLSClientConfig.Certificates = []tls.Certificate{clientCert}
transport.TLSClientConfig.BuildNameToCertificate()
hclient.Transport = transport
return hclient, nil
}
func (fp *FProxy) WithTo(addr, tocrt string) error {
fp.tocrt = tocrt
toCert, err := ioutil.ReadFile(tocrt)
if err != nil {
return err
}
rootCAs, err := x509.SystemCertPool()
if err != nil {
return err
}
rootCAs.AppendCertsFromPEM(toCert)
if fp.transport.TLSClientConfig == nil {
fp.transport.TLSClientConfig = &tls.Config{}
}
fp.transport.TLSClientConfig.RootCAs = rootCAs
fp.transport.Proxy = func(*http.Request) (*url.URL, error) {
u, err := url.Parse(addr)
if err != nil {
return nil, err
}
u.Scheme = "https"
return u, nil
}
return nil
}
func (fp *FProxy) WithFrom(fromcrt string) error {
fp.fromcrt = fromcrt
if fp.sserver.TLSConfig == nil {
fp.sserver.TLSConfig = &tls.Config{}
}
caCert, err := ioutil.ReadFile(fromcrt)
if err != nil {
return err
}
clientCAs := x509.NewCertPool()
clientCAs.AppendCertsFromPEM(caCert)
fp.sserver.TLSConfig.ClientCAs = clientCAs
fp.sserver.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
fp.sserver.TLSConfig.MinVersion = tls.VersionTLS12
fp.sserver.TLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}
fp.sserver.TLSConfig.PreferServerCipherSuites = true
fp.sserver.TLSConfig.CipherSuites = []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
}
fp.sserver.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0)
return nil
}
func (fp *FProxy) WithCerts(crt, key string) error {
fp.mycrt = crt
fp.mykey = key
clientCert, err := tls.LoadX509KeyPair(crt, key)
if err != nil {
return err
}
if fp.transport.TLSClientConfig == nil {
fp.transport.TLSClientConfig = &tls.Config{}
}
fp.transport.TLSClientConfig.Certificates = []tls.Certificate{clientCert}
return nil
}
func (fp *FProxy) WithWhitelist(whitelist []string) {
sort.Strings(whitelist)
fp.whitelist = whitelist
}
func (fp *FProxy) WithBypass(bypass []string) {
sort.Strings(bypass)
fp.bypass = bypass
}
func (fp *FProxy) WithSecure(secure []string) {
sort.Strings(secure)
fp.secure = secure
}