mirror of
https://github.com/umputun/reproxy.git
synced 2024-11-24 08:12:31 +02:00
detect in-container and set listen address to 0.0.0.0 (#62)
* detect in-container and set listen to 0.0.0.0 * simplify default address logic don't change if user defined, use 127.0.0.1:8080 for non-docker and 0.0.0.0:8080 for in-docker only if nothing set * add dynamic default to redir http port * add docs about dynamic defaults * add ssl example * lint: params warn
This commit is contained in:
parent
c6ec677710
commit
4c051ca37f
16
README.md
16
README.md
@ -147,6 +147,16 @@ There are two ways to set cache duration:
|
||||
|
||||
- `--timeout.*` various timeouts for both server and proxy transport. See `timeout` section in [All Application Options](#all-application-options)
|
||||
|
||||
## Default ports
|
||||
|
||||
In order to eliminate the need to pass custom params/environment, the default `--listen` is dynamic and trying to be reasonable and helpful for the typical cases:
|
||||
|
||||
- If anything set by users to `--listen` all the logic below ignored and host:port passed in and used directly.
|
||||
- If nothing set by users to `--listen` and reproxy runs outside of the docker container, the default is `127.0.0.1:80` for http mode (`ssl.type=none`) and `127.0.0.1:443` for ssl mode (`ssl.type=auto` or `ssl.type=static`).
|
||||
- If nothing set by users to `--listen` and reproxy runs inside the docker, the default is `0.0.0.0:8080` for http mode, and `0.0.0.0:8443` for ssl mode.
|
||||
|
||||
Another default set in the similar dynamic way is `-ssl.http-port`. For run inside of the docker container it set to `8080` and without to `80`.
|
||||
|
||||
## Ping and health checks
|
||||
|
||||
reproxy provides 2 endpoints for this purpose:
|
||||
@ -170,7 +180,8 @@ Reproxy returns 502 (Bad Gateway) error in case if request doesn't match to any
|
||||
## All Application Options
|
||||
|
||||
```
|
||||
-l, --listen= listen on host:port (default: 127.0.0.1:8080) [$LISTEN]
|
||||
Application Options:
|
||||
-l, --listen= listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without) [$LISTEN]
|
||||
-m, --max= max request size (default: 64000) [$MAX_SIZE]
|
||||
-g, --gzip enable gz compression [$GZIP]
|
||||
-x, --header= proxy headers [$HEADER]
|
||||
@ -183,7 +194,7 @@ ssl:
|
||||
--ssl.key= path to key.pem file [$SSL_KEY]
|
||||
--ssl.acme-location= dir where certificates will be stored by autocert manager (default: ./var/acme) [$SSL_ACME_LOCATION]
|
||||
--ssl.acme-email= admin email for certificate notifications [$SSL_ACME_EMAIL]
|
||||
--ssl.http-port= http port for redirect to https and acme challenge test (default: 80) [$SSL_HTTP_PORT]
|
||||
--ssl.http-port= http port for redirect to https and acme challenge test (default: 8080 under docker, 80 without) [$SSL_HTTP_PORT]
|
||||
--ssl.fqdn= FQDN(s) for ACME certificates [$SSL_ACME_FQDN]
|
||||
|
||||
assets:
|
||||
@ -237,7 +248,6 @@ error:
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
|
||||
```
|
||||
|
||||
## Status
|
||||
|
49
app/main.go
49
app/main.go
@ -24,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
var opts struct {
|
||||
Listen string `short:"l" long:"listen" env:"LISTEN" default:"127.0.0.1:8080" description:"listen on host:port"`
|
||||
Listen string `short:"l" long:"listen" env:"LISTEN" description:"listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without)"`
|
||||
MaxSize int64 `short:"m" long:"max" env:"MAX_SIZE" default:"64000" description:"max request size"`
|
||||
GzipEnabled bool `short:"g" long:"gzip" env:"GZIP" description:"enable gz compression"`
|
||||
ProxyHeaders []string `short:"x" long:"header" env:"HEADER" description:"proxy headers" env-delim:","`
|
||||
@ -35,7 +35,7 @@ var opts struct {
|
||||
Key string `long:"key" env:"KEY" description:"path to key.pem file"`
|
||||
ACMELocation string `long:"acme-location" env:"ACME_LOCATION" description:"dir where certificates will be stored by autocert manager" default:"./var/acme"`
|
||||
ACMEEmail string `long:"acme-email" env:"ACME_EMAIL" description:"admin email for certificate notifications"`
|
||||
RedirHTTPPort int `long:"http-port" env:"HTTP_PORT" default:"80" description:"http port for redirect to https and acme challenge test"`
|
||||
RedirHTTPPort int `long:"http-port" env:"HTTP_PORT" description:"http port for redirect to https and acme challenge test (default: 8080 under docker, 80 without)"`
|
||||
FQDNs []string `long:"fqdn" env:"ACME_FQDN" env-delim:"," description:"FQDN(s) for ACME certificates"`
|
||||
} `group:"ssl" namespace:"ssl" env-namespace:"SSL"`
|
||||
|
||||
@ -111,7 +111,7 @@ func main() {
|
||||
if err.(*flags.Error).Type != flags.ErrHelp {
|
||||
log.Printf("[ERROR] cli error: %v", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
setupLog(opts.Dbg)
|
||||
@ -194,10 +194,13 @@ func run() error {
|
||||
return fmt.Errorf("failed to make error reporter: %w", err)
|
||||
}
|
||||
|
||||
addr := listenAddress(opts.Listen, opts.SSL.Type)
|
||||
log.Printf("[DEBUG] listen address %s", addr)
|
||||
|
||||
px := &proxy.Http{
|
||||
Version: revision,
|
||||
Matcher: svc,
|
||||
Address: opts.Listen,
|
||||
Address: addr,
|
||||
MaxBodySize: opts.MaxSize,
|
||||
AssetsLocation: opts.Assets.Location,
|
||||
AssetsWebRoot: opts.Assets.WebRoot,
|
||||
@ -286,13 +289,13 @@ func makeSSLConfig() (config proxy.SSLConfig, err error) {
|
||||
config.SSLMode = proxy.SSLStatic
|
||||
config.Cert = opts.SSL.Cert
|
||||
config.Key = opts.SSL.Key
|
||||
config.RedirHTTPPort = opts.SSL.RedirHTTPPort
|
||||
config.RedirHTTPPort = redirHTTPPort(opts.SSL.RedirHTTPPort)
|
||||
case "auto":
|
||||
config.SSLMode = proxy.SSLAuto
|
||||
config.ACMELocation = opts.SSL.ACMELocation
|
||||
config.ACMEEmail = opts.SSL.ACMEEmail
|
||||
config.FQDNs = opts.SSL.FQDNs
|
||||
config.RedirHTTPPort = opts.SSL.RedirHTTPPort
|
||||
config.RedirHTTPPort = redirHTTPPort(opts.SSL.RedirHTTPPort)
|
||||
}
|
||||
return config, err
|
||||
}
|
||||
@ -325,6 +328,40 @@ func makeAccessLogWriter() (accessLog io.WriteCloser) {
|
||||
}
|
||||
}
|
||||
|
||||
// listenAddress sets default to 127.0.0.0:8080/80443 and, if detected REPROXY_IN_DOCKER env, to 0.0.0.0:80/443
|
||||
func listenAddress(addr, sslType string) string {
|
||||
|
||||
// don't set default if any opts.Listen address defined by user
|
||||
if addr != "" {
|
||||
return addr
|
||||
}
|
||||
|
||||
// http, set default to 8080 in docker, 80 without
|
||||
if sslType == "none" {
|
||||
if v, ok := os.LookupEnv("REPROXY_IN_DOCKER"); ok && (v == "1" || v == "true") {
|
||||
return "0.0.0.0:8080"
|
||||
}
|
||||
return "127.0.0.1:80"
|
||||
}
|
||||
|
||||
// https, set default to 8443 in docker, 443 without
|
||||
if v, ok := os.LookupEnv("REPROXY_IN_DOCKER"); ok && (v == "1" || v == "true") {
|
||||
return "0.0.0.0:443"
|
||||
}
|
||||
return "127.0.0.1:8443"
|
||||
}
|
||||
|
||||
func redirHTTPPort(port int) int {
|
||||
// don't set default if any ssl.http-port defined by user
|
||||
if port != 0 {
|
||||
return port
|
||||
}
|
||||
if v, ok := os.LookupEnv("REPROXY_IN_DOCKER"); ok && (v == "1" || v == "true") {
|
||||
return 8080
|
||||
}
|
||||
return 80
|
||||
}
|
||||
|
||||
type nopWriteCloser struct{ io.Writer }
|
||||
|
||||
func (n nopWriteCloser) Close() error { return nil }
|
||||
|
@ -174,3 +174,62 @@ func waitForHTTPServerStart(port int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listenAddress(t *testing.T) {
|
||||
|
||||
tbl := []struct {
|
||||
addr string
|
||||
sslType string
|
||||
env string
|
||||
res string
|
||||
}{
|
||||
{"", "none", "1", "0.0.0.0:8080"},
|
||||
{"", "none", "0", "127.0.0.1:80"},
|
||||
{"", "auto", "false", "127.0.0.1:8443"},
|
||||
{"", "auto", "true", "0.0.0.0:443"},
|
||||
{"127.0.0.1:8081", "none", "true", "127.0.0.1:8081"},
|
||||
{"192.168.1.1:8081", "none", "false", "192.168.1.1:8081"},
|
||||
{"127.0.0.1:8080", "none", "0", "127.0.0.1:8080"},
|
||||
{"127.0.0.1:8443", "auto", "true", "127.0.0.1:8443"},
|
||||
}
|
||||
|
||||
defer os.Unsetenv("REPROXY_IN_DOCKER")
|
||||
|
||||
for i, tt := range tbl {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
assert.NoError(t, os.Unsetenv("REPROXY_IN_DOCKER"))
|
||||
if tt.env != "" {
|
||||
assert.NoError(t, os.Setenv("REPROXY_IN_DOCKER", tt.env))
|
||||
}
|
||||
assert.Equal(t, tt.res, listenAddress(tt.addr, tt.sslType))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_redirHTTPPort(t *testing.T) {
|
||||
tbl := []struct {
|
||||
port int
|
||||
env string
|
||||
res int
|
||||
}{
|
||||
{0, "1", 8080},
|
||||
{0, "0", 80},
|
||||
{0, "true", 8080},
|
||||
{0, "false", 80},
|
||||
{1234, "true", 1234},
|
||||
{1234, "false", 1234},
|
||||
}
|
||||
|
||||
defer os.Unsetenv("REPROXY_IN_DOCKER")
|
||||
|
||||
for i, tt := range tbl {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
assert.NoError(t, os.Unsetenv("REPROXY_IN_DOCKER"))
|
||||
if tt.env != "" {
|
||||
assert.NoError(t, os.Setenv("REPROXY_IN_DOCKER", tt.env))
|
||||
}
|
||||
assert.Equal(t, tt.res, redirHTTPPort(tt.port))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
9
examples/ssl/README.md
Normal file
9
examples/ssl/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Example of a docker provider with an automatic SSL (Let's Encrypt)
|
||||
|
||||
This example should run on the machine with resolvable FQDN. All files use example.com, make sure you **replace it with your domain**.
|
||||
|
||||
run this example with `docker-compose up` and try to hit containers:
|
||||
|
||||
- `curl https://example.com/api/svc1/123`
|
||||
- `curl http://example.com/api/svc2/345`
|
||||
- `curl http://example.com/whoami/test`
|
50
examples/ssl/docker-compose.yml
Normal file
50
examples/ssl/docker-compose.yml
Normal file
@ -0,0 +1,50 @@
|
||||
services:
|
||||
reproxy:
|
||||
image: umputun/reproxy:master
|
||||
container_name: reproxy
|
||||
hostname: reproxy
|
||||
ports:
|
||||
- "80:8080"
|
||||
- "443:8443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./web:/web
|
||||
environment:
|
||||
- TZ=America/Chicago
|
||||
- DOCKER_ENABLED=true
|
||||
- SSL_TYPE=auto
|
||||
- SSL_ACME_FQDN=example.com <-- replace it
|
||||
- HEADER=
|
||||
X-Frame-Options:SAMEORIGIN,
|
||||
X-XSS-Protection:1; mode=block;,
|
||||
Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline';
|
||||
|
||||
# automatic destination, will be mapped for ^/api/svc1/(.*)
|
||||
svc1:
|
||||
image: ghcr.io/umputun/echo-http
|
||||
hostname: svc1
|
||||
container_name: svc1
|
||||
command: --message="hello world from svc1"
|
||||
labels:
|
||||
reproxy.route: '^/svc1/(.*)'
|
||||
reproxy.dest: '/@1'
|
||||
|
||||
|
||||
# explicit destination, will be mapped for ^/api/svc2/(.*)
|
||||
svc2:
|
||||
image: ghcr.io/umputun/echo-http
|
||||
hostname: svc2
|
||||
container_name: svc2
|
||||
command: --message="hello world from svc2"
|
||||
labels:
|
||||
reproxy.route: '^/svc2/(.*)'
|
||||
reproxy.dest: '/@1'
|
||||
|
||||
# explicit destination, routing match defined by lables
|
||||
whoami:
|
||||
image: 'containous/whoami'
|
||||
hostname: whoami
|
||||
container_name: whoami
|
||||
labels:
|
||||
reproxy.route: '^/whoami/(.*)'
|
||||
reproxy.dest: '/@1'
|
Loading…
Reference in New Issue
Block a user