Merge remote-tracking branch 'upstream/master' into bugs/fix-forward-auth

Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
This commit is contained in:
Bobby DeSimone 2019-11-25 15:02:25 -08:00
commit c8e6277a30
No known key found for this signature in database
GPG key ID: AEE4CF12FE86D07E
30 changed files with 845 additions and 581 deletions

31
.github/release-drafter.yml vendored Normal file
View file

@ -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: |
<!-- Optional: add a release summary here -->
## Changes
$CHANGES
replacers:
- search: '/CVE-(\d{4})-(\d+)/g'
replace: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-$1-$2"

16
.github/workflows/release-drafter.yml vendored Normal file
View file

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

View file

@ -1,10 +1,10 @@
# forked from istio # forked from istio
service: service:
# When updating this, also update bin/linters.sh accordingly # 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: run:
# timeout for analysis, e.g. 30s, 5m, default is 1m # timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 5m deadline: 20m
# which dirs to skip: they won't be analyzed; # which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path; # can use regexp here: generated.*, regexp is applied on full path;
@ -28,16 +28,20 @@ linters:
disable: disable:
- depguard - depguard
- dupl - dupl
- funlen
- gochecknoglobals - gochecknoglobals
- gochecknoinits - gochecknoinits
- gocognit
- goconst - goconst
- gocyclo - gocyclo
- godox
- interfacer
- maligned
- nakedret - nakedret
- prealloc - prealloc
- scopelint - scopelint
- maligned - whitespace
- interfacer - wsl
- funlen
fast: false fast: false
linters-settings: linters-settings:

View file

@ -27,7 +27,7 @@ CTIMEVAR=-X $(PKG)/internal/version.GitCommit=$(GITCOMMIT) \
-X $(PKG)/internal/version.ProjectURL=$(PKG) -X $(PKG)/internal/version.ProjectURL=$(PKG)
GO_LDFLAGS=-ldflags "-s -w $(CTIMEVAR)" GO_LDFLAGS=-ldflags "-s -w $(CTIMEVAR)"
GOOSARCHES = linux/amd64 darwin/amd64 windows/amd64 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 .PHONY: all
all: clean build-deps test lint spellcheck build ## Runs a clean, build, fmt, lint, test, and vet. 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)" git tag -sa $(VERSION) -m "$(VERSION)"
@echo "Run git push origin $(VERSION) to push your new tag to GitHub." @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 .PHONY: build
build: ## Builds dynamic executables and/or packages. build: ## Builds dynamic executables and/or packages.
@echo "==> $@" @echo "==> $@"

View file

@ -14,9 +14,9 @@ import (
"github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/ecjson" "github.com/pomerium/pomerium/internal/encoding/ecjson"
"github.com/pomerium/pomerium/internal/encoding/jws" "github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/frontend"
"github.com/pomerium/pomerium/internal/identity" "github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/templates"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
) )
@ -147,6 +147,6 @@ func New(opts config.Options) (*Authenticate, error) {
// IdP // IdP
provider: provider, provider: provider,
templates: templates.New(), templates: template.Must(frontend.NewTemplates()),
}, nil }, nil
} }

View file

@ -21,21 +21,10 @@ import (
"github.com/pomerium/pomerium/internal/urlutil" "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. // Handler returns the authenticate service's handler chain.
func (a *Authenticate) Handler() http.Handler { func (a *Authenticate) Handler() http.Handler {
r := httputil.NewRouter() r := httputil.NewRouter()
r.Use(middleware.SetHeaders(CSPHeaders)) r.Use(middleware.SetHeaders(httputil.HeadersContentSecurityPolicy))
r.Use(csrf.Protect( r.Use(csrf.Protect(
a.cookieSecret, a.cookieSecret,
csrf.Secure(a.cookieOptions.Secure), csrf.Secure(a.cookieOptions.Secure),

View file

@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
@ -13,9 +14,9 @@ import (
"github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/cryptutil"
"github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/mock" "github.com/pomerium/pomerium/internal/encoding/mock"
"github.com/pomerium/pomerium/internal/frontend"
"github.com/pomerium/pomerium/internal/identity" "github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/templates"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -30,7 +31,7 @@ func testAuthenticate() *Authenticate {
auth.sharedKey = cryptutil.NewBase64Key() auth.sharedKey = cryptutil.NewBase64Key()
auth.cookieSecret = cryptutil.NewKey() auth.cookieSecret = cryptutil.NewKey()
auth.cookieOptions = &sessions.CookieOptions{Name: "name"} auth.cookieOptions = &sessions.CookieOptions{Name: "name"}
auth.templates = templates.New() auth.templates = template.Must(frontend.NewTemplates())
return &auth return &auth
} }
@ -192,7 +193,7 @@ func TestAuthenticate_SignOut(t *testing.T) {
a := &Authenticate{ a := &Authenticate{
sessionStore: tt.sessionStore, sessionStore: tt.sessionStore,
provider: tt.provider, provider: tt.provider,
templates: templates.New(), templates: template.Must(frontend.NewTemplates()),
} }
u, _ := url.Parse("/sign_out") u, _ := url.Parse("/sign_out")
params, _ := url.ParseQuery(u.RawQuery) params, _ := url.ParseQuery(u.RawQuery)

View file

@ -14,6 +14,7 @@ import (
"github.com/pomerium/pomerium/authenticate" "github.com/pomerium/pomerium/authenticate"
"github.com/pomerium/pomerium/authorize" "github.com/pomerium/pomerium/authorize"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/frontend"
"github.com/pomerium/pomerium/internal/grpcutil" "github.com/pomerium/pomerium/internal/grpcutil"
"github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
@ -136,7 +137,7 @@ func newProxyService(opt config.Options, r *mux.Router) (*proxy.Proxy, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.PathPrefix("/").Handler(service.Handler) r.PathPrefix("/").Handler(service)
return service, nil return service, nil
} }
@ -169,6 +170,7 @@ func newGlobalRouter(o *config.Options) *mux.Router {
mux.Use(middleware.Healthcheck("/ping", version.UserAgent())) mux.Use(middleware.Healthcheck("/ping", version.UserAgent()))
mux.HandleFunc("/healthz", httputil.HealthCheck) mux.HandleFunc("/healthz", httputil.HealthCheck)
mux.HandleFunc("/ping", httputil.HealthCheck) mux.HandleFunc("/ping", httputil.HealthCheck)
mux.PathPrefix("/.pomerium/assets/").Handler(http.StripPrefix("/.pomerium/assets/", frontend.MustAssetHandler()))
return mux return mux
} }

View file

@ -34,136 +34,136 @@ const DefaultAlternativeAddr = ":5443"
// Use NewXXXOptions() methods for a safely initialized data structure. // Use NewXXXOptions() methods for a safely initialized data structure.
type Options struct { type Options struct {
// Debug outputs human-readable logs to Stdout. // 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. // 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". // 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 // SharedKey is the shared secret authorization key used to mutually authenticate
// requests between services. // 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. // Services is a list enabled service mode. If none are selected, "all" is used.
// Available options are : "all", "authenticate", "proxy". // 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 // Addr specifies the host and port on which the server should serve
// HTTPS requests. If empty, ":443" (localhost:443) is used. // 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. // InsecureServer when enabled disables all transport security.
// In this mode, Pomerium is susceptible to man-in-the-middle attacks. // In this mode, Pomerium is susceptible to man-in-the-middle attacks.
// This should be used only for testing. // 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 and Key is the x509 certificate used to hydrate TLSCertificate
Cert string `mapstructure:"certificate"` Cert string `mapstructure:"certificate" yaml:"certificate,omitempty"`
Key string `mapstructure:"certificate_key"` Key string `mapstructure:"certificate_key" yaml:"certificate_key,omitempty"`
// CertFile and KeyFile is the x509 certificate used to hydrate TLSCertificate // CertFile and KeyFile is the x509 certificate used to hydrate TLSCertificate
CertFile string `mapstructure:"certificate_file"` CertFile string `mapstructure:"certificate_file" yaml:"certificate_file,omitempty"`
KeyFile string `mapstructure:"certificate_key_file"` KeyFile string `mapstructure:"certificate_key_file" yaml:"certificate_key_file,omitempty"`
// TLSCertificate is the hydrated tls.Certificate. // 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 // HttpRedirectAddr, if set, specifies the host and port to run the HTTP
// to HTTPS redirect server on. If empty, no redirect server is started. // 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 // Timeout settings : https://github.com/pomerium/pomerium/issues/40
ReadTimeout time.Duration `mapstructure:"timeout_read"` ReadTimeout time.Duration `mapstructure:"timeout_read" yaml:"timeout_read,omitempty"`
WriteTimeout time.Duration `mapstructure:"timeout_write"` WriteTimeout time.Duration `mapstructure:"timeout_write" yaml:"timeout_write,omitempty"`
ReadHeaderTimeout time.Duration `mapstructure:"timeout_read_header"` ReadHeaderTimeout time.Duration `mapstructure:"timeout_read_header" yaml:"timeout_read_header,omitempty"`
IdleTimeout time.Duration `mapstructure:"timeout_idle"` IdleTimeout time.Duration `mapstructure:"timeout_idle" yaml:"timeout_idle,omitempty"`
// Policies define per-route configuration and access control policies. // Policies define per-route configuration and access control policies.
Policies []Policy Policies []Policy
PolicyEnv string PolicyEnv string `yaml:",omitempty"`
PolicyFile string `mapstructure:"policy_file"` PolicyFile string `mapstructure:"policy_file" yaml:"policy_file,omitempty"`
// AuthenticateURL represents the externally accessible http endpoints // AuthenticateURL represents the externally accessible http endpoints
// used for authentication requests and callbacks // used for authentication requests and callbacks
AuthenticateURLString string `mapstructure:"authenticate_service_url"` AuthenticateURLString string `mapstructure:"authenticate_service_url" yaml:"authenticate_service_url,omitempty"`
AuthenticateURL *url.URL AuthenticateURL *url.URL `yaml:"-,omitempty"`
// Session/Cookie management // Session/Cookie management
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
CookieName string `mapstructure:"cookie_name"` CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"`
CookieSecret string `mapstructure:"cookie_secret"` CookieSecret string `mapstructure:"cookie_secret" yaml:"cookie_secret,omitempty"`
CookieDomain string `mapstructure:"cookie_domain"` CookieDomain string `mapstructure:"cookie_domain" yaml:"cookie_domain,omitempty"`
CookieSecure bool `mapstructure:"cookie_secure"` CookieSecure bool `mapstructure:"cookie_secure" yaml:"cookie_secure,omitempty"`
CookieHTTPOnly bool `mapstructure:"cookie_http_only"` CookieHTTPOnly bool `mapstructure:"cookie_http_only" yaml:"cookie_http_only,omitempty"`
CookieExpire time.Duration `mapstructure:"cookie_expire"` CookieExpire time.Duration `mapstructure:"cookie_expire" yaml:"cookie_expire,omitempty"`
CookieRefresh time.Duration `mapstructure:"cookie_refresh"` CookieRefresh time.Duration `mapstructure:"cookie_refresh" yaml:"cookie_refresh,omitempty"`
// Identity provider configuration variables as specified by RFC6749 // Identity provider configuration variables as specified by RFC6749
// https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749 // https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749
ClientID string `mapstructure:"idp_client_id"` ClientID string `mapstructure:"idp_client_id" yaml:"idp_client_id,omitempty"`
ClientSecret string `mapstructure:"idp_client_secret"` ClientSecret string `mapstructure:"idp_client_secret" yaml:"idp_client_secret,omitempty"`
Provider string `mapstructure:"idp_provider"` Provider string `mapstructure:"idp_provider" yaml:"idp_provider,omitempty"`
ProviderURL string `mapstructure:"idp_provider_url"` ProviderURL string `mapstructure:"idp_provider_url" yaml:"idp_provider_url,omitempty"`
Scopes []string `mapstructure:"idp_scopes"` Scopes []string `mapstructure:"idp_scopes" yaml:"idp_scopes,omitempty"`
ServiceAccount string `mapstructure:"idp_service_account"` ServiceAccount string `mapstructure:"idp_service_account" yaml:"idp_service_account,omitempty"`
// Administrators contains a set of emails with users who have super user // Administrators contains a set of emails with users who have super user
// (sudo) access including the ability to impersonate other users' access // (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 // AuthorizeURL is the routable destination of the authorize service's
// gRPC endpoint. NOTE: As many load balancers do not support // gRPC endpoint. NOTE: As many load balancers do not support
// externally routed gRPC so this may be an internal location. // externally routed gRPC so this may be an internal location.
AuthorizeURLString string `mapstructure:"authorize_service_url"` AuthorizeURLString string `mapstructure:"authorize_service_url" yaml:"authorize_service_url,omitempty"`
AuthorizeURL *url.URL AuthorizeURL *url.URL `yaml:",omitempty"`
// Settings to enable custom behind-the-ingress service communication // Settings to enable custom behind-the-ingress service communication
OverrideCertificateName string `mapstructure:"override_certificate_name"` OverrideCertificateName string `mapstructure:"override_certificate_name" yaml:"override_certificate_name,omitempty"`
CA string `mapstructure:"certificate_authority"` CA string `mapstructure:"certificate_authority" yaml:"certificate_authority,omitempty"`
CAFile string `mapstructure:"certificate_authority_file"` CAFile string `mapstructure:"certificate_authority_file" yaml:"certificate_authority_file,omitempty"`
// SigningKey is the private key used to add a JWT-signature. // SigningKey is the private key used to add a JWT-signature.
// https://www.pomerium.io/docs/signed-headers.html // 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. // Headers to set on all proxied requests. Add a 'disable' key map to turn off.
HeadersEnv string HeadersEnv string `yaml:",omitempty"`
Headers map[string]string Headers map[string]string `yaml:",omitempty"`
// RefreshCooldown limits the rate a user can refresh her session // 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"` //Routes map[string]string `mapstructure:"routes" yaml:"routes,omitempty"`
DefaultUpstreamTimeout time.Duration `mapstructure:"default_upstream_timeout"` DefaultUpstreamTimeout time.Duration `mapstructure:"default_upstream_timeout" yaml:"default_upstream_timeout,omitempty"`
// Address/Port to bind to for prometheus metrics // 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 // Tracing shared settings
TracingProvider string `mapstructure:"tracing_provider"` TracingProvider string `mapstructure:"tracing_provider" yaml:"tracing_provider,omitempty"`
TracingDebug bool `mapstructure:"tracing_debug"` TracingDebug bool `mapstructure:"tracing_debug" yaml:"tracing_debug,omitempty"`
// Jaeger // Jaeger
// //
// CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector. // CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector.
// For example, http://localhost:14268/api/traces // 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. // AgentEndpoint instructs exporter to send spans to jaeger-agent at this address.
// For example, localhost:6831. // 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 // GRPC Service Settings
// GRPCAddr specifies the host and port on which the server should serve // 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. // 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. // GRPCInsecure disables transport security.
// If running in all-in-one mode, defaults to true. // 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"` GRPCClientTimeout time.Duration `mapstructure:"grpc_client_timeout" yaml:"grpc_client_timeout,omitempty"`
GRPCClientDNSRoundRobin bool `mapstructure:"grpc_client_dns_roundrobin"` 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 // 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 // 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 // allow you to delegate and authenticate each request to your website
// with an external server or service. Pomerium can be configured to accept // with an external server or service. Pomerium can be configured to accept
// these requests with this switch // these requests with this switch
ForwardAuthURLString string `mapstructure:"forward_auth_url"` ForwardAuthURLString string `mapstructure:"forward_auth_url" yaml:"forward_auth_url,omitempty"`
ForwardAuthURL *url.URL ForwardAuthURL *url.URL `yaml:",omitempty"`
viper *viper.Viper viper *viper.Viper
} }

View file

@ -16,27 +16,27 @@ type Policy struct {
From string `mapstructure:"from" yaml:"from"` From string `mapstructure:"from" yaml:"from"`
To string `mapstructure:"to" yaml:"to"` To string `mapstructure:"to" yaml:"to"`
// Identity related policy // Identity related policy
AllowedEmails []string `mapstructure:"allowed_users" yaml:"allowed_users"` AllowedEmails []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty"`
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups"` AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty"`
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains"` AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty"`
Source *url.URL Source *url.URL `yaml:",omitempty"`
Destination *url.URL Destination *url.URL `yaml:",omitempty"`
// Allow unauthenticated HTTP OPTIONS requests as per the CORS spec // Allow unauthenticated HTTP OPTIONS requests as per the CORS spec
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests // 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** // 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 // UpstreamTimeout is the route specific timeout. Must be less than the global
// timeout. If unset, route will fallback to the proxy's DefaultUpstreamTimeout. // 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. // Enable proxying of websocket connections by removing the default timeout handler.
// Caution: Enabling this feature could result in abuse via DOS attacks. // 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 // TLSSkipVerify controls whether a client verifies the server's certificate
// chain and host name. // chain and host name.
@ -44,32 +44,32 @@ type Policy struct {
// server and any host name in that certificate. // server and any host name in that certificate.
// In this mode, TLS is susceptible to man-in-the-middle attacks. // In this mode, TLS is susceptible to man-in-the-middle attacks.
// This should be used only for testing. // 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 // TLSServerName overrides the hostname in the `to` field. This is useful
// if your backend is an HTTPS server with a valid certificate, but you // 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. // want to communicate to the backend with an internal hostname (e.g.
// Docker container name). // 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 // TLSCustomCA defines the root certificate to use with a given
// route when verifying server certificates. // route when verifying server certificates.
TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca"` TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca,omitempty"`
TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file"` TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file,omitempty"`
RootCAs *x509.CertPool RootCAs *x509.CertPool `yaml:",omitempty"`
// Contains the x.509 client certificate to to present to the downstream // Contains the x.509 client certificate to to present to the downstream
// host. // host.
TLSClientCert string `mapstructure:"tls_client_cert" yaml:"tls_client_cert"` TLSClientCert string `mapstructure:"tls_client_cert" yaml:"tls_client_cert,omitempty"`
TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key"` TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key,omitempty"`
TLSClientCertFile string `mapstructure:"tls_client_cert_file" yaml:"tls_client_cert_file"` 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"` TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file,omitempty"`
ClientCertificate *tls.Certificate ClientCertificate *tls.Certificate `yaml:",omitempty"`
// SetRequestHeaders adds a collection of headers to the downstream request // SetRequestHeaders adds a collection of headers to the downstream request
// in the form of key value pairs. Note bene, this will overwrite the // in the form of key value pairs. Note bene, this will overwrite the
// value of any existing value of a given header key. // 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. // Validate checks the validity of a policy.

View file

@ -1,5 +1,19 @@
# Changelog # 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 ## v0.5.0
### New ### New

1
go.mod
View file

@ -18,6 +18,7 @@ require (
github.com/pomerium/go-oidc v2.0.0+incompatible github.com/pomerium/go-oidc v2.0.0+incompatible
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_golang v0.9.3 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/cors v1.7.0
github.com/rs/zerolog v1.16.0 github.com/rs/zerolog v1.16.0
github.com/spf13/afero v1.2.2 // indirect github.com/spf13/afero v1.2.2 // indirect

2
go.sum
View file

@ -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 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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/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/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/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=

View file

@ -0,0 +1,237 @@
{{define "dashboard.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>Pomerium</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
{{if .Session.Picture }}
<img class="icon" src="{{.Session.Picture}}" alt="user image" />
{{else}}
<img
class="icon"
src="/.pomerium/assets/img/account_circle-24px.svg"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
/>
{{end}}
<form method="POST" action="/.pomerium/sign_out">
<section>
<h2>Current user</h2>
<p class="message">Your current session details.</p>
<fieldset>
{{if .Session.Name}}
<label>
<span>Name</span>
<input
type="text"
class="field"
value="{{.Session.Name}}"
title="{{.Session.Name}}"
disabled
/>
</label>
{{else}} {{if .Session.GivenName}}
<label>
<span>Given Name</span>
<input
type="text"
class="field"
value="{{.Session.GivenName}}"
title="{{.Session.GivenName}}"
disabled
/>
</label>
{{end}} {{if .Session.FamilyName}}
<label>
<span>Family Name</span>
<input
type="text"
class="field"
value="{{.Session.FamilyName}}"
title="{{.Session.FamilyName}}"
disabled
/>
</label>
{{end}} {{end}} {{if .Session.Subject}}
<label>
<span>UserID</span>
<input
type="text"
class="field"
value="{{.Session.Subject}}"
title="{{.Session.Subject}}"
disabled
/>
</label>
{{end}} {{if .Session.Email}}
<label>
<span>Email</span>
<input
type="email"
class="field"
value="{{.Session.Email}}"
title="{{.Session.Email}}"
disabled
/>
</label>
{{end}} {{if .Session.User}}
<label>
<span>User</span>
<input
type="text"
class="field"
value="{{.Session.User}}"
title="{{.Session.User}}"
disabled
/>
</label>
{{end}} {{range $i,$_:= .Session.Groups}}
<label>
{{if eq $i 0}}
<span>Group</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
value="{{.}}"
title="{{.}}"
disabled
/>
</label>
{{end}} {{if .Session.Expiry}}
<label>
<span>Expiry</span>
<input
type="text"
class="field"
value="{{.Session.Expiry.Time}}"
title="{{.Session.Expiry.Time}}"
disabled
/>
</label>
{{end}} {{if .Session.IssuedAt}}
<label>
<span>Issued</span>
<input
type="text"
class="field"
value="{{.Session.IssuedAt.Time}}"
title="{{.Session.IssuedAt.Time}}"
disabled
/>
</label>
{{end}} {{if .Session.Issuer}}
<label>
<span>Issuer</span>
<input
type="text"
class="field"
value="{{ .Session.Issuer}}"
title="{{ .Session.Issuer}}"
disabled
/>
</label>
{{end}} {{range $i, $_:= .Session.Audience}}
<label>
{{if eq $i 0}}
<span>Audience</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
title="{{ . }}"
value="{{ . }}"
disabled
/>
</label>
{{end}} {{if .Session.ImpersonateEmail}}
<label>
<span>Impersonating Email</span>
<input
type="text"
class="field"
value="{{.Session.ImpersonateEmail}}"
disabled
/>
</label>
{{end}} {{range $i,$_:= .Session.ImpersonateGroups}}
<label>
{{if eq $i 0}}
<span>Impersonating Group</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
value="{{.}}"
title="{{.}}"
disabled
/>
</label>
{{end}}
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">Sign Out</button>
</div>
</form>
{{if .IsAdmin}}
<form method="POST" action="/.pomerium/impersonate">
<section>
<h2>Sign-in-as</h2>
<p class="message">
Administrators can temporarily impersonate another user.
</p>
<fieldset>
<label>
<span>Email</span>
<input
name="email"
type="email"
class="field"
value=""
placeholder="user@example.com"
/>
</label>
<label>
<span>Group</span>
<input
name="group"
type="text"
class="field"
value=""
placeholder="engineering"
/>
</label>
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">
Impersonate session
</button>
</div>
</form>
{{ end }}
</div>
</div>
{{template "footer.html"}}
</div>
</body>
</html>
{{end}}

View file

@ -0,0 +1,35 @@
{{define "error.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>{{.Code}} - {{.Title}}</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
<img
class="icon"
src="/.pomerium/assets/img/error-24px.svg"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
/>
<h1 class="title">{{.Title}}</h1>
<section>
<p class="message">
{{if .Message}}{{.Message}}{{end}} {{if .CanDebug}}Troubleshoot
your
<a href="/.pomerium/">session</a>.{{end}} {{if .RequestID}}
Request {{.RequestID}}{{end}}
</p>
</section>
</div>
</div>
{{template "footer.html"}}
</div>
</body>
</html>
{{end}}

View file

@ -0,0 +1,12 @@
{{define "footer.html"}}
<footer>
<a href="https://www.pomerium.io">
<img
class="powered-by-pomerium"
src="/.pomerium/assets/img/pomerium.svg"
xmlns="http://www.w3.org/2000/svg"
height="25"
/>
</a>
</footer>
{{end}}

View file

@ -0,0 +1,12 @@
{{define "header.html"}}
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<link
rel="stylesheet"
type="text/css"
href="/.pomerium/assets/style/main.css"
/>
{{end}}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#6e43e8" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 380 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="#6e43e8" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

View file

@ -0,0 +1 @@
<svg viewBox="0 0 139 30" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-5 -5.5)"><path d="m10.6 5.5h127.8c3.09 0 5.6 2 5.6 4.39v21.22c0 2.42-2.51 4.39-5.6 4.39h-127.8c-3.09 0-5.6-2-5.6-4.39v-21.22c0-2.42 2.51-4.39 5.6-4.39z" fill="#6e43e8" fill-rule="evenodd"/><g fill="#fff"><path d="m75.4 26.62h-1.46l1.13-2.79-2.25-5.69h1.54l1.42 3.86 1.43-3.87h1.54zm-5.61-2.44a2.42 2.42 0 0 1 -1.5-.55v.37h-1.51v-8.44h1.51v3a2.48 2.48 0 0 1 1.5-.55c1.58 0 2.66 1.28 2.66 3.09s-1.08 3.08-2.66 3.08zm-.32-4.88a1.68 1.68 0 0 0 -1.18.53v2.52a1.65 1.65 0 0 0 1.18.54c.85 0 1.44-.73 1.44-1.8s-.59-1.79-1.44-1.79zm-8.8 4.33a2.38 2.38 0 0 1 -1.5.55c-1.57 0-2.66-1.27-2.66-3.09s1.09-3.09 2.66-3.09a2.44 2.44 0 0 1 1.5.55v-3h1.52v8.45h-1.52zm0-3.8a1.63 1.63 0 0 0 -1.17-.53c-.86 0-1.45.73-1.45 1.79s.59 1.8 1.45 1.8a1.6 1.6 0 0 0 1.17-.54zm-9 1.68a1.69 1.69 0 0 0 1.8 1.49 3.55 3.55 0 0 0 1.76-.56v1.26a4.73 4.73 0 0 1 -2 .46 3 3 0 0 1 -3-3.13 2.87 2.87 0 0 1 2.88-3.03 2.66 2.66 0 0 1 2.59 3 5.53 5.53 0 0 1 0 .56zm1.37-2.34a1.38 1.38 0 0 0 -1.37 1.36h2.57a1.28 1.28 0 0 0 -1.19-1.36zm-5.34.93v3.9h-1.5v-5.9h1.51v.59a2 2 0 0 1 1.45-.69 1.65 1.65 0 0 1 .49.06v1.35a1.83 1.83 0 0 0 -.53-.07 1.87 1.87 0 0 0 -1.41.76zm-6.7 1.41a1.69 1.69 0 0 0 1.76 1.49 3.55 3.55 0 0 0 1.76-.56v1.26a4.73 4.73 0 0 1 -2 .46 3 3 0 0 1 -3-3.13 2.87 2.87 0 0 1 2.88-3.03 2.66 2.66 0 0 1 2.6 3 5.53 5.53 0 0 1 0 .56zm1.37-2.34a1.38 1.38 0 0 0 -1.37 1.36h2.57a1.28 1.28 0 0 0 -1.23-1.36zm-6.67 4.83-1.2-4-1.2 4h-1.3l-2-5.9h1.51l1.19 4 1.19-4h1.37l1.19 4 1.19-4h1.51l-2 5.9zm-9.23.14a2.94 2.94 0 0 1 -3-3.09 3 3 0 1 1 6.07 0 2.94 2.94 0 0 1 -3.07 3.13zm0-4.92c-.88 0-1.49.75-1.49 1.83s.61 1.83 1.49 1.83 1.53-.7 1.53-1.79-.65-1.83-1.53-1.83zm-6.62 1.87h-1.36v2.91h-1.49v-8.07h2.87a2.61 2.61 0 1 1 0 5.2zm-.22-4h-1.14v2.81h1.14a1.38 1.38 0 1 0 0-2.75z" fill-rule="evenodd"/><path d="m132.71 14.9a3.93 3.93 0 0 0 -3.93-3.9h-34.19a3.93 3.93 0 0 0 -3.93 3.92v16.14h2.71v-4.51a5.49 5.49 0 1 1 11 0v4.51h2v-4.51a5.49 5.49 0 1 1 11 0v4.51h2v-4.51a5.49 5.49 0 1 1 11 0v4.51h2.47zm-39.34 4.1a5.49 5.49 0 1 1 11 0zm12.95 0a5.49 5.49 0 1 1 11 0zm12.94 0a5.49 5.49 0 1 1 11 0z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -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;
}

File diff suppressed because one or more lines are too long

View file

@ -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)
}

View file

@ -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()
})
}
}

View file

@ -4,11 +4,12 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"html/template"
"io" "io"
"net/http" "net/http"
"github.com/pomerium/pomerium/internal/frontend"
"github.com/pomerium/pomerium/internal/log" "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 // 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, RequestID: requestID,
CanDebug: canDebug, CanDebug: canDebug,
} }
templates.New().ExecuteTemplate(w, "error.html", t) template.Must(frontend.NewTemplates()).ExecuteTemplate(w, "error.html", t)
} }
} }

View file

@ -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"}}
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
* {
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;
// margin-right: 10px;
}
.icon {
display: inline-table;
margin-top: -72px;
background: #F8F8FF;
text-align: center;
width: 75px;
height: auto;
border-radius: 50%;
}
.logo {
padding-bottom: 20px;
padding-top: 20px;
width: 115px;
height: auto;
}
.ok{
fill: #6E43E8;
}
.error{
fill: #EB292F;
}
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;
background: #6E43E8 url("data:image/svg+xml;utf8,<svg viewBox='0 0 140 140' width='24' height='24' xmlns='http://www.w3.org/2000/svg'><g><path d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' fill='white'/></g></svg>") no-repeat;
}
input {
// flex: 1;
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;
}
</style>
{{end}}`))
template.Must(t.Parse(`
{{define "error.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>{{.Code}} - {{.Title}}</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
<svg class="icon error" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></svg>
<h1 class="title">{{.Title}}</h1>
<section>
<p class="message">
{{if .Message}}{{.Message}}</br>{{end}}
{{if .CanDebug}}Troubleshoot your <a href="/.pomerium/">session</a>.</br>{{end}}
{{if .RequestID}} Request {{.RequestID}}</br>{{end}}
</p>
</section>
</form>
</div>
</div>
<footer>
<a href="https://www.pomerium.io" style="display: block;">
<svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 139 30"><defs><style>.a {fill: #6e43e8;}.a,.b {fill-rule: evenodd;}.b,.c {fill: #fff;}</style></defs><title>powered-by-pomerium</title><path class="a" d="M10.6,5.5H138.4c3.09,0,5.6,2,5.6,4.39V31.11c0,2.42-2.51,4.39-5.6,4.39H10.6c-3.09,0-5.6-2-5.6-4.39V9.89C5,7.47,7.51,5.5,10.6,5.5Z" transform="translate(-5 -5.5)" /><path class="b" d="M75.4,26.62H73.94l1.13-2.79-2.25-5.69h1.54L75.78,22l1.43-3.87h1.54Zm-5.61-2.44a2.42,2.42,0,0,1-1.5-.55V24H66.78V15.56h1.51v3a2.48,2.48,0,0,1,1.5-.55c1.58,0,2.66,1.28,2.66,3.09S71.37,24.18,69.79,24.18Zm-.32-4.88a1.68,1.68,0,0,0-1.18.53v2.52a1.65,1.65,0,0,0,1.18.54c.85,0,1.44-.73,1.44-1.8S70.32,19.3,69.47,19.3Zm-8.8,4.33a2.38,2.38,0,0,1-1.5.55c-1.57,0-2.66-1.27-2.66-3.09S57.6,18,59.17,18a2.44,2.44,0,0,1,1.5.55v-3h1.52V24H60.67Zm0-3.8a1.63,1.63,0,0,0-1.17-.53c-.86,0-1.45.73-1.45,1.79s.59,1.8,1.45,1.8a1.6,1.6,0,0,0,1.17-.54Zm-9,1.68A1.69,1.69,0,0,0,53.47,23a3.55,3.55,0,0,0,1.76-.56v1.26a4.73,4.73,0,0,1-2,.46,3,3,0,0,1-3-3.13A2.87,2.87,0,0,1,53.11,18,2.66,2.66,0,0,1,55.7,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34a1.38,1.38,0,0,0-1.37,1.36h2.57A1.28,1.28,0,0,0,53.05,19.17Zm-5.34.93V24H46.2v-5.9h1.51v.59A2,2,0,0,1,49.16,18a1.65,1.65,0,0,1,.49.06v1.35a1.83,1.83,0,0,0-.53-.07A1.87,1.87,0,0,0,47.71,20.1ZM41,21.51A1.69,1.69,0,0,0,42.76,23a3.55,3.55,0,0,0,1.76-.56v1.26a4.73,4.73,0,0,1-2,.46,3,3,0,0,1-3-3.13A2.87,2.87,0,0,1,42.4,18,2.66,2.66,0,0,1,45,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34A1.38,1.38,0,0,0,41,20.53h2.57A1.28,1.28,0,0,0,42.34,19.17ZM35.7,24l-1.2-4-1.2,4H32l-2-5.9h1.51l1.19,4,1.19-4h1.37l1.19,4,1.19-4h1.51l-2,5.9Zm-9.23.14a2.94,2.94,0,0,1-3-3.09,3,3,0,1,1,6.07,0A2.94,2.94,0,0,1,26.47,24.18Zm0-4.92c-.88,0-1.49.75-1.49,1.83s.61,1.83,1.49,1.83S28,22.18,28,21.09,27.35,19.26,26.47,19.26Zm-6.62,1.87H18.49V24H17V15.93h2.87a2.61,2.61,0,1,1,0,5.2Zm-.22-4H18.49V19.9h1.14a1.38,1.38,0,1,0,0-2.75Z" transform="translate(-5 -5.5)" /><path class="c" d="M132.71,14.9A3.93,3.93,0,0,0,128.78,11H94.59a3.93,3.93,0,0,0-3.93,3.92V31.06h2.71V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2.47ZM93.37,19a5.49,5.49,0,1,1,11,0Zm12.95,0a5.49,5.49,0,1,1,11,0Zm12.94,0a5.49,5.49,0,1,1,11,0Z" transform="translate(-5 -5.5)" /></svg>
</a>
</footer>
</div>
</body>
</html>
{{end}}`))
t = template.Must(t.Parse(`
{{define "dashboard.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>Pomerium</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
{{if .Session.Picture }}
<img class="icon" src="{{.Session.Picture}}" alt="user image">
{{else}}
<svg class="icon ok" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
{{end}}
<form method="POST" action="/.pomerium/sign_out">
<section>
<h2>Current user</h2>
<p class="message">Your current session details.</p>
<fieldset>
{{if .Session.Name}}
<label>
<span>Name</span>
<input type="text" class="field" value="{{.Session.Name}}" title="{{.Session.Name}}" disabled>
</label>
{{else}}
{{if .Session.GivenName}}
<label>
<span>Given Name</span>
<input type="text" class="field" value="{{.Session.GivenName}}" title="{{.Session.GivenName}}" disabled>
</label>
{{end}}
{{if .Session.FamilyName}}
<label>
<span>Family Name</span>
<input type="text" class="field" value="{{.Session.FamilyName}}" title="{{.Session.FamilyName}}" disabled>
</label>
{{end}}
{{end}}
{{if .Session.Subject}}
<label>
<span>UserID</span>
<input type="text" class="field" value="{{.Session.Subject}}" title="{{.Session.Subject}}" disabled>
</label>
{{end}}
{{if .Session.Email}}
<label>
<span>Email</span>
<input type="email" class="field" value="{{.Session.Email}}" title="{{.Session.Email}}" disabled>
</label>
{{end}}
{{if .Session.User}}
<label>
<span>User</span>
<input type="text" class="field" value="{{.Session.User}}" title="{{.Session.User}}" disabled>
</label>
{{end}}
{{range $i,$_:= .Session.Groups}}
<label>
{{if eq $i 0}}
<span>Group</span>
{{else}}
<span></span>
{{end}}
<input type="text" class="field" value="{{.}}" title="{{.}}" disabled>
</label>
{{end}}
{{if .Session.Expiry}}
<label>
<span>Expiry</span>
<input type="text" class="field" value="{{.Session.Expiry.Time}}" title="{{.Session.Expiry.Time}}" disabled>
</label>
{{end}}
{{if .Session.IssuedAt}}
<label>
<span>Issued</span>
<input type="text" class="field" value="{{.Session.IssuedAt.Time}}" title="{{.Session.IssuedAt.Time}}" disabled>
</label>
{{end}}
{{if .Session.Issuer}}
<label>
<span>Issuer</span>
<input type="text" class="field" value="{{ .Session.Issuer}}" title="{{ .Session.Issuer}}" disabled>
</label>
{{end}}
{{range $i, $_:= .Session.Audience}}
<label>
{{if eq $i 0}}
<span>Audience</span>
{{else}}
<span></span>
{{end}}
<input type="text" class="field" title="{{ . }}" value="{{ . }}" disabled>
</label>
{{end}}
{{if .Session.ImpersonateEmail}}
<label>
<span>Impersonating Email</span>
<input type="text" class="field" value="{{.Session.ImpersonateEmail}}" disabled>
</label>
{{end}}
{{range $i,$_:= .Session.ImpersonateGroups}}
<label>
{{if eq $i 0}}
<span>Impersonating Group</span>
{{else}}
<span></span>
{{end}}
<input type="text" class="field" value="{{.}}" title="{{.}}" disabled>
</label>
{{end}}
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">Sign Out</button>
</div>
</form>
{{if .IsAdmin}}
<form method="POST" action="/.pomerium/impersonate">
<section>
<h2>Sign-in-as</h2>
<p class="message">Administrators can temporarily impersonate another user.</p>
<fieldset>
<label>
<span>Email</span>
<input name="email" type="email" class="field" value="" placeholder="user@example.com">
</label>
<label>
<span>Group</span>
<input name="group" type="text" class="field" value="" placeholder="engineering">
</label>
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">Impersonate session</button>
</div>
</form>
{{ end }}
</div>
</div>
<footer>
<a href="https://www.pomerium.io" style="display: block;">
<svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 139 30"><defs><style>.a {fill: #6e43e8;}.a,.b {fill-rule: evenodd;}.b,.c {fill: #fff;}</style></defs><title>powered-by-pomerium</title><path class="a" d="M10.6,5.5H138.4c3.09,0,5.6,2,5.6,4.39V31.11c0,2.42-2.51,4.39-5.6,4.39H10.6c-3.09,0-5.6-2-5.6-4.39V9.89C5,7.47,7.51,5.5,10.6,5.5Z" transform="translate(-5 -5.5)" /><path class="b" d="M75.4,26.62H73.94l1.13-2.79-2.25-5.69h1.54L75.78,22l1.43-3.87h1.54Zm-5.61-2.44a2.42,2.42,0,0,1-1.5-.55V24H66.78V15.56h1.51v3a2.48,2.48,0,0,1,1.5-.55c1.58,0,2.66,1.28,2.66,3.09S71.37,24.18,69.79,24.18Zm-.32-4.88a1.68,1.68,0,0,0-1.18.53v2.52a1.65,1.65,0,0,0,1.18.54c.85,0,1.44-.73,1.44-1.8S70.32,19.3,69.47,19.3Zm-8.8,4.33a2.38,2.38,0,0,1-1.5.55c-1.57,0-2.66-1.27-2.66-3.09S57.6,18,59.17,18a2.44,2.44,0,0,1,1.5.55v-3h1.52V24H60.67Zm0-3.8a1.63,1.63,0,0,0-1.17-.53c-.86,0-1.45.73-1.45,1.79s.59,1.8,1.45,1.8a1.6,1.6,0,0,0,1.17-.54Zm-9,1.68A1.69,1.69,0,0,0,53.47,23a3.55,3.55,0,0,0,1.76-.56v1.26a4.73,4.73,0,0,1-2,.46,3,3,0,0,1-3-3.13A2.87,2.87,0,0,1,53.11,18,2.66,2.66,0,0,1,55.7,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34a1.38,1.38,0,0,0-1.37,1.36h2.57A1.28,1.28,0,0,0,53.05,19.17Zm-5.34.93V24H46.2v-5.9h1.51v.59A2,2,0,0,1,49.16,18a1.65,1.65,0,0,1,.49.06v1.35a1.83,1.83,0,0,0-.53-.07A1.87,1.87,0,0,0,47.71,20.1ZM41,21.51A1.69,1.69,0,0,0,42.76,23a3.55,3.55,0,0,0,1.76-.56v1.26a4.73,4.73,0,0,1-2,.46,3,3,0,0,1-3-3.13A2.87,2.87,0,0,1,42.4,18,2.66,2.66,0,0,1,45,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34A1.38,1.38,0,0,0,41,20.53h2.57A1.28,1.28,0,0,0,42.34,19.17ZM35.7,24l-1.2-4-1.2,4H32l-2-5.9h1.51l1.19,4,1.19-4h1.37l1.19,4,1.19-4h1.51l-2,5.9Zm-9.23.14a2.94,2.94,0,0,1-3-3.09,3,3,0,1,1,6.07,0A2.94,2.94,0,0,1,26.47,24.18Zm0-4.92c-.88,0-1.49.75-1.49,1.83s.61,1.83,1.49,1.83S28,22.18,28,21.09,27.35,19.26,26.47,19.26Zm-6.62,1.87H18.49V24H17V15.93h2.87a2.61,2.61,0,1,1,0,5.2Zm-.22-4H18.49V19.9h1.14a1.38,1.38,0,1,0,0-2.75Z" transform="translate(-5 -5.5)" /><path class="c" d="M132.71,14.9A3.93,3.93,0,0,0,128.78,11H94.59a3.93,3.93,0,0,0-3.93,3.92V31.06h2.71V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2.47ZM93.37,19a5.49,5.49,0,1,1,11,0Zm12.95,0a5.49,5.49,0,1,1,11,0Zm12.94,0a5.49,5.49,0,1,1,11,0Z" transform="translate(-5 -5.5)" /></svg>
</a>
</footer>
</div>
</body>
</html>
{{end}}`))
return t
}

View file

@ -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)
}
}

View file

@ -13,13 +13,13 @@ import (
"github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/middleware"
"github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/templates"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
) )
// registerDashboardHandlers returns the proxy service's ServeMux // registerDashboardHandlers returns the proxy service's ServeMux
func (p *Proxy) registerDashboardHandlers(r *mux.Router) *mux.Router { func (p *Proxy) registerDashboardHandlers(r *mux.Router) *mux.Router {
h := r.PathPrefix(dashboardURL).Subrouter() h := r.PathPrefix(dashboardURL).Subrouter()
h.Use(middleware.SetHeaders(httputil.HeadersContentSecurityPolicy))
// 1. Retrieve the user session and add it to the request context // 1. Retrieve the user session and add it to the request context
h.Use(sessions.RetrieveSession(p.sessionStore)) h.Use(sessions.RetrieveSession(p.sessionStore))
// 2. AuthN - Verify the user is authenticated. Set email, group, & id headers // 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 return
} }
templates.New().ExecuteTemplate(w, "dashboard.html", map[string]interface{}{ p.templates.ExecuteTemplate(w, "dashboard.html", map[string]interface{}{
"Session": session, "Session": session,
"IsAdmin": isAdmin, "IsAdmin": isAdmin,
"csrfField": csrf.TemplateField(r), "csrfField": csrf.TemplateField(r),

View file

@ -16,12 +16,12 @@ import (
"github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/cryptutil"
"github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/jws" "github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/frontend"
"github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/middleware"
"github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/telemetry/metrics" "github.com/pomerium/pomerium/internal/telemetry/metrics"
"github.com/pomerium/pomerium/internal/templates"
"github.com/pomerium/pomerium/internal/tripper" "github.com/pomerium/pomerium/internal/tripper"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/proxy/clients" "github.com/pomerium/pomerium/proxy/clients"
@ -132,7 +132,7 @@ func New(opts config.Options) (*Proxy, error) {
sessions.NewHeaderStore(encoder, "Pomerium"), sessions.NewHeaderStore(encoder, "Pomerium"),
sessions.NewQueryParamStore(encoder, "pomerium_session")}, sessions.NewQueryParamStore(encoder, "pomerium_session")},
signingKey: opts.SigningKey, signingKey: opts.SigningKey,
templates: templates.New(), templates: template.Must(frontend.NewTemplates()),
} }
// errors checked in ValidateOptions // errors checked in ValidateOptions
p.authorizeURL, _ = urlutil.DeepCopy(opts.AuthorizeURL) p.authorizeURL, _ = urlutil.DeepCopy(opts.AuthorizeURL)
@ -302,3 +302,7 @@ func (p *Proxy) roundTripperFromPolicy(policy *config.Policy) http.RoundTripper
} }
return c.Then(transport) return c.Then(transport)
} }
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p.Handler.ServeHTTP(w, r)
}

View file

@ -222,7 +222,7 @@ func Test_UpdateOptions(t *testing.T) {
if err == nil { if err == nil {
r := httptest.NewRequest("GET", tt.host, nil) r := httptest.NewRequest("GET", tt.host, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
p.Handler.ServeHTTP(w, r) p.ServeHTTP(w, r)
if tt.wantRoute && w.Code != http.StatusNotFound { if tt.wantRoute && w.Code != http.StatusNotFound {
t.Errorf("Failed to find route handler") t.Errorf("Failed to find route handler")
return return