Merge remote-tracking branch 'origin/main' into cdoxsey/remove-identity-provider

This commit is contained in:
Caleb Doxsey 2022-11-03 10:57:00 -06:00
commit aada204557
42 changed files with 587 additions and 415 deletions

View file

@ -83,14 +83,14 @@ jobs:
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
- name: Bump psql environment
uses: mikefarah/yq@1f0881fb5faf371694bfa108753cda0b824f5037
uses: mikefarah/yq@5e490527de24715db37869037083f7f391dde5a6
with:
cmd:
yq eval '.pomerium.image.tag = "${{ needs.publish.outputs.sha-tag }}"' -i
projects/pomerium-master-postgres/pomerium/values.yaml
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@fd157da78fa13d9383e5580d1fd1184d89554b51
uses: stefanzweifel/git-auto-commit-action@0049e3fa4059ca715255fbbcb7dea4516f02ce0a
with:
commit_message: |
Bump test environment pomerium/pomerium

View file

@ -122,12 +122,12 @@ jobs:
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
- name: Bump test environment
uses: mikefarah/yq@1f0881fb5faf371694bfa108753cda0b824f5037
uses: mikefarah/yq@5e490527de24715db37869037083f7f391dde5a6
with:
cmd: yq eval '.pomerium.image.tag = "${{ needs.goreleaser.outputs.tag }}"' -i projects/pomerium-demo/pomerium-demo/values.yaml
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@fd157da78fa13d9383e5580d1fd1184d89554b51
uses: stefanzweifel/git-auto-commit-action@0049e3fa4059ca715255fbbcb7dea4516f02ce0a
with:
commit_message: |
Bump test environment pomerium/pomerium

View file

@ -192,7 +192,7 @@ jobs:
make build
- name: save binary
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb
with:
path: bin/pomerium*
name: pomerium ${{ github.run_id }} ${{ matrix.platform }}
@ -252,7 +252,7 @@ jobs:
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: retrieve binary
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7
with:
name: pomerium ${{ github.run_id }} ${{ matrix.platform }}
path: bin/

View file

@ -13,7 +13,7 @@ RUN make yarn
COPY ./ui/ ./ui/
RUN make build-ui
FROM golang:1.19.2-buster@sha256:403f38941d7643bc91fad0227ebee6ddd80159b79fc339f6702271a2679a5f11 as build
FROM golang:1.19.2-buster@sha256:b4480899915b13370161a7e574174212b21baccade35b4df86358d76f83da7f9 as build
WORKDIR /go/src/github.com/pomerium/pomerium
RUN apt-get update \
@ -30,12 +30,12 @@ RUN make build-go NAME=pomerium
RUN touch /config.yaml
# build our own root trust store from current stable
FROM debian:stable@sha256:1b1d158efc589b1eb8858acdc95bd0ff4c5344958f834b7326662da8482b3e7d as casource
FROM debian:stable@sha256:9583740c100697a7dd186b80198a66ad24927e03437414559f90e0ed2639346e as casource
RUN apt-get update && apt-get install -y ca-certificates
# Remove expired root (https://github.com/pomerium/pomerium/issues/2653)
RUN rm /usr/share/ca-certificates/mozilla/DST_Root_CA_X3.crt && update-ca-certificates
FROM gcr.io/distroless/base:debug@sha256:9681f07e699fa65958ef9b902399d618cb6a4638868d468cf730a2bafd8f3dcc
FROM gcr.io/distroless/base:debug@sha256:856944e81ffb36babf549e52933e1c09379372135a9b73a97f275fa2973de13a
ENV AUTOCERT_DIR /data/autocert
WORKDIR /pomerium
COPY --from=build /go/src/github.com/pomerium/pomerium/bin/* /bin/

View file

@ -13,7 +13,7 @@ RUN make yarn
COPY ./ui/ ./ui/
RUN make build-ui
FROM golang:1.19.2-buster@sha256:403f38941d7643bc91fad0227ebee6ddd80159b79fc339f6702271a2679a5f11 as build
FROM golang:1.19.2-buster@sha256:b4480899915b13370161a7e574174212b21baccade35b4df86358d76f83da7f9 as build
WORKDIR /go/src/github.com/pomerium/pomerium
RUN apt-get update \

View file

@ -73,7 +73,6 @@ func (a *Authenticate) Mount(r *mux.Router) {
r.Path("/oauth2/callback").Handler(httputil.HandlerFunc(a.OAuthCallback)).Methods(http.MethodGet)
a.mountDashboard(r)
a.mountWellKnown(r)
}
func (a *Authenticate) mountDashboard(r *mux.Router) {
@ -111,22 +110,9 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
cr.Path("/").Handler(a.requireValidSignature(a.Callback)).Methods(http.MethodGet)
}
func (a *Authenticate) mountWellKnown(r *mux.Router) {
r.Path("/.well-known/pomerium/jwks.json").Handler(cors.AllowAll().Handler(httputil.HandlerFunc(a.jwks))).Methods(http.MethodGet)
}
// jwks returns the signing key(s) the client can use to validate signatures
// from the authorization server.
//
// https://tools.ietf.org/html/rfc8414
func (a *Authenticate) jwks(w http.ResponseWriter, r *http.Request) error {
httputil.RenderJSON(w, http.StatusOK, a.state.Load().jwk)
return nil
}
// RetrieveSession is the middleware used retrieve session by the sessionLoaders
// RetrieveSession is the middleware used retrieve session by the sessionLoader
func (a *Authenticate) RetrieveSession(next http.Handler) http.Handler {
return sessions.RetrieveSession(a.state.Load().sessionLoaders...)(next)
return sessions.RetrieveSession(a.state.Load().sessionLoader)(next)
}
// VerifySession is the middleware used to enforce a valid authentication

View file

@ -1,32 +0,0 @@
package webauthn
import "github.com/pomerium/pomerium/pkg/grpc/session"
func containsString(elements []string, value string) bool {
for _, element := range elements {
if element == value {
return true
}
}
return false
}
func removeString(elements []string, value string) []string {
dup := make([]string, 0, len(elements))
for _, element := range elements {
if element != value {
dup = append(dup, element)
}
}
return dup
}
func removeSessionDeviceCredential(elements []*session.Session_DeviceCredential, id string) []*session.Session_DeviceCredential {
dup := make([]*session.Session_DeviceCredential, 0, len(elements))
for _, element := range elements {
if element.GetId() != id {
dup = append(dup, element)
}
}
return dup
}

View file

@ -324,7 +324,7 @@ func (h *Handler) handleRegister(w http.ResponseWriter, r *http.Request, state *
}
// save the user
u.DeviceCredentialIds = append(u.DeviceCredentialIds, deviceCredential.GetId())
u.AddDeviceCredentialID(deviceCredential.GetId())
_, err = databroker.Put(ctx, state.Client, u)
if err != nil {
return err
@ -356,7 +356,7 @@ func (h *Handler) handleUnregister(w http.ResponseWriter, r *http.Request, state
}
// ensure we only allow removing a device credential the user owns
if !containsString(u.GetDeviceCredentialIds(), deviceCredentialID) {
if !u.HasDeviceCredentialID(deviceCredentialID) {
return errInvalidDeviceCredential
}
@ -373,14 +373,14 @@ func (h *Handler) handleUnregister(w http.ResponseWriter, r *http.Request, state
}
// remove the credential from the user
u.DeviceCredentialIds = removeString(u.DeviceCredentialIds, deviceCredentialID)
u.RemoveDeviceCredentialID(deviceCredentialID)
_, err = databroker.Put(ctx, state.Client, u)
if err != nil {
return err
}
// remove the credential from the session
state.Session.DeviceCredentials = removeSessionDeviceCredential(state.Session.DeviceCredentials, deviceCredentialID)
state.Session.RemoveDeviceCredentialID(deviceCredentialID)
return h.saveSessionAndRedirect(w, r, state, urlutil.GetAbsoluteURL(r).ResolveReference(&url.URL{
Path: "/.pomerium",
}).String())

View file

@ -289,7 +289,7 @@ func TestAuthenticate_SignOut(t *testing.T) {
identity.MockProvider{LogOutResponse: (*uriParseHelper("https://microsoft.com"))},
&mstore.Store{Encrypted: true, Session: &sessions.State{}},
http.StatusOK,
"{\"Status\":200,\"Error\":\"OK: user logged out\"}\n",
"{\"Status\":200}\n",
},
}
for _, tt := range tests {
@ -587,27 +587,6 @@ func TestAuthenticate_SessionValidatorMiddleware(t *testing.T) {
}
}
func TestJwksEndpoint(t *testing.T) {
o := newTestOptions(t)
o.SigningKey = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUpCMFZkbko1VjEvbVlpYUlIWHhnd2Q0Yzd5YWRTeXMxb3Y0bzA1b0F3ekdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUc1eENQMEpUVDFINklvbDhqS3VUSVBWTE0wNENnVzlQbEV5cE5SbVdsb29LRVhSOUhUMwpPYnp6aktZaWN6YjArMUt3VjJmTVRFMTh1dy82MXJVQ0JBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
auth, err := New(&config.Config{Options: o})
if err != nil {
t.Fatal(err)
return
}
h := auth.Handler()
if h == nil {
t.Error("handler cannot be nil")
}
req := httptest.NewRequest("GET", "/.well-known/pomerium/jwks.json", nil)
req.Header.Set("Accept", "application/json")
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
body := rr.Body.String()
expected := "{\"keys\":[{\"use\":\"sig\",\"kty\":\"EC\",\"kid\":\"5b419ade1895fec2d2def6cd33b1b9a018df60db231dc5ecb85cbed6d942813c\",\"crv\":\"P-256\",\"alg\":\"ES256\",\"x\":\"UG5xCP0JTT1H6Iol8jKuTIPVLM04CgW9PlEypNRmWlo\",\"y\":\"KChF0fR09zm884ymInM29PtSsFdnzExNfLsP-ta1AgQ\"}]}\n"
assert.Equal(t, expected, body)
}
func TestAuthenticate_userInfo(t *testing.T) {
t.Parallel()

View file

@ -41,7 +41,7 @@ type authenticateState struct {
sessionStore sessions.SessionStore
// sessionLoaders are a collection of session loaders to attempt to pull
// a user's session state from
sessionLoaders []sessions.SessionLoader
sessionLoader sessions.SessionLoader
jwk *jose.JSONWebKeySet
@ -118,7 +118,7 @@ func newAuthenticateStateFromConfig(cfg *config.Config) (*authenticateState, err
}
state.sessionStore = cookieStore
state.sessionLoaders = []sessions.SessionLoader{cookieStore}
state.sessionLoader = cookieStore
state.jwk = new(jose.JSONWebKeySet)
signingKey, err := cfg.Options.GetSigningKey()
if err != nil {

View file

@ -19,7 +19,6 @@ import (
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/atomicutil"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/testutil"
"github.com/pomerium/pomerium/pkg/policy/criteria"
)
@ -68,8 +67,6 @@ func TestAuthorize_okResponse(t *testing.T) {
JWTClaimsHeaders: config.NewJWTClaimHeaders("email"),
}
a := &Authorize{currentOptions: config.NewAtomicOptions(), state: atomicutil.NewValue(new(authorizeState))}
encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0})
a.state.Load().encoder = encoder
a.currentOptions.Store(opt)
a.store = store.New()
pe, err := newPolicyEvaluator(opt, a.store)
@ -124,8 +121,6 @@ func TestAuthorize_okResponse(t *testing.T) {
func TestAuthorize_deniedResponse(t *testing.T) {
a := &Authorize{currentOptions: config.NewAtomicOptions(), state: atomicutil.NewValue(new(authorizeState))}
encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0})
a.state.Load().encoder = encoder
a.currentOptions.Store(&config.Options{
Policies: []config.Policy{{
Source: &config.StringURL{URL: &url.URL{Host: "example.com"}},

View file

@ -55,8 +55,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
}
}
rawJWT, _ := loadRawSession(hreq, a.currentOptions.Load(), state.encoder)
sessionState, _ := loadSession(state.encoder, rawJWT)
sessionState, _ := state.sessionStore.LoadSessionState(hreq)
var s sessionOrServiceAccount
var u *user.User

View file

@ -16,7 +16,6 @@ import (
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/atomicutil"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
@ -48,8 +47,6 @@ yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
func Test_getEvaluatorRequest(t *testing.T) {
a := &Authorize{currentOptions: config.NewAtomicOptions(), state: atomicutil.NewValue(new(authorizeState))}
encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0})
a.state.Load().encoder = encoder
a.currentOptions.Store(&config.Options{
Policies: []config.Policy{{
Source: &config.StringURL{URL: &url.URL{Host: "example.com"}},
@ -262,8 +259,6 @@ func Test_handleForwardAuth(t *testing.T) {
func Test_getEvaluatorRequestWithPortInHostHeader(t *testing.T) {
a := &Authorize{currentOptions: config.NewAtomicOptions(), state: atomicutil.NewValue(new(authorizeState))}
encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0})
a.state.Load().encoder = encoder
a.currentOptions.Store(&config.Options{
Policies: []config.Policy{{
Source: &config.StringURL{URL: &url.URL{Host: "example.com"}},

View file

@ -1,63 +0,0 @@
package authorize
import (
"errors"
"net/http"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/sessions/cookie"
"github.com/pomerium/pomerium/internal/sessions/header"
"github.com/pomerium/pomerium/internal/sessions/queryparam"
"github.com/pomerium/pomerium/internal/urlutil"
)
func loadRawSession(req *http.Request, options *config.Options, encoder encoding.MarshalUnmarshaler) ([]byte, error) {
var loaders []sessions.SessionLoader
cookieStore, err := getCookieStore(options, encoder)
if err != nil {
return nil, err
}
loaders = append(loaders,
cookieStore,
header.NewStore(encoder),
queryparam.NewStore(encoder, urlutil.QuerySession),
)
for _, loader := range loaders {
sess, err := loader.LoadSession(req)
if err != nil && !errors.Is(err, sessions.ErrNoSessionFound) {
return nil, err
} else if err == nil {
return []byte(sess), nil
}
}
return nil, sessions.ErrNoSessionFound
}
func loadSession(encoder encoding.MarshalUnmarshaler, rawJWT []byte) (*sessions.State, error) {
var s sessions.State
err := encoder.Unmarshal(rawJWT, &s)
if err != nil {
return nil, err
}
return &s, nil
}
func getCookieStore(options *config.Options, encoder encoding.MarshalUnmarshaler) (sessions.SessionStore, error) {
cookieStore, err := cookie.NewStore(func() cookie.Options {
return cookie.Options{
Name: options.CookieName,
Domain: options.CookieDomain,
Secure: options.CookieSecure,
HTTPOnly: options.CookieHTTPOnly,
Expire: options.CookieExpire,
}
}, encoder)
if err != nil {
return nil, err
}
return cookieStore, nil
}

View file

@ -1,76 +0,0 @@
package authorize
import (
"net/url"
"testing"
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
"github.com/stretchr/testify/assert"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/sessions"
)
func TestLoadSession(t *testing.T) {
opts := config.NewDefaultOptions()
encoder, err := jws.NewHS256Signer(nil)
if !assert.NoError(t, err) {
return
}
state := &sessions.State{ID: "xyz"}
rawjwt, err := encoder.Marshal(state)
if !assert.NoError(t, err) {
return
}
load := func(t *testing.T, hattrs *envoy_service_auth_v3.AttributeContext_HttpRequest) (*sessions.State, error) {
req := getHTTPRequestFromCheckRequest(&envoy_service_auth_v3.CheckRequest{
Attributes: &envoy_service_auth_v3.AttributeContext{
Request: &envoy_service_auth_v3.AttributeContext_Request{
Http: hattrs,
},
},
})
raw, err := loadRawSession(req, opts, encoder)
if err != nil {
return nil, err
}
var state sessions.State
err = encoder.Unmarshal(raw, &state)
if err != nil {
return nil, err
}
return &state, nil
}
t.Run("header", func(t *testing.T) {
hattrs := &envoy_service_auth_v3.AttributeContext_HttpRequest{
Id: "req-1",
Method: "GET",
Headers: map[string]string{
"Authorization": "Pomerium " + string(rawjwt),
},
Path: "/hello/world",
Host: "example.com",
Scheme: "https",
}
sess, err := load(t, hattrs)
assert.NoError(t, err)
assert.NotNil(t, sess)
})
t.Run("query param", func(t *testing.T) {
hattrs := &envoy_service_auth_v3.AttributeContext_HttpRequest{
Id: "req-1",
Method: "GET",
Path: "/hello/world?" + url.Values{
"pomerium_session": []string{string(rawjwt)},
}.Encode(),
Host: "example.com",
Scheme: "https",
}
sess, err := load(t, hattrs)
assert.NoError(t, err)
assert.NotNil(t, sess)
})
}

View file

@ -9,8 +9,6 @@ import (
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/pkg/grpc"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/protoutil"
@ -21,10 +19,10 @@ var outboundGRPCConnection = new(grpc.CachedOutboundGRPClientConn)
type authorizeState struct {
sharedKey []byte
evaluator *evaluator.Evaluator
encoder encoding.MarshalUnmarshaler
dataBrokerClientConnection *googlegrpc.ClientConn
dataBrokerClient databroker.DataBrokerServiceClient
auditEncryptor *protoutil.Encryptor
sessionStore *config.SessionStore
}
func newAuthorizeStateFromConfig(cfg *config.Config, store *store.Store) (*authorizeState, error) {
@ -46,11 +44,6 @@ func newAuthorizeStateFromConfig(cfg *config.Config, store *store.Store) (*autho
return nil, err
}
state.encoder, err = jws.NewHS256Signer(state.sharedKey)
if err != nil {
return nil, err
}
sharedKey, err := cfg.Options.GetSharedKey()
if err != nil {
return nil, err
@ -76,5 +69,10 @@ func newAuthorizeStateFromConfig(cfg *config.Config, store *store.Store) (*autho
state.auditEncryptor = protoutil.NewEncryptor(auditKey)
}
state.sessionStore, err = config.NewSessionStore(cfg.Options)
if err != nil {
return nil, fmt.Errorf("authorize: invalid session store: %w", err)
}
return state, nil
}

View file

@ -67,7 +67,7 @@ func (b *Builder) BuildClusters(ctx context.Context, cfg *config.Config) ([]*env
}
if len(authorizeURLs) > 1 {
authorizeCluster.HealthChecks = grpcHealthChecks("pomerium-authorize")
authorizeCluster.OutlierDetection = grpcAuthorizeOutlierDetection()
authorizeCluster.OutlierDetection = grpcOutlierDetection()
}
databrokerCluster, err := b.buildInternalCluster(ctx, cfg.Options, "pomerium-databroker", databrokerURLs, upstreamProtocolHTTP2)
@ -75,8 +75,8 @@ func (b *Builder) BuildClusters(ctx context.Context, cfg *config.Config) ([]*env
return nil, err
}
if len(databrokerURLs) > 1 {
authorizeCluster.HealthChecks = grpcHealthChecks("pomerium-databroker")
authorizeCluster.OutlierDetection = grpcAuthorizeOutlierDetection()
databrokerCluster.HealthChecks = grpcHealthChecks("pomerium-databroker")
databrokerCluster.OutlierDetection = grpcOutlierDetection()
}
envoyAdminCluster, err := b.buildEnvoyAdminCluster(ctx, cfg)
@ -406,8 +406,8 @@ func (b *Builder) buildCluster(
return cluster.Validate()
}
// grpcAuthorizeOutlierDetection defines slightly more aggressive malfunction detection for authorize endpoints
func grpcAuthorizeOutlierDetection() *envoy_config_cluster_v3.OutlierDetection {
// grpcOutlierDetection defines slightly more aggressive malfunction detection for grpc endpoints
func grpcOutlierDetection() *envoy_config_cluster_v3.OutlierDetection {
return &envoy_config_cluster_v3.OutlierDetection{
Consecutive_5Xx: wrapperspb.UInt32(5),
Interval: durationpb.New(time.Second * 10),

View file

@ -1,14 +1,16 @@
package config
import (
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/grpc/identity"
)
// GetIdentityProviderForID returns the identity provider associated with the given IDP id.
// If none is found the default provider is returned.
func (o *Options) GetIdentityProviderForID(idpID string) (*identity.Provider, error) {
for _, policy := range o.GetAllPolicies() {
idp, err := o.GetIdentityProviderForPolicy(&policy) //nolint
for _, p := range o.GetAllPolicies() {
p := p
idp, err := o.GetIdentityProviderForPolicy(&p)
if err != nil {
return nil, err
}
@ -47,3 +49,19 @@ func (o *Options) GetIdentityProviderForPolicy(policy *Policy) (*identity.Provid
idp.Id = idp.Hash()
return idp, nil
}
// GetIdentityProviderForRequestURL gets the identity provider associated with the given request URL.
func (o *Options) GetIdentityProviderForRequestURL(requestURL string) (*identity.Provider, error) {
u, err := urlutil.ParseAndValidateURL(requestURL)
if err != nil {
return nil, err
}
for _, p := range o.GetAllPolicies() {
p := p
if p.Matches(*u) {
return o.GetIdentityProviderForPolicy(&p)
}
}
return o.GetIdentityProviderForPolicy(nil)
}

View file

@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"time"
@ -384,10 +385,15 @@ func optionsFromViper(configFile string) (*Options, error) {
return o, nil
}
var (
// policy's embedded protobuf structs are decoded by separate hook and are unknown to mapstructure
routesEmbeddedFieldsRe = regexp.MustCompile(`(routes|policy)\[\.*`)
)
func checkUnusedConfigFields(configFile string, unused []string) {
keys := make([]string, 0, len(unused))
for _, k := range unused {
if !strings.HasPrefix(k, "policy[") { // policy's embedded protobuf structs are decoded by separate hook and are unknown to mapstructure
if !routesEmbeddedFieldsRe.MatchString(k) {
keys = append(keys, k)
}
}

85
config/session.go Normal file
View file

@ -0,0 +1,85 @@
package config
import (
"fmt"
"net/http"
"github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/sessions/cookie"
"github.com/pomerium/pomerium/internal/sessions/header"
"github.com/pomerium/pomerium/internal/sessions/queryparam"
"github.com/pomerium/pomerium/internal/urlutil"
)
// A SessionStore saves and loads sessions based on the options.
type SessionStore struct {
options *Options
encoder encoding.MarshalUnmarshaler
loader sessions.SessionLoader
}
// NewSessionStore creates a new SessionStore from the Options.
func NewSessionStore(options *Options) (*SessionStore, error) {
store := &SessionStore{
options: options,
}
sharedKey, err := options.GetSharedKey()
if err != nil {
return nil, fmt.Errorf("config/sessions: shared_key is required: %w", err)
}
store.encoder, err = jws.NewHS256Signer(sharedKey)
if err != nil {
return nil, fmt.Errorf("config/sessions: invalid session encoder: %w", err)
}
cookieStore, err := cookie.NewStore(func() cookie.Options {
return cookie.Options{
Name: options.CookieName,
Domain: options.CookieDomain,
Secure: options.CookieSecure,
HTTPOnly: options.CookieHTTPOnly,
Expire: options.CookieExpire,
}
}, store.encoder)
if err != nil {
return nil, err
}
headerStore := header.NewStore(store.encoder)
queryParamStore := queryparam.NewStore(store.encoder, urlutil.QuerySession)
store.loader = sessions.MultiSessionLoader(cookieStore, headerStore, queryParamStore)
return store, nil
}
// LoadSessionState loads the session state from a request.
func (store *SessionStore) LoadSessionState(r *http.Request) (*sessions.State, error) {
rawJWT, err := store.loader.LoadSession(r)
if err != nil {
return nil, err
}
var state sessions.State
err = store.encoder.Unmarshal([]byte(rawJWT), &state)
if err != nil {
return nil, err
}
// confirm that the identity provider id matches the state
if state.IdentityProviderID != "" {
idp, err := store.options.GetIdentityProviderForRequestURL(urlutil.GetAbsoluteURL(r).String())
if err != nil {
return nil, err
}
if idp.GetId() != state.IdentityProviderID {
return nil, fmt.Errorf("unexpected session state identity provider id: %s != %s",
idp.GetId(), state.IdentityProviderID)
}
}
return &state, nil
}

144
config/session_test.go Normal file
View file

@ -0,0 +1,144 @@
package config
import (
"encoding/base64"
"net/http"
"net/url"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
func TestSessionStore_LoadSessionState(t *testing.T) {
t.Parallel()
sharedKey := cryptutil.NewKey()
options := NewDefaultOptions()
options.SharedKey = base64.StdEncoding.EncodeToString(sharedKey)
options.Provider = "oidc"
options.ProviderURL = "https://oidc.example.com"
options.ClientID = "client_id"
options.ClientSecret = "client_secret"
options.Policies = append(options.Policies,
Policy{
From: "https://p1.example.com",
To: mustParseWeightedURLs(t, "https://p1"),
IDPClientID: "client_id_1",
IDPClientSecret: "client_secret_1",
},
Policy{
From: "https://p2.example.com",
To: mustParseWeightedURLs(t, "https://p2"),
IDPClientID: "client_id_2",
IDPClientSecret: "client_secret_2",
})
require.NoError(t, options.Validate())
store, err := NewSessionStore(options)
require.NoError(t, err)
idp1, err := options.GetIdentityProviderForPolicy(nil)
require.NoError(t, err)
require.NotNil(t, idp1)
idp2, err := options.GetIdentityProviderForPolicy(&options.Policies[0])
require.NoError(t, err)
require.NotNil(t, idp2)
idp3, err := options.GetIdentityProviderForPolicy(&options.Policies[1])
require.NoError(t, err)
require.NotNil(t, idp3)
makeJWS := func(t *testing.T, state *sessions.State) string {
e, err := jws.NewHS256Signer(sharedKey)
require.NoError(t, err)
rawJWS, err := e.Marshal(state)
require.NoError(t, err)
return string(rawJWS)
}
t.Run("mssing", func(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "https://p1.example.com", nil)
require.NoError(t, err)
s, err := store.LoadSessionState(r)
assert.ErrorIs(t, err, sessions.ErrNoSessionFound)
assert.Nil(t, s)
})
t.Run("query", func(t *testing.T) {
rawJWS := makeJWS(t, &sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
IdentityProviderID: idp2.GetId(),
})
r, err := http.NewRequest(http.MethodGet, "https://p1.example.com?"+url.Values{
urlutil.QuerySession: {rawJWS},
}.Encode(), nil)
require.NoError(t, err)
s, err := store.LoadSessionState(r)
assert.NoError(t, err)
assert.Empty(t, cmp.Diff(&sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
IdentityProviderID: idp2.GetId(),
}, s))
})
t.Run("header", func(t *testing.T) {
rawJWS := makeJWS(t, &sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
IdentityProviderID: idp3.GetId(),
})
r, err := http.NewRequest(http.MethodGet, "https://p2.example.com", nil)
require.NoError(t, err)
r.Header.Set(httputil.HeaderPomeriumAuthorization, rawJWS)
s, err := store.LoadSessionState(r)
assert.NoError(t, err)
assert.Empty(t, cmp.Diff(&sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
IdentityProviderID: idp3.GetId(),
}, s))
})
t.Run("wrong idp", func(t *testing.T) {
rawJWS := makeJWS(t, &sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
IdentityProviderID: idp1.GetId(),
})
r, err := http.NewRequest(http.MethodGet, "https://p2.example.com", nil)
require.NoError(t, err)
r.Header.Set(httputil.HeaderPomeriumAuthorization, rawJWS)
s, err := store.LoadSessionState(r)
assert.Error(t, err)
assert.Nil(t, s)
})
t.Run("blank idp", func(t *testing.T) {
rawJWS := makeJWS(t, &sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
})
r, err := http.NewRequest(http.MethodGet, "https://p2.example.com", nil)
require.NoError(t, err)
r.Header.Set(httputil.HeaderPomeriumAuthorization, rawJWS)
s, err := store.LoadSessionState(r)
assert.NoError(t, err)
assert.Empty(t, cmp.Diff(&sessions.State{
Issuer: "authenticate.example.com",
ID: "example",
}, s))
})
}

48
go.mod
View file

@ -14,19 +14,19 @@ require (
github.com/cespare/xxhash/v2 v2.1.2
github.com/client9/misspell v0.3.4
github.com/coreos/go-oidc/v3 v3.4.0
github.com/docker/docker v20.10.19+incompatible
github.com/docker/docker v20.10.21+incompatible
github.com/envoyproxy/go-control-plane v0.10.3-0.20220819153403-8a9be01c9575
github.com/envoyproxy/protoc-gen-validate v0.6.13
github.com/fsnotify/fsnotify v1.5.4
github.com/fsnotify/fsnotify v1.6.0
github.com/go-chi/chi/v5 v5.0.7
github.com/go-jose/go-jose/v3 v3.0.0
github.com/go-redis/redis/v8 v8.11.5
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
github.com/golangci/golangci-lint v1.50.0
github.com/golangci/golangci-lint v1.50.1
github.com/google/btree v1.1.2
github.com/google/go-cmp v0.5.9
github.com/google/go-jsonnet v0.18.0
github.com/google/go-jsonnet v0.19.1
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
@ -48,26 +48,26 @@ require (
github.com/pomerium/csrf v1.7.0
github.com/pomerium/webauthn v0.0.0-20211014213840-422c7ce1077f
github.com/prometheus/client_golang v1.13.0
github.com/prometheus/client_model v0.2.0
github.com/prometheus/client_model v0.3.0
github.com/prometheus/common v0.37.0
github.com/prometheus/procfs v0.8.0
github.com/rs/cors v1.8.2
github.com/rs/zerolog v1.28.0
github.com/shirou/gopsutil/v3 v3.22.9
github.com/spf13/viper v1.13.0
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f
github.com/volatiletech/null/v9 v9.0.0
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
go.opencensus.io v0.23.0
go.uber.org/zap v1.23.0
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2
golang.org/x/crypto v0.1.0
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0
google.golang.org/api v0.99.0
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e
golang.org/x/net v0.1.0
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783
golang.org/x/sync v0.1.0
google.golang.org/api v0.101.0
google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55
google.golang.org/grpc v1.50.1
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
@ -82,7 +82,7 @@ require (
github.com/Antonboom/errname v0.1.7 // indirect
github.com/Antonboom/nilnil v0.1.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/BurntSushi/toml v1.2.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/DataDog/datadog-go v3.5.0+incompatible // indirect
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
@ -110,7 +110,7 @@ require (
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/curioswitch/go-reassign v0.2.0 // indirect
github.com/daixiang0/gci v0.8.0 // indirect
github.com/daixiang0/gci v0.8.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingaikin/go-header v0.4.3 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
@ -168,7 +168,7 @@ require (
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@ -181,7 +181,7 @@ require (
github.com/julz/importas v0.1.0 // indirect
github.com/kisielk/errcheck v1.6.2 // indirect
github.com/kisielk/gotool v1.0.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.2 // indirect
github.com/kkHAIKE/contextcheck v1.1.3 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/kulti/thelper v0.6.3 // indirect
@ -246,12 +246,12 @@ require (
github.com/sourcegraph/go-diff v0.6.1 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/cobra v1.6.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
github.com/stretchr/objx v0.4.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tdakkota/asciicheck v0.1.1 // indirect
@ -261,8 +261,8 @@ require (
github.com/tinylib/msgp v1.1.2 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.6.2 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.7.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
github.com/ultraware/funlen v0.0.3 // indirect
github.com/ultraware/whitespace v0.0.5 // indirect
@ -281,10 +281,10 @@ require (
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.22.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

92
go.sum
View file

@ -77,8 +77,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CAFxX/httpcompression v0.0.8 h1:UBWojERnpCS6X7whJkGGZeCC3ruZBRwkwkcnfGfb0ko=
github.com/CAFxX/httpcompression v0.0.8/go.mod h1:bVd1taHK1vYb5SWe9lwNDCqrfj2ka+C1Zx7JHzxuHnU=
@ -211,8 +211,8 @@ github.com/cristalhq/acmd v0.8.1/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBj
github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/daixiang0/gci v0.8.0 h1:DzWYUm4+bc+taVUtuq1tsIMb/QFMMYgDIiykSoO98ZU=
github.com/daixiang0/gci v0.8.0/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
github.com/daixiang0/gci v0.8.1 h1:T4xpSC+hmsi4CSyuYfIJdMZAr9o7xZmHpQVygMghGZ4=
github.com/daixiang0/gci v0.8.1/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -230,8 +230,8 @@ github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32Paq
github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64=
github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog=
github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@ -260,7 +260,7 @@ github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStB
github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw=
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
@ -275,8 +275,9 @@ github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
@ -393,8 +394,8 @@ github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY=
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs=
github.com/golangci/golangci-lint v1.50.0 h1:+Xmyt8rKLauNLp2gzcxKMN8VNGqGc5Avc2ZLTwIOpEA=
github.com/golangci/golangci-lint v1.50.0/go.mod h1:UqtDvK24R9OizqRF06foPX8opRMzQB0HQK90uI2JgKc=
github.com/golangci/golangci-lint v1.50.1 h1:C829clMcZXEORakZlwpk7M4iDw2XiwxxKaG504SZ9zY=
github.com/golangci/golangci-lint v1.50.1/go.mod h1:AQjHBopYS//oB8xs0y0M/dtxdKHkdhl0RvmjUct0/4w=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
@ -428,8 +429,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg=
github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0=
github.com/google/go-jsonnet v0.19.1 h1:MORxkrG0elylUqh36R4AcSPX0oZQa9hvI3lroN+kDhs=
github.com/google/go-jsonnet v0.19.1/go.mod h1:5JVT33JVCoehdTj5Z2KJq1eIdt3Nb8PCmZ+W5D8U350=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.2 h1:3iQQ2dlEf+1no7CLlfLPYzxhQy7j2G/emBqU5okydaw=
@ -530,8 +531,9 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@ -605,8 +607,8 @@ github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7
github.com/kisielk/errcheck v1.6.2/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkHAIKE/contextcheck v1.1.2 h1:BYUSG/GhMhqVz//yjl8IkBDlMEws+9DtCmkz18QO1gg=
github.com/kkHAIKE/contextcheck v1.1.2/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo=
github.com/kkHAIKE/contextcheck v1.1.3 h1:l4pNvrb8JSwRd51ojtcOxOeHJzHek+MtOyXbaR0uvmw=
github.com/kkHAIKE/contextcheck v1.1.3/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo=
github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
@ -801,8 +803,9 @@ github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@ -912,8 +915,8 @@ github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@ -932,8 +935,9 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -942,8 +946,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@ -971,10 +976,10 @@ github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hM
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f h1:C43EMGXFtvYf/zunHR6ivZV7Z6ytg73t0GXwYyicXMQ=
github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f/go.mod h1:N+sR0vLSCTtI6o06PMWsjMB4TVqqDttKNq4iC9wvxVY=
github.com/tomarrell/wrapcheck/v2 v2.6.2 h1:3dI6YNcrJTQ/CJQ6M/DUkc0gnqYSIk6o0rChn9E/D0M=
github.com/tomarrell/wrapcheck/v2 v2.6.2/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg=
github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s=
github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/tomarrell/wrapcheck/v2 v2.7.0 h1:J/F8DbSKJC83bAvC6FoZaRjZiZ/iKoueSdrEkmGeacA=
github.com/tomarrell/wrapcheck/v2 v2.7.0/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg=
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@ -1072,8 +1077,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI=
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1118,8 +1123,9 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1178,8 +1184,8 @@ golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 h1:MgJ6t2zo8v0tbmLCueaCbF1RM+TtB0rs3Lv8DGtOIpY=
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1201,8 +1207,8 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1 h1:3VPzK7eqH25j7GYw5w6g/GzNRc0/fYtrxz27z1gD4W0=
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1216,8 +1222,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1318,8 +1324,9 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908150016-7ac13a9a928d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1332,8 +1339,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1429,8 +1436,9 @@ golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlz
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1480,8 +1488,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.99.0 h1:tsBtOIklCE2OFxhmcYSVqGwSAN/Y897srxmcvAQnwK8=
google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=
google.golang.org/api v0.101.0 h1:lJPPeEBIRxGpGLwnBTam1NPEM8Z2BmmXEd3z812pjwM=
google.golang.org/api v0.101.0/go.mod h1:CjxAAWWt3A3VrUE2IGDY2bgK5qhoG/OkyWVlYcP05MY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1571,8 +1579,8 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg=
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 h1:U1u4KB2kx6KR/aJDjQ97hZ15wQs8ZPvDcGcRynBhkvg=
google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -2,15 +2,14 @@
package controlplane
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/CAFxX/httpcompression"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/pomerium/csrf"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log"
@ -47,32 +46,23 @@ func (srv *Server) addHTTPMiddleware(root *mux.Router, cfg *config.Config) {
root.Use(telemetry.HTTPStatsHandler(func() string {
return srv.currentConfig.Load().Options.InstallationID
}, srv.name))
root.HandleFunc("/healthz", httputil.HealthCheck)
root.HandleFunc("/ping", httputil.HealthCheck)
root.Handle("/.well-known/pomerium", httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return wellKnownPomerium(w, r, cfg)
}))
root.Handle("/.well-known/pomerium/", httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return wellKnownPomerium(w, r, cfg)
}))
}
func wellKnownPomerium(w http.ResponseWriter, r *http.Request, cfg *config.Config) error {
func (srv *Server) mountCommonEndpoints(root *mux.Router, cfg *config.Config) error {
authenticateURL, err := cfg.Options.GetAuthenticateURL()
if err != nil {
return err
return fmt.Errorf("invalid authenticate URL: %w", err)
}
wellKnownURLs := struct {
OAuth2Callback string `json:"authentication_callback_endpoint"` // RFC6749
JSONWebKeySetURL string `json:"jwks_uri"` // RFC7517
FrontchannelLogoutURI string `json:"frontchannel_logout_uri"` // https://openid.net/specs/openid-connect-frontchannel-1_0.html
}{
authenticateURL.ResolveReference(&url.URL{Path: "/oauth2/callback"}).String(),
authenticateURL.ResolveReference(&url.URL{Path: "/.well-known/pomerium/jwks.json"}).String(),
authenticateURL.ResolveReference(&url.URL{Path: "/.pomerium/sign_out"}).String(),
rawSigningKey, err := cfg.Options.GetSigningKey()
if err != nil {
return fmt.Errorf("invalid signing key: %w", err)
}
w.Header().Set("X-CSRF-Token", csrf.Token(r))
httputil.RenderJSON(w, http.StatusOK, wellKnownURLs)
root.HandleFunc("/healthz", httputil.HealthCheck)
root.HandleFunc("/ping", httputil.HealthCheck)
root.Handle("/.well-known/pomerium", httputil.WellKnownPomeriumHandler(authenticateURL))
root.Handle("/.well-known/pomerium/", httputil.WellKnownPomeriumHandler(authenticateURL))
root.Path("/.well-known/pomerium/jwks.json").Methods(http.MethodGet).Handler(httputil.JWKSHandler(rawSigningKey))
return nil
}

View file

@ -285,6 +285,9 @@ func (srv *Server) EnableProxy(svc Service) error {
func (srv *Server) updateRouter(cfg *config.Config) error {
httpRouter := mux.NewRouter()
srv.addHTTPMiddleware(httpRouter, cfg)
if err := srv.mountCommonEndpoints(httpRouter, cfg); err != nil {
return err
}
if srv.authenticateSvc != nil {
authenticateURL, err := cfg.Options.GetInternalAuthenticateURL()
if err != nil {

View file

@ -15,7 +15,7 @@ import (
"github.com/pomerium/pomerium/pkg/netutil"
)
func TestServerWellKnown(t *testing.T) {
func TestServerHTTP(t *testing.T) {
ports, err := netutil.AllocatePorts(5)
require.NoError(t, err)
@ -33,23 +33,49 @@ func TestServerWellKnown(t *testing.T) {
Options: config.NewDefaultOptions(),
}
cfg.Options.AuthenticateURLString = "https://authenticate.localhost.pomerium.io"
cfg.Options.SigningKey = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUpCMFZkbko1VjEvbVlpYUlIWHhnd2Q0Yzd5YWRTeXMxb3Y0bzA1b0F3ekdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUc1eENQMEpUVDFINklvbDhqS3VUSVBWTE0wNENnVzlQbEV5cE5SbVdsb29LRVhSOUhUMwpPYnp6aktZaWN6YjArMUt3VjJmTVRFMTh1dy82MXJVQ0JBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
src := config.NewStaticSource(cfg)
srv, err := NewServer(cfg, config.NewMetricsManager(ctx, src), events.New())
require.NoError(t, err)
go srv.Run(ctx)
res, err := http.Get(fmt.Sprintf("http://localhost:%s/.well-known/pomerium", src.GetConfig().HTTPPort))
require.NoError(t, err)
defer res.Body.Close()
t.Run("well-known", func(t *testing.T) {
res, err := http.Get(fmt.Sprintf("http://localhost:%s/.well-known/pomerium", src.GetConfig().HTTPPort))
require.NoError(t, err)
defer res.Body.Close()
var actual map[string]any
err = json.NewDecoder(res.Body).Decode(&actual)
require.NoError(t, err)
var actual map[string]any
err = json.NewDecoder(res.Body).Decode(&actual)
require.NoError(t, err)
expect := map[string]any{
"authentication_callback_endpoint": "https://authenticate.localhost.pomerium.io/oauth2/callback",
"frontchannel_logout_uri": "https://authenticate.localhost.pomerium.io/.pomerium/sign_out",
"jwks_uri": "https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json",
}
assert.Equal(t, expect, actual)
expect := map[string]any{
"authentication_callback_endpoint": "https://authenticate.localhost.pomerium.io/oauth2/callback",
"frontchannel_logout_uri": "https://authenticate.localhost.pomerium.io/.pomerium/sign_out",
"jwks_uri": "https://authenticate.localhost.pomerium.io/.well-known/pomerium/jwks.json",
}
assert.Equal(t, expect, actual)
})
t.Run("jwks", func(t *testing.T) {
res, err := http.Get(fmt.Sprintf("http://localhost:%s/.well-known/pomerium/jwks.json", src.GetConfig().HTTPPort))
require.NoError(t, err)
defer res.Body.Close()
var actual map[string]any
err = json.NewDecoder(res.Body).Decode(&actual)
require.NoError(t, err)
expect := map[string]any{
"keys": []any{map[string]any{
"alg": "ES256",
"crv": "P-256",
"kid": "5b419ade1895fec2d2def6cd33b1b9a018df60db231dc5ecb85cbed6d942813c",
"kty": "EC",
"use": "sig",
"x": "UG5xCP0JTT1H6Iol8jKuTIPVLM04CgW9PlEypNRmWlo",
"y": "KChF0fR09zm884ymInM29PtSsFdnzExNfLsP-ta1AgQ",
}},
}
assert.Equal(t, expect, actual)
})
}

View file

@ -5,6 +5,7 @@ import (
"net/http"
"net/url"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/requestid"
"github.com/pomerium/pomerium/pkg/contextutil"
"github.com/pomerium/pomerium/ui"
@ -52,7 +53,6 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
}
response := struct {
Status int
Error string
StatusText string `json:"-"`
RequestID string `json:",omitempty"`
CanDebug bool `json:"-"`
@ -61,7 +61,6 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
}{
Status: e.Status,
StatusText: StatusText(e.Status),
Error: e.Error(),
RequestID: reqID,
CanDebug: e.Status/100 == 4 && (e.DebugURL != nil || reqID != ""),
DebugURL: e.DebugURL,
@ -70,6 +69,13 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
// indicate to clients that the error originates from Pomerium, not the app
w.Header().Set(HeaderPomeriumResponse, "true")
log.Error(ctx).
Err(e.Err).
Int("status", e.Status).
Str("status-text", StatusText(e.Status)).
Str("request-id", reqID).
Msg("httputil: error")
if r.Header.Get("Accept") == "application/json" {
RenderJSON(w, e.Status, response)
return
@ -77,7 +83,6 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
m := map[string]any{
"canDebug": response.CanDebug,
"error": response.Error,
"requestId": response.RequestID,
"status": response.Status,
"statusText": response.StatusText,

View file

@ -19,7 +19,7 @@ func TestHTTPError_ErrorResponse(t *testing.T) {
wantStatus int
wantBody string
}{
{"404 json", http.StatusNotFound, errors.New("route not known"), "application/json", http.StatusNotFound, "{\"Status\":404,\"Error\":\"Not Found: route not known\"}\n"},
{"404 json", http.StatusNotFound, errors.New("route not known"), "application/json", http.StatusNotFound, "{\"Status\":404}\n"},
{"404 html", http.StatusNotFound, errors.New("route not known"), "", http.StatusNotFound, ""},
}
for _, tt := range tests {

View file

@ -2,10 +2,17 @@ package httputil
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"github.com/go-jose/go-jose/v3"
"github.com/pomerium/csrf"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
// HealthCheck is a simple healthcheck handler that responds to GET and HEAD
@ -64,3 +71,41 @@ func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
e.ErrorResponse(r.Context(), w, r)
}
}
// JWKSHandler returns the /.well-known/pomerium/jwks.json handler.
func JWKSHandler(rawSigningKey string) http.Handler {
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
var jwks jose.JSONWebKeySet
if rawSigningKey != "" {
decodedCert, err := base64.StdEncoding.DecodeString(rawSigningKey)
if err != nil {
return NewError(http.StatusInternalServerError, errors.New("bad signing key"))
}
jwk, err := cryptutil.PublicJWKFromBytes(decodedCert)
if err != nil {
return NewError(http.StatusInternalServerError, errors.New("bad signing key"))
}
jwks.Keys = append(jwks.Keys, *jwk)
}
RenderJSON(w, http.StatusOK, jwks)
return nil
})
}
// WellKnownPomeriumHandler returns the /.well-known/pomerium handler.
func WellKnownPomeriumHandler(authenticateURL *url.URL) http.Handler {
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
wellKnownURLs := struct {
OAuth2Callback string `json:"authentication_callback_endpoint"` // RFC6749
JSONWebKeySetURL string `json:"jwks_uri"` // RFC7517
FrontchannelLogoutURI string `json:"frontchannel_logout_uri"` // https://openid.net/specs/openid-connect-frontchannel-1_0.html
}{
authenticateURL.ResolveReference(&url.URL{Path: "/oauth2/callback"}).String(),
authenticateURL.ResolveReference(&url.URL{Path: "/.well-known/pomerium/jwks.json"}).String(),
authenticateURL.ResolveReference(&url.URL{Path: "/.pomerium/sign_out"}).String(),
}
w.Header().Set("X-CSRF-Token", csrf.Token(r))
RenderJSON(w, http.StatusOK, wellKnownURLs)
return nil
})
}

View file

@ -75,8 +75,8 @@ func TestHandlerFunc_ServeHTTP(t *testing.T) {
f HandlerFunc
wantBody string
}{
{"good http error", func(w http.ResponseWriter, r *http.Request) error { return NewError(404, errors.New("404")) }, "{\"Status\":404,\"Error\":\"Not Found: 404\"}\n"},
{"good std error", func(w http.ResponseWriter, r *http.Request) error { return errors.New("404") }, "{\"Status\":500,\"Error\":\"Internal Server Error: 404\"}\n"},
{"good http error", func(w http.ResponseWriter, r *http.Request) error { return NewError(404, errors.New("404")) }, "{\"Status\":404}\n"},
{"good std error", func(w http.ResponseWriter, r *http.Request) error { return errors.New("404") }, "{\"Status\":500}\n"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View file

@ -55,7 +55,7 @@ func TestValidateSignature(t *testing.T) {
wantBody string
}{
{"good", []byte("secret"), []byte("secret"), http.StatusOK, http.StatusText(http.StatusOK)},
{"secret mistmatch", []byte("secret"), []byte("hunter42"), http.StatusBadRequest, "{\"Status\":400,\"Error\":\"Bad Request: internal/urlutil: hmac failed\"}\n"},
{"secret mistmatch", []byte("secret"), []byte("hunter42"), http.StatusBadRequest, "{\"Status\":400}\n"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View file

@ -2,7 +2,6 @@ package sessions
import (
"context"
"errors"
"net/http"
)
@ -14,17 +13,17 @@ var (
// RetrieveSession takes a slice of session loaders and tries to find a valid
// session in the order they were supplied and is added to the request's context
func RetrieveSession(s ...SessionLoader) func(http.Handler) http.Handler {
func RetrieveSession(s SessionLoader) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return retrieve(s...)(next)
return retrieve(s)(next)
}
}
func retrieve(s ...SessionLoader) func(http.Handler) http.Handler {
func retrieve(s SessionLoader) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
hfn := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
jwt, err := retrieveFromRequest(r, s...)
jwt, err := s.LoadSession(r)
ctx = NewContext(ctx, jwt, err)
next.ServeHTTP(w, r.WithContext(ctx))
}
@ -32,21 +31,6 @@ func retrieve(s ...SessionLoader) func(http.Handler) http.Handler {
}
}
// retrieveFromRequest extracts sessions state from the request by calling
// token find functions in the order they where provided.
func retrieveFromRequest(r *http.Request, sessions ...SessionLoader) (string, error) {
for _, s := range sessions {
jwt, err := s.LoadSession(r)
if err != nil && !errors.Is(err, ErrNoSessionFound) {
return "", err
} else if err == nil {
return jwt, nil
}
}
return "", ErrNoSessionFound
}
// NewContext sets context values for the user session state and error.
func NewContext(ctx context.Context, jwt string, err error) context.Context {
ctx = context.WithValue(ctx, SessionCtxKey, jwt)

View file

@ -3,6 +3,7 @@
package sessions
import (
"errors"
"net/http"
)
@ -17,3 +18,21 @@ type SessionStore interface {
type SessionLoader interface {
LoadSession(*http.Request) (string, error)
}
type multiSessionLoader []SessionLoader
func (l multiSessionLoader) LoadSession(r *http.Request) (string, error) {
for _, ll := range l {
s, err := ll.LoadSession(r)
if errors.Is(err, ErrNoSessionFound) {
continue
}
return s, err
}
return "", ErrNoSessionFound
}
// MultiSessionLoader returns a session loader that returns the first session available.
func MultiSessionLoader(loaders ...SessionLoader) SessionLoader {
return multiSessionLoader(loaders)
}

View file

@ -12,6 +12,7 @@ import (
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/protoutil"
"github.com/pomerium/pomerium/pkg/slices"
)
// Delete deletes a session from the databroker.
@ -78,3 +79,10 @@ func (x *Session) SetRawIDToken(rawIDToken string) {
}
x.IdToken.Raw = rawIDToken
}
// RemoveDeviceCredentialID removes a device credential id.
func (x *Session) RemoveDeviceCredentialID(deviceCredentialID string) {
x.DeviceCredentials = slices.Filter(x.DeviceCredentials, func(el *Session_DeviceCredential) bool {
return el.GetId() != deviceCredentialID
})
}

View file

@ -8,6 +8,7 @@ import (
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/slices"
)
// Get gets a user from the databroker.
@ -47,3 +48,18 @@ func (x *User) GetClaim(claim string) []interface{} {
}
return vs
}
// AddDeviceCredentialID adds a device credential id to the list of device credential ids.
func (x *User) AddDeviceCredentialID(deviceCredentialID string) {
x.DeviceCredentialIds = slices.Unique(append(x.DeviceCredentialIds, deviceCredentialID))
}
// HasDeviceCredentialID returns true if the user has the device credential id.
func (x *User) HasDeviceCredentialID(deviceCredentialID string) bool {
return slices.Contains(x.DeviceCredentialIds, deviceCredentialID)
}
// RemoveDeviceCredentialID removes the device credential id from the list of device credential ids.
func (x *User) RemoveDeviceCredentialID(deviceCredentialID string) {
x.DeviceCredentialIds = slices.Remove(x.DeviceCredentialIds, deviceCredentialID)
}

47
pkg/slices/slices.go Normal file
View file

@ -0,0 +1,47 @@
// Package slices contains functions for working with slices.
package slices
// Contains returns true if e is in s.
func Contains[S ~[]E, E comparable](s S, e E) bool {
for _, el := range s {
if el == e {
return true
}
}
return false
}
// Filter returns a new slice containing only those elements for which f(element) is true.
func Filter[S ~[]E, E any](s S, f func(E) bool) S {
var ns S
for _, el := range s {
if f(el) {
ns = append(ns, el)
}
}
return ns
}
// Remove removes e from s.
func Remove[S ~[]E, E comparable](s S, e E) S {
var ns S
for _, el := range s {
if el != e {
ns = append(ns, el)
}
}
return ns
}
// Unique returns the unique elements of s.
func Unique[S ~[]E, E comparable](s S) S {
var ns S
h := map[E]struct{}{}
for _, el := range s {
if _, ok := h[el]; !ok {
h[el] = struct{}{}
ns = append(ns, el)
}
}
return ns
}

View file

@ -11,7 +11,7 @@ import (
"github.com/pomerium/pomerium/pkg/storage"
)
const recordBatchSize = 64
const recordBatchSize = 4 * 1024
type recordStream struct {
backend *Backend

View file

@ -63,9 +63,9 @@ func TestProxy_ForwardAuth(t *testing.T) {
wantBody string
}{
{"good verify only, no redirect", opts, nil, http.MethodGet, nil, nil, "https://some.domain.example/verify", "https://some.domain.example", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusOK, ""},
{"bad empty domain uri", opts, nil, http.MethodGet, nil, map[string]string{"uri": ""}, "https://some.domain.example/", "", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400,\"Error\":\"Bad Request: https: url does contain a valid hostname\"}\n"},
{"bad naked domain uri", opts, nil, http.MethodGet, nil, nil, "https://some.domain.example/", "a.naked.domain", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400,\"Error\":\"Bad Request: a.naked.domain url does contain a valid scheme\"}\n"},
{"bad empty verification uri", opts, nil, http.MethodGet, nil, nil, "https://some.domain.example/", " ", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400,\"Error\":\"Bad Request: %20 url does contain a valid scheme\"}\n"},
{"bad empty domain uri", opts, nil, http.MethodGet, nil, map[string]string{"uri": ""}, "https://some.domain.example/", "", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400}\n"},
{"bad naked domain uri", opts, nil, http.MethodGet, nil, nil, "https://some.domain.example/", "a.naked.domain", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400}\n"},
{"bad empty verification uri", opts, nil, http.MethodGet, nil, nil, "https://some.domain.example/", " ", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, "{\"Status\":400}\n"},
// traefik
{"good traefik callback", opts, nil, http.MethodGet, map[string]string{httputil.HeaderForwardedURI: "https://some.domain.example?" + urlutil.QuerySessionEncrypted + "=" + goodEncryptionString}, nil, "https://some.domain.example/", "https://some.domain.example", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusFound, ""},
{"bad traefik callback bad session", opts, nil, http.MethodGet, map[string]string{httputil.HeaderForwardedURI: "https://some.domain.example?" + urlutil.QuerySessionEncrypted + "=" + goodEncryptionString + "garbage"}, nil, "https://some.domain.example/", "https://some.domain.example", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, allowClient, http.StatusBadRequest, ""},

View file

@ -331,14 +331,14 @@ func TestProxy_ProgrammaticLogin(t *testing.T) {
opts, http.MethodGet, "https", "corp.example.example", "/.pomerium/api/v1/login", nil,
map[string]string{urlutil.QueryRedirectURI: "localhost"},
http.StatusBadRequest,
"{\"Status\":400,\"Error\":\"Bad Request: localhost url does contain a valid scheme\"}\n",
"{\"Status\":400}\n",
},
{
"bad redirect_uri not whitelisted",
opts, http.MethodGet, "https", "corp.example.example", "/.pomerium/api/v1/login", nil,
map[string]string{urlutil.QueryRedirectURI: "https://example.com"},
http.StatusBadRequest,
"{\"Status\":400,\"Error\":\"Bad Request: invalid redirect uri\"}\n",
"{\"Status\":400}\n",
},
{
"bad http method",

View file

@ -9,8 +9,6 @@ import (
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/sessions/cookie"
"github.com/pomerium/pomerium/internal/sessions/header"
"github.com/pomerium/pomerium/internal/sessions/queryparam"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
@ -26,7 +24,6 @@ type proxyState struct {
encoder encoding.MarshalUnmarshaler
cookieSecret []byte
sessionStore sessions.SessionStore
sessionLoaders []sessions.SessionLoader
jwtClaimHeaders config.JWTClaimHeaders
programmaticRedirectDomainWhitelist []string
@ -84,11 +81,6 @@ func newProxyStateFromConfig(cfg *config.Config) (*proxyState, error) {
if err != nil {
return nil, err
}
state.sessionLoaders = []sessions.SessionLoader{
state.sessionStore,
header.NewStore(state.encoder),
queryparam.NewStore(state.encoder, "pomerium_session"),
}
state.programmaticRedirectDomainWhitelist = cfg.Options.ProgrammaticRedirectDomainWhitelist
return state, nil

View file

@ -1,30 +1,31 @@
import {ErrorPageData, PolicyEvaluationTrace} from "../types";
import SectionFooter from "./SectionFooter";
import { ListItemProps, TableCell } from "@mui/material";
import Alert from "@mui/material/Alert";
import AlertTitle from "@mui/material/AlertTitle";
import Box from "@mui/material/Box";
import Container from "@mui/material/Container";
import Paper from "@mui/material/Paper";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import React, { FC } from "react";
import Markdown from "markdown-to-jsx";
import {ListItemProps, TableCell} from "@mui/material";
import {CheckCircle, MinusCircle, XCircle} from "react-feather";
import Table from "@mui/material/Table";
import TableRow from "@mui/material/TableRow";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";
import Markdown from "markdown-to-jsx";
import React, { FC } from "react";
import { CheckCircle, MinusCircle, XCircle } from "react-feather";
import { ErrorPageData, PolicyEvaluationTrace } from "../types";
import SectionFooter from "./SectionFooter";
type PolicyEvaluationTraceDetailsProps = {
trace: PolicyEvaluationTrace;
} & ListItemProps;
const PolicyEvaluationTraceDetails: FC<PolicyEvaluationTraceDetailsProps> = ({
trace,
...props
trace,
...props
}) => {
return (
<TableRow>
<TableCell align={'center'}>
<TableCell align={"center"}>
{trace.deny ? (
<XCircle color="red" />
) : trace.allow ? (
@ -34,9 +35,7 @@ const PolicyEvaluationTraceDetails: FC<PolicyEvaluationTraceDetailsProps> = ({
)}
</TableCell>
<TableCell>
<Markdown>
{trace.explanation || trace.id}
</Markdown>
<Markdown>{trace.explanation || trace.id}</Markdown>
</TableCell>
<TableCell>
<Markdown>
@ -63,14 +62,11 @@ export const ErrorPage: FC<ErrorPageProps> = ({ data }) => {
{data?.status || 500}{" "}
{data?.statusText || "Internal Server Error"}
</AlertTitle>
{data?.error || "Internal Server Error"}
</Alert>
</Box>
{!!data?.errorMessageFirstParagraph && (
<Box sx={{p: 4}}>
<Markdown>
{data.errorMessageFirstParagraph}
</Markdown>
<Box sx={{ p: 4 }}>
<Markdown>{data.errorMessageFirstParagraph}</Markdown>
</Box>
)}
{traces?.length > 0 && (

View file

@ -92,7 +92,6 @@ export type ErrorPageData = BasePageData & {
canDebug?: boolean;
debugUrl?: string;
error?: string;
requestId?: string;
status?: number;
statusText?: string;