Skip to content

Commit

Permalink
Support upstream via unix socket
Browse files Browse the repository at this point in the history
  • Loading branch information
alebedev87 committed Nov 16, 2022
1 parent f9b65c7 commit a301259
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 17 deletions.
13 changes: 11 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type config struct {
upstream string
upstreamForceH2C bool
upstreamCAFile string
upstreamUnixSocket string
auth proxy.Config
tls tlsConfig
kubeconfigLocation string
Expand Down Expand Up @@ -104,6 +105,7 @@ func main() {
flagset.StringVar(&cfg.upstream, "upstream", "", "The upstream URL to proxy to once requests have successfully been authenticated and authorized.")
flagset.BoolVar(&cfg.upstreamForceH2C, "upstream-force-h2c", false, "Force h2c to communiate with the upstream. This is required when the upstream speaks h2c(http/2 cleartext - insecure variant of http/2) only. For example, go-grpc server in the insecure mode, such as helm's tiller w/o TLS, speaks h2c only")
flagset.StringVar(&cfg.upstreamCAFile, "upstream-ca-file", "", "The CA the upstream uses for TLS connection. This is required when the upstream uses TLS and its own CA certificate")
flagset.StringVar(&cfg.upstreamUnixSocket, "upstream-unix-socket", "", "The upstream unix socket to proxy to once requests have successfully been authenticated and authorized.")
flagset.StringVar(&configFileName, "config-file", "", "Configuration file to configure kube-rbac-proxy.")
flagset.StringSliceVar(&cfg.allowPaths, "allow-paths", nil, "Comma-separated list of paths against which kube-rbac-proxy pattern-matches the incoming request. If the request doesn't match, kube-rbac-proxy responds with a 404 status code. If omitted, the incoming request path isn't checked. Cannot be used with --ignore-paths.")
flagset.StringSliceVar(&cfg.ignorePaths, "ignore-paths", nil, "Comma-separated list of paths against which kube-rbac-proxy pattern-matches the incoming request. If the requst matches, it will proxy the request without performing an authentication or authorization check. Cannot be used with --allow-paths.")
Expand Down Expand Up @@ -141,6 +143,9 @@ func main() {
}
kcfg := initKubeConfig(cfg.kubeconfigLocation)

if cfg.upstream == "" && cfg.upstreamUnixSocket != "" {
cfg.upstream = "http://localhost"
}
upstreamURL, err := url.Parse(cfg.upstream)
if err != nil {
klog.Fatalf("Failed to parse upstream URL: %v", err)
Expand Down Expand Up @@ -245,9 +250,9 @@ For more information, please go to https://github.com/brancz/kube-rbac-proxy/iss
sarAuthorizer,
)

upstreamTransport, err := initTransport(cfg.upstreamCAFile)
upstreamTransport, err := initTransport(cfg.upstreamCAFile, cfg.upstreamUnixSocket)
if err != nil {
klog.Fatalf("Failed to set up upstream TLS connection: %v", err)
klog.Fatalf("Failed to init transport: %v", err)
}

if len(cfg.allowPaths) > 0 && len(cfg.ignorePaths) > 0 {
Expand All @@ -272,6 +277,7 @@ For more information, please go to https://github.com/brancz/kube-rbac-proxy/iss
proxy.Transport = upstreamTransport

if cfg.upstreamForceH2C {
upstreamUnixSocket := cfg.upstreamUnixSocket
// Force http/2 for connections to the upstream i.e. do not start with HTTP1.1 UPGRADE req to
// initialize http/2 session.
// See https://github.com/golang/go/issues/14141#issuecomment-219212895 for more context
Expand All @@ -281,6 +287,9 @@ For more information, please go to https://github.com/brancz/kube-rbac-proxy/iss
// Do disable TLS.
// In combination with the schema check above. We could enforce h2c against the upstream server
DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
if upstreamUnixSocket != "" {
return net.Dial("unix", upstreamUnixSocket)
}
return net.Dial(netw, addr)
},
}
Expand Down
69 changes: 54 additions & 15 deletions transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
Expand All @@ -27,35 +28,73 @@ import (
"time"
)

func initTransport(upstreamCAFile string) (http.RoundTripper, error) {
if upstreamCAFile == "" {
func initTransport(upstreamCAFile, upstreamUnixSocket string) (http.RoundTripper, error) {
if upstreamCAFile == "" && upstreamUnixSocket == "" {
return http.DefaultTransport, nil
}

rootPEM, err := os.ReadFile(upstreamCAFile)
if err != nil {
return nil, fmt.Errorf("error reading upstream CA file: %v", err)
builder := newHTTPTransportBuilder()

if upstreamCAFile != "" {
rootPEM, err := os.ReadFile(upstreamCAFile)
if err != nil {
return nil, fmt.Errorf("error reading upstream CA file: %v", err)
}

roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM([]byte(rootPEM)); !ok {
return nil, errors.New("error parsing upstream CA certificate")
}
builder.withRootCAs(roots)
}

roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM([]byte(rootPEM)); !ok {
return nil, errors.New("error parsing upstream CA certificate")
if upstreamUnixSocket != "" {
builder.withUnixDialContext(upstreamUnixSocket)
}

// http.Transport sourced from go 1.10.7
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
return builder.build(), nil
}

type httpTransportBuilder struct {
tlsClientConfig *tls.Config
dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
}

func newHTTPTransportBuilder() *httpTransportBuilder {
return &httpTransportBuilder{
dialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
}
}

func (b *httpTransportBuilder) withRootCAs(certs *x509.CertPool) *httpTransportBuilder {
b.tlsClientConfig = &tls.Config{RootCAs: certs}
return b
}

func (b *httpTransportBuilder) withUnixDialContext(socket string) *httpTransportBuilder {
b.dialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext(ctx, "unix", socket)
}
return b
}

func (b *httpTransportBuilder) build() *http.Transport {
// http.Transport sourced from go 1.10.7
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: b.dialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{RootCAs: roots},
TLSClientConfig: b.tlsClientConfig,
}

return transport, nil
}

0 comments on commit a301259

Please sign in to comment.