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(``), new: []byte(fmt.Sprintf(``, s.baseHRef))} } + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-inline'") + if s.hsts { + w.Header().Set("Strict-Transport-Security", "max-age=31536000") + } + // in my IDE (IntelliJ) the next line is red for some reason - but this is fine ServeHTTP(w, r) } diff --git a/ui/src/app/assets/fonts/Heebo-Regular.ttf b/ui/src/app/assets/fonts/Heebo-Regular.ttf new file mode 100755 index 000000000000..d69308419835 Binary files /dev/null and b/ui/src/app/assets/fonts/Heebo-Regular.ttf differ diff --git a/ui/src/app/assets/styles/heebo.css b/ui/src/app/assets/styles/heebo.css new file mode 100644 index 000000000000..4a5e16ae3ce0 --- /dev/null +++ b/ui/src/app/assets/styles/heebo.css @@ -0,0 +1,18 @@ +/* hebrew */ +@font-face { + font-family: 'Heebo'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Heebo'), local('Heebo-Regular'), url(../fonts/Heebo-Regular.ttf) format('ttf'); + unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F; +} +/* latin */ +@font-face { + font-family: 'Heebo'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Heebo'), local('Heebo-Regular'), url(../fonts/Heebo-Regular.ttf) format('ttf'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} \ No newline at end of file diff --git a/ui/src/app/index.html b/ui/src/app/index.html index fadf6cf77970..ba2c7aa92ed3 100644 --- a/ui/src/app/index.html +++ b/ui/src/app/index.html @@ -5,7 +5,7 @@ Argo - + diff --git a/ui/src/app/webpack.config.js b/ui/src/app/webpack.config.js index 77549118b3df..8432fe50958f 100644 --- a/ui/src/app/webpack.config.js +++ b/ui/src/app/webpack.config.js @@ -54,6 +54,8 @@ const config = { from: 'node_modules/argo-ui/src/assets', to: 'assets' }, { from: 'node_modules/@fortawesome/fontawesome-free/webfonts', to: 'assets/fonts' + }, { + from: 'src/app/assets', to: 'assets' }]), ], devServer: {