diff --git a/cmd/argo/commands/server.go b/cmd/argo/commands/server.go
index 011bf1abd4d8..1d4d64f1028f 100644
--- a/cmd/argo/commands/server.go
+++ b/cmd/argo/commands/server.go
@@ -28,6 +28,7 @@ func NewServerCommand() *cobra.Command {
port int
baseHRef string
secure bool
+ htst bool
namespaced bool // --namespaced
managedNamespace string // --managed-namespace
enableOpenBrowser bool
@@ -86,6 +87,7 @@ See %s`, help.ArgoSever),
opts := apiserver.ArgoServerOpts{
BaseHRef: baseHRef,
TLSConfig: tlsConfig,
+ HSTS: htst,
Namespace: namespace,
WfClientSet: wflientset,
KubeClientset: kubeConfig,
@@ -121,6 +123,7 @@ See %s`, help.ArgoSever),
command.Flags().StringVar(&baseHRef, "basehref", defaultBaseHRef, "Value for base href in index.html. Used if the server is running behind reverse proxy under subpath different from /. Defaults to the environment variable BASE_HREF.")
// "-e" for encrypt, like zip
command.Flags().BoolVarP(&secure, "secure", "e", false, "Whether or not we should listen on TLS.")
+ command.Flags().BoolVar(&htst, "hsts", true, "Whether or not we should add a HTTP Secure Transport Security header. This only has effect if secure is enabled.")
command.Flags().StringVar(&authMode, "auth-mode", "server", "API server authentication mode. One of: client|server|hybrid")
command.Flags().StringVar(&configMap, "configmap", "workflow-controller-configmap", "Name of K8s configmap to retrieve workflow controller configuration")
command.Flags().BoolVar(&namespaced, "namespaced", false, "run as namespaced mode")
diff --git a/server/apiserver/argoserver.go b/server/apiserver/argoserver.go
index 22740ddd4100..a5aa7db8e1a9 100644
--- a/server/apiserver/argoserver.go
+++ b/server/apiserver/argoserver.go
@@ -54,6 +54,7 @@ type argoServer struct {
baseHRef string
// https://itnext.io/practical-guide-to-securing-grpc-connections-with-go-and-tls-part-1-f63058e9d6d1
tlsConfig *tls.Config
+ hsts bool
namespace string
managedNamespace string
kubeClientset *kubernetes.Clientset
@@ -73,12 +74,14 @@ type ArgoServerOpts struct {
// config map name
ConfigName string
ManagedNamespace string
+ HSTS bool
}
func NewArgoServer(opts ArgoServerOpts) *argoServer {
return &argoServer{
baseHRef: opts.BaseHRef,
tlsConfig: opts.TLSConfig,
+ hsts: opts.HSTS,
namespace: opts.Namespace,
managedNamespace: opts.ManagedNamespace,
kubeClientset: opts.KubeClientset,
@@ -257,7 +260,8 @@ func (as *argoServer) newHTTPServer(ctx context.Context, port int, artifactServe
mux.Handle("/api/", gwmux)
mux.HandleFunc("/artifacts/", artifactServer.GetArtifact)
mux.HandleFunc("/artifacts-by-uid/", artifactServer.GetArtifactByUID)
- mux.HandleFunc("/", static.NewFilesServer(as.baseHRef).ServerFiles)
+ // we only enable HTST if we are insecure mode, otherwise you would never be able access the UI
+ mux.HandleFunc("/", static.NewFilesServer(as.baseHRef, as.tlsConfig != nil && as.hsts).ServerFiles)
return &httpServer
}
diff --git a/server/static/static.go b/server/static/static.go
index feac4a68344c..1a852d9dc9d0 100644
--- a/server/static/static.go
+++ b/server/static/static.go
@@ -8,10 +8,11 @@ import (
type FilesServer struct {
baseHRef string
+ hsts bool
}
-func NewFilesServer(baseHRef string) *FilesServer {
- return &FilesServer{baseHRef}
+func NewFilesServer(baseHRef string, hsts bool) *FilesServer {
+ return &FilesServer{baseHRef, hsts}
}
func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) {
@@ -26,6 +27,12 @@ func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) {
w = &responseRewriter{ResponseWriter: w, old: []byte(`