Documentation
¶
Overview ¶
Package proxy provides a simple proxy. The proxy can be protected with basic auth. It can also forward connections to a parent proxy, and authorize connections against that. Both local, and parent credentials can be set via environment variables. For local proxy credential, set `PROXY_CREDENTIAL`. For remote proxy credential, set `PROXY_PARENT_CREDENTIAL`.
Index ¶
Examples ¶
Constants ¶
const ( ConstantBackoff = 300 DNSTimeout = 1 * time.Minute MaxRetry = 3 )
const ( HTTP = "http" HTTPS = "https" SOCKS5 = "socks5" SOCKS = "socks" QUIC = "quic" )
Valid proxy schemes.
Variables ¶
var ( ErrFailedToAllocatePort = customerror.New("No available port to use", customerror.WithStatusCode(http.StatusInternalServerError)) ErrFailedToDialToDNS = customerror.NewFailedToError("dial to DNS") ErrFailedToStartProxy = customerror.NewFailedToError("start proxy") ErrInvalidDNSURI = customerror.NewInvalidError("dns URI") ErrInvalidLocalProxyURI = customerror.NewInvalidError("local proxy URI") ErrInvalidOrParentOrPac = customerror.NewInvalidError("params. Can't set upstream proxy, and PAC at the same time") ErrInvalidPACProxyURI = customerror.NewInvalidError("PAC proxy URI") ErrInvalidPACURI = customerror.NewInvalidError("PAC URI") ErrInvalidProxyParams = customerror.NewInvalidError("params") ErrInvalidUpstreamProxyURI = customerror.NewInvalidError("upstream proxy URI") )
var ErrFailedToCopyOptions = customerror.NewFailedToError("deepCopy options")
Functions ¶
This section is empty.
Types ¶
type Options ¶ added in v0.1.3
type Options struct {
*LoggingOptions
*RetryPortOptions
// AutomaticallyRetryPort if `true`, and the specified port is in-use, will
// try to automatically allocate a free port.
AutomaticallyRetryPort bool
// DNSURIs are DNS URIs:
// - Known protocol: udp, tcp
// - Some hostname (x.io - min 4 chars), or IP
// - Port in a valid range: 53 - 65535.
// Example: udp://10.0.0.3:53
DNSURIs []string `json:"dns_uris" validate:"omitempty,dive,dnsURI"`
// ProxyLocalhost if `true`, requests to `localhost`/`127.0.0.1` will be
// forwarded to any upstream - if set.
ProxyLocalhost bool
}
Options definition.
type Proxy ¶
type Proxy struct {
// LocalProxyURI is the local proxy URI:
// - Known schemes: http, https, socks, socks5, or quic
// - Some hostname (x.io - min 4 chars), or IP
// - Port in a valid range: 80 - 65535.
// Example: http://127.0.0.1:8080
LocalProxyURI string `json:"local_proxy_uri" validate:"required,proxyURI"`
// UpstreamProxyURI is the upstream proxy URI:
// - Known schemes: http, https, socks, socks5, or quic
// - Some hostname (x.io - min 4 chars), or IP
// - Port in a valid range: 80 - 65535.
// Example: http://u456:p456@127.0.0.1:8085
UpstreamProxyURI string `json:"upstream_proxy_uri" validate:"omitempty,proxyURI"`
// PACURI is the PAC URI:
// - Known schemes: http, https, socks, socks5, or quic
// - Some hostname (x.io - min 4 chars), or IP
// - Port in a valid range: 80 - 65535.
// Example: http://127.0.0.1:8087/data.pac
PACURI string `json:"pac_uri" validate:"omitempty,gte=6"`
// Mode the Proxy is running.
Mode Mode
// Current state of the proxy. Multiple calls to `Run`, if running, will do
// nothing.
State State
// Options to setup proxy.
*Options
// contains filtered or unexported fields
}
Proxy definition. Proxy can be protected, or not. It can forward connections to an upstream proxy protected, or not. The upstream proxy can be automatically setup via PAC. PAC content can be retrieved from multiple sources, e.g.: a HTTP server, also, protected or not.
Protection means basic auth protection.
func New ¶
func New( localProxyURI string, upstreamProxyURI string, pacURI string, pacProxiesCredentials []string, options *Options, ) (*Proxy, error)
New is the Proxy factory. Errors can be introspected, and provide contextual information.
Example ¶
Complete, and complex example.
client -> protected local proxy -> protected pac server - connection setup -> protected upstream proxy -> protected target.
//////
// Setup demo logger.
//////
// Only `stdout`, and `stderr`
loggingOptions := &LoggingOptions{
FileLevel: level.None.String(),
FilePath: "-",
// Change to `Trace` for debugging, and demonstration purposes.
Level: level.None.String(),
}
l := logger.Setup(loggingOptions)
//////
// Randomness automates port allocation, ensuring no collision happens
// between tests, and examples.
//////
r, err := randomness.New(49000, 50000, 100, true)
if err != nil {
log.Fatalln("Failed to create randomness.", err)
}
//////
// Target/end server.
//////
targetServer := createMockedHTTPServer(http.StatusOK, "body", "dXNlcjE6cGFzczE=")
defer func() { targetServer.Close() }()
targetServerURI, err := url.ParseRequestURI(targetServer.URL)
if err != nil {
//nolint:gocritic
log.Fatalln("Failed to parse target server URL.", err)
}
targetServerURI.User = url.UserPassword("user1", "pass1")
l.Debuglnf("Target/end server started @ %s", targetServerURI.Redacted())
//////
// PAC content.
//////
// Use `int(r.MustGenerate())` for testing purposes. Specify a port if using
// a manual - external proxy (e.g.: NGINX). Good for debugging, and demo
// purposes.
upstreamProxyPort := int(r.MustGenerate())
templateMap := map[string]int{
"port": upstreamProxyPort,
}
var pacText strings.Builder
_ = template.Must(template.New("pacTemplate").Parse(pacTemplate)).Execute(&pacText, templateMap)
l.Debuglnf("PAC template parsed: \n%s", pacText.String())
//////
// PAC server.
//////
pacServer := createMockedHTTPServer(http.StatusOK, pacText.String(), "dXNlcjpwYXNz")
defer func() { pacServer.Close() }()
pacServerURI, err := url.ParseRequestURI(pacServer.URL)
if err != nil {
log.Fatalln("Failed to parse PAC server URL.", err)
}
pacServerURI.User = url.UserPassword("user", "pass")
l.Debuglnf("PAC server started @ %s", pacServerURI.Redacted())
//////
// URL for both proxies, local, and upstream.
//////
// Local proxy.
localProxyURI := URIBuilder(defaultProxyHostname, r.MustGenerate(), localProxyCredentialUsername, localProxyCredentialPassword)
// Upstream proxy.
upstreamProxyURI := URIBuilder(defaultProxyHostname, int64(upstreamProxyPort), upstreamProxyCredentialUsername, upstreamProxyCredentialPassword)
//////
// Local proxy.
//
// It's protected with Basic Auth. Upstream proxy will be automatically, and
// dynamically setup via PAC, including credentials for proxies specified
// in the PAC content.
//////
localProxy, err := New(
// Local proxy URI.
localProxyURI.String(),
// Upstream proxy URI.
"",
// PAC URI.
pacServerURI.String(),
// PAC proxies credentials in standard URI format.
[]string{upstreamProxyURI.String()},
// Logging settings.
&Options{
LoggingOptions: loggingOptions,
},
)
if err != nil {
log.Fatalln("Failed to create proxy.", err)
}
go localProxy.Run()
// Give enough time to start, and be ready.
time.Sleep(1 * time.Second)
//////
// Upstream Proxy.
//////
upstreamProxy, err := New(
// Local proxy URI.
upstreamProxyURI.String(),
// Upstream proxy URI.
"",
// PAC URI.
"",
// PAC proxies credentials in standard URI format.
nil,
// Logging settings.
&Options{
LoggingOptions: loggingOptions,
},
)
if err != nil {
log.Fatalln("Failed to create upstream proxy.", err)
}
go upstreamProxy.Run()
// Give enough time to start, and be ready.
time.Sleep(1 * time.Second)
//////
// Client.
//////
l.Debuglnf("Client is using %s as proxy", localProxyURI.Redacted())
// Client's proxy settings.
tr := &http.Transport{
Proxy: http.ProxyURL(localProxyURI),
}
client := &http.Client{
Transport: tr,
}
statusCode, body, err := executeRequest(client, targetServerURI.String())
if err != nil {
log.Fatalf("Failed to execute request: %v", err)
}
fmt.Println(statusCode)
fmt.Println(body)
Output: 200 body
Example (AutomaticallyRetryPort) ¶
Automatically retry port example.
if os.Getenv("FORWARDER_TEST_MODE") != "integration" {
fmt.Println("true")
return
}
//////
// Randomness automates port allocation, ensuring no collision happens.
//////
r, err := randomness.New(55000, 65000, 100, true)
if err != nil {
log.Fatalln("Failed to create randomness.", err)
}
randomPort := r.MustGenerate()
errored := false
proxy1, err := New(fmt.Sprintf("http://0.0.0.0:%d", randomPort), "", "", nil, &Options{
LoggingOptions: &LoggingOptions{
Level: "none",
FileLevel: "none",
},
})
if err != nil {
errored = true
}
go proxy1.Run()
time.Sleep(1 * time.Second)
proxy2, err := New(fmt.Sprintf("http://0.0.0.0:%d", randomPort), "", "", nil, &Options{
AutomaticallyRetryPort: true,
LoggingOptions: &LoggingOptions{
Level: "none",
FileLevel: "none",
},
})
if err != nil {
errored = true
}
go proxy2.Run()
time.Sleep(1 * time.Second)
fmt.Println(errored == false)
Output: true
Example (SypplyingLogger) ¶
Supplying a logger, and calling multiple times Run example.
//////
// Custom logger
//////
customLogger := sypl.NewDefault("customLogger", level.Trace)
//////
// Randomness automates port allocation, ensuring no collision happens.
//////
r, err := randomness.New(55000, 65000, 100, true)
if err != nil {
log.Fatalln("Failed to create randomness.", err)
}
randomPort := r.MustGenerate()
errored := false
proxy1, err := New(fmt.Sprintf("http://0.0.0.0:%d", randomPort), "", "", nil, &Options{
LoggingOptions: &LoggingOptions{
Logger: customLogger,
Level: level.Trace.String(),
FileLevel: "none",
},
})
if err != nil {
log.Fatalln(err)
}
go proxy1.Run()
time.Sleep(1 * time.Second)
fmt.Println(errored == false)
Output: true
type RetryPortOptions ¶ added in v0.1.4
type RetryPortOptions struct {
// MaxRange defines the max port number. Default value is `65535`.
MaxRange int
// MaxRetry defines how many times to retry, until fail.
MaxRetry int
}
RetryPortOptions defines port's retry options.
func (*RetryPortOptions) Default ¶ added in v0.1.4
func (r *RetryPortOptions) Default() *RetryPortOptions
Default sets `RetryPortOptions` default values.