diff --git a/README.md b/README.md index e18cd25..866d3e1 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,18 @@ inbound: allowlist: [...] ``` +### HttpClient + +The `httpClient` configuration section modifies the HTTP client used for proxying requests. + +Example: +```yaml +inbound: + httpClient: + additionalCACerts: + - /path/to/custom/cert.pem +``` + ### GitHub The `github` configuration section simplifies granting Semgrep access to leave PR comments. diff --git a/pkg/config.go b/pkg/config.go index 6092491..314b89f 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -209,14 +209,19 @@ type GitLab struct { Token string `mapstructure:"token" json:"token"` } +type HttpClientConfig struct { + AdditionalCACerts []string `mapstructure:"additionalCACerts" json:"additionalCACerts"` +} + type InboundProxyConfig struct { - Wireguard WireguardBase `mapstructure:"wireguard" json:"wireguard"` - Allowlist Allowlist `mapstructure:"allowlist" json:"allowlist"` - ProxyListenPort int `mapstructure:"proxyListenPort" json:"proxyListenPort" validate:"gte=0" default:"80"` - Logging LoggingConfig `mapstructure:"logging" json:"logging"` - Heartbeat HeartbeatConfig `mapstructure:"heartbeat" json:"heartbeat"` - GitHub *GitHub `mapstructure:"github" json:"github"` - GitLab *GitLab `mapstructure:"gitlab" json:"gitlab"` + Wireguard WireguardBase `mapstructure:"wireguard" json:"wireguard"` + Allowlist Allowlist `mapstructure:"allowlist" json:"allowlist"` + ProxyListenPort int `mapstructure:"proxyListenPort" json:"proxyListenPort" validate:"gte=0" default:"80"` + Logging LoggingConfig `mapstructure:"logging" json:"logging"` + Heartbeat HeartbeatConfig `mapstructure:"heartbeat" json:"heartbeat"` + GitHub *GitHub `mapstructure:"github" json:"github"` + GitLab *GitLab `mapstructure:"gitlab" json:"gitlab"` + HttpClient HttpClientConfig `mapstructure:"httpClient" json:"httpClient"` } type FilteredRelayConfig struct { diff --git a/pkg/http_client.go b/pkg/http_client.go new file mode 100644 index 0000000..9f10772 --- /dev/null +++ b/pkg/http_client.go @@ -0,0 +1,52 @@ +package pkg + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "net" + "net/http" + "os" + "time" +) + +func (hcc *HttpClientConfig) BuildRoundTripper() (http.RoundTripper, error) { + dialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + } + + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: dialer.DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + if len(hcc.AdditionalCACerts) > 0 { + certPool, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + + for i := range hcc.AdditionalCACerts { + caCert, err := os.ReadFile(hcc.AdditionalCACerts[i]) + if err != nil { + return nil, fmt.Errorf("failed to add CA cert to pool: %v", err) + } + + if ok := certPool.AppendCertsFromPEM(caCert); !ok { + return nil, fmt.Errorf("failed to add CA cert to pool: %v", hcc.AdditionalCACerts[i]) + } + } + transport.TLSClientConfig = &tls.Config{ + ClientCAs: certPool, + MinVersion: tls.VersionTLS13, + } + } + + return transport, nil +} diff --git a/pkg/inbound_proxy.go b/pkg/inbound_proxy.go index d6b1c3b..e404d55 100644 --- a/pkg/inbound_proxy.go +++ b/pkg/inbound_proxy.go @@ -28,6 +28,12 @@ func (config *InboundProxyConfig) Start(tnet *netstack.Net) error { return fmt.Errorf("invalid inbound config: %v", err) } + // build http transport (needed for custom CA certs, etc...) + transport, err := config.HttpClient.BuildRoundTripper() + if err != nil { + return err + } + // setup http server gin.SetMode(gin.ReleaseMode) r := gin.New() @@ -90,6 +96,7 @@ func (config *InboundProxyConfig) Start(tnet *netstack.Net) error { reqLogger.Info("proxy.request") proxy := httputil.ReverseProxy{ + Transport: transport, Director: func(req *http.Request) { req.URL = destinationUrl req.Host = destinationUrl.Host