1
0
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:
Umputun 2021-05-03 21:40:21 -05:00 committed by GitHub
parent c6ec677710
commit 4c051ca37f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 174 additions and 9 deletions

View File

@ -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

View File

@ -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 }

View File

@ -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
View 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`

View 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'