Files
argo-cd/util/dex/dex.go
github-actions[bot] 4d9835927d Bump major version to 3 (#21410)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: crenshaw-dev <350466+crenshaw-dev@users.noreply.github.com>
2025-01-10 16:14:00 -05:00

138 lines
3.9 KiB
Go

package dex
import (
"bytes"
"crypto/tls"
"crypto/x509"
stderrors "errors"
"io"
"net/http"
"net/http/httputil"
"net/url"
"path"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/v3/common"
"github.com/argoproj/argo-cd/v3/util/errors"
)
func decorateDirector(director func(req *http.Request), target *url.URL) func(req *http.Request) {
return func(req *http.Request) {
director(req)
req.Host = target.Host
}
}
type DexTLSConfig struct {
DisableTLS bool
StrictValidation bool
RootCAs *x509.CertPool
Certificate []byte
}
func TLSConfig(tlsConfig *DexTLSConfig) *tls.Config {
if tlsConfig == nil || tlsConfig.DisableTLS {
return nil
}
if !tlsConfig.StrictValidation {
return &tls.Config{
InsecureSkipVerify: true,
}
}
return &tls.Config{
InsecureSkipVerify: false,
RootCAs: tlsConfig.RootCAs,
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if !bytes.Equal(rawCerts[0], tlsConfig.Certificate) {
return stderrors.New("dex server certificate does not match")
}
return nil
},
}
}
// NewDexHTTPReverseProxy returns a reverse proxy to the Dex server. Dex is assumed to be configured
// with the external issuer URL muxed to the same path configured in server.go. In other words, if
// Argo CD API server wants to proxy requests at /api/dex, then the dex config yaml issuer URL should
// also be /api/dex (e.g. issuer: https://argocd.example.com/api/dex)
func NewDexHTTPReverseProxy(serverAddr string, baseHRef string, tlsConfig *DexTLSConfig) func(writer http.ResponseWriter, request *http.Request) {
fullAddr := DexServerAddressWithProtocol(serverAddr, tlsConfig)
target, err := url.Parse(fullAddr)
errors.CheckError(err)
target.Path = baseHRef
proxy := httputil.NewSingleHostReverseProxy(target)
if tlsConfig != nil && !tlsConfig.DisableTLS {
proxy.Transport = &http.Transport{
TLSClientConfig: TLSConfig(tlsConfig),
}
}
proxy.ModifyResponse = func(resp *http.Response) error {
if resp.StatusCode == http.StatusInternalServerError {
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
err = resp.Body.Close()
if err != nil {
return err
}
log.WithFields(log.Fields{
common.SecurityField: common.SecurityMedium,
}).Errorf("received error from dex: %s", string(b))
resp.ContentLength = 0
resp.Header.Set("Content-Length", strconv.Itoa(0))
resp.Header.Set("Location", path.Join(baseHRef, "login")+"?has_sso_error=true")
resp.StatusCode = http.StatusSeeOther
resp.Body = io.NopCloser(bytes.NewReader(make([]byte, 0)))
return nil
}
return nil
}
proxy.Director = decorateDirector(proxy.Director, target)
return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}
// NewDexRewriteURLRoundTripper creates a new DexRewriteURLRoundTripper
func NewDexRewriteURLRoundTripper(dexServerAddr string, t http.RoundTripper) DexRewriteURLRoundTripper {
dexURL, _ := url.Parse(dexServerAddr)
return DexRewriteURLRoundTripper{
DexURL: dexURL,
T: t,
}
}
// DexRewriteURLRoundTripper is an HTTP RoundTripper to rewrite HTTP requests to the specified
// dex server address. This is used when reverse proxying Dex to avoid the API server from
// unnecessarily communicating to Argo CD through its externally facing load balancer, which is not
// always permitted in firewalled/air-gapped networks.
type DexRewriteURLRoundTripper struct {
DexURL *url.URL
T http.RoundTripper
}
func (s DexRewriteURLRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
r.URL.Host = s.DexURL.Host
r.URL.Scheme = s.DexURL.Scheme
r.Host = s.DexURL.Host
return s.T.RoundTrip(r)
}
func DexServerAddressWithProtocol(orig string, tlsConfig *DexTLSConfig) string {
if strings.Contains(orig, "://") {
return orig
}
if tlsConfig == nil || tlsConfig.DisableTLS {
return "http://" + orig
}
return "https://" + orig
}