Serve PAC files from memory
This commit is contained in:
parent
9e92549830
commit
f2019d57c4
6 changed files with 91 additions and 8 deletions
|
|
@ -13,6 +13,7 @@ forwardproxy {
|
|||
basicauth caddyuser2 秘密
|
||||
ports 80 443
|
||||
hide_ip
|
||||
serve_pac proxy.pac
|
||||
experimental_probe_resist secretlink-7qS4+3dqm.localhost
|
||||
response_timeout 30
|
||||
dial_timeout 30
|
||||
|
|
@ -28,7 +29,11 @@ Default: no restrictions.
|
|||
* hide_ip
|
||||
If set, forwardproxy will not add user's IP to "Forwarded:" header.
|
||||
Default: no hiding, "_Forwarded: for="useraddress"_" will be sent out.
|
||||
* experimental_probe_resist secretlink.tld
|
||||
* serve_pac path_to_pac_file.pac(optional)
|
||||
Generate in memory and serve [Proxy Auto-Config](https://en.wikipedia.org/wiki/Proxy_auto-config) file on given path.
|
||||
If no path is provided, PAC file will be served at /proxy.pac
|
||||
Default: no PAC file generated by Caddy (you still can manually create and serve proxy.pac like a regular file)
|
||||
* experimental_probe_resist secretlink.tld(optional)
|
||||
EXPERIMENTAL, HERE BE DRAGONS.
|
||||
Attempts to hide the fact that the site is a forwardproxy.
|
||||
Proxy will no longer respond with _"407 Proxy Authentication Required"_ if credentials are incorrect or absent,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ func (c *caddyTestServer) StartTestServer() {
|
|||
func TestMain(m *testing.M) {
|
||||
caddyForwardProxy = caddyTestServer{addr: "127.0.0.1:1984", root: "./test/forwardproxy",
|
||||
directives: []string{"tls self_signed"},
|
||||
proxyEnabled: true}
|
||||
proxyEnabled: true, proxyDirectives: []string{"serve_pac"}}
|
||||
caddyForwardProxy.StartTestServer()
|
||||
|
||||
caddyForwardProxyAuth = caddyTestServer{addr: "127.0.0.1:4891", root: "./test/forwardproxy",
|
||||
|
|
@ -121,7 +121,9 @@ func TestMain(m *testing.M) {
|
|||
|
||||
caddyForwardProxyProbeResist = caddyTestServer{addr: "127.0.0.1:8888", root: "./test/forwardproxy",
|
||||
directives: []string{"tls self_signed"}, HTTPRedirectPort: "8880",
|
||||
proxyEnabled: true, proxyDirectives: []string{"basicauth test pass", "experimental_probe_resist test.localhost"}}
|
||||
proxyEnabled: true, proxyDirectives: []string{"basicauth test pass",
|
||||
"experimental_probe_resist test.localhost",
|
||||
"serve_pac superhiddenfile.pac"}}
|
||||
caddyForwardProxyProbeResist.StartTestServer()
|
||||
|
||||
caddyDummyProbeResist = caddyTestServer{addr: "127.0.0.1:9999", root: "./test/forwardproxy",
|
||||
|
|
|
|||
|
|
@ -39,9 +39,11 @@ type ForwardProxy struct {
|
|||
hideIP bool
|
||||
whitelistedPorts []int
|
||||
probeResistDomain string
|
||||
pacFilePath string
|
||||
probeResistEnabled bool
|
||||
dialTimeout time.Duration // for initial tcp connection
|
||||
hostname string // do not intercept requests to the hostname (except for hidden link)
|
||||
port string // port on which chain with forwardproxy is listening on
|
||||
}
|
||||
|
||||
var bufferPool sync.Pool
|
||||
|
|
@ -185,7 +187,7 @@ func serveHiddenPage(w http.ResponseWriter, authErr error) (int, error) {
|
|||
</body>
|
||||
</html>`
|
||||
const AuthFail = "Please authenticate yourself to the proxy."
|
||||
const AuthOk = "Congratulations, you are succussfully authenticated to the proxy! Go browse all the things!"
|
||||
const AuthOk = "Congratulations, you are successfully authenticated to the proxy! Go browse all the things!"
|
||||
|
||||
if authErr != nil {
|
||||
w.Header().Set("Proxy-Authenticate", "Basic")
|
||||
|
|
@ -197,6 +199,24 @@ func serveHiddenPage(w http.ResponseWriter, authErr error) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func (fp *ForwardProxy) shouldServePacFile(r *http.Request) bool {
|
||||
if len(fp.pacFilePath) > 0 && r.URL.Path == fp.pacFilePath {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const pacFile = `
|
||||
function FindProxyForURL(url, host) {
|
||||
return "HTTPS %s:%s";
|
||||
}
|
||||
`
|
||||
|
||||
func (fp *ForwardProxy) servePacFile(w http.ResponseWriter) (int, error) {
|
||||
fmt.Fprintf(w, pacFile, fp.hostname, fp.port)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (fp *ForwardProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
var authErr error
|
||||
if fp.authRequired {
|
||||
|
|
@ -208,6 +228,9 @@ func (fp *ForwardProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int,
|
|||
if isSubdomain(stripPort(r.Host), fp.hostname) && (r.Method != http.MethodConnect || authErr != nil) {
|
||||
// Always pass non-CONNECT requests to hostname
|
||||
// Pass CONNECT requests only if probe resistance is enabled and not authenticated
|
||||
if fp.shouldServePacFile(r) {
|
||||
return fp.servePacFile(w)
|
||||
}
|
||||
return fp.Next.ServeHTTP(w, r)
|
||||
}
|
||||
if authErr != nil {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func dial(proxyAddr string, useTls bool) (net.Conn, error) {
|
||||
|
|
@ -319,3 +320,28 @@ func TestConnectAuthWrong(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPAC(t *testing.T) {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
ResponseHeaderTimeout: 2 * time.Second,
|
||||
}
|
||||
client := &http.Client{Transport: tr, Timeout: 2 * time.Second}
|
||||
resp, err := client.Get("https://" + caddyForwardProxy.addr + "/proxy.pac")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
splitAddr := strings.Split(caddyForwardProxy.addr, ":")
|
||||
if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, splitAddr[0], splitAddr[1]))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err = client.Get("https://" + caddyForwardProxyProbeResist.addr + "/superhiddenfile.pac")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
splitAddr = strings.Split(caddyForwardProxyProbeResist.addr, ":")
|
||||
if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, splitAddr[0], splitAddr[1]))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
setup.go
30
setup.go
|
|
@ -19,6 +19,7 @@ import (
|
|||
"errors"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
|
@ -29,8 +30,8 @@ import (
|
|||
|
||||
func setup(c *caddy.Controller) error {
|
||||
httpserver.GetConfig(c).FallbackSite = true
|
||||
fp := &ForwardProxy{dialTimeout: time.Second * 20, hostname: httpserver.GetConfig(c).Host(),
|
||||
httpTransport: *http.DefaultTransport.(*http.Transport)}
|
||||
fp := &ForwardProxy{dialTimeout: time.Second * 20, httpTransport: *http.DefaultTransport.(*http.Transport),
|
||||
hostname: httpserver.GetConfig(c).Host(), port: httpserver.GetConfig(c).Port()}
|
||||
fp.httpTransport.DialTLS = func(network, addr string) (net.Conn, error) {
|
||||
return nil, &http.ProtocolError{ErrorString: "Proxy does not fetch TLS resources, use CONNECT instead"}
|
||||
}
|
||||
|
|
@ -94,6 +95,22 @@ func setup(c *caddy.Controller) error {
|
|||
if len(args) == 1 {
|
||||
fp.probeResistDomain = args[0]
|
||||
}
|
||||
case "serve_pac":
|
||||
if len(args) > 1 {
|
||||
return c.ArgErr()
|
||||
}
|
||||
if len(fp.pacFilePath) != 0 {
|
||||
return errors.New("Parse error: serve_pac subdirective specified twice")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
fp.pacFilePath = args[0]
|
||||
if !strings.HasPrefix(fp.pacFilePath, "/") {
|
||||
fp.pacFilePath = "/" + fp.pacFilePath
|
||||
}
|
||||
} else {
|
||||
fp.pacFilePath = "/proxy.pac"
|
||||
}
|
||||
log.Printf("Proxy Auto-Config will be served at %s%s\n", fp.hostname, fp.pacFilePath)
|
||||
case "response_timeout":
|
||||
if len(args) != 1 {
|
||||
return c.ArgErr()
|
||||
|
|
@ -123,8 +140,13 @@ func setup(c *caddy.Controller) error {
|
|||
}
|
||||
}
|
||||
|
||||
if fp.probeResistEnabled && !fp.authRequired {
|
||||
return errors.New("Parse error: probing resistance requires authentication")
|
||||
if fp.probeResistEnabled {
|
||||
if !fp.authRequired {
|
||||
return errors.New("Parse error: probing resistance requires authentication")
|
||||
}
|
||||
if len(fp.probeResistDomain) > 0 {
|
||||
log.Printf("Secret domain used to connect to proxy: %s\n", fp.probeResistDomain)
|
||||
}
|
||||
}
|
||||
|
||||
fp.httpTransport.DialContext = (&net.Dialer{
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ func TestSetup(t *testing.T) {
|
|||
testParsing([]string{"experimental_probe_resist local.host", "basicauth john doe"}, true)
|
||||
testParsing([]string{"experimental_probe_resist local.host very.local.host", "basicauth john doe"}, false)
|
||||
|
||||
testParsing([]string{"serve_pac"}, true)
|
||||
testParsing([]string{"serve_pac \"\""}, true)
|
||||
testParsing([]string{"serve_pac proxyautoconfig.pac"}, true)
|
||||
testParsing([]string{"serve_pac 1.pac 2.pac"}, false)
|
||||
|
||||
testParsing([]string{"response_timeout"}, false)
|
||||
testParsing([]string{"response_timeout -1"}, false)
|
||||
testParsing([]string{"response_timeout 1 2"}, false)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue