Skip to content

Commit

Permalink
Support for specific http proxy for the service
Browse files Browse the repository at this point in the history
  • Loading branch information
martinkunc committed Apr 1, 2020
1 parent 508da9e commit 65e2183
Show file tree
Hide file tree
Showing 8 changed files with 558 additions and 8 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
google.golang.org/appengine v1.6.1 // indirect
k8s.io/api v0.17.1
Expand Down
27 changes: 26 additions & 1 deletion pkg/authorizer/clusterauthorizer/clusterauthorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package clusterauthorizer
import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/openshift/insights-operator/pkg/config"
"golang.org/x/net/http/httpproxy"
knet "k8s.io/apimachinery/pkg/util/net"
)

type Configurator interface {
Expand All @@ -14,11 +17,14 @@ type Configurator interface {

type Authorizer struct {
configurator Configurator
// exposed for tests
proxyFromEnvironment func(*http.Request) (*url.URL, error)
}

func New(configurator Configurator) *Authorizer {
return &Authorizer{
configurator: configurator,
configurator: configurator,
proxyFromEnvironment: http.ProxyFromEnvironment,
}
}

Expand All @@ -44,3 +50,22 @@ func (a *Authorizer) Authorize(req *http.Request) error {
}
return nil
}

func (a *Authorizer) NewSystemOrConfiguredProxy() func(*http.Request) (*url.URL, error) {
// using specific proxy settings
if c := a.configurator.Config(); c != nil {
if len(c.HTTPConfig.HTTPProxy) > 0 || len(c.HTTPConfig.HTTPSProxy) > 0 || len(c.HTTPConfig.NoProxy) > 0 {
proxyConfig := httpproxy.Config{
HTTPProxy: c.HTTPConfig.HTTPProxy,
HTTPSProxy: c.HTTPConfig.HTTPSProxy,
NoProxy: c.HTTPConfig.NoProxy,
}
// The golang ProxyFunc seems to have NoProxy already built in
return func(req *http.Request) (*url.URL, error) {
return proxyConfig.ProxyFunc()(req.URL)
}
}
}
// defautl system proxy
return knet.NewProxierWithNoProxyCIDR(a.proxyFromEnvironment)
}
134 changes: 134 additions & 0 deletions pkg/authorizer/clusterauthorizer/clusterauthorizer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package clusterauthorizer

import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"

"github.com/openshift/insights-operator/pkg/config"
"golang.org/x/net/http/httpproxy"
)

// nonCachedProxyFromEnvironment creates Proxier if Proxy is set. It uses always fresh Env
func nonCachedProxyFromEnvironment() func(*http.Request) (*url.URL, error) {
return func(req *http.Request) (*url.URL, error) {
return httpproxy.FromEnvironment().ProxyFunc()(req.URL)
}
}

func TestProxy(tt *testing.T) {
testCases := []struct {
Name string
EnvValues map[string]interface{}
RequestURL string
HttpConfig config.HTTPConfig
ProxyURL string
}{
{
Name: "No env set, no specific proxy",
EnvValues: map[string]interface{}{"HTTP_PROXY": nil},
RequestURL: "http://google.com",
ProxyURL: "",
},
{
Name: "Env set, no specific proxy",
EnvValues: map[string]interface{}{"HTTP_PROXY": "proxy.to"},
RequestURL: "http://google.com",
ProxyURL: "http://proxy.to",
},
{
Name: "Env set with HTTPS, no specific proxy",
EnvValues: map[string]interface{}{"HTTPS_PROXY": "secproxy.to"},
RequestURL: "https://google.com",
ProxyURL: "http://secproxy.to",
},
{
Name: "Env not set, specific proxy set",
EnvValues: map[string]interface{}{"HTTP_PROXY": nil},
RequestURL: "http://google.com",
HttpConfig: config.HTTPConfig{HTTPProxy: "specproxy.to"},
ProxyURL: "http://specproxy.to",
},
{
Name: "Env set, specific proxy set http",
EnvValues: map[string]interface{}{"HTTP_PROXY": "envproxy.to"},
RequestURL: "http://google.com",
HttpConfig: config.HTTPConfig{HTTPProxy: "specproxy.to"},
ProxyURL: "http://specproxy.to",
},
{
Name: "Env set, specific proxy set https",
EnvValues: map[string]interface{}{"HTTPS_PROXY": "envsecproxy.to"},
RequestURL: "https://google.com",
HttpConfig: config.HTTPConfig{HTTPProxy: "specsecproxy.to"},
ProxyURL: "http://specsecproxy.to",
},
{
Name: "Env set, specific proxy set noproxy, request without noproxy",
EnvValues: map[string]interface{}{"HTTPS_PROXY": "envsecproxy.to", "NO_PROXY": "envnoproxy.to"},
RequestURL: "https://google.com",
HttpConfig: config.HTTPConfig{HTTPProxy: "specsecproxy.to", NoProxy: "specnoproxy.to"},
ProxyURL: "http://specsecproxy.to",
},
{
Name: "Env set, specific proxy set noproxy, request with noproxy",
EnvValues: map[string]interface{}{"HTTPS_PROXY": "envsecproxy.to", "NO_PROXY": "envnoproxy.to"},
RequestURL: "https://specnoproxy.to",
HttpConfig: config.HTTPConfig{HTTPProxy: "specsecproxy.to", NoProxy: "specnoproxy.to"},
ProxyURL: "",
},
}
for _, tcase := range testCases {
tc := tcase
tt.Run(tc.Name, func(t *testing.T) {
for k, v := range tc.EnvValues {
defer SafeRestoreEnv(k)()
// nil will indicate the need to unset Env
if v != nil {
vv := v.(string)
os.Setenv(k, vv)
} else {
os.Unsetenv(k)
}
}

co2 := &testConfig{config: &config.Controller{HTTPConfig: tc.HttpConfig}}
a := Authorizer{proxyFromEnvironment: nonCachedProxyFromEnvironment(), configurator: co2}
p := a.NewSystemOrConfiguredProxy()
req := httptest.NewRequest("GET", tc.RequestURL, nil)
url, err := p(req)

if err != nil {
t.Fatalf("unexpected err %s", err)
}
if (tc.ProxyURL == "" && url != nil) ||
(len(tc.ProxyURL) > 0 && (url == nil || tc.ProxyURL != url.String())) {
t.Fatalf("Unexpected value of Proxy Url. Test %s Expected Url %s Received Url %s", tc.Name, tc.ProxyURL, url)
}
})
}
}

type testConfig struct {
config *config.Controller
}

func (t *testConfig) Config() *config.Controller {
return t.config
}

func SafeRestoreEnv(key string) func() {
originalVal, wasSet := os.LookupEnv(key)
return func() {
if !wasSet {
fmt.Printf("unsetting key %s", key)
os.Unsetenv(key)
} else {
fmt.Printf("restoring key %s", key)
os.Setenv(key, originalVal)
}
}
}
9 changes: 9 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,13 @@ type Controller struct {
Username string
Password string
Token string

HTTPConfig HTTPConfig
}

// HTTPConfig configures http proxy and exception settings if they come from config
type HTTPConfig struct {
HTTPProxy string
HTTPSProxy string
NoProxy string
}
10 changes: 10 additions & 0 deletions pkg/config/configobserver/configobserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ func (c *Controller) retrieveConfig() error {
if endpoint, ok := secret.Data["endpoint"]; ok {
nextConfig.Endpoint = string(endpoint)
}
if httpproxy, ok := secret.Data["httpProxy"]; ok {
nextConfig.HTTPConfig.HTTPProxy = string(httpproxy)
}
if httpsproxy, ok := secret.Data["httpsProxy"]; ok {
nextConfig.HTTPConfig.HTTPSProxy = string(httpsproxy)
}
if noproxy, ok := secret.Data["noProxy"]; ok {
nextConfig.HTTPConfig.NoProxy = string(noproxy)
}
nextConfig.Report = len(nextConfig.Endpoint) > 0

if intervalString, ok := secret.Data["interval"]; ok {
Expand Down Expand Up @@ -207,6 +216,7 @@ func (c *Controller) mergeConfigLocked() {
if len(c.secretConfig.Endpoint) > 0 {
cfg.Endpoint = c.secretConfig.Endpoint
}
cfg.HTTPConfig = c.secretConfig.HTTPConfig
}
if c.tokenConfig != nil {
cfg.Token = c.tokenConfig.Token
Expand Down
13 changes: 7 additions & 6 deletions pkg/insights/insightsclient/insightsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (
"net"
"net/http"
"net/textproto"
"net/url"
"os"
"strconv"
"time"

knet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/client-go/pkg/version"
"k8s.io/client-go/transport"
"k8s.io/component-base/metrics"
Expand All @@ -40,6 +40,7 @@ type Client struct {

type Authorizer interface {
Authorize(req *http.Request) error
NewSystemOrConfiguredProxy() func(*http.Request) (*url.URL, error)
}

type ClusterVersionInfo interface {
Expand Down Expand Up @@ -88,10 +89,10 @@ func getTrustedCABundle() (*x509.CertPool, error) {
return certs, nil
}

func clientTransport() http.RoundTripper {
// default transport, proxy from env
// clientTransport creates new http.Transport with either system or configured Proxy
func clientTransport(authorizer Authorizer) http.RoundTripper {
clientTransport := &http.Transport{
Proxy: knet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment),
Proxy: authorizer.NewSystemOrConfiguredProxy(),
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
Expand Down Expand Up @@ -158,14 +159,14 @@ func (c *Client) Send(ctx context.Context, endpoint string, source Source) error
req.Body = pr

// dynamically set the proxy environment
c.client.Transport = clientTransport()
c.client.Transport = clientTransport(c.authorizer)

klog.V(4).Infof("Uploading %s to %s", source.Type, req.URL.String())
resp, err := c.client.Do(req)
if err != nil {
klog.V(4).Infof("Unable to build a request, possible invalid token: %v", err)
// if the request is not build, for example because of invalid endpoint,(maybe some problem with DNS), we want to have record about it in metrics as well.
counterRequestSend.WithLabelValues(c.metricsName, "0").Inc()
counterRequestSend.WithLabelValues(c.metricsName, "0").Inc()
return fmt.Errorf("unable to build request to connect to Insights server: %v", err)
}

Expand Down
Loading

0 comments on commit 65e2183

Please sign in to comment.