commit
3074510fea
35
README.md
35
README.md
|
@ -26,6 +26,7 @@ You can say thanks to the author by donations to these wallets:
|
||||||
* Supports CONNECT method and forwarding of HTTPS connections
|
* Supports CONNECT method and forwarding of HTTPS connections
|
||||||
* Supports `Basic` proxy authentication
|
* Supports `Basic` proxy authentication
|
||||||
* Supports TLS operation mode (HTTP(S) proxy over TLS)
|
* Supports TLS operation mode (HTTP(S) proxy over TLS)
|
||||||
|
* Native ACME support (can issue TLS certificates automatically using Let's Encrypt or BuyPass)
|
||||||
* Supports client authentication with client TLS certificates
|
* Supports client authentication with client TLS certificates
|
||||||
* Supports HTTP/2
|
* Supports HTTP/2
|
||||||
* Resilient to DPI (including active probing, see `hidden_domain` option for authentication providers)
|
* Resilient to DPI (including active probing, see `hidden_domain` option for authentication providers)
|
||||||
|
@ -69,15 +70,33 @@ sudo snap install dumbproxy
|
||||||
|
|
||||||
Just run program and it'll start accepting connections on port 8080 (default).
|
Just run program and it'll start accepting connections on port 8080 (default).
|
||||||
|
|
||||||
Example: run proxy on port 1234 with `Basic` authentication with username `admin` and password `123456`:
|
### Example: plain proxy
|
||||||
|
|
||||||
|
Run proxy on port 1234 with `Basic` authentication with username `admin` and password `123456`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dumbproxy -bind-address :1234 -auth 'static://?username=admin&password=123456'
|
dumbproxy -bind-address :1234 -auth 'static://?username=admin&password=123456'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example: HTTP proxy over TLS (LetsEncrypt automatic certs)
|
||||||
|
|
||||||
|
Run HTTPS proxy (HTTP proxy over TLS) with automatic certs from LetsEncrypt on port 443 with `Basic` authentication with username `admin` and password `123456`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
dumbproxy -bind-address :443 -auth 'static://?username=admin&password=123456' -autocert
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: HTTP proxy over TLS (BuyPass automatic certs)
|
||||||
|
|
||||||
|
Run HTTPS proxy (HTTP proxy over TLS) with automatic certs from BuyPass on port 443 with `Basic` authentication with username `admin` and password `123456`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
dumbproxy -bind-address :443 -auth 'static://?username=admin&password=123456' -autocert -autocert-acme 'https://api.buypass.com/acme/directory' -autocert-email YOUR-EMAIL@EXAMPLE.ORG -autocert-http :80
|
||||||
|
```
|
||||||
|
|
||||||
## Using HTTP-over-TLS proxy
|
## Using HTTP-over-TLS proxy
|
||||||
|
|
||||||
It's quite trivial to set up program which supports proxies to use dumbproxy in plain HTTP mode. However, using HTTP proxy over TLS connection with browsers is little bit tricky. Note that TLS must be enabled (`-cert` and `-key` options) for this to work.
|
It's quite trivial to set up program which supports proxies to use dumbproxy in plain HTTP mode. However, using HTTP proxy over TLS connection with browsers is little bit tricky. Note that TLS must be enabled (`-cert` and `-key` options or `-autocert` option) for this to work.
|
||||||
|
|
||||||
### Routing all browsers on Windows via HTTPS proxy
|
### Routing all browsers on Windows via HTTPS proxy
|
||||||
|
|
||||||
|
@ -149,6 +168,18 @@ Authentication parameters are passed as URI via `-auth` parameter. Scheme of URI
|
||||||
$ ~/go/bin/dumbproxy -h
|
$ ~/go/bin/dumbproxy -h
|
||||||
-auth string
|
-auth string
|
||||||
auth parameters (default "none://")
|
auth parameters (default "none://")
|
||||||
|
-autocert
|
||||||
|
issue TLS certificates automatically
|
||||||
|
-autocert-acme string
|
||||||
|
custom ACME endpoint (default "https://acme-v02.api.letsencrypt.org/directory")
|
||||||
|
-autocert-dir string
|
||||||
|
path to autocert cache (default "/home/user/.dumbproxy/autocert")
|
||||||
|
-autocert-email string
|
||||||
|
email used for ACME registration
|
||||||
|
-autocert-http string
|
||||||
|
listen address for HTTP-01 challenges handler of ACME
|
||||||
|
-autocert-whitelist value
|
||||||
|
restrict autocert domains to this comma-separated list
|
||||||
-bind-address string
|
-bind-address string
|
||||||
HTTP proxy listen address (default ":8080")
|
HTTP proxy listen address (default ":8080")
|
||||||
-cafile string
|
-cafile string
|
||||||
|
|
3
auth.go
3
auth.go
|
@ -5,12 +5,13 @@ import (
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AUTH_REQUIRED_MSG = "Proxy authentication required.\n"
|
const AUTH_REQUIRED_MSG = "Proxy authentication required.\n"
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -2,4 +2,7 @@ module github.com/Snawoot/dumbproxy
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
|
require (
|
||||||
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||||
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||||
|
)
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -1,7 +1,15 @@
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|
58
main.go
58
main.go
|
@ -7,10 +7,16 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
home, _ = os.UserHomeDir()
|
||||||
version = "undefined"
|
version = "undefined"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +32,23 @@ func arg_fail(msg string) {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CSVArg []string
|
||||||
|
|
||||||
|
func (a *CSVArg) Set(s string) error {
|
||||||
|
*a = strings.Split(s, ",")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *CSVArg) String() string {
|
||||||
|
if a == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
if *a == nil {
|
||||||
|
return "<empty>"
|
||||||
|
}
|
||||||
|
return strings.Join(*a, ",")
|
||||||
|
}
|
||||||
|
|
||||||
type CLIArgs struct {
|
type CLIArgs struct {
|
||||||
bind_address string
|
bind_address string
|
||||||
auth string
|
auth string
|
||||||
|
@ -35,7 +58,13 @@ type CLIArgs struct {
|
||||||
list_ciphers bool
|
list_ciphers bool
|
||||||
ciphers string
|
ciphers string
|
||||||
disableHTTP2 bool
|
disableHTTP2 bool
|
||||||
showVersion bool
|
showVersion bool
|
||||||
|
autocert bool
|
||||||
|
autocertWhitelist CSVArg
|
||||||
|
autocertDir string
|
||||||
|
autocertACME string
|
||||||
|
autocertEmail string
|
||||||
|
autocertHTTP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func list_ciphers() {
|
func list_ciphers() {
|
||||||
|
@ -58,6 +87,12 @@ func parse_args() CLIArgs {
|
||||||
flag.StringVar(&args.ciphers, "ciphers", "", "colon-separated list of enabled ciphers")
|
flag.StringVar(&args.ciphers, "ciphers", "", "colon-separated list of enabled ciphers")
|
||||||
flag.BoolVar(&args.disableHTTP2, "disable-http2", false, "disable HTTP2")
|
flag.BoolVar(&args.disableHTTP2, "disable-http2", false, "disable HTTP2")
|
||||||
flag.BoolVar(&args.showVersion, "version", false, "show program version and exit")
|
flag.BoolVar(&args.showVersion, "version", false, "show program version and exit")
|
||||||
|
flag.BoolVar(&args.autocert, "autocert", false, "issue TLS certificates automatically")
|
||||||
|
flag.Var(&args.autocertWhitelist, "autocert-whitelist", "restrict autocert domains to this comma-separated list")
|
||||||
|
flag.StringVar(&args.autocertDir, "autocert-dir", filepath.Join(home, ".dumbproxy", "autocert"), "path to autocert cache")
|
||||||
|
flag.StringVar(&args.autocertACME, "autocert-acme", autocert.DefaultACMEDirectory, "custom ACME endpoint")
|
||||||
|
flag.StringVar(&args.autocertEmail, "autocert-email", "", "email used for ACME registration")
|
||||||
|
flag.StringVar(&args.autocertHTTP, "autocert-http", "", "listen address for HTTP-01 challenges handler of ACME")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
@ -70,7 +105,6 @@ func run() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if args.list_ciphers {
|
if args.list_ciphers {
|
||||||
list_ciphers()
|
list_ciphers()
|
||||||
return 0
|
return 0
|
||||||
|
@ -116,6 +150,26 @@ func run() int {
|
||||||
cfg.CipherSuites = makeCipherList(args.ciphers)
|
cfg.CipherSuites = makeCipherList(args.ciphers)
|
||||||
server.TLSConfig = cfg
|
server.TLSConfig = cfg
|
||||||
err = server.ListenAndServeTLS("", "")
|
err = server.ListenAndServeTLS("", "")
|
||||||
|
} else if args.autocert {
|
||||||
|
m := &autocert.Manager{
|
||||||
|
Cache: autocert.DirCache(args.autocertDir),
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
Client: &acme.Client{DirectoryURL: args.autocertACME},
|
||||||
|
Email: args.autocertEmail,
|
||||||
|
}
|
||||||
|
if args.autocertWhitelist != nil {
|
||||||
|
m.HostPolicy = autocert.HostWhitelist([]string(args.autocertWhitelist)...)
|
||||||
|
}
|
||||||
|
if args.autocertHTTP != "" {
|
||||||
|
go func() {
|
||||||
|
log.Fatalf("HTTP-01 ACME challenge server stopped: %v",
|
||||||
|
http.ListenAndServe(args.autocertHTTP, m.HTTPHandler(nil)))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
cfg := m.TLSConfig()
|
||||||
|
cfg.CipherSuites = makeCipherList(args.ciphers)
|
||||||
|
server.TLSConfig = cfg
|
||||||
|
err = server.ListenAndServeTLS("", "")
|
||||||
} else {
|
} else {
|
||||||
err = server.ListenAndServe()
|
err = server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue