From c3ee25ef88970ebd9aa3c6ba4bd17891758e1bfe Mon Sep 17 00:00:00 2001 From: Mark van Holsteijn Date: Mon, 6 Dec 2021 20:58:15 +0100 Subject: [PATCH] refactored processed almost all go vet and lint remarks --- clusterinfo/cache.go | 49 ++-- clusterinfo/cache_test.go | 4 +- cmd/{client.go => gke_client.go} | 51 ++-- cmd/{server.go => gke_server.go} | 11 +- cmd/root.go | 21 +- {client => gke_client}/main.go | 68 +++--- {client => gke_client}/rewrite_request_url.go | 7 +- {server => gke_server}/main.go | 26 +- go.mod | 6 +- go.sum | 7 + old_main.go | 227 ------------------ terraform/iap-proxy.service | 2 +- terraform/output.tf | 2 +- 13 files changed, 134 insertions(+), 347 deletions(-) rename cmd/{client.go => gke_client.go} (54%) rename cmd/{server.go => gke_server.go} (73%) rename {client => gke_client}/main.go (72%) rename {client => gke_client}/rewrite_request_url.go (85%) rename {server => gke_server}/main.go (80%) delete mode 100644 old_main.go diff --git a/clusterinfo/cache.go b/clusterinfo/cache.go index 0f0eb5d..e4e0686 100644 --- a/clusterinfo/cache.go +++ b/clusterinfo/cache.go @@ -14,30 +14,33 @@ import ( "time" ) -type ClusterInfo struct { +// ConnectInfo provides basie GKE cluster connect information +type ConnectInfo struct { Name string Endpoint string ClusterCaCertificate string RootCAs *x509.CertPool } -// map from endpoint to name and certificate -type ClusterInfoMap map[string]*ClusterInfo +// Map provides a lookup for cluster connection information base on the hostname +type Map map[string]*ConnectInfo -type ClusterInfoCache struct { +// Cache provides access to a cached cluster information map +type Cache struct { ctx context.Context - projectId string + projectID string credentials *google.Credentials refresh time.Duration - clusterInfo *ClusterInfoMap + clusterInfo *Map mutex sync.Mutex } -func NewClusterInfoCache(ctx context.Context, projectId string, credentials *google.Credentials, refresh time.Duration) (*ClusterInfoCache, error) { - cache := &ClusterInfoCache{ +// NewCache creates a cluster info cache which is refreshed every `refresh` +func NewCache(ctx context.Context, projectID string, credentials *google.Credentials, refresh time.Duration) (*Cache, error) { + cache := &Cache{ ctx: ctx, credentials: credentials, - projectId: projectId, + projectID: projectID, refresh: refresh, } clusterInfo, err := cache.retrieveClusters() @@ -49,34 +52,34 @@ func NewClusterInfoCache(ctx context.Context, projectId string, credentials *goo return cache, nil } -func (c *ClusterInfoCache) GetClusterInfoForEndpoint(endpoint string) *ClusterInfo { +// GetConnectInfoForEndpoint returns connect information for the host, or nil if not found +func (c *Cache) GetConnectInfoForEndpoint(endpoint string) *ConnectInfo { host := strings.Split(endpoint, ":") if r, ok := (*c.clusterInfo)[host[0]]; ok { return r - } else { - return nil } + return nil } // thread safe get cluster info -func (c *ClusterInfoCache) getClusterInfo() *ClusterInfoMap { +func (c *Cache) getClusterInfo() *Map { c.mutex.Lock() defer c.mutex.Unlock() return c.clusterInfo } // thread safe set cluster info -func (c *ClusterInfoCache) setClusterInfo(m *ClusterInfoMap) { +func (c *Cache) setClusterInfo(m *Map) { c.mutex.Lock() defer c.mutex.Unlock() c.clusterInfo = m } -// returns a copy of the cluster info map -func (c *ClusterInfoCache) GetClusterInfo() *ClusterInfoMap { - result := make(ClusterInfoMap) +// GetMap returns a copy of the cluster info map +func (c *Cache) GetMap() *Map { + result := make(Map) for k, v := range *c.getClusterInfo() { - result[k] = &ClusterInfo{ + result[k] = &ConnectInfo{ Endpoint: v.Endpoint, Name: v.Name, ClusterCaCertificate: v.ClusterCaCertificate, @@ -86,7 +89,7 @@ func (c *ClusterInfoCache) GetClusterInfo() *ClusterInfoMap { return &result } -func (c *ClusterInfoCache) run() { +func (c *Cache) run() { for { select { case <-c.ctx.Done(): @@ -117,15 +120,15 @@ func createCertPool(name string, clusterCaCertificate string) *x509.CertPool { return result } -func (c *ClusterInfoCache) retrieveClusters() (*ClusterInfoMap, error) { - result := make(ClusterInfoMap) +func (c *Cache) retrieveClusters() (*Map, error) { + result := make(Map) service, err := container.NewService(c.ctx, option.WithTokenSource(c.credentials.TokenSource)) if err != nil { return nil, err } - parent := fmt.Sprintf("projects/%s/locations/-", c.projectId) + parent := fmt.Sprintf("projects/%s/locations/-", c.projectID) response, err := service.Projects.Locations.Clusters.List(parent).Do() if err != nil { return nil, err @@ -135,7 +138,7 @@ func (c *ClusterInfoCache) retrieveClusters() (*ClusterInfoMap, error) { log.Printf("INFO: skipping cluster %s in status %s", cluster.Name, cluster.Status) continue } - result[cluster.Endpoint] = &ClusterInfo{ + result[cluster.Endpoint] = &ConnectInfo{ Name: cluster.Name, Endpoint: cluster.Endpoint, ClusterCaCertificate: cluster.MasterAuth.ClusterCaCertificate, diff --git a/clusterinfo/cache_test.go b/clusterinfo/cache_test.go index 5bc331c..a9b63bd 100644 --- a/clusterinfo/cache_test.go +++ b/clusterinfo/cache_test.go @@ -16,12 +16,12 @@ func TestListClusters(t *testing.T) { if err != nil { t.Fatal(err) } - cache, err := NewClusterInfoCache(ctx, creds.ProjectID, creds, time.Second) + cache, err := NewCache(ctx, creds.ProjectID, creds, time.Second) if err != nil { t.Fatal(err) } - clusters := cache.GetClusterInfo() + clusters := cache.GetMap() if len(*clusters) == 0 { t.Fatalf("expected at least 1 cluster, found none") } diff --git a/cmd/client.go b/cmd/gke_client.go similarity index 54% rename from cmd/client.go rename to cmd/gke_client.go index 6bce8ad..550d242 100644 --- a/cmd/client.go +++ b/cmd/gke_client.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/binxio/simple-iap-proxy/client" + "github.com/binxio/simple-iap-proxy/gke_client" "github.com/spf13/cobra" "log" "net/url" @@ -22,31 +22,34 @@ func validateClientArguments(cmd *cobra.Command, args []string) error { return fmt.Errorf("specify either --use-default-credentials or --configuration, not both") } - if u, err := url.Parse(targetURL); err != nil { + u, err := url.Parse(targetURL) + if err != nil { return fmt.Errorf("invalid target-url %s, %s", targetURL, err) - } else { - if u.Scheme != "https" { - return fmt.Errorf("target-url must be https") - } + } + + if u.Scheme != "https" { + return fmt.Errorf("target-url must be https") } return validateRootArguments(cmd, args) } func init() { - clientCmd.Flags().StringVarP(&targetURL, "target-url", "t", "", "to forward requests to") - clientCmd.Flags().StringVarP(&audience, "iap-audience", "a", "", "of the IAP application") - clientCmd.Flags().StringVarP(&serviceAccount, "service-account", "s", "", "to impersonate") - clientCmd.Flags().BoolVarP(&useDefaultCredentials, "use-default-credentials", "u", false, "use default credentials instead of gcloud configuration") - clientCmd.Flags().StringVarP(&configurationName, "configuration", "C", "", "name of gcloud configuration to use for credentials") - clientCmd.MarkFlagRequired("iap-audience") - clientCmd.MarkFlagRequired("service-account") - clientCmd.MarkFlagRequired("target-url") - clientCmd.Flags().SortFlags = false + gkeClientCmd.Flags().StringVarP(&targetURL, "target-url", "t", "", "to forward requests to") + gkeClientCmd.Flags().StringVarP(&audience, "iap-audience", "a", "", "of the IAP application") + gkeClientCmd.Flags().StringVarP(&serviceAccount, "service-account", "s", "", "to impersonate") + gkeClientCmd.Flags().BoolVarP(&useDefaultCredentials, "use-default-credentials", "u", false, "use default credentials instead of gcloud configuration") + gkeClientCmd.Flags().StringVarP(&configurationName, "configuration", "C", "", "name of gcloud configuration to use for credentials") + if err := gkeClientCmd.MarkFlagRequired("iap-audience"); err != nil { + log.Fatal(err) + } + gkeClientCmd.MarkFlagRequired("service-account") + gkeClientCmd.MarkFlagRequired("target-url") + gkeClientCmd.Flags().SortFlags = false } -var clientCmd = &cobra.Command{ - Use: "client", +var gkeClientCmd = &cobra.Command{ + Use: "gke-client", Short: "starts a client side proxy, forwarding requests to the GKE cluster via the IAP", Long: `The client will start a real HTTP/S proxy and forward any requests for ip address of GKE cluster master endpoints, to the IAP proxy. @@ -54,17 +57,17 @@ ip address of GKE cluster master endpoints, to the IAP proxy. Args: validateClientArguments, Run: func(cmd *cobra.Command, args []string) { var err error - if keyFile != "" { - certificate, err = loadCertificate(keyFile) - if err != nil { - log.Fatal(err) - } + + certificate, err = loadCertificate(keyFile) + if err != nil { + log.Fatal(err) } - c := client.Proxy{ + + c := gke_client.Proxy{ Debug: debug, Port: port, ServiceAccount: serviceAccount, - ProjectId: projectID, + ProjectID: projectID, UseDefaultCredentials: useDefaultCredentials, ConfigurationName: configurationName, Audience: audience, diff --git a/cmd/server.go b/cmd/gke_server.go similarity index 73% rename from cmd/server.go rename to cmd/gke_server.go index 504547a..97dd5a9 100644 --- a/cmd/server.go +++ b/cmd/gke_server.go @@ -1,17 +1,16 @@ package cmd import ( - "github.com/binxio/simple-iap-proxy/server" + "github.com/binxio/simple-iap-proxy/gke_server" "github.com/spf13/cobra" ) func init() { - serverCmd.MarkFlagRequired("key-file") - serverCmd.MarkFlagRequired("certificate-file") + gkeServerCmd.Flags().SortFlags = false } -var serverCmd = &cobra.Command{ - Use: "server", +var gkeServerCmd = &cobra.Command{ + Use: "gke-server", Short: "forwards requests from the load balancer to the appropriate GKE cluster", Long: `reads the Host header of the http requests and if it matches the ip address of GKE cluster master endpoint, forwards the request to it. @@ -19,7 +18,7 @@ var serverCmd = &cobra.Command{ TraverseChildren: true, Args: validateRootArguments, Run: func(cmd *cobra.Command, args []string) { - s := server.ReverseProxy{ + s := gke_server.ReverseProxy{ Debug: debug, Port: port, ProjectID: projectID, diff --git a/cmd/root.go b/cmd/root.go index 05ddc50..f286195 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -26,10 +26,12 @@ with a private master IP address via an IAP proxy. It consists of a proxy which can be run on the client side, and a reverse-proxy which is run inside the VPC. `, + Args: validateRootArguments, } ) +// Execute the main command func Execute() error { return rootCmd.Execute() } @@ -70,15 +72,8 @@ func loadCertificate(keyFile string) (*tls.Certificate, error) { } func validateRootArguments(_ *cobra.Command, _ []string) error { - // mis-using the positional argument validator here. - if certificateFile != "" && keyFile == "" || keyFile != "" && certificateFile == "" { - return fmt.Errorf("both --certificate-file and --key-file are required.") - } - - if keyFile != "" { - if _, err := loadCertificate(keyFile); err != nil { - return err - } + if _, err := loadCertificate(keyFile); err != nil { + return err } return nil @@ -92,8 +87,10 @@ func init() { rootCmd.PersistentFlags().StringVarP(&certificateFile, "certificate-file", "c", "", "certificate of the server") rootCmd.MarkFlagFilename("key-file") rootCmd.MarkFlagFilename("certificate-file") + rootCmd.MarkFlagRequired("key-file") + rootCmd.MarkFlagRequired("certificate-file") + rootCmd.PersistentFlags().SortFlags = false - rootCmd.AddCommand(clientCmd) - rootCmd.AddCommand(serverCmd) - rootCmd.Flags().SortFlags = false + rootCmd.AddCommand(gkeClientCmd) + rootCmd.AddCommand(gkeServerCmd) } diff --git a/client/main.go b/gke_client/main.go similarity index 72% rename from client/main.go rename to gke_client/main.go index d83c3fa..2ef8f9a 100644 --- a/client/main.go +++ b/gke_client/main.go @@ -1,4 +1,4 @@ -package client +package gke_client import ( "context" @@ -17,10 +17,11 @@ import ( "time" ) +// Proxy provides the runtime configuration of the GKE Proxy type Proxy struct { Debug bool Port int - ProjectId string + ProjectID string KeyFile string CertificateFile string Certificate *tls.Certificate @@ -33,7 +34,7 @@ type Proxy struct { targetURL *url.URL credentials *google.Credentials tokenSource oauth2.TokenSource - clusterInfoCache *clusterinfo.ClusterInfoCache + clusterInfoCache *clusterinfo.Cache } func (p *Proxy) getCredentials(ctx context.Context) error { @@ -47,20 +48,23 @@ func (p *Proxy) getCredentials(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to obtain credentials, %s", err) } - if p.ProjectId == "" { - p.ProjectId = p.credentials.ProjectID + if p.ProjectID == "" { + p.ProjectID = p.credentials.ProjectID } - if p.ProjectId == "" { - fmt.Errorf("specify a --project as there is no default one") + if p.ProjectID == "" { + return fmt.Errorf("specify a --project as there is no default one") } return nil } +// IsClusterEndpoint return true if the request is targeting an GKE cluster endpoint func (p *Proxy) IsClusterEndpoint() goproxy.ReqConditionFunc { return func(req *http.Request, ctx *goproxy.ProxyCtx) bool { - return p.clusterInfoCache.GetClusterInfoForEndpoint(req.URL.Host) != nil + return p.clusterInfoCache.GetConnectInfoForEndpoint(req.URL.Host) != nil } } + +// OnRequest inserts the IAP required token and renames an existing Authorization header func (p *Proxy) OnRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { log.Printf("on request to %s", r.URL) @@ -92,31 +96,30 @@ func (p *Proxy) createProxy() *goproxy.ProxyHttpServer { proxy.OnRequest(p.IsClusterEndpoint()).HandleConnect(goproxy.AlwaysMitm) proxy.OnRequest(p.IsClusterEndpoint()).DoFunc(p.OnRequest) - if p.Certificate != nil { - - goproxy.GoproxyCa = *p.Certificate - tlsConfig := goproxy.TLSConfigFromCA(p.Certificate) + goproxy.GoproxyCa = *p.Certificate + tlsConfig := goproxy.TLSConfigFromCA(p.Certificate) - goproxy.OkConnect = &goproxy.ConnectAction{ - Action: goproxy.ConnectAccept, - TLSConfig: tlsConfig, - } - goproxy.MitmConnect = &goproxy.ConnectAction{ - Action: goproxy.ConnectMitm, - TLSConfig: tlsConfig, - } - goproxy.HTTPMitmConnect = &goproxy.ConnectAction{ - Action: goproxy.ConnectHTTPMitm, - TLSConfig: tlsConfig, - } - goproxy.RejectConnect = &goproxy.ConnectAction{ - Action: goproxy.ConnectReject, - TLSConfig: tlsConfig, - } + goproxy.OkConnect = &goproxy.ConnectAction{ + Action: goproxy.ConnectAccept, + TLSConfig: tlsConfig, + } + goproxy.MitmConnect = &goproxy.ConnectAction{ + Action: goproxy.ConnectMitm, + TLSConfig: tlsConfig, } + goproxy.HTTPMitmConnect = &goproxy.ConnectAction{ + Action: goproxy.ConnectHTTPMitm, + TLSConfig: tlsConfig, + } + goproxy.RejectConnect = &goproxy.ConnectAction{ + Action: goproxy.ConnectReject, + TLSConfig: tlsConfig, + } + return proxy } +// Run the proxy until stopped func (p *Proxy) Run() { var err error @@ -132,7 +135,7 @@ func (p *Proxy) Run() { log.Fatalf("%s", err) } - p.clusterInfoCache, err = clusterinfo.NewClusterInfoCache(ctx, p.ProjectId, p.credentials, 5*time.Minute) + p.clusterInfoCache, err = clusterinfo.NewCache(ctx, p.ProjectID, p.credentials, 5*time.Minute) if err != nil { log.Fatalf("%s", err) } @@ -155,11 +158,8 @@ func (p *Proxy) Run() { Addr: fmt.Sprintf(":%d", p.Port), TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), } - if p.KeyFile == "" { - err = srv.ListenAndServe() - } else { - err = srv.ListenAndServeTLS(p.CertificateFile, p.KeyFile) - } + + err = srv.ListenAndServeTLS(p.CertificateFile, p.KeyFile) if err != nil { log.Fatal(err) } diff --git a/client/rewrite_request_url.go b/gke_client/rewrite_request_url.go similarity index 85% rename from client/rewrite_request_url.go rename to gke_client/rewrite_request_url.go index 7c905b6..133aa36 100644 --- a/client/rewrite_request_url.go +++ b/gke_client/rewrite_request_url.go @@ -1,4 +1,4 @@ -package client +package gke_client import ( "net/http" @@ -6,7 +6,7 @@ import ( "strings" ) -// function copied from httputil.reverseproxy.go +// SingleJoiningSlash function copied from httputil.reverseproxy.go func SingleJoiningSlash(a, b string) string { aslash := strings.HasSuffix(a, "/") bslash := strings.HasPrefix(b, "/") @@ -19,7 +19,7 @@ func SingleJoiningSlash(a, b string) string { return a + b } -// function copied from httputil.reverseproxy.go +// JoinURLPath function copied from httputil.reverseproxy.go func JoinURLPath(a, b *url.URL) (path, rawpath string) { if a.RawPath == "" && b.RawPath == "" { return SingleJoiningSlash(a.Path, b.Path), "" @@ -41,6 +41,7 @@ func JoinURLPath(a, b *url.URL) (path, rawpath string) { return a.Path + b.Path, apath + bpath } +// RewriteRequestURL rewrites the `req` URL so that it can be forwarded to `target` func RewriteRequestURL(req *http.Request, target *url.URL) { targetQuery := target.RawQuery req.URL.Scheme = target.Scheme diff --git a/server/main.go b/gke_server/main.go similarity index 80% rename from server/main.go rename to gke_server/main.go index b952b89..34cc55f 100644 --- a/server/main.go +++ b/gke_server/main.go @@ -1,4 +1,4 @@ -package server +package gke_server import ( "context" @@ -14,13 +14,14 @@ import ( "time" ) +// ReverseProxy provides the runtime configuration of the Reverse Proxy type ReverseProxy struct { Debug bool Port int ProjectID string KeyFile string CertificateFile string - clusterInfoCache *clusterinfo.ClusterInfoCache + clusterInfoCache *clusterinfo.Cache } func (p *ReverseProxy) retrieveClusterInfo(ctx context.Context) error { @@ -36,13 +37,13 @@ func (p *ReverseProxy) retrieveClusterInfo(ctx context.Context) error { return fmt.Errorf("specify a --project as there is no default one") } - p.clusterInfoCache, err = clusterinfo.NewClusterInfoCache(ctx, p.ProjectID, credentials, 5*time.Minute) + p.clusterInfoCache, err = clusterinfo.NewCache(ctx, p.ProjectID, credentials, 5*time.Minute) return err } -func (h *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { - clusterInfo := h.clusterInfoCache.GetClusterInfoForEndpoint(r.Host) + clusterInfo := p.clusterInfoCache.GetConnectInfoForEndpoint(r.Host) if clusterInfo == nil { w.WriteHeader(http.StatusBadGateway) w.Write([]byte(fmt.Sprintf("%s is not a cluster endpoint", r.Host))) @@ -70,7 +71,7 @@ func (h *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - if h.Debug { + if p.Debug { x, err := httputil.DumpRequest(r, true) if err != nil { log.Printf("failed to dump the response body, %s", err) @@ -82,7 +83,7 @@ func (h *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { rec := httptest.NewRecorder() proxy.ServeHTTP(rec, r) - if h.Debug { + if p.Debug { x, err := httputil.DumpResponse(rec.Result(), true) if err != nil { log.Printf("failed to dump the response body, %s", err) @@ -104,6 +105,7 @@ func (h *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +// Run the reverse proxy until stopped func (p *ReverseProxy) Run() { var err error @@ -115,12 +117,12 @@ func (p *ReverseProxy) Run() { } http.Handle("/", p) - - if p.KeyFile == "" { - err = http.ListenAndServe(fmt.Sprintf(":%d", p.Port), nil) - } else { - err = http.ListenAndServeTLS(fmt.Sprintf(":%d", p.Port), p.CertificateFile, p.KeyFile, nil) + srv := &http.Server{ + Addr: fmt.Sprintf(":%d", p.Port), + TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), } + + err = srv.ListenAndServeTLS(p.CertificateFile, p.KeyFile) if err != nil { log.Fatalf("failed to start server, %s", err) } diff --git a/go.mod b/go.mod index 80e331d..1650ea7 100644 --- a/go.mod +++ b/go.mod @@ -28,9 +28,11 @@ require ( github.com/spf13/viper v1.9.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect + golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect google.golang.org/grpc v1.40.0 // indirect diff --git a/go.sum b/go.sum index 5c65577..989a035 100644 --- a/go.sum +++ b/go.sum @@ -338,6 +338,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -389,6 +390,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -488,6 +491,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -547,6 +552,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/old_main.go b/old_main.go deleted file mode 100644 index 2c8880c..0000000 --- a/old_main.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2021 binx.io B.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package main - -import ( - "context" - "crypto/tls" - "flag" - "fmt" - "golang.org/x/oauth2" - "google.golang.org/api/idtoken" - "google.golang.org/api/impersonate" - "log" - "net/http" - "net/http/httptest" - "net/http/httputil" - "net/url" - "os" - "strconv" -) - -type ProxyHandler struct { - proxy *httputil.ReverseProxy - target *url.URL - tokenSource oauth2.TokenSource - debug bool - renameAuthHeader bool -} - -func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - - if h.renameAuthHeader { - // If there is a X-Real-Authorization header, make it Authorization header - if realAuthHeaders := r.Header.Values("X-Real-Authorization"); len(realAuthHeaders) > 0 { - for _, v := range r.Header.Values("X-Real-Authorization") { - r.Header.Add("Authorization", v) - } - } else { - // If there is a Authorization header, make it a X-Real-Authorization header - for _, v := range r.Header.Values("Authorization") { - r.Header.Add("X-Real-Authorization", v) - } - r.Header.Del("Authorization") - } - } - - if h.tokenSource != nil { - if token, err := h.tokenSource.Token(); err != nil { - http.Error(w, fmt.Sprintf("Failed to obtained IAP token, %s", err), http.StatusInternalServerError) - return - } else { - authorization := fmt.Sprintf("%s %s", token.Type(), token.AccessToken) - if r.Header.Get("Authorization") == "" { - r.Header.Set("Authorization", authorization) - } else { - r.Header.Set("Proxy-Authorization", authorization) - } - } - } - r.Host = h.target.Host - - if h.debug { - x, err := httputil.DumpRequest(r, true) - if err != nil { - log.Printf("failed to dump the response body, %s", err) - } else { - log.Println(fmt.Sprintf("%q", x)) - } - } - - rec := httptest.NewRecorder() - h.proxy.ServeHTTP(rec, r) - - if h.debug { - x, err := httputil.DumpResponse(rec.Result(), true) - if err != nil { - log.Printf("failed to dump the response body, %s", err) - } else { - log.Println(fmt.Sprintf("%q", x)) - } - } - - for key, values := range rec.Header() { - for _, value := range values { - w.Header().Add(key, value) - } - } - w.WriteHeader(rec.Code) - _, err := rec.Body.WriteTo(w) - if err != nil { - log.Printf("error writing body, %s", err) - } -} - -func old_main() { - var insecure bool - var debug bool - var renameAuthHeader bool - var targetURL string - var listenPort string - var tokenSource oauth2.TokenSource - var certificateFile string - var keyFile string - var audience string - var serviceAccount string - - flag.StringVar(&targetURL, "target-url", "", "to forward HTTP requests to") - flag.StringVar(&serviceAccount, "service-account", "", "to impersonate") - flag.StringVar(&audience, "iap-audience", "", "to call a service behind the Identity Aware Proxy") - flag.StringVar(&certificateFile, "certificate-file", "", "for TLS") - flag.StringVar(&keyFile, "key-file", "", "for TLS") - flag.BoolVar(&insecure, "insecure", false, "allows insecure TLS connections") - flag.BoolVar(&renameAuthHeader, "rename-auth-header", false, "rename Authorization Header to X-Real-Authorization to workaround IAP limitation") - flag.BoolVar(&debug, "debug", false, "logs requests and responses") - flag.Parse() - if targetURL == "" { - log.Fatal("option -target-url is missing") - } - - if certificateFile != "" && keyFile == "" || keyFile != "" && certificateFile == "" { - log.Fatalf("both -certificate-file and -certificate-key are required.") - } else if keyFile != "" { - if s, err := os.Stat(keyFile); err != nil { - log.Fatalf("invalid option -key-file, %s", err) - } else { - if s.IsDir() { - log.Fatalf("option -key-file must be a file") - } - } - if s, err := os.Stat(certificateFile); err != nil { - log.Fatalf("invalid option -certificate-file, %s", err) - } else { - if s.IsDir() { - log.Fatalf("option -certificate-file must be a file") - } - } - } - - target, err := url.Parse(targetURL) - if err != nil { - log.Fatalf("failed to parse target URL %s, %s", targetURL, err) - } - if target.Scheme != "https" { - log.Fatalf("invalid target url %s, only HTTPS target urls are supported", targetURL) - } - - listenPort = os.Getenv("PORT") - if listenPort == "" { - if keyFile == "" { - listenPort = "8080" - } else { - listenPort = "8443" - } - } - - if port, err := strconv.ParseUint(listenPort, 10, 64); err != nil || port > 65535 { - log.Fatalf("the environment variable PORT is not a valid port number") - } - - proxy := httputil.NewSingleHostReverseProxy(target) - if audience != "" { - if serviceAccount != "" { - tokenSource, err = impersonate.IDTokenSource(context.Background(), impersonate.IDTokenConfig{ - TargetPrincipal: serviceAccount, - Audience: audience, - IncludeEmail: true, - }) - if err != nil { - log.Fatalf("failed to create a token source for audience %s as %s, %s", - audience, serviceAccount, err) - } - } else { - tokenSource, err = idtoken.NewTokenSource(context.Background(), audience) - if err != nil { - log.Fatalf("failed to create a token source for audience %s, %s", - audience, err) - } - } - } - - if tokenSource != nil { - if _, err := tokenSource.Token(); err != nil { - if serviceAccount != "" { - log.Fatalf("cannot create id token for audience %s as %s, %s", audience, serviceAccount, err) - } else { - log.Fatalf("cannot create id token for audience %s, %s", audience, err) - } - } - } - - if insecure { - proxy.Transport = - &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - } - - http.Handle("/", &ProxyHandler{ - proxy: proxy, - target: target, - tokenSource: tokenSource, - renameAuthHeader: renameAuthHeader, - debug: debug}) - - if keyFile == "" { - err = http.ListenAndServe(":"+listenPort, nil) - } else { - err = http.ListenAndServeTLS(":"+listenPort, certificateFile, keyFile, nil) - } - - if err != nil { - log.Fatalf("server failed, %s", err) - } - -} diff --git a/terraform/iap-proxy.service b/terraform/iap-proxy.service index 913b6ee..9e6c45b 100644 --- a/terraform/iap-proxy.service +++ b/terraform/iap-proxy.service @@ -13,7 +13,7 @@ ExecStart=ctr run \ --rm --net-host \ --mount type=bind,src=/etc/ssl,dst=/etc/ssl,options=rbind:ro \ ${IMAGE} iap-proxy \ - /simple-iap-proxy server\ + /simple-iap-proxy gke-server \ --certificate-file /etc/ssl/certs/iap-proxy.cert.pem \ --key-file /etc/ssl/private/iap-proxy.key \ --port 8443 diff --git a/terraform/output.tf b/terraform/output.tf index e4fee03..fd24c48 100644 --- a/terraform/output.tf +++ b/terraform/output.tf @@ -1,6 +1,6 @@ output "iap_proxy_command" { value = <