Skip to content

Commit

Permalink
fix: enforce content type header for API requests (argoproj#16860)
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
  • Loading branch information
alexmt authored and Julien Fuix committed Feb 6, 2024
1 parent 1e080e5 commit 875284c
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 0 deletions.
4 changes: 4 additions & 0 deletions cmd/argocd-server/commands/argocd_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"math"
"strings"
"time"

"github.com/argoproj/pkg/stats"
Expand Down Expand Up @@ -63,6 +64,7 @@ func NewCommand() *cobra.Command {
repoServerAddress string
dexServerAddress string
disableAuth bool
contentTypes string
enableGZip bool
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
cacheSrc func() (*servercache.Cache, error)
Expand Down Expand Up @@ -185,6 +187,7 @@ func NewCommand() *cobra.Command {
DexServerAddr: dexServerAddress,
DexTLSConfig: dexTlsConfig,
DisableAuth: disableAuth,
ContentTypes: strings.Split(contentTypes, ";"),
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
Expand Down Expand Up @@ -240,6 +243,7 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address")
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
command.Flags().StringVar(&contentTypes, "api-content-types", "application/json", "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression")
command.AddCommand(cli.NewVersionCmd(cliName))
command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address")
Expand Down
1 change: 1 addition & 0 deletions docs/operator-manual/server-commands/argocd-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ argocd-server [flags]

```
--address string Listen on given address (default "0.0.0.0")
--api-content-types string Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty. (default "application/json")
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
--application-namespaces strings List of additional namespaces where application resources can be managed in
--as string Username to impersonate for the operation
Expand Down
18 changes: 18 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ type ArgoCDServer struct {

type ArgoCDServerOpts struct {
DisableAuth bool
ContentTypes []string
EnableGZip bool
Insecure bool
StaticAssetsDir string
Expand Down Expand Up @@ -990,6 +991,9 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
if a.EnableGZip {
handler = compressHandler(handler)
}
if len(a.ContentTypes) > 0 {
handler = enforceContentTypes(handler, a.ContentTypes)
}
mux.Handle("/api/", handler)

terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells, *a.sessionMgr).
Expand Down Expand Up @@ -1056,6 +1060,20 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
return &httpS
}

func enforceContentTypes(handler http.Handler, types []string) http.Handler {
allowedTypes := map[string]bool{}
for _, t := range types {
allowedTypes[strings.ToLower(t)] = true
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet || allowedTypes[strings.ToLower(r.Header.Get("Content-Type"))] {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Invalid content type", http.StatusUnsupportedMediaType)
}
})
}

// registerExtensions will try to register all configured extensions
// in the given mux. If any error is returned while registering
// extensions handlers, no route will be added in the given mux.
Expand Down
1 change: 1 addition & 0 deletions test/e2e/fixture/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func DoHttpRequest(method string, path string, data ...byte) (*http.Response, er
return nil, err
}
req.AddCookie(&http.Cookie{Name: common.AuthCookieName, Value: token})
req.Header.Set("Content-Type", "application/json")

httpClient := &http.Client{
Transport: &http.Transport{
Expand Down

0 comments on commit 875284c

Please sign in to comment.