started adding proxy_outbound with support for ENV HTTP_PROXY and exclusion of specified destination networks or addresses from proxying

This commit is contained in:
Paige Thompson 2023-03-24 01:18:22 -07:00
parent a62941cc80
commit f2b10d5fc5
No known key found for this signature in database
GPG key ID: 071CFBC989D32591
8 changed files with 122 additions and 12 deletions

View file

@ -18,6 +18,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"net/http" "net/http"
"net/url"
"sync" "sync"
"time" "time"
@ -51,7 +52,9 @@ func NewInternalAPI(
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
InsecureSkipVerify: cfg.AppServiceAPI.DisableTLSValidation, InsecureSkipVerify: cfg.AppServiceAPI.DisableTLSValidation,
}, },
Proxy: http.ProxyFromEnvironment, Proxy: func(req *http.Request) (*url.URL, error) {
return cfg.AppServiceAPI.Proxy.GetApplicableProxy(req, &cfg.Global.Proxy)
},
}, },
} }
// Create appserivce query API with an HTTP client that will be used for all // Create appserivce query API with an HTTP client that will be used for all

View file

@ -7,9 +7,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"time" "time"
"github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/setup/config"
) )
type httpClient struct { type httpClient struct {
@ -17,15 +19,17 @@ type httpClient struct {
} }
// NewHTTPClient creates a new Push Gateway client. // NewHTTPClient creates a new Push Gateway client.
func NewHTTPClient(disableTLSValidation bool) Client { func NewHTTPClient(cfg *config.Dendrite) Client {
hc := &http.Client{ hc := &http.Client{
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
Transport: &http.Transport{ Transport: &http.Transport{
DisableKeepAlives: true, DisableKeepAlives: true,
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
InsecureSkipVerify: disableTLSValidation, InsecureSkipVerify: cfg.UserAPI.PushGatewayDisableTLSValidation,
},
Proxy: func(req *http.Request) (*url.URL, error) {
return cfg.UserAPI.Proxy.GetApplicableProxy(req, &cfg.Global.Proxy)
}, },
Proxy: http.ProxyFromEnvironment,
}, },
} }
return &httpClient{hc: hc} return &httpClient{hc: hc}

View file

@ -34,12 +34,17 @@ type AppServiceAPI struct {
DisableTLSValidation bool `yaml:"disable_tls_validation"` DisableTLSValidation bool `yaml:"disable_tls_validation"`
ConfigFiles []string `yaml:"config_files"` ConfigFiles []string `yaml:"config_files"`
// Proxy for outbound requests
Proxy Proxy `yaml:"proxy_outbound"`
} }
func (c *AppServiceAPI) Defaults(opts DefaultOpts) { func (c *AppServiceAPI) Defaults(opts DefaultOpts) {
c.Proxy.Defaults()
} }
func (c *AppServiceAPI) Verify(configErrs *ConfigErrors) { func (c *AppServiceAPI) Verify(configErrs *ConfigErrors) {
c.Proxy.Verify(configErrs)
} }
// ApplicationServiceNamespace is the namespace that a specific application // ApplicationServiceNamespace is the namespace that a specific application

View file

@ -3,6 +3,10 @@ package config
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"net"
"net/http"
"net/url"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -78,6 +82,9 @@ type Global struct {
// Configuration for the caches. // Configuration for the caches.
Cache Cache `yaml:"cache"` Cache Cache `yaml:"cache"`
// Proxy for outbound requests
Proxy Proxy `yaml:"proxy_outbound"`
} }
func (c *Global) Defaults(opts DefaultOpts) { func (c *Global) Defaults(opts DefaultOpts) {
@ -102,6 +109,7 @@ func (c *Global) Defaults(opts DefaultOpts) {
c.ServerNotices.Defaults(opts) c.ServerNotices.Defaults(opts)
c.ReportStats.Defaults() c.ReportStats.Defaults()
c.Cache.Defaults() c.Cache.Defaults()
c.Proxy.Defaults()
} }
func (c *Global) Verify(configErrs *ConfigErrors) { func (c *Global) Verify(configErrs *ConfigErrors) {
@ -119,6 +127,7 @@ func (c *Global) Verify(configErrs *ConfigErrors) {
c.ServerNotices.Verify(configErrs) c.ServerNotices.Verify(configErrs)
c.ReportStats.Verify(configErrs) c.ReportStats.Verify(configErrs)
c.Cache.Verify(configErrs) c.Cache.Verify(configErrs)
c.Proxy.Verify(configErrs)
} }
func (c *Global) IsLocalServerName(serverName gomatrixserverlib.ServerName) bool { func (c *Global) IsLocalServerName(serverName gomatrixserverlib.ServerName) bool {
@ -437,3 +446,70 @@ func (d *DataUnit) UnmarshalText(text []byte) error {
*d = DataUnit(v * magnitude) *d = DataUnit(v * magnitude)
return nil return nil
} }
// The config for setting a proxy to use for server->server requests
type Proxy struct {
// Is the proxy enabled?
Enabled bool `yaml:"enabled"`
// The protocol for the proxy (http / https / socks5)
Protocol string `yaml:"protocol"`
// The host where the proxy is listening
Host string `yaml:"host"`
// The port on which the proxy is listening
Port uint16 `yaml:"port"`
// A list of destination addresses/networks not intended to be proxied
ExcludeAddresses []string `yaml:exclude_addresses`
}
func (c *Proxy) Defaults() {
c.Enabled = false
c.Protocol = "http"
c.Host = "localhost"
c.Port = 8080
}
func (c *Proxy) Verify(configErrs *ConfigErrors) {
}
func (c *Proxy) RequestAddressIsExcluded(req *http.Request) bool {
for _, s := range c.ExcludeAddresses {
var exclude *net.IPNet
if strings.Contains(s, "/") {
_, exclude, _ = net.ParseCIDR(s)
} else {
_, exclude, _ = net.ParseCIDR(fmt.Sprintf("%s/32", s))
}
_, remote_net, _ := net.ParseCIDR(fmt.Sprintf("%s/32", s))
if exclude.Contains(remote_net.IP) {
return true
}
}
return false
}
func (c *Proxy) ConfigIsDefault() bool {
if c.Enabled == false && c.Protocol == "http" && c.Port == 8080 {
return true
}
return false
}
func (c *Proxy) GetApplicableProxy(req *http.Request, parent *Proxy) (*url.URL, error) {
if parent.RequestAddressIsExcluded(req) || c.RequestAddressIsExcluded(req) {
return req.URL, nil
}
if os.Getenv("HTTP_PROXY") != "" || os.Getenv("HTTPS_PROXY") != "" {
return http.ProxyFromEnvironment(req)
} else if parent.Enabled && c.ConfigIsDefault() {
// Proxy defined in Global section
return url.Parse(fmt.Sprintf("%s://%s:%s", parent.Protocol, parent.Host, parent.Port))
} else if !c.ConfigIsDefault() {
// Proxy defined in this section
return url.Parse(fmt.Sprintf("%s://%s:%s", c.Protocol, c.Host, c.Port))
} else {
// No proxy
return nil, nil
}
}

View file

@ -79,6 +79,12 @@ global:
room_name: "Server Alerts" room_name: "Server Alerts"
jetstream: jetstream:
addresses: ["test"] addresses: ["test"]
proxy_outbound:
enabled: false
protocol: http
host: localhost
port: 8080
exclude_addresses: []
app_service_api: app_service_api:
database: database:
connection_string: file:appservice.db connection_string: file:appservice.db
@ -86,6 +92,12 @@ app_service_api:
max_idle_conns: 2 max_idle_conns: 2
conn_max_lifetime: -1 conn_max_lifetime: -1
config_files: [] config_files: []
proxy_outbound:
enabled: false
protocol: http
host: localhost
port: 8080
exclude_addresses: []
client_api: client_api:
registration_disabled: true registration_disabled: true
registration_shared_secret: "" registration_shared_secret: ""
@ -103,11 +115,6 @@ client_api:
federation_api: federation_api:
database: database:
connection_string: file:federationapi.db connection_string: file:federationapi.db
proxy_outbound:
enabled: false
protocol: http
host: localhost
port: 8080
key_server: key_server:
database: database:
connection_string: file:keyserver.db connection_string: file:keyserver.db
@ -170,6 +177,12 @@ user_api:
max_open_conns: 100 max_open_conns: 100
max_idle_conns: 2 max_idle_conns: 2
conn_max_lifetime: -1 conn_max_lifetime: -1
proxy_outbound:
enabled: false
protocol: http
host: localhost
port: 8080
exclude_addresses: []
relay_api: relay_api:
database: database:
connection_string: file:relayapi.db connection_string: file:relayapi.db

View file

@ -21,6 +21,8 @@ type UserAPI struct {
// Users who register on this homeserver will automatically // Users who register on this homeserver will automatically
// be joined to the rooms listed under this option. // be joined to the rooms listed under this option.
AutoJoinRooms []string `yaml:"auto_join_rooms"` AutoJoinRooms []string `yaml:"auto_join_rooms"`
// Proxy for outbound requests
Proxy Proxy `yaml:"proxy_outbound"`
} }
const DefaultOpenIDTokenLifetimeMS = 3600000 // 60 minutes const DefaultOpenIDTokenLifetimeMS = 3600000 // 60 minutes
@ -33,6 +35,7 @@ func (c *UserAPI) Defaults(opts DefaultOpts) {
c.AccountDatabase.ConnectionString = "file:userapi_accounts.db" c.AccountDatabase.ConnectionString = "file:userapi_accounts.db"
} }
} }
c.Proxy.Defaults()
} }
func (c *UserAPI) Verify(configErrs *ConfigErrors) { func (c *UserAPI) Verify(configErrs *ConfigErrors) {
@ -40,4 +43,5 @@ func (c *UserAPI) Verify(configErrs *ConfigErrors) {
if c.Matrix.DatabaseOptions.ConnectionString == "" { if c.Matrix.DatabaseOptions.ConnectionString == "" {
checkNotEmpty(configErrs, "user_api.account_database.connection_string", string(c.AccountDatabase.ConnectionString)) checkNotEmpty(configErrs, "user_api.account_database.connection_string", string(c.AccountDatabase.ConnectionString))
} }
c.Proxy.Verify(configErrs)
} }

View file

@ -47,7 +47,7 @@ func NewInternalAPI(
js, _ := natsInstance.Prepare(processContext, &dendriteCfg.Global.JetStream) js, _ := natsInstance.Prepare(processContext, &dendriteCfg.Global.JetStream)
appServices := dendriteCfg.Derived.ApplicationServices appServices := dendriteCfg.Derived.ApplicationServices
pgClient := pushgateway.NewHTTPClient(dendriteCfg.UserAPI.PushGatewayDisableTLSValidation) pgClient := pushgateway.NewHTTPClient(dendriteCfg)
db, err := storage.NewUserDatabase( db, err := storage.NewUserDatabase(
processContext.Context(), processContext.Context(),

View file

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"math" "math"
"net/http" "net/http"
"net/url"
"runtime" "runtime"
"syscall" "syscall"
"time" "time"
@ -58,7 +59,11 @@ func StartPhoneHomeCollector(startTime time.Time, cfg *config.Dendrite, statsDB
isMonolith: true, isMonolith: true,
client: &http.Client{ client: &http.Client{
Timeout: time.Second * 30, Timeout: time.Second * 30,
Transport: http.DefaultTransport, Transport: &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return cfg.UserAPI.Proxy.GetApplicableProxy(req, &cfg.Global.Proxy)
},
},
}, },
} }