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 }}
+

+ {{else}}
+

+ {{end}}
+
+
+
+ {{if .IsAdmin}}
+
+ {{ 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 }}
-

- {{else}}
-
- {{end}}
-
-
-
-
- {{if .IsAdmin}}
-
- {{ 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