github.com/argoproj/argo-cd/v3@v3.2.1/util/dex/dex.go (about) 1 package dex 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "crypto/x509" 7 stderrors "errors" 8 "io" 9 "net/http" 10 "net/http/httputil" 11 "net/url" 12 "path" 13 "strconv" 14 "strings" 15 16 log "github.com/sirupsen/logrus" 17 18 "github.com/argoproj/argo-cd/v3/common" 19 "github.com/argoproj/argo-cd/v3/util/errors" 20 ) 21 22 func decorateDirector(director func(req *http.Request), target *url.URL) func(req *http.Request) { 23 return func(req *http.Request) { 24 director(req) 25 req.Host = target.Host 26 } 27 } 28 29 type DexTLSConfig struct { 30 DisableTLS bool 31 StrictValidation bool 32 RootCAs *x509.CertPool 33 Certificate []byte 34 } 35 36 func TLSConfig(tlsConfig *DexTLSConfig) *tls.Config { 37 if tlsConfig == nil || tlsConfig.DisableTLS { 38 return nil 39 } 40 if !tlsConfig.StrictValidation { 41 return &tls.Config{ 42 InsecureSkipVerify: true, 43 } 44 } 45 return &tls.Config{ 46 InsecureSkipVerify: false, 47 RootCAs: tlsConfig.RootCAs, 48 VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error { 49 if !bytes.Equal(rawCerts[0], tlsConfig.Certificate) { 50 return stderrors.New("dex server certificate does not match") 51 } 52 return nil 53 }, 54 } 55 } 56 57 // NewDexHTTPReverseProxy returns a reverse proxy to the Dex server. Dex is assumed to be configured 58 // with the external issuer URL muxed to the same path configured in server.go. In other words, if 59 // Argo CD API server wants to proxy requests at /api/dex, then the dex config yaml issuer URL should 60 // also be /api/dex (e.g. issuer: https://argocd.example.com/api/dex) 61 func NewDexHTTPReverseProxy(serverAddr string, baseHRef string, tlsConfig *DexTLSConfig) func(writer http.ResponseWriter, request *http.Request) { 62 fullAddr := DexServerAddressWithProtocol(serverAddr, tlsConfig) 63 64 target, err := url.Parse(fullAddr) 65 errors.CheckError(err) 66 target.Path = baseHRef 67 68 proxy := httputil.NewSingleHostReverseProxy(target) 69 70 if tlsConfig != nil && !tlsConfig.DisableTLS { 71 proxy.Transport = &http.Transport{ 72 TLSClientConfig: TLSConfig(tlsConfig), 73 } 74 } 75 76 proxy.ModifyResponse = func(resp *http.Response) error { 77 if resp.StatusCode == http.StatusInternalServerError { 78 b, err := io.ReadAll(resp.Body) 79 if err != nil { 80 return err 81 } 82 err = resp.Body.Close() 83 if err != nil { 84 return err 85 } 86 log.WithFields(log.Fields{ 87 common.SecurityField: common.SecurityMedium, 88 }).Errorf("received error from dex: %s", string(b)) 89 resp.ContentLength = 0 90 resp.Header.Set("Content-Length", strconv.Itoa(0)) 91 resp.Header.Set("Location", path.Join(baseHRef, "login")+"?has_sso_error=true") 92 resp.StatusCode = http.StatusSeeOther 93 resp.Body = io.NopCloser(bytes.NewReader(make([]byte, 0))) 94 return nil 95 } 96 return nil 97 } 98 proxy.Director = decorateDirector(proxy.Director, target) 99 return func(w http.ResponseWriter, r *http.Request) { 100 proxy.ServeHTTP(w, r) 101 } 102 } 103 104 // NewDexRewriteURLRoundTripper creates a new DexRewriteURLRoundTripper 105 func NewDexRewriteURLRoundTripper(dexServerAddr string, t http.RoundTripper) DexRewriteURLRoundTripper { 106 dexURL, _ := url.Parse(dexServerAddr) 107 return DexRewriteURLRoundTripper{ 108 DexURL: dexURL, 109 T: t, 110 } 111 } 112 113 // DexRewriteURLRoundTripper is an HTTP RoundTripper to rewrite HTTP requests to the specified 114 // dex server address. This is used when reverse proxying Dex to avoid the API server from 115 // unnecessarily communicating to Argo CD through its externally facing load balancer, which is not 116 // always permitted in firewalled/air-gapped networks. 117 type DexRewriteURLRoundTripper struct { 118 DexURL *url.URL 119 T http.RoundTripper 120 } 121 122 func (s DexRewriteURLRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { 123 r.URL.Host = s.DexURL.Host 124 r.URL.Scheme = s.DexURL.Scheme 125 r.Host = s.DexURL.Host 126 return s.T.RoundTrip(r) 127 } 128 129 func DexServerAddressWithProtocol(orig string, tlsConfig *DexTLSConfig) string { 130 if strings.Contains(orig, "://") { 131 return orig 132 } 133 if tlsConfig == nil || tlsConfig.DisableTLS { 134 return "http://" + orig 135 } 136 return "https://" + orig 137 }