diff --git a/core/providers/utils/utils.go b/core/providers/utils/utils.go index 1c86ecf5d..847e150c8 100644 --- a/core/providers/utils/utils.go +++ b/core/providers/utils/utils.go @@ -4,6 +4,8 @@ package utils import ( "context" + "crypto/tls" + "crypto/x509" "errors" "fmt" "io" @@ -103,7 +105,6 @@ func ConfigureProxy(client *fasthttp.Client, proxyConfig *schemas.ProxyConfig, l } var dialFunc fasthttp.DialFunc - // Create the appropriate proxy based on type switch proxyConfig.Type { case schemas.NoProxy: @@ -113,7 +114,18 @@ func ConfigureProxy(client *fasthttp.Client, proxyConfig *schemas.ProxyConfig, l logger.Warn("Warning: HTTP proxy URL is required for setting up proxy") return client } - dialFunc = fasthttpproxy.FasthttpHTTPDialer(proxyConfig.URL) + proxyURL := proxyConfig.URL + if proxyConfig.Username != "" && proxyConfig.Password != "" { + parsedURL, err := url.Parse(proxyConfig.URL) + if err != nil { + logger.Warn("Invalid proxy configuration: invalid HTTP proxy URL") + return client + } + // Set user and password in the parsed URL + parsedURL.User = url.UserPassword(proxyConfig.Username, proxyConfig.Password) + proxyURL = parsedURL.String() + } + dialFunc = fasthttpproxy.FasthttpHTTPDialer(proxyURL) case schemas.Socks5Proxy: if proxyConfig.URL == "" { logger.Warn("Warning: SOCKS5 proxy URL is required for setting up proxy") @@ -144,9 +156,40 @@ func ConfigureProxy(client *fasthttp.Client, proxyConfig *schemas.ProxyConfig, l client.Dial = dialFunc } + // Configure custom CA certificate if provided + if proxyConfig.CACertPEM != "" { + tlsConfig, err := createTLSConfigWithCA(proxyConfig.CACertPEM) + if err != nil { + logger.Warn(fmt.Sprintf("Failed to configure custom CA certificate: %v", err)) + } else { + client.TLSConfig = tlsConfig + } + } + return client } +// createTLSConfigWithCA creates a TLS configuration with a custom CA certificate +// appended to the system root CA pool. +func createTLSConfigWithCA(caCertPEM string) (*tls.Config, error) { + // Get the system root CA pool + rootCAs, err := x509.SystemCertPool() + if err != nil { + // If we can't get system certs, create a new pool + rootCAs = x509.NewCertPool() + } + + // Append the custom CA certificate + if !rootCAs.AppendCertsFromPEM([]byte(caCertPEM)) { + return nil, fmt.Errorf("failed to parse CA certificate PEM") + } + + return &tls.Config{ + RootCAs: rootCAs, + MinVersion: tls.VersionTLS12, + }, nil +} + // hopByHopHeaders are HTTP/1.1 headers that must not be forwarded by proxies. var hopByHopHeaders = map[string]bool{ "connection": true, diff --git a/core/schemas/provider.go b/core/schemas/provider.go index 966a1914a..1aabebb3e 100644 --- a/core/schemas/provider.go +++ b/core/schemas/provider.go @@ -155,10 +155,11 @@ const ( // ProxyConfig holds the configuration for proxy settings. type ProxyConfig struct { - Type ProxyType `json:"type"` // Type of proxy to use - URL string `json:"url"` // URL of the proxy server - Username string `json:"username"` // Username for proxy authentication - Password string `json:"password"` // Password for proxy authentication + Type ProxyType `json:"type"` // Type of proxy to use + URL string `json:"url"` // URL of the proxy server + Username string `json:"username"` // Username for proxy authentication + Password string `json:"password"` // Password for proxy authentication + CACertPEM string `json:"ca_cert_pem"` // PEM-encoded CA certificate to trust for TLS connections through the proxy } // AllowedRequests controls which operations are permitted. diff --git a/docs/docs.json b/docs/docs.json index 162a3e706..fe3ae0d17 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -5,6 +5,10 @@ "dark": "/media/bifrost-logo-dark.png", "light": "/media/bifrost-logo.png" }, + "banner": { + "content": "Try Bifrost Enterprise free for 14 days. [Explore now](https://www.getmaxim.ai/bifrost/enterprise)", + "dismissible": true + }, "theme": "mint", "colors": { "primary": "#0C3B43", @@ -21,19 +25,21 @@ "name": "Dashboard", "url": "https://www.getbifrost.ai" }, - "anchors": [ - { - "name": "Community", - "icon": "discord", - "url": "https://getmax.im/bifrost-discord" - }, - { - "name": "Blog", - "icon": "newspaper", - "url": "https://getmaxim.ai/blog" - } - ], "navigation": { + "global": { + "anchors": [ + { + "anchor": "Discord", + "icon": "discord", + "href": "https://getmax.im/bifrost-discord" + }, + { + "anchor": "Try Enterprise", + "icon": "building", + "href": "https://www.getmaxim.ai/bifrost/enterprise" + } + ] + }, "tabs": [ { "tab": "Documentation", @@ -380,4 +386,4 @@ "linkedin": "https://linkedin.com/company/maxim-ai" } } -} +} \ No newline at end of file diff --git a/transports/config.schema.json b/transports/config.schema.json index b23e9a00a..fa7a1a9e2 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -1842,6 +1842,10 @@ "password": { "type": "string", "description": "Password for proxy authentication" + }, + "ca_cert_pem": { + "type": "string", + "description": "PEM-encoded CA certificate to trust for TLS connections through the proxy (for SSL-intercepting proxies)" } }, "required": [ diff --git a/ui/app/workspace/config/views/proxyView.tsx b/ui/app/workspace/config/views/proxyView.tsx index 4430b7427..c4b9a269a 100644 --- a/ui/app/workspace/config/views/proxyView.tsx +++ b/ui/app/workspace/config/views/proxyView.tsx @@ -256,6 +256,31 @@ export default function ProxyView() { )} /> + {/* CA Certificate */} + ( + + CA Certificate (PEM) (Optional) + +