diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..1e2ce1f18 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,31 @@ +name-template: v$NEXT_MINOR_VERSION +tag-template: v$NEXT_MINOR_VERSION +categories: + - title: New + labels: + - enhancement + - feature + - improvement + - title: Fixed + label: bug + - title: Removed + label: removed + - title: Documentation + label: docs + - title: Depedency + label: depedency + - title: Breaking + label: breaking +exclude-labels: + - no-changelog + +change-template: "- $TITLE @$AUTHOR (#$NUMBER)" +template: | + + + ## Changes + $CHANGES + +replacers: + - search: '/CVE-(\d{4})-(\d+)/g' + replace: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-$1-$2" diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 000000000..ae68cab67 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,16 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: toolmantim/release-drafter@v5.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.golangci.yml b/.golangci.yml index 44a8a1cda..dea9f19e8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,10 +1,10 @@ # forked from istio service: # When updating this, also update bin/linters.sh accordingly - golangci-lint-version: 1.18.x # use the fixed version to not introduce new linters unexpectedly + golangci-lint-version: 1.21.x # use the fixed version to not introduce new linters unexpectedly run: # timeout for analysis, e.g. 30s, 5m, default is 1m - deadline: 5m + deadline: 20m # which dirs to skip: they won't be analyzed; # can use regexp here: generated.*, regexp is applied on full path; @@ -28,16 +28,20 @@ linters: disable: - depguard - dupl + - funlen - gochecknoglobals - gochecknoinits + - gocognit - goconst - gocyclo + - godox + - interfacer + - maligned - nakedret - prealloc - scopelint - - maligned - - interfacer - - funlen + - whitespace + - wsl fast: false linters-settings: diff --git a/Makefile b/Makefile index b6ea86756..a199015be 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ CTIMEVAR=-X $(PKG)/internal/version.GitCommit=$(GITCOMMIT) \ -X $(PKG)/internal/version.ProjectURL=$(PKG) GO_LDFLAGS=-ldflags "-s -w $(CTIMEVAR)" GOOSARCHES = linux/amd64 darwin/amd64 windows/amd64 -GOLANGCI_VERSION = v1.18.0 # .... for some reason v1.18.0 misses? +GOLANGCI_VERSION = v1.21.0 # .... for some reason v1.18.0 misses? .PHONY: all all: clean build-deps test lint spellcheck build ## Runs a clean, build, fmt, lint, test, and vet. @@ -49,6 +49,11 @@ tag: ## Create a new git tag to prepare to build a release git tag -sa $(VERSION) -m "$(VERSION)" @echo "Run git push origin $(VERSION) to push your new tag to GitHub." +.PHONY: frontend +frontend: ## Runs go generate on the static assets package. + @echo "==> $@" + @CGO_ENABLED=0 GO111MODULE=on go generate github.com/pomerium/pomerium/internal/frontend + .PHONY: build build: ## Builds dynamic executables and/or packages. @echo "==> $@" diff --git a/authenticate/authenticate.go b/authenticate/authenticate.go index 0aa155aa8..0e80fcb1d 100644 --- a/authenticate/authenticate.go +++ b/authenticate/authenticate.go @@ -14,9 +14,9 @@ import ( "github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding/ecjson" "github.com/pomerium/pomerium/internal/encoding/jws" + "github.com/pomerium/pomerium/internal/frontend" "github.com/pomerium/pomerium/internal/identity" "github.com/pomerium/pomerium/internal/sessions" - "github.com/pomerium/pomerium/internal/templates" "github.com/pomerium/pomerium/internal/urlutil" ) @@ -147,6 +147,6 @@ func New(opts config.Options) (*Authenticate, error) { // IdP provider: provider, - templates: templates.New(), + templates: template.Must(frontend.NewTemplates()), }, nil } diff --git a/authenticate/handlers.go b/authenticate/handlers.go index 6081627c8..92d3557be 100644 --- a/authenticate/handlers.go +++ b/authenticate/handlers.go @@ -21,21 +21,10 @@ import ( "github.com/pomerium/pomerium/internal/urlutil" ) -// CSPHeaders are the content security headers added to the service's handlers -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src -var CSPHeaders = map[string]string{ - "Content-Security-Policy": "default-src 'none'; style-src " + - "'sha256-spMkVDoBBY86p0RC1fBYwdnGyMypJM8eG57+p3VASyk=' " + - "'sha256-qnVkQSG7pWu17hBhIw0kCpfEB3XGvt0mNRa6+uM6OUU=' " + - "'sha256-qOdRsNZhtR+htazbcy7guQl3Cn1cqOw1FcE4d3llae0=';" + - "img-src 'self';", - "Referrer-Policy": "Same-origin", -} - // Handler returns the authenticate service's handler chain. func (a *Authenticate) Handler() http.Handler { r := httputil.NewRouter() - r.Use(middleware.SetHeaders(CSPHeaders)) + r.Use(middleware.SetHeaders(httputil.HeadersContentSecurityPolicy)) r.Use(csrf.Protect( a.cookieSecret, csrf.Secure(a.cookieOptions.Secure), diff --git a/authenticate/handlers_test.go b/authenticate/handlers_test.go index df51752d2..cf2599161 100644 --- a/authenticate/handlers_test.go +++ b/authenticate/handlers_test.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "errors" "fmt" + "html/template" "net/http" "net/http/httptest" "net/url" @@ -13,9 +14,9 @@ import ( "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding/mock" + "github.com/pomerium/pomerium/internal/frontend" "github.com/pomerium/pomerium/internal/identity" "github.com/pomerium/pomerium/internal/sessions" - "github.com/pomerium/pomerium/internal/templates" "github.com/pomerium/pomerium/internal/urlutil" "github.com/google/go-cmp/cmp" @@ -30,7 +31,7 @@ func testAuthenticate() *Authenticate { auth.sharedKey = cryptutil.NewBase64Key() auth.cookieSecret = cryptutil.NewKey() auth.cookieOptions = &sessions.CookieOptions{Name: "name"} - auth.templates = templates.New() + auth.templates = template.Must(frontend.NewTemplates()) return &auth } @@ -192,7 +193,7 @@ func TestAuthenticate_SignOut(t *testing.T) { a := &Authenticate{ sessionStore: tt.sessionStore, provider: tt.provider, - templates: templates.New(), + templates: template.Must(frontend.NewTemplates()), } u, _ := url.Parse("/sign_out") params, _ := url.ParseQuery(u.RawQuery) diff --git a/cmd/pomerium/main.go b/cmd/pomerium/main.go index d6733a219..eb363a30d 100644 --- a/cmd/pomerium/main.go +++ b/cmd/pomerium/main.go @@ -14,6 +14,7 @@ import ( "github.com/pomerium/pomerium/authenticate" "github.com/pomerium/pomerium/authorize" "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/frontend" "github.com/pomerium/pomerium/internal/grpcutil" "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" @@ -136,7 +137,7 @@ func newProxyService(opt config.Options, r *mux.Router) (*proxy.Proxy, error) { if err != nil { return nil, err } - r.PathPrefix("/").Handler(service.Handler) + r.PathPrefix("/").Handler(service) return service, nil } @@ -169,6 +170,7 @@ func newGlobalRouter(o *config.Options) *mux.Router { mux.Use(middleware.Healthcheck("/ping", version.UserAgent())) mux.HandleFunc("/healthz", httputil.HealthCheck) mux.HandleFunc("/ping", httputil.HealthCheck) + mux.PathPrefix("/.pomerium/assets/").Handler(http.StripPrefix("/.pomerium/assets/", frontend.MustAssetHandler())) return mux } diff --git a/config/options.go b/config/options.go index 739e08f88..828724197 100644 --- a/config/options.go +++ b/config/options.go @@ -34,136 +34,136 @@ const DefaultAlternativeAddr = ":5443" // Use NewXXXOptions() methods for a safely initialized data structure. type Options struct { // Debug outputs human-readable logs to Stdout. - Debug bool `mapstructure:"pomerium_debug"` + Debug bool `mapstructure:"pomerium_debug" yaml:"pomerium_debug,omitempty"` // LogLevel sets the global override for log level. All Loggers will use at least this value. // Possible options are "info","warn", and "error". Defaults to "debug". - LogLevel string `mapstructure:"log_level"` + LogLevel string `mapstructure:"log_level" yaml:"log_level,omitempty"` // SharedKey is the shared secret authorization key used to mutually authenticate // requests between services. - SharedKey string `mapstructure:"shared_secret"` + SharedKey string `mapstructure:"shared_secret" yaml:"shared_secret,omitempty"` // Services is a list enabled service mode. If none are selected, "all" is used. // Available options are : "all", "authenticate", "proxy". - Services string `mapstructure:"services"` + Services string `mapstructure:"services" yaml:"services,omitempty"` // Addr specifies the host and port on which the server should serve // HTTPS requests. If empty, ":443" (localhost:443) is used. - Addr string `mapstructure:"address"` + Addr string `mapstructure:"address" yaml:"address,omitempty"` // InsecureServer when enabled disables all transport security. // In this mode, Pomerium is susceptible to man-in-the-middle attacks. // This should be used only for testing. - InsecureServer bool `mapstructure:"insecure_server"` + InsecureServer bool `mapstructure:"insecure_server" yaml:"insecure_server,omitempty"` // Cert and Key is the x509 certificate used to hydrate TLSCertificate - Cert string `mapstructure:"certificate"` - Key string `mapstructure:"certificate_key"` + Cert string `mapstructure:"certificate" yaml:"certificate,omitempty"` + Key string `mapstructure:"certificate_key" yaml:"certificate_key,omitempty"` // CertFile and KeyFile is the x509 certificate used to hydrate TLSCertificate - CertFile string `mapstructure:"certificate_file"` - KeyFile string `mapstructure:"certificate_key_file"` + CertFile string `mapstructure:"certificate_file" yaml:"certificate_file,omitempty"` + KeyFile string `mapstructure:"certificate_key_file" yaml:"certificate_key_file,omitempty"` // TLSCertificate is the hydrated tls.Certificate. - TLSCertificate *tls.Certificate + TLSCertificate *tls.Certificate `yaml:",omitempty"` // HttpRedirectAddr, if set, specifies the host and port to run the HTTP // to HTTPS redirect server on. If empty, no redirect server is started. - HTTPRedirectAddr string `mapstructure:"http_redirect_addr"` + HTTPRedirectAddr string `mapstructure:"http_redirect_addr" yaml:"http_redirect_addr,omitempty"` // Timeout settings : https://github.com/pomerium/pomerium/issues/40 - ReadTimeout time.Duration `mapstructure:"timeout_read"` - WriteTimeout time.Duration `mapstructure:"timeout_write"` - ReadHeaderTimeout time.Duration `mapstructure:"timeout_read_header"` - IdleTimeout time.Duration `mapstructure:"timeout_idle"` + ReadTimeout time.Duration `mapstructure:"timeout_read" yaml:"timeout_read,omitempty"` + WriteTimeout time.Duration `mapstructure:"timeout_write" yaml:"timeout_write,omitempty"` + ReadHeaderTimeout time.Duration `mapstructure:"timeout_read_header" yaml:"timeout_read_header,omitempty"` + IdleTimeout time.Duration `mapstructure:"timeout_idle" yaml:"timeout_idle,omitempty"` // Policies define per-route configuration and access control policies. Policies []Policy - PolicyEnv string - PolicyFile string `mapstructure:"policy_file"` + PolicyEnv string `yaml:",omitempty"` + PolicyFile string `mapstructure:"policy_file" yaml:"policy_file,omitempty"` // AuthenticateURL represents the externally accessible http endpoints // used for authentication requests and callbacks - AuthenticateURLString string `mapstructure:"authenticate_service_url"` - AuthenticateURL *url.URL + AuthenticateURLString string `mapstructure:"authenticate_service_url" yaml:"authenticate_service_url,omitempty"` + AuthenticateURL *url.URL `yaml:"-,omitempty"` // Session/Cookie management // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie - CookieName string `mapstructure:"cookie_name"` - CookieSecret string `mapstructure:"cookie_secret"` - CookieDomain string `mapstructure:"cookie_domain"` - CookieSecure bool `mapstructure:"cookie_secure"` - CookieHTTPOnly bool `mapstructure:"cookie_http_only"` - CookieExpire time.Duration `mapstructure:"cookie_expire"` - CookieRefresh time.Duration `mapstructure:"cookie_refresh"` + CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"` + CookieSecret string `mapstructure:"cookie_secret" yaml:"cookie_secret,omitempty"` + CookieDomain string `mapstructure:"cookie_domain" yaml:"cookie_domain,omitempty"` + CookieSecure bool `mapstructure:"cookie_secure" yaml:"cookie_secure,omitempty"` + CookieHTTPOnly bool `mapstructure:"cookie_http_only" yaml:"cookie_http_only,omitempty"` + CookieExpire time.Duration `mapstructure:"cookie_expire" yaml:"cookie_expire,omitempty"` + CookieRefresh time.Duration `mapstructure:"cookie_refresh" yaml:"cookie_refresh,omitempty"` // Identity provider configuration variables as specified by RFC6749 // https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749 - ClientID string `mapstructure:"idp_client_id"` - ClientSecret string `mapstructure:"idp_client_secret"` - Provider string `mapstructure:"idp_provider"` - ProviderURL string `mapstructure:"idp_provider_url"` - Scopes []string `mapstructure:"idp_scopes"` - ServiceAccount string `mapstructure:"idp_service_account"` + ClientID string `mapstructure:"idp_client_id" yaml:"idp_client_id,omitempty"` + ClientSecret string `mapstructure:"idp_client_secret" yaml:"idp_client_secret,omitempty"` + Provider string `mapstructure:"idp_provider" yaml:"idp_provider,omitempty"` + ProviderURL string `mapstructure:"idp_provider_url" yaml:"idp_provider_url,omitempty"` + Scopes []string `mapstructure:"idp_scopes" yaml:"idp_scopes,omitempty"` + ServiceAccount string `mapstructure:"idp_service_account" yaml:"idp_service_account,omitempty"` // Administrators contains a set of emails with users who have super user // (sudo) access including the ability to impersonate other users' access - Administrators []string `mapstructure:"administrators"` + Administrators []string `mapstructure:"administrators" yaml:"administrators,omitempty"` // AuthorizeURL is the routable destination of the authorize service's // gRPC endpoint. NOTE: As many load balancers do not support // externally routed gRPC so this may be an internal location. - AuthorizeURLString string `mapstructure:"authorize_service_url"` - AuthorizeURL *url.URL + AuthorizeURLString string `mapstructure:"authorize_service_url" yaml:"authorize_service_url,omitempty"` + AuthorizeURL *url.URL `yaml:",omitempty"` // Settings to enable custom behind-the-ingress service communication - OverrideCertificateName string `mapstructure:"override_certificate_name"` - CA string `mapstructure:"certificate_authority"` - CAFile string `mapstructure:"certificate_authority_file"` + OverrideCertificateName string `mapstructure:"override_certificate_name" yaml:"override_certificate_name,omitempty"` + CA string `mapstructure:"certificate_authority" yaml:"certificate_authority,omitempty"` + CAFile string `mapstructure:"certificate_authority_file" yaml:"certificate_authority_file,omitempty"` // SigningKey is the private key used to add a JWT-signature. // https://www.pomerium.io/docs/signed-headers.html - SigningKey string `mapstructure:"signing_key"` + SigningKey string `mapstructure:"signing_key" yaml:"signing_key,omitempty"` // Headers to set on all proxied requests. Add a 'disable' key map to turn off. - HeadersEnv string - Headers map[string]string + HeadersEnv string `yaml:",omitempty"` + Headers map[string]string `yaml:",omitempty"` // RefreshCooldown limits the rate a user can refresh her session - RefreshCooldown time.Duration `mapstructure:"refresh_cooldown"` + RefreshCooldown time.Duration `mapstructure:"refresh_cooldown" yaml:"refresh_cooldown,omitempty"` - //Routes map[string]string `mapstructure:"routes"` - DefaultUpstreamTimeout time.Duration `mapstructure:"default_upstream_timeout"` + //Routes map[string]string `mapstructure:"routes" yaml:"routes,omitempty"` + DefaultUpstreamTimeout time.Duration `mapstructure:"default_upstream_timeout" yaml:"default_upstream_timeout,omitempty"` // Address/Port to bind to for prometheus metrics - MetricsAddr string `mapstructure:"metrics_address"` + MetricsAddr string `mapstructure:"metrics_address" yaml:"metrics_address,omitempty"` // Tracing shared settings - TracingProvider string `mapstructure:"tracing_provider"` - TracingDebug bool `mapstructure:"tracing_debug"` + TracingProvider string `mapstructure:"tracing_provider" yaml:"tracing_provider,omitempty"` + TracingDebug bool `mapstructure:"tracing_debug" yaml:"tracing_debug,omitempty"` // Jaeger // // CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector. // For example, http://localhost:14268/api/traces - TracingJaegerCollectorEndpoint string `mapstructure:"tracing_jaeger_collector_endpoint"` + TracingJaegerCollectorEndpoint string `mapstructure:"tracing_jaeger_collector_endpoint" yaml:"tracing_jaeger_collector_endpoint,omitempty"` // AgentEndpoint instructs exporter to send spans to jaeger-agent at this address. // For example, localhost:6831. - TracingJaegerAgentEndpoint string `mapstructure:"tracing_jaeger_agent_endpoint"` + TracingJaegerAgentEndpoint string `mapstructure:"tracing_jaeger_agent_endpoint" yaml:"tracing_jaeger_agent_endpoint,omitempty"` // GRPC Service Settings // GRPCAddr specifies the host and port on which the server should serve // gRPC requests. If running in all-in-one mode, ":5443" (localhost:5443) is used. - GRPCAddr string `mapstructure:"grpc_address"` + GRPCAddr string `mapstructure:"grpc_address" yaml:"grpc_address,omitempty"` // GRPCInsecure disables transport security. // If running in all-in-one mode, defaults to true. - GRPCInsecure bool `mapstructure:"grpc_insecure"` + GRPCInsecure bool `mapstructure:"grpc_insecure" yaml:"grpc_insecure,omitempty"` - GRPCClientTimeout time.Duration `mapstructure:"grpc_client_timeout"` - GRPCClientDNSRoundRobin bool `mapstructure:"grpc_client_dns_roundrobin"` + GRPCClientTimeout time.Duration `mapstructure:"grpc_client_timeout" yaml:"grpc_client_timeout,omitempty"` + GRPCClientDNSRoundRobin bool `mapstructure:"grpc_client_dns_roundrobin" yaml:"grpc_client_dns_roundrobin,omitempty"` // ForwardAuthEndpoint allows for a given route to be used as a forward-auth // endpoint instead of a reverse proxy. Some third-party proxies that do not @@ -171,8 +171,8 @@ type Options struct { // allow you to delegate and authenticate each request to your website // with an external server or service. Pomerium can be configured to accept // these requests with this switch - ForwardAuthURLString string `mapstructure:"forward_auth_url"` - ForwardAuthURL *url.URL + ForwardAuthURLString string `mapstructure:"forward_auth_url" yaml:"forward_auth_url,omitempty"` + ForwardAuthURL *url.URL `yaml:",omitempty"` viper *viper.Viper } diff --git a/config/policy.go b/config/policy.go index 148353b5d..6a2050ec0 100644 --- a/config/policy.go +++ b/config/policy.go @@ -16,27 +16,27 @@ type Policy struct { From string `mapstructure:"from" yaml:"from"` To string `mapstructure:"to" yaml:"to"` // Identity related policy - AllowedEmails []string `mapstructure:"allowed_users" yaml:"allowed_users"` - AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups"` - AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains"` + AllowedEmails []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty"` + AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty"` + AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty"` - Source *url.URL - Destination *url.URL + Source *url.URL `yaml:",omitempty"` + Destination *url.URL `yaml:",omitempty"` // Allow unauthenticated HTTP OPTIONS requests as per the CORS spec // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests - CORSAllowPreflight bool `mapstructure:"cors_allow_preflight" yaml:"cors_allow_preflight"` + CORSAllowPreflight bool `mapstructure:"cors_allow_preflight" yaml:"cors_allow_preflight,omitempty"` // Allow any public request to access this route. **Bypasses authentication** - AllowPublicUnauthenticatedAccess bool `mapstructure:"allow_public_unauthenticated_access" yaml:"allow_public_unauthenticated_access"` + AllowPublicUnauthenticatedAccess bool `mapstructure:"allow_public_unauthenticated_access" yaml:"allow_public_unauthenticated_access,omitempty"` // UpstreamTimeout is the route specific timeout. Must be less than the global // timeout. If unset, route will fallback to the proxy's DefaultUpstreamTimeout. - UpstreamTimeout time.Duration `mapstructure:"timeout" yaml:"timeout"` + UpstreamTimeout time.Duration `mapstructure:"timeout" yaml:"timeout,omitempty"` // Enable proxying of websocket connections by removing the default timeout handler. // Caution: Enabling this feature could result in abuse via DOS attacks. - AllowWebsockets bool `mapstructure:"allow_websockets" yaml:"allow_websockets"` + AllowWebsockets bool `mapstructure:"allow_websockets" yaml:"allow_websockets,omitempty"` // TLSSkipVerify controls whether a client verifies the server's certificate // chain and host name. @@ -44,32 +44,32 @@ type Policy struct { // server and any host name in that certificate. // In this mode, TLS is susceptible to man-in-the-middle attacks. // This should be used only for testing. - TLSSkipVerify bool `mapstructure:"tls_skip_verify" yaml:"tls_skip_verify"` + TLSSkipVerify bool `mapstructure:"tls_skip_verify" yaml:"tls_skip_verify,omitempty"` // TLSServerName overrides the hostname in the `to` field. This is useful // if your backend is an HTTPS server with a valid certificate, but you // want to communicate to the backend with an internal hostname (e.g. // Docker container name). - TLSServerName string `mapstructure:"tls_server_name" yaml:"tls_server_name"` + TLSServerName string `mapstructure:"tls_server_name" yaml:"tls_server_name,omitempty"` // TLSCustomCA defines the root certificate to use with a given // route when verifying server certificates. - TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca"` - TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file"` - RootCAs *x509.CertPool + TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca,omitempty"` + TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file,omitempty"` + RootCAs *x509.CertPool `yaml:",omitempty"` // Contains the x.509 client certificate to to present to the downstream // host. - TLSClientCert string `mapstructure:"tls_client_cert" yaml:"tls_client_cert"` - TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key"` - TLSClientCertFile string `mapstructure:"tls_client_cert_file" yaml:"tls_client_cert_file"` - TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file"` - ClientCertificate *tls.Certificate + TLSClientCert string `mapstructure:"tls_client_cert" yaml:"tls_client_cert,omitempty"` + TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key,omitempty"` + TLSClientCertFile string `mapstructure:"tls_client_cert_file" yaml:"tls_client_cert_file,omitempty"` + TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file,omitempty"` + ClientCertificate *tls.Certificate `yaml:",omitempty"` // SetRequestHeaders adds a collection of headers to the downstream request // in the form of key value pairs. Note bene, this will overwrite the // value of any existing value of a given header key. - SetRequestHeaders map[string]string `mapstructure:"set_request_headers" yaml:"set_request_headers"` + SetRequestHeaders map[string]string `mapstructure:"set_request_headers" yaml:"set_request_headers,omitempty"` } // Validate checks the validity of a policy. diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index 686d7171f..1893cba3a 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## vUnreleased + +### New + +### Changed + +- Added yaml tags to all options struct fields + - [GH-394](https://github.com/pomerium/pomerium/pull/394) + - [GH-397](https://github.com/pomerium/pomerium/pull/397) + +### Fixed + +- Fixed regression preventing policy reload [GH-396](https://github.com/pomerium/pomerium/pull/396) + ## v0.5.0 ### New diff --git a/go.mod b/go.mod index a9d813c94..4d89b6c98 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/pomerium/go-oidc v2.0.0+incompatible github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v0.9.3 + github.com/rakyll/statik v0.1.6 github.com/rs/cors v1.7.0 github.com/rs/zerolog v1.16.0 github.com/spf13/afero v1.2.2 // indirect diff --git a/go.sum b/go.sum index 3038bcbd8..905ae81e5 100644 --- a/go.sum +++ b/go.sum @@ -170,6 +170,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/internal/frontend/assets/html/dashboard.go.html b/internal/frontend/assets/html/dashboard.go.html new file mode 100644 index 000000000..19aa714ce --- /dev/null +++ b/internal/frontend/assets/html/dashboard.go.html @@ -0,0 +1,237 @@ +{{define "dashboard.html"}} + + + + Pomerium + {{template "header.html"}} + + + +
+
+
+ {{if .Session.Picture }} + user image + {{else}} + + {{end}} + +
+
+

Current user

+

Your current session details.

+
+ {{if .Session.Name}} + + {{else}} {{if .Session.GivenName}} + + {{end}} {{if .Session.FamilyName}} + + {{end}} {{end}} {{if .Session.Subject}} + + {{end}} {{if .Session.Email}} + + {{end}} {{if .Session.User}} + + {{end}} {{range $i,$_:= .Session.Groups}} + + {{end}} {{if .Session.Expiry}} + + {{end}} {{if .Session.IssuedAt}} + + {{end}} {{if .Session.Issuer}} + + {{end}} {{range $i, $_:= .Session.Audience}} + + {{end}} {{if .Session.ImpersonateEmail}} + + {{end}} {{range $i,$_:= .Session.ImpersonateGroups}} + + {{end}} +
+
+
+ {{ .csrfField }} + +
+
+ + {{if .IsAdmin}} +
+
+

Sign-in-as

+

+ Administrators can temporarily impersonate another user. +

+
+ + +
+
+
+ {{ .csrfField }} + +
+
+ {{ end }} +
+
+ {{template "footer.html"}} +
+ + +{{end}} diff --git a/internal/frontend/assets/html/error.go.html b/internal/frontend/assets/html/error.go.html new file mode 100644 index 000000000..c662f3725 --- /dev/null +++ b/internal/frontend/assets/html/error.go.html @@ -0,0 +1,35 @@ +{{define "error.html"}} + + + + {{.Code}} - {{.Title}} + {{template "header.html"}} + + +
+
+
+ +

{{.Title}}

+
+

+ {{if .Message}}{{.Message}}{{end}} {{if .CanDebug}}Troubleshoot + your + session.{{end}} {{if .RequestID}} + Request {{.RequestID}}{{end}} +

+
+
+
+ {{template "footer.html"}} +
+ + + +{{end}} diff --git a/internal/frontend/assets/html/footer.go.html b/internal/frontend/assets/html/footer.go.html new file mode 100644 index 000000000..beff39223 --- /dev/null +++ b/internal/frontend/assets/html/footer.go.html @@ -0,0 +1,12 @@ +{{define "footer.html"}} + +{{end}} diff --git a/internal/frontend/assets/html/header.go.html b/internal/frontend/assets/html/header.go.html new file mode 100644 index 000000000..c21cbb6c0 --- /dev/null +++ b/internal/frontend/assets/html/header.go.html @@ -0,0 +1,12 @@ +{{define "header.html"}} + + + +{{end}} diff --git a/internal/frontend/assets/img/account_circle-24px.svg b/internal/frontend/assets/img/account_circle-24px.svg new file mode 100644 index 000000000..fdcb5a87f --- /dev/null +++ b/internal/frontend/assets/img/account_circle-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/internal/frontend/assets/img/error-24px.svg b/internal/frontend/assets/img/error-24px.svg new file mode 100644 index 000000000..6c2f5fbe2 --- /dev/null +++ b/internal/frontend/assets/img/error-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/internal/frontend/assets/img/pomerium.svg b/internal/frontend/assets/img/pomerium.svg new file mode 100644 index 000000000..e585c45bb --- /dev/null +++ b/internal/frontend/assets/img/pomerium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/internal/frontend/assets/style/main.css b/internal/frontend/assets/style/main.css new file mode 100644 index 000000000..9ffb43a67 --- /dev/null +++ b/internal/frontend/assets/style/main.css @@ -0,0 +1,239 @@ +* { + margin: 0; + padding: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: none; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", sans-serif; + font-size: 15px; + line-height: 1.4em; +} + +body { + display: flex; + flex-direction: row; + align-items: center; + background: #f8f8ff; +} + +#main { + width: 100%; + height: 100vh; + text-align: center; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +#info-box { + max-width: 480px; + width: 480px; + margin-top: 200px; + margin-right: auto; + margin-bottom: 0px; + margin-left: auto; + justify-content: center; + flex-grow: 1; +} + +section { + display: flex; + flex-direction: column; + position: relative; + text-align: left; +} + +h1 { + font-size: 36px; + font-weight: 400; + text-align: center; + letter-spacing: 0.3px; + text-transform: uppercase; + color: #32325d; +} + +h1.title { + text-align: center; + background: #f8f8ff; + margin: 15px 0; +} + +h2 { + margin: 15px 0; + color: #32325d; + text-transform: uppercase; + letter-spacing: 0.3px; + font-size: 18px; + font-weight: 650; + padding-top: 20px; +} + +.card { + margin: 0 -30px; + padding: 20px 30px 30px; + border-radius: 4px; + border: 1px solid #e8e8fb; + background-color: #f8f8ff; +} + +fieldset { + margin-bottom: 20px; + background: #fcfcff; + box-shadow: 0 1px 3px 0 rgba(50, 50, 93, 0.15), + 0 4px 6px 0 rgba(112, 157, 199, 0.15); + border-radius: 4px; + border: none; + font-size: 0; +} + +fieldset label { + position: relative; + display: flex; + flex-direction: row; + height: 42px; + padding: 10px 0; + align-items: center; + justify-content: center; + font-weight: 400; +} + +fieldset label:not(:last-child) { + border-bottom: 1px solid #f0f5fa; +} + +fieldset label span { + min-width: 125px; + padding: 0 15px; + text-align: right; +} + +#group { + display: flex; + align-items: center; +} + +#group::before { + display: inline-flex; + content: ""; + height: 15px; + background-position: -1000px -1000px; + background-repeat: no-repeat; +} + +.icon { + display: inline-table; + margin-top: -72px; + text-align: center; + width: 75px; + height: auto; + border-radius: 50%; +} + +.icon svg { + fill: #6e43e8; + background: red; +} + +.logo { + padding-bottom: 20px; + padding-top: 20px; + width: 115px; + height: auto; +} + +p.message { + margin-top: 10px; + margin-bottom: 10px; + padding-bottom: 20px; +} + +.field { + flex: 1; + padding: 0 15px; + background: transparent; + font-weight: 400; + color: #31325f; + outline: none; + cursor: text; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +fieldset .select::after { + content: ""; + position: absolute; + width: 9px; + height: 5px; + right: 20px; + top: 50%; + margin-top: -2px; + pointer-events: none; +} + +input { + border-style: none; + outline: none; + color: #313b3f; +} + +select { + flex: 1; + border-style: none; + outline: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + outline: none; + color: #313b3f; + cursor: pointer; + background: transparent; +} + +.flex { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.button { + color: #fcfcff; + background: #6e43e8; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); + border-radius: 4px; + border: 0; + font-weight: 700; + width: 50%; + height: 40px; + outline: none; + cursor: pointer; + padding: 10px; + text-decoration: none; +} + +.button.half { + flex-grow: 0; + flex-shrink: 0; + flex-basis: calc(50% - 10px); +} + +.button.full { + flex-grow: 1; +} + +.button:hover { + transform: translateY(-1px); + box-shadow: 0 7px 14px 0 rgba(50, 50, 93, 0.1), + 0 3px 6px 0 rgba(0, 0, 0, 0.08); +} +.off-color { + background: #5735b5; +} + +.powered-by-pomerium { + align-items: center; +} diff --git a/internal/frontend/statik/statik.go b/internal/frontend/statik/statik.go new file mode 100644 index 000000000..e7ae38ab8 --- /dev/null +++ b/internal/frontend/statik/statik.go @@ -0,0 +1,12 @@ +// Code generated by statik. DO NOT EDIT. + +package statik + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00@\x19tO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00 \x00html/dashboard.go.htmlUT\x05\x00\x01\x89\xae\xd4]\xecYMo\xe36\x13\xbe\xe7W\xccK\xe4\xf8F\x0c\xd2=\x14\x0b\xd9h\xb0\x1fE.M\x80d\x0f{\nhi$M\xc1\x0f\x95\xa4\x12\x07\x86\xff{!\xc9v$K\x89eGI\x8d\xa2{Y\x8b\x9c\x87\xe4<\x0f9\xc3\x0c\x17\x8b\x18\x13\xd2\x08,\x16.\x9b\x19a\xe3 \xf3J\xb2\xe5\xf2$\xfc\xdf\xd7\xeb/w?o\xbeA\xd92= \xcb\xff@\n\x9dN\x18j\x06Q&\xacC?a\x85O\xce~e\xd3\x13\x800C\x11\x97?\x00BO^\xe2\xf4\xc6(\xb4T\xa8\x90\xd7\xdfU\xdfb\xe1Q\xe5Rx\x04V\"\xd0n&\x05\x08y=H\xf9sf\xe2\xa7\xd5p1=\x00\xc5\x13\xa6\x04iV\xb75ZI'\xe6lf\xe6\x9b\x9eU_$\x85s\x13\x16 \x1b7\xba\xca\x15P\x02\xc1-:GF\x077\x14\xf9\xc2\"T\xf3o\xf0\xa4\xd25\x9e\"\xa3\x198\x1bM\xd8b\xb1\x0d[.\x19\x08Y\xf2\xe0\xd0\x02)\x91\"\x03\xde\x9e\x0d\xa5\xc3\xce\xe8\x8dOh\xcd\xd4\xea\xa8f\xe5A\xbe\"\x92\x0b\xe7\xd0;N*\xe5\"\x8aL\xa1\xfd}D6\x92xv\xf1)\x9f\x07\xee!m\xe3\xe7Jj7a\x99\xf7\xf9g\xce\x1f\x1f\x1f\x83\xc7_\x02cS~q~~\xce;\xe6\x8f\x14\xfbl\xc2.>\xb5\x9b3\xa44\xf3\xdb\xed\xdb^\xeax\xb93[\\\xffN\x0f\xa8\x0f$\xbc\xc2\xc2Q\xd1\xdepg(\xf7;!\xe3 P\x1e\xe7-\xfe\xbf\x0bE\xf2\xe9@\x01j\xf0q)\xd0th\xa8\x04\xbb1\xa3k\xd0\xa7\xc5m1\xfb\x13#\x7f\x80\x10?\x1c\xda\xab\xafG\xa3\xc1\xc6\x91\xa1\x02\xec\x00\x8c\xce~\x93\xf5oJ\x90<\x80\xf3\n\xf7\x06\xca\xb1\xc4\x8f\xc7\xf9\xca\x8d\xa1\x8c\xbfj\xfe\xae|\x97{\xf5\x00\xba\x7fT\x17\x83\x83\xd9\x1ew\x83\xd7>\x0c\xe5\xfa5\xeb\xd1\xa9\xb6B\xa7\x08\xa7\xf4\xff\xd3\xfb\xcf\x93F\x9e\xb5\xa6\xc8\xdd^\xbcW\xb2\xe1_pJp\xde\x03\xdcd\xe1r\xe4\x97\x95\xe9\xb9+\xb7\xf1\xafB\xab\x0bh\x0f\xf2c\xe4\xde-\xf1\x87\xc9\xda\x8aX\xf3\x9c\xec\xd3^Z\xaeBV\x05<\x9aST/'\xb8\xa3}r\xf5\x00\xd0\xbb\x92\x7f\xe5\\\x81\xf1\xe5!Y\xba\x86\x1e\x0d\xfdkO\xf6\x14`\x10\xec\xfd%8$\x87\xd4\xc0\x7fZ\x80\xae#;\xa8\x1f\n\x18\x9d\xf4M.\x81v2\xb9,bB\x1d\xed\xf7'\xc3\xc0t\xb2\x1e\xfb\xdf\x94Q\x1aJ\xc2K\xe25\xb6\xc7\x8b6\xa3\x0b\xdc:U*G\xeb\x8c\x16\x1e\x0f\xbd\x12?\x0fA:\x85\xb7^\x90\xdf\xc2xO\xb4\xeb\xb8\xf7A\x1c\xbfp!k\xac\xe7\xfd\xeefmA\xfe\xbb\xa9\xf5\xfc\x1bW\xef\x93m\xf3\xfe\x9ac\xc8{K\xa1\xcd\x12w\"q\xce\xb6\xe7)\xa3C\xe4l\xf2\xbd\x1c\x15\xba\xd3\xcd\n\xef\x8d^\x0f\xb1\xfaJ\n)\xd9\x8amW\xcc\x14y6\xbd\xa5T\xc3u\xe1C^\x1bm//\xa6\x87fS\xc8\x13c\xd5\xb4Y\n\xae\x83\xc7\x95\xbb\x8c\x15\xe9v)|X\x91\x98\x9e\x0f\xc0\x1eu\xe2r\xe1g\xa4\xcf\x84\x1bX%\xee(U-\x98\x9c\xb7\xc2\x1b\xeb \x12\x1a<\xaa\xdcXaI>AcY \xb4\xf1\x19\xda\xaa(\x1dt\xa4\xdd\xa7\xc0\xbc+r\x1e\x1e+\xb5P\xaf\x17\x13\xc6\xab6\xf4w\xe6RD\x98\x19\x19\xa3\xad\x9fL~\xc3\xb9P\xb9\xc4 2\xaa\x0f\xb2\xcf\xb1\xda\xc5\xdb\x8e\x90\xb6\x93\xb7\xb4\xc4\xbf\xc6\xdb\x08\xe1i\x00m\xa8S\xd2\x88\x96t\xfa6\xc6\x8e6\xe0t\x1ch\xe4\xbf\xf5\xe3M\xc7\x97\xbdb\xd3s\xcbb\x01\xa8[\xabm\x81Z\x1f\xcd\xa7\xcb\xc4\x18\xdfz\xbal\xd8\x86\xbc~\xbb\x0cy\xfdx\xba\x8e\xf7\x7f\x07\x00\x00\xff\xffPK\x07\x08v^\xe1\xe5@\x04\x00\x00q\x1d\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00A\x19tO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00 \x00html/error.go.htmlUT\x05\x00\x01\x8a\xae\xd4]dS\xc1\x8e\xdb \x10\xbd\xe7+\xa6\xdcc\xb2\xe9\x1e\xaa\n\xfb\x92\xf4\xd0C\xd5\xaa\xca\xa5Gb\xc6\x06\xc9\x06\x17p\x9c\x15\xe2\xdf+\xc7\xf6\x16\xb2\xbe\x18\xde{3\x0c\xcf\xcf!\x08l\x94F h\xad\xb1\x85\xf4}Gb\xdc\xb1O\xe7\x9f\xa7\xcb\x9f_\xdf`F\xaa\x1d\x9b_\xd0q\xdd\x96\x045\x81Zr\xeb\xd0\x97d\xf4\xcd\xfe\x0b\xa9v\x00L\"\x17\xf3\x02\x80y\xe5;\xacB(NF`\x8c\xb0\x87\x10\x8a\xcb\x0c\xc6\xc8\xe8\xc2>\x94!x\xec\x87\x8e{\x042\xd7\xe3\xff\x11\x00\x18\xddZ\xb2\xab\x11oko\xa1n\xa0DIz\xae4Y\xb0\x04U\xba1\xfb\xab\xb9\xbf3+Ww\xdc\xb9\x92\xd4\xdc\x8a\x84\x02`\xaao\x93-lBU\x1bM2\xc2\xd9\xba$\xb4\x18L\x8fV\x8d=\xe5\xce\xa1wT\xf5-}x\xb7?\xbe\x0e\xf7\xc2\xdd\xda\xbc\xec\xdew\xda\x95Dz?|\xa5t\x9a\xa6b\xfa\\\x18\xdb\xd2\xe3\xe1p\xa0\x1f\xe4\x93\x12^\x96\xe4\xf8\x9a\xc3\x12U+\xfd3N\xb3\x9b\xc8\x97m\xf8\x87\xc1\xa4J=\x97/\x99\xd6a\xed\x95\xd1Uv\x08\x1b\xb6\x06=:\xc7[$9?\x7f.\xd5@\xf1cac\x0c!]\xa3\x161\xae\x92\x13\xd7g\xbc\x8em\x8c\x17k\xc6k\x87N\x1a\xe3\x9f\xba\xbd\x99\xd1>A\x8c\x83\xb4\xd8dF\x93\xca\xa1s\xcahFyU\xe4\xe7\xfc\xc6\xbf#:\xff\xfd\xfc\x08L\xfa\xac\xcc\x1c\xbcD\xb4V\xe7\xd7\xa6C\xe6\x0d\xfd`\x0e\xa3B\xdd\xde\xa3\x96n\xd2\xfc6\xc6\xf8,\xbf\x89\x96\xd1%\xc1\x8c.\xff\xd3n\x1b\xe4_\x00\x00\x00\xff\xffPK\x07\x08)\xb7\xe1\x1d\x97\x01\x00\x00\x81\x03\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00B\x19tO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00 \x00html/footer.go.htmlUT\x05\x00\x01\x8d\xae\xd4]L\xcf\xc1n\xc3 \x0c\x06\xe0{\x9e\xc2\xf2\xbd8\xea\xb4\xcb\x04}\x17\xd68\x80\x14B\x84\xd9\xd8\x84x\xf7iM\x13\xf5\x06\xd6\xa7\xdf\xfe[\x9bx\x0e+\x03\xce)\x15\xce\xca\x97\xb8`\xef\x83\xde\xff\xb7\x01@[\xf0\x99g\x83\xbe\x94M>\x88j\xadjK\x91s\xf8\x8a*$\xfcG\x00:D\xf7x\x00\xdc\x17+bpK\x953O\x97\xcf\xdf\xcb\xc1\xf1)$\xdf\x0d\xd2\x99BV\x84\x8bP\x88\x8e\xced\xf9v\x07\xff\x89\xcb*\xfb\x01\xcf\xfd\xf5M\xa5\xec\xe8:\x8e#\xbd@\xcf\xc1\xf9b\xf0\xfa\xbeO\xe8Q\x80\xecm\xd0t4j\x8d\xd7\xa9\xf7\xe1/\x00\x00\xff\xffPK\x07\x08{\x07T\xee\xa8\x00\x00\x00\xfc\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00C\x19tO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00 \x00html/header.go.htmlUT\x05\x00\x01\x8f\xae\xd4]D\x8dKn\xc30\x0cD\xf7:\x85\xa0ub\xa1\xfb(wa\xed DT\xa4\x0d\x91\xce\x07\x86\xef^T\x9b.\xdf\xc3\x0c\xdeq,x\xb0\"\xa6\nZ\xd0\xa7\xea\xd2\xd2y\x86\x9b\xc0)\xc4\xa8$(\xe9\xc9xmk\xf7\x14b\x9cWu\xa8\x97\xf4\xe2\xc5kY\xf0\xe4\x19\xd7\x01\x97\xc8\xca\xce\xd4\xae6SC\xf9\xbaD\xa17\xcb.\xffb7\xf4A\xf4\xddPtM!\xdf\xc3\xad\xb1\xfe\x84\x18;ZI\xe6\x9f\x06\xab\xc0\xc8\xf9gCI\x8e\xb7\xe7\xd9\xec\xcf\xd4\x8eGIy\xdaVA\xe7]2\x99\xc1-\x8f_\x16b\x9d\xc62\xdfC8\x0e\xe8r\x9e\xe17\x00\x00\xff\xffPK\x07\x08j\x97\x93v\xaa\x00\x00\x00\xe8\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\xac\x0etO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00 \x00img/account_circle-24px.svgUT\x05\x00\x01\x95\x9c\xd4]<\x90Ao\x830\x0c\x85\xff\xcaSv\x8eq\x9c\x90\xc2Tz\xd8.\xbb\xec\xb4_0\xb5\x8c \xb5\xa5\x1a\x88T\xfc\xfa\xc9\x80&\x81\xfc\xf9\xe5=K\xf6q\x9c;\xee\x82?}qb\x02\x1a\x1e\x8f'\x84Nsn\xe96\xf4\xe3\x1c\xd8R\xba\xbe\xd6u)\xa5*\xbbj\xfakk\x88H=\xe7\x96\xa9t\x97d\x81\xa1L\x16\xbb\xd6\xd2\x83s\x17\xcb\xdbt\x0b,$\x04%(\x9fO\xd7\x9fdt \xfc)$\x06\xcd\xd0\x0fY\x98~\xbb\xbe\x0f\xe9\xbe\x8d\x9f\xe1\xe5\xcf\xe3~\xb7\xd9/\xfb\xa7\xe7\x87\x87\xcd\xd6\xaaZ\xc3\x1e\x1f\x1f?\x96P3\x12IA\x9190R92X\x83\xa0\xf6 \x90l\x89\xf4\x99\x91\xd3\x91-\x01E+\xc4H\x1a\x14\xad\xba\xe3r\xb2 \xb6\x04\xd3\xb4f\x99\x84\x9cv\n\x8c\x1c\x90\xf3\x02\xad\xf6\x82\xccKhHi\xf6\xa5Z|#7#~\x0d\xbfgd\x83\x04\xc5^'m\xac\x8c\xa2s`\xc4f\xcb\x16\xae`\xbb\x9c\x02TBBk\x13\xa34r\x13\xfd/0\xb8!\xeb\"\xc8b\xdeLn\x86w8\xd3=Z\xf6_)\x05T\x1d\x0bF;\x07\xe4\x1e\xd8\xe8X\xa1\xda/\xa7\xd0\xd0L<\xcb_-7m\x1f\xea\xb5\xfc\xedY)z\x86\x81!u\xac\xbc\x00F\xecC\xed\x1bf<$rs\xe3\xc1X\x0bjD\xc9\xd2\x90\xb2\xd3'\x97S4\xea\xad\x0e\xcb\xb2\xe8\x87*k@\xd6\xfb`\x12EK7\xa3\xaa?\xc9\xd2>#wb\x187\x8e\xf8\x19v\xc4;\x13v\x80\xe9\xd9\x9d?\xf3\xfb\xaa\xdf\"|s'E\xce\xc3\\\x1d\xb5\x04\xe4\xb20\xa4L\xc9\x08t\xb3r\"\x84TH\xe9\x06\xd8\xeda%A\xab\xc3\x0c\\\xd0\x9a\xd1\xa1Cm7WO\xee\xa4\xd6j\xab\x19p$\xe4r91\xd4\xf8\xd54\xb1)\xc1W9\x9c\x15\xad\x06\x94Y\x90\xeb\xe4W\xc9\xcd;k\xa6\xac\x16\xbf\xc5\x9a\xd0uQt\xe7z \x19}\\T\xe4> \xc9M\x9c\x94\xc3\xca\xcd\x87\xcb\xc4\x84\xd4\x11\x8d\x05\xcd\x13\xa3\x99@\xed&\x10\xb2\x06D\xcb\xa6\xad\xe6\x96C2\x02/\xa7P`\x9e\xc4?a\xbe\x96\xff\x8d\xfa\xf2\x1f1/ze\xbe\xa0TJh\x06H\xb0.\x13J\xa6\x81\x1e}\x16\x0e\x15l4uJ\xe4\x9a\xd9\x04\xd1\xfa\x03\x94\xf9\x18l\xf6Z\x93v\x88\x82m&u\xeb\xad\x9e>r\x10\xfb\xca\n\x13S1]\"\xfd\x18h\xb8\xf1e]\x97\xd0\xc5\x9a\xab\x8d\xe6\xea\xa8\xd9\x9f.\xf2\x19\x85i\x95|\x85\xac\x875\xb8\xa2Y\xc7\x08A\xb1-^\xa6CMG\xf1\xe2\xf7\xc1\x0b.\x8b\xa0\xb3\xcf\xe2n\xa32\xd6\xd9d\x9a\xc4^\xe0f\xa4l\x9f\x19\xb1\xb1'\x12\x9c*N\x8b\xa0\xf1l\xab\xbf\xa9a\xb16\x88j\xbe|\xf6qx\xff\xa0\xa9\xa02qB\x9f\x14]\xc9\xcd\xaa\x98\xad\x837\x87&\xf0'\x01\x06\xc8\xc2\x05\x9cf;k \x99\xa7l\xac\xb8\x19\xe93S\\\xcc3\xcb\xbf\x11\x81T/\xa7\xa0\x1d\x9a(\xe1\xe7\xa1\x97\x13\x0bz\xa6\xf8\x0f\xde\xf4\x99\xd7X\xda\x1e\xd6\xff\xf3r\xb8\xfb+\x00\x00\xff\xffPK\x07\x08K\xfe\x8b#M\x03\x00\x00d\x08\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00;\x19tO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00style/main.cssUT\x05\x00\x01\x83\xae\xd4]\x8cVMo\xe38\x12\xbd\xebW\x10 \x1aH\x16\xa6!\xd9V\xe2\xb0o{X\xec\x1ev\x0e\xd3\x98\xc3\x1c)\xa9hqB\x91\x02I\xc5v\x0f\xf2\xdf\x07$\xf5A\xc9J'@>l\xb1T\xacz\xf5\xeaU\xfd\x0b\xfd\x9d \xd4P}\xe2\x92\xa0\xf4{\x82PK\xab\x8a\xcbS\xff\x0d\x9f\xa1x\xe5\x163%-6\x8dR\xb6\xf6\x87TZN\x05\xa7\x06*o\xd6\xa8\x9fX\x99\xcb\x8d\xddI\xd3\xab)\xa9\x80\xd8\x99\x85\x8b\xc5\x86\xff\x04L\xab\xbf:c \x92Jz\x8bB]\xdc\x81\x7f\xb5P\xba\x02\x8d\x0buq'\xde1\xa3\x0d\x17W\x820m[\x01\xd8\\\x8d\x85f\x83\xfe-\xb8|\xfd?-\x7f\xf8\xef\xffQ\xd2n\xd0\xdd\x0f8)@\x7f\xfc\xefn\x83~W\x85\xb2j\x93 \x84\xd0\xdd\x7fA\xbc\x81\xe5%E\xbfA\x07w\x1bd\xa84\xd8\x80\xe6l\xbc\xc7\xc5FP\x96\xb7\xfej\xc1%\xe0\x1a\xf8\xa9\xb6\x04e\xdb\x034\xdf\x93\xf7$)Tu\xf5\xf0U\xdc\xb4\x82^ b\x02B\xac\x02.\xb8\xe2\x1aJ\xcb\x95$H\xab\xb3{L\x05?I\xcc-4\x86\xa0\x12\xa4\x05\xeds\xa6\xe5\xebI\xabNV\x04\xdd\xb3#;2\xe6\xfd\xdf7\x94K\x7f\xc1\x99W\xb6&(K\xd3o\xee\x851\x944}\xab\xdd\x03\x8f\xa7\xf7\x1e\xfb\xfd4\xacR\x89\xae\x91\xee\xc4\x15\x81\xb3+.\x95\xb4 -A\xa6\xa5%\xe0\x02\xec\x19@\x86h\xb8d\xca\x15\xa3g\xcc\x05\xf7Q\x1d\x8ei\x80i\xf9=\xb0\n[\xd5\x12\xb4K\xe7\x0fu\xc8\x80vVEO\x0be\xadj\x08\x9a\xdb\n`\x91\xe9M\xa8S\xc2>\xbf\x93Vg\x822\x1f\xb3 \x99~\xadH\x13\x1a\xad2\xbc/\x1c\x08j\xf9\x1b,Av!\xf9\x1b\xea\xcc;\x8fH\xb3\x7fj'\xbe\x9e\xfbJ\x1d\xd2\xf4\xe3: \xb0\x164v\x90\x87\xbe\xdb\xee\x83\x0bon5\x95\x86)\xdd\x10\xd4\xb5-\xe8\x92\x1a\x1fN\xa9\x84\xd2\x04\xdd\xefw\xfb]^\xf5\xc1l-\xb7\x02|H\xebw\xadrm\x12\x00\xc7x\xd7\xf7\xce\xd9n&\x0d\xc3\xc9\xed\xc5\x9f\xc4\xf9avq\xa3\x1dW0{\xcac9\x1ah\xe4\x0c\xdf\x93d[R]\xcd\xb5\x0b\xe1}\xcf\x9bQ\xc1\x9c9\xda\x0f\x7f|\xfeAS4\xadxg\x08:\xc4O \xca\xda\x0b2J\xf0\n\xdd\xc3\x11\x8e\xac\x98C\x86\x87\xd4\xa3&e\x1cDe\xc0F\xb1\x8c4\xde\x0d\x97\xce@/Y\x19@\xf7ZW\xd3\xca\xf15\xf5w\xef\x1d\xc6H\x9f\n\xfa\x90\xa7\x1b\xe4~_\xf6\x1b\x94n\xb3\xfc1\xa8W\xeaBFO\x93]\x96\xed6(\xcb\x9f7({y\xe9M?Ot\x90\xdb\xa8\x06\xe9<\x1dA\x0b\x10>\xa9\xf5n\xf8\xb4\x9dz\xcd\x1b\xb4\xea\xb0[\xd4&K\x07B} \x8b\xbfj\xf4\x9b\xde\xba \x9dHe\x1f\x88\xa0\xc6\xe2\xb2\xe6\xa2z\xf4\xb9\x8c#%\x14(\xaa7KY\xce\xe8\x1a\x06\xa6\xa5AA\x1a.\x07\xcd\xcbv\xf9\"\x9dt\x9c\x16q\xe3y\x99\x0b\xf2\xe9\x08\xd0\xae+\xd1*\x00\xe3;\x84\x14\xc0\x94\x86\xf9\xbb\\\xfa\xa94\xb8\x18A\xba\xbb\x8bQ\x1fb\x8a8<\x95\x13g\xa9S\xe5\xe1\xff\xc2NC\x0b\xd4O\xe6\xfec\xe8;^.\xf5\xb4\x8f\xc4\xd2\"\x0c\xf9X\xf6\xf1\xf3\xee\x16\x94\xa9\x8e=\x9a\xcf}\x94C\xd4\x83\xd4/8\x9c\xbb\xe97\xc6`\xdeNAz\xb9\x10\x04\xdd?\xc1a\x0f\xc7e\xb7i\x08\xca\xb8\x15\xea\xa4\x02\x9b{9Y\xf6\xe8\x8a\xccLs7[\x0d\xf0=I\xdam\x03\xc6\xd0\x13\xc4\xed\xef]d\xf3\x1962nq\xdd<\x0c\x17\xa9g_\xc8L\xc0\xc5O\xb2U\x9a\xc5iz\xedm\xa9\x06i\xd7\xdb#R\xedl\xbf\xcb\xbd\xfc\xa8\xce\xba\xc2MbPv\xda8\x1bW\xab\xef\x89K\xbf\xe6\x16\xbcr{\xab\xb3\xa6\xad\x7f\xf1\x0d4\x13N\xb5j^U \xc7\xfaN\x07 \x04o\x0d7\xf3~\xda\x1a\x10PZB(\xb3\xa0}\x8e\x0b\xdeN\xe4\xa4\x85Q\xa2\xb3\x10\x95\xe1e^\x84\x1e\x87~\x93\x18J\xe6\xc1\xcf\xc3\x9e4c\xe2\xa0?\x8a;\xf6ax\x03i\xcd\x90\xfc{\x92p\xd9v6\x16 c\xaf\"B\xe7\x16\xae \xd1b\xcf\xfa}\xc3\xe5\xb7,\xde\x17\xfd\x0d\xcb1m[\xa0\x9a\xca2>s\xeb\xf5\xda\xc1\xda\xb3O#\x9dJ\xdd\xa3\xf1K>yR\n\xb8|m\x8b\xeae\xdf?v\x84!h\xa0\xcd\x17v\xccm\xd1Y\xdb\xeb\xcb8k\xa7y\x19\x8f\xd1\xa8\xdfgct\x18\x8fkC4{\xdcD\x83\xd6[\xa4\x1b\xd4\xffl\xd3\xe3\x17\x06gz\xd3_\xcf\xa1\xbfz\x8e\xe6\xf3\x0d\xfd\xd0\xb3\xf2\xc3^\x8b\n0\x9b\x8ccOUP*M\x03\xb6#W{\x9c\xb65\x15ld[\xbf\xf9\xa6#\xfc\xa6\xd6\\\xbe\xc6O\nj\xb8\x1b1T\x94\x0fy\xfa\x0da\x7f\xd7\xe3\xcc'\xeb\x84X\xfa\xccb\x0bR\xbb>\x0f;\xe6\xb4\xf3\xf9\x8f\x82Z\xf8\xf3\x01g\xde\xe7\xb22\xcf\xed\x05e\x87\x0f7\x9cq\xc1\xd9\xcf\x17\x9ce\x85\xde\x93\xadb,\xacb\xa1]cZ\xe4\xcf\xfb\xbc\xc8C\xb8\xad:\x83\x86\n\x17W\xdc\xaa\x064\xef\x1a\xff\xc2\x07\xe3\xf6\x9f\x00\x00\x00\xff\xffPK\x07\x08z\xea\xdb\xd3\x10\x05\x00\x00\x0b\x0f\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00@\x19tOv^\xe1\xe5@\x04\x00\x00q\x1d\x00\x00\x16\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00html/dashboard.go.htmlUT\x05\x00\x01\x89\xae\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00A\x19tO)\xb7\xe1\x1d\x97\x01\x00\x00\x81\x03\x00\x00\x12\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8d\x04\x00\x00html/error.go.htmlUT\x05\x00\x01\x8a\xae\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00B\x19tO{\x07T\xee\xa8\x00\x00\x00\xfc\x00\x00\x00\x13\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81m\x06\x00\x00html/footer.go.htmlUT\x05\x00\x01\x8d\xae\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00C\x19tOj\x97\x93v\xaa\x00\x00\x00\xe8\x00\x00\x00\x13\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81_\x07\x00\x00html/header.go.htmlUT\x05\x00\x01\x8f\xae\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\xac\x0etO\x83\xba\x83\xe4\xf5\x00\x00\x00|\x01\x00\x00\x1b\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81S\x08\x00\x00img/account_circle-24px.svgUT\x05\x00\x01\x95\x9c\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x1d\x17tO\xd8\xca9F\xb9\x00\x00\x00\xf9\x00\x00\x00\x12\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9a \x00\x00img/error-24px.svgUT\x05\x00\x01z\xab\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00}\x18tOK\xfe\x8b#M\x03\x00\x00d\x08\x00\x00\x10\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x9c\n\x00\x00img/pomerium.svgUT\x05\x00\x01\x1e\xad\xd4]PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00;\x19tOz\xea\xdb\xd3\x10\x05\x00\x00\x0b\x0f\x00\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x810\x0e\x00\x00style/main.cssUT\x05\x00\x01\x83\xae\xd4]PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00Q\x02\x00\x00\x85\x13\x00\x00\x00\x00" + fs.Register(data) +} diff --git a/internal/frontend/templates.go b/internal/frontend/templates.go new file mode 100644 index 000000000..8be57731d --- /dev/null +++ b/internal/frontend/templates.go @@ -0,0 +1,55 @@ +//go:generate statik -src=./assets -include=*.svg,*.html,*.css,*.js + +package frontend // import "github.com/pomerium/pomerium/internal/frontend" + +import ( + "fmt" + "html/template" + "io/ioutil" + "net/http" + "os" + + "github.com/rakyll/statik/fs" + + _ "github.com/pomerium/pomerium/internal/frontend/statik" // load static assets +) + +// NewTemplates loads pomerium's templates. Panics on failure. +func NewTemplates() (*template.Template, error) { + t := template.New("pomerium-templates") + statikFS, err := fs.New() + if err != nil { + return nil, fmt.Errorf("internal/frontend: error creating new file system: %w", err) + } + + err = fs.Walk(statikFS, "/html", func(filePath string, fileInfo os.FileInfo, err error) error { + if !fileInfo.IsDir() { + file, err := statikFS.Open(filePath) + if err != nil { + return fmt.Errorf("internal/frontend: error opening %s: %w", filePath, err) + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return fmt.Errorf("internal/frontend: error reading %s: %w", filePath, err) + } + t.Parse(string(buf)) + } + return nil + }) + if err != nil { + return nil, err + } + + return t, nil +} + +// MustAssetHandler wraps a call to the embedded static file system and panics +// if the error is non-nil. It is intended for use in variable initializations +func MustAssetHandler() http.Handler { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + return http.FileServer(statikFS) +} diff --git a/internal/frontend/templates_test.go b/internal/frontend/templates_test.go new file mode 100644 index 000000000..e8a965dac --- /dev/null +++ b/internal/frontend/templates_test.go @@ -0,0 +1,63 @@ +package frontend + +import ( + "html/template" + "reflect" + "testing" + + _ "github.com/pomerium/pomerium/internal/frontend/statik" + "github.com/rakyll/statik/fs" +) + +func TestTemplatesCompile(t *testing.T) { + templates := template.Must(NewTemplates()) + if templates == nil { + t.Errorf("unexpected nil value %#v", templates) + } +} + +func TestNewTemplates(t *testing.T) { + tests := []struct { + name string + testData string + want *template.Template + wantErr bool + }{ + {"empty statik fs", "", nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs.Register(tt.testData) + got, err := NewTemplates() + if (err != nil) != tt.wantErr { + t.Errorf("NewTemplates() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewTemplates() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMustAssetHandler(t *testing.T) { + tests := []struct { + name string + testData string + wantPanic bool + }{ + {"empty statik fs", "", true}, + {"empty statik fs", "", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + MustAssetHandler() + + }) + } +} diff --git a/internal/httputil/errors.go b/internal/httputil/errors.go index 4bf1397ab..c68d181c1 100644 --- a/internal/httputil/errors.go +++ b/internal/httputil/errors.go @@ -4,11 +4,12 @@ import ( "encoding/json" "errors" "fmt" + "html/template" "io" "net/http" + "github.com/pomerium/pomerium/internal/frontend" "github.com/pomerium/pomerium/internal/log" - "github.com/pomerium/pomerium/internal/templates" ) // Error formats creates a HTTP error with code, user friendly (and safe) error @@ -94,7 +95,7 @@ func ErrorResponse(w http.ResponseWriter, r *http.Request, e error) { RequestID: requestID, CanDebug: canDebug, } - templates.New().ExecuteTemplate(w, "error.html", t) + template.Must(frontend.NewTemplates()).ExecuteTemplate(w, "error.html", t) } } diff --git a/internal/templates/templates.go b/internal/templates/templates.go deleted file mode 100644 index 7e15108ab..000000000 --- a/internal/templates/templates.go +++ /dev/null @@ -1,461 +0,0 @@ -package templates // import "github.com/pomerium/pomerium/internal/templates" - -import ( - "html/template" -) - -// New loads html and style resources directly. Panics on failure. -func New() *template.Template { - t := template.New("pomerium-templates") - template.Must(t.Parse(` -{{define "header.html"}} - - -{{end}}`)) - - template.Must(t.Parse(` -{{define "error.html"}} - - - - {{.Code}} - {{.Title}} - {{template "header.html"}} - - -
-
-
- -

{{.Title}}

-
-

- {{if .Message}}{{.Message}}
{{end}} - {{if .CanDebug}}Troubleshoot your session.
{{end}} - {{if .RequestID}} Request {{.RequestID}}
{{end}} - -

-
- -
-
- -
- - - -{{end}}`)) - - t = template.Must(t.Parse(` - {{define "dashboard.html"}} - - - - - Pomerium - {{template "header.html"}} - - - -
-
-
- {{if .Session.Picture }} - user image - {{else}} - - - - - {{end}} - -
-
-

Current user

-

Your current session details.

-
- {{if .Session.Name}} - - {{else}} - {{if .Session.GivenName}} - - {{end}} - {{if .Session.FamilyName}} - - {{end}} - {{end}} - {{if .Session.Subject}} - - {{end}} - {{if .Session.Email}} - - {{end}} - {{if .Session.User}} - - {{end}} - {{range $i,$_:= .Session.Groups}} - - {{end}} - {{if .Session.Expiry}} - - {{end}} - {{if .Session.IssuedAt}} - - {{end}} - {{if .Session.Issuer}} - - {{end}} - {{range $i, $_:= .Session.Audience}} - - {{end}} - - {{if .Session.ImpersonateEmail}} - - {{end}} - {{range $i,$_:= .Session.ImpersonateGroups}} - - {{end}} -
-
-
- {{ .csrfField }} - -
-
- - - {{if .IsAdmin}} -
-
-

Sign-in-as

-

Administrators can temporarily impersonate another user.

-
- - -
-
-
- {{ .csrfField }} - -
-
- {{ end }} -
-
- -
- - -{{end}}`)) - return t -} diff --git a/internal/templates/templates_test.go b/internal/templates/templates_test.go deleted file mode 100644 index d4fdb1e55..000000000 --- a/internal/templates/templates_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package templates // import "github.com/pomerium/pomerium/internal/templates" - -import ( - "testing" -) - -func TestTemplatesCompile(t *testing.T) { - templates := New() - if templates == nil { - t.Errorf("unexpected nil value %#v", templates) - - } -} diff --git a/proxy/handlers.go b/proxy/handlers.go index 396a8ea50..7ff964525 100644 --- a/proxy/handlers.go +++ b/proxy/handlers.go @@ -13,13 +13,13 @@ import ( "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/sessions" - "github.com/pomerium/pomerium/internal/templates" "github.com/pomerium/pomerium/internal/urlutil" ) // registerDashboardHandlers returns the proxy service's ServeMux func (p *Proxy) registerDashboardHandlers(r *mux.Router) *mux.Router { h := r.PathPrefix(dashboardURL).Subrouter() + h.Use(middleware.SetHeaders(httputil.HeadersContentSecurityPolicy)) // 1. Retrieve the user session and add it to the request context h.Use(sessions.RetrieveSession(p.sessionStore)) // 2. AuthN - Verify the user is authenticated. Set email, group, & id headers @@ -97,7 +97,7 @@ func (p *Proxy) UserDashboard(w http.ResponseWriter, r *http.Request) { return } - templates.New().ExecuteTemplate(w, "dashboard.html", map[string]interface{}{ + p.templates.ExecuteTemplate(w, "dashboard.html", map[string]interface{}{ "Session": session, "IsAdmin": isAdmin, "csrfField": csrf.TemplateField(r), diff --git a/proxy/proxy.go b/proxy/proxy.go index dc9c65fb2..f57a14f99 100755 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -16,12 +16,12 @@ import ( "github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding/jws" + "github.com/pomerium/pomerium/internal/frontend" "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/telemetry/metrics" - "github.com/pomerium/pomerium/internal/templates" "github.com/pomerium/pomerium/internal/tripper" "github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/proxy/clients" @@ -132,7 +132,7 @@ func New(opts config.Options) (*Proxy, error) { sessions.NewHeaderStore(encoder, "Pomerium"), sessions.NewQueryParamStore(encoder, "pomerium_session")}, signingKey: opts.SigningKey, - templates: templates.New(), + templates: template.Must(frontend.NewTemplates()), } // errors checked in ValidateOptions p.authorizeURL, _ = urlutil.DeepCopy(opts.AuthorizeURL) @@ -302,3 +302,7 @@ func (p *Proxy) roundTripperFromPolicy(policy *config.Policy) http.RoundTripper } return c.Then(transport) } + +func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + p.Handler.ServeHTTP(w, r) +} diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index f66d43f4a..5fc16dccf 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -222,7 +222,7 @@ func Test_UpdateOptions(t *testing.T) { if err == nil { r := httptest.NewRequest("GET", tt.host, nil) w := httptest.NewRecorder() - p.Handler.ServeHTTP(w, r) + p.ServeHTTP(w, r) if tt.wantRoute && w.Code != http.StatusNotFound { t.Errorf("Failed to find route handler") return