265 lines
5.9 KiB
Go
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
|
|
}
|