mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-04 18:38:12 +02:00
Merge branch 'main' into cdoxsey/upgrade-lint
This commit is contained in:
commit
359376e786
33 changed files with 2016 additions and 57 deletions
2
.github/Dockerfile-cloudrun
vendored
2
.github/Dockerfile-cloudrun
vendored
|
@ -2,7 +2,7 @@
|
||||||
FROM busybox:latest@sha256:1ceb872bcc68a8fcd34c97952658b58086affdcb604c90c1dee2735bde5edc2f as build
|
FROM busybox:latest@sha256:1ceb872bcc68a8fcd34c97952658b58086affdcb604c90c1dee2735bde5edc2f as build
|
||||||
RUN touch /config.yaml
|
RUN touch /config.yaml
|
||||||
|
|
||||||
FROM gcr.io/distroless/base:latest@sha256:46c5b9bd3e3efff512e28350766b54355fce6337a0b44ba3f822ab918eca4520
|
FROM gcr.io/distroless/base:latest@sha256:b31a6e02605827e77b7ebb82a0ac9669ec51091edd62c2c076175e05556f4ab9
|
||||||
ENV AUTOCERT_DIR /data/autocert
|
ENV AUTOCERT_DIR /data/autocert
|
||||||
WORKDIR /pomerium
|
WORKDIR /pomerium
|
||||||
COPY pomerium* /bin/
|
COPY pomerium* /bin/
|
||||||
|
|
4
.github/workflows/benchmark.yaml
vendored
4
.github/workflows/benchmark.yaml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
platform: [ubuntu-latest]
|
platform: [ubuntu-latest]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||||
with:
|
with:
|
||||||
|
@ -43,7 +43,7 @@ jobs:
|
||||||
- name: start cluster
|
- name: start cluster
|
||||||
run: |
|
run: |
|
||||||
export POMERIUM_TAG=dev
|
export POMERIUM_TAG=dev
|
||||||
cd ./integration/clusters/single
|
cd ./integration/clusters/single-stateful
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
- name: integration tests
|
- name: integration tests
|
||||||
|
|
4
.github/workflows/docker-main.yaml
vendored
4
.github/workflows/docker-main.yaml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3
|
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3
|
||||||
|
@ -75,7 +75,7 @@ jobs:
|
||||||
needs: publish
|
needs: publish
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Gitops Repo
|
- name: Checkout Gitops Repo
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
with:
|
with:
|
||||||
repository: pomerium/gitops-argocd
|
repository: pomerium/gitops-argocd
|
||||||
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
|
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
labels: linux
|
labels: linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
|
|
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||||
with:
|
with:
|
||||||
|
|
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
tag: ${{ steps.tagName.outputs.tag }}
|
tag: ${{ steps.tagName.outputs.tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- name: Unshallow
|
- name: Unshallow
|
||||||
run: git fetch --prune --unshallow
|
run: git fetch --prune --unshallow
|
||||||
|
@ -121,7 +121,7 @@ jobs:
|
||||||
needs: goreleaser
|
needs: goreleaser
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Gitops Repo
|
- name: Checkout Gitops Repo
|
||||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
with:
|
with:
|
||||||
repository: pomerium/gitops-argocd
|
repository: pomerium/gitops-argocd
|
||||||
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
|
token: ${{ secrets.APPARITOR_GITHUB_TOKEN }}
|
||||||
|
|
13
.github/workflows/test.yaml
vendored
13
.github/workflows/test.yaml
vendored
|
@ -16,9 +16,10 @@ jobs:
|
||||||
node-version: [16.x]
|
node-version: [16.x]
|
||||||
platform: [ubuntu-latest]
|
platform: [ubuntu-latest]
|
||||||
deployment: [multi, single]
|
deployment: [multi, single]
|
||||||
|
authenticate-flow: [stateful, stateless]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||||
with:
|
with:
|
||||||
|
@ -41,12 +42,12 @@ jobs:
|
||||||
- name: start cluster
|
- name: start cluster
|
||||||
run: |
|
run: |
|
||||||
export POMERIUM_TAG=dev
|
export POMERIUM_TAG=dev
|
||||||
cd ./integration/clusters/${{matrix.deployment}}
|
cd ./integration/clusters/${{matrix.deployment}}-${{matrix.authenticate-flow}}
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
- name: integration tests
|
- name: integration tests
|
||||||
run: |
|
run: |
|
||||||
(cd ./integration/clusters/${{matrix.deployment}} && docker-compose logs -f &)
|
(cd ./integration/clusters/${{matrix.deployment}}-${{matrix.authenticate-flow}} && docker-compose logs -f &)
|
||||||
go test -v ./integration/...
|
go test -v ./integration/...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
@ -57,7 +58,7 @@ jobs:
|
||||||
platform: [ubuntu-latest, macos-latest]
|
platform: [ubuntu-latest, macos-latest]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||||
with:
|
with:
|
||||||
|
@ -101,7 +102,7 @@ jobs:
|
||||||
build-docker:
|
build-docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226
|
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226
|
||||||
|
@ -119,7 +120,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
|
||||||
AllowedHeaders: []string{"*"},
|
AllowedHeaders: []string{"*"},
|
||||||
})
|
})
|
||||||
sr.Use(c.Handler)
|
sr.Use(c.Handler)
|
||||||
|
sr.Use(a.RetrieveSession)
|
||||||
|
|
||||||
// routes that don't need a session:
|
// routes that don't need a session:
|
||||||
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
||||||
|
@ -91,7 +92,6 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
|
||||||
|
|
||||||
// routes that need a session:
|
// routes that need a session:
|
||||||
sr = sr.NewRoute().Subrouter()
|
sr = sr.NewRoute().Subrouter()
|
||||||
sr.Use(a.RetrieveSession)
|
|
||||||
sr.Use(a.VerifySession)
|
sr.Use(a.VerifySession)
|
||||||
sr.Path("/").Handler(a.requireValidSignatureOnRedirect(a.userInfo))
|
sr.Path("/").Handler(a.requireValidSignatureOnRedirect(a.userInfo))
|
||||||
sr.Path("/sign_in").Handler(httputil.HandlerFunc(a.SignIn))
|
sr.Path("/sign_in").Handler(httputil.HandlerFunc(a.SignIn))
|
||||||
|
@ -475,7 +475,9 @@ func (a *Authenticate) revokeSession(ctx context.Context, w http.ResponseWriter,
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.flow.RevokeSession(ctx, r, authenticator, nil)
|
sessionState, _ := a.getSessionFromCtx(ctx)
|
||||||
|
|
||||||
|
return state.flow.RevokeSession(ctx, r, authenticator, sessionState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback handles the result of a successful call to the authenticate service
|
// Callback handles the result of a successful call to the authenticate service
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -248,6 +249,40 @@ func TestAuthenticate_SignOut(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthenticate_SignOutDoesNotRequireSession(t *testing.T) {
|
||||||
|
// A direct sign_out request would not be signed.
|
||||||
|
f := new(stubFlow)
|
||||||
|
f.verifySignatureErr = errors.New("no signature")
|
||||||
|
|
||||||
|
sessionStore := &mstore.Store{LoadError: errors.New("no session")}
|
||||||
|
a := &Authenticate{
|
||||||
|
cfg: getAuthenticateConfig(WithGetIdentityProvider(func(options *config.Options, idpID string) (identity.Authenticator, error) {
|
||||||
|
return identity.MockProvider{}, nil
|
||||||
|
})),
|
||||||
|
state: atomicutil.NewValue(&authenticateState{
|
||||||
|
cookieSecret: cryptutil.NewKey(),
|
||||||
|
sessionLoader: sessionStore,
|
||||||
|
sessionStore: sessionStore,
|
||||||
|
sharedEncoder: mock.Encoder{},
|
||||||
|
flow: f,
|
||||||
|
}),
|
||||||
|
options: config.NewAtomicOptions(),
|
||||||
|
}
|
||||||
|
r := httptest.NewRequest(http.MethodGet, "/.pomerium/sign_out", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
a.Handler().ServeHTTP(w, r)
|
||||||
|
result := w.Result()
|
||||||
|
|
||||||
|
// The handler should serve a sign out confirmation page, not a login redirect.
|
||||||
|
expectedStatus := "200 OK"
|
||||||
|
if result.Status != expectedStatus {
|
||||||
|
t.Fatalf("wrong status code: got %q want %q", result.Status, expectedStatus)
|
||||||
|
}
|
||||||
|
body, _ := io.ReadAll(result.Body)
|
||||||
|
assert.Contains(t, string(body), `"page":"SignOutConfirm"`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthenticate_OAuthCallback(t *testing.T) {
|
func TestAuthenticate_OAuthCallback(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -144,13 +144,17 @@ func newAuthenticateStateFromConfig(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.flow, err = authenticateflow.NewStateless(
|
if cfg.Options.UseStatelessAuthenticateFlow() {
|
||||||
cfg,
|
state.flow, err = authenticateflow.NewStateless(
|
||||||
cookieStore,
|
cfg,
|
||||||
authenticateConfig.getIdentityProvider,
|
cookieStore,
|
||||||
authenticateConfig.profileTrimFn,
|
authenticateConfig.getIdentityProvider,
|
||||||
authenticateConfig.authEventFn,
|
authenticateConfig.profileTrimFn,
|
||||||
)
|
authenticateConfig.authEventFn,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.flow, err = authenticateflow.NewStateful(cfg, cookieStore)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,11 @@ func newAuthorizeStateFromConfig(
|
||||||
return nil, fmt.Errorf("authorize: invalid session store: %w", err)
|
return nil, fmt.Errorf("authorize: invalid session store: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.authenticateFlow, err = authenticateflow.NewStateless(cfg, nil, nil, nil, nil)
|
if cfg.Options.UseStatelessAuthenticateFlow() {
|
||||||
|
state.authenticateFlow, err = authenticateflow.NewStateless(cfg, nil, nil, nil, nil)
|
||||||
|
} else {
|
||||||
|
state.authenticateFlow, err = authenticateflow.NewStateful(cfg, nil)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -827,6 +827,25 @@ func (o *Options) GetInternalAuthenticateURL() (*url.URL, error) {
|
||||||
return urlutil.ParseAndValidateURL(o.AuthenticateInternalURLString)
|
return urlutil.ParseAndValidateURL(o.AuthenticateInternalURLString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseStatelessAuthenticateFlow returns true if the stateless authentication
|
||||||
|
// flow should be used (i.e. for hosted authenticate).
|
||||||
|
func (o *Options) UseStatelessAuthenticateFlow() bool {
|
||||||
|
if flow := os.Getenv("DEBUG_FORCE_AUTHENTICATE_FLOW"); flow != "" {
|
||||||
|
if flow == "stateless" {
|
||||||
|
return true
|
||||||
|
} else if flow == "stateful" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.Warn(context.Background()).
|
||||||
|
Msgf("ignoring unknown DEBUG_FORCE_AUTHENTICATE_FLOW setting %q", flow)
|
||||||
|
}
|
||||||
|
u, err := o.GetInternalAuthenticateURL()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return urlutil.IsHostedAuthenticateDomain(u.Hostname())
|
||||||
|
}
|
||||||
|
|
||||||
// GetAuthorizeURLs returns the AuthorizeURLs in the options or 127.0.0.1:5443.
|
// GetAuthorizeURLs returns the AuthorizeURLs in the options or 127.0.0.1:5443.
|
||||||
func (o *Options) GetAuthorizeURLs() ([]*url.URL, error) {
|
func (o *Options) GetAuthorizeURLs() ([]*url.URL, error) {
|
||||||
if IsAll(o.Services) && o.AuthorizeURLString == "" && len(o.AuthorizeURLStrings) == 0 {
|
if IsAll(o.Services) && o.AuthorizeURLString == "" && len(o.AuthorizeURLStrings) == 0 {
|
||||||
|
|
|
@ -856,6 +856,31 @@ func TestOptions_DefaultURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOptions_UseStatelessAuthenticateFlow(t *testing.T) {
|
||||||
|
t.Run("enabled by default", func(t *testing.T) {
|
||||||
|
options := &Options{}
|
||||||
|
assert.True(t, options.UseStatelessAuthenticateFlow())
|
||||||
|
})
|
||||||
|
t.Run("enabled explicitly", func(t *testing.T) {
|
||||||
|
options := &Options{AuthenticateURLString: "https://authenticate.pomerium.app"}
|
||||||
|
assert.True(t, options.UseStatelessAuthenticateFlow())
|
||||||
|
})
|
||||||
|
t.Run("disabled", func(t *testing.T) {
|
||||||
|
options := &Options{AuthenticateURLString: "https://authenticate.example.com"}
|
||||||
|
assert.False(t, options.UseStatelessAuthenticateFlow())
|
||||||
|
})
|
||||||
|
t.Run("force enabled", func(t *testing.T) {
|
||||||
|
options := &Options{AuthenticateURLString: "https://authenticate.example.com"}
|
||||||
|
t.Setenv("DEBUG_FORCE_AUTHENTICATE_FLOW", "stateless")
|
||||||
|
assert.True(t, options.UseStatelessAuthenticateFlow())
|
||||||
|
})
|
||||||
|
t.Run("force disabled", func(t *testing.T) {
|
||||||
|
options := &Options{}
|
||||||
|
t.Setenv("DEBUG_FORCE_AUTHENTICATE_FLOW", "stateful")
|
||||||
|
assert.False(t, options.UseStatelessAuthenticateFlow())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestOptions_GetOauthOptions(t *testing.T) {
|
func TestOptions_GetOauthOptions(t *testing.T) {
|
||||||
opts := &Options{AuthenticateURLString: "https://authenticate.example.com"}
|
opts := &Options{AuthenticateURLString: "https://authenticate.example.com"}
|
||||||
oauthOptions, err := opts.GetOauthOptions()
|
oauthOptions, err := opts.GetOauthOptions()
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -53,7 +53,7 @@ require (
|
||||||
github.com/pomerium/zero-sdk v0.0.0-20231127153820-dcd408d87b54
|
github.com/pomerium/zero-sdk v0.0.0-20231127153820-dcd408d87b54
|
||||||
github.com/prometheus/client_golang v1.17.0
|
github.com/prometheus/client_golang v1.17.0
|
||||||
github.com/prometheus/client_model v0.5.0
|
github.com/prometheus/client_model v0.5.0
|
||||||
github.com/prometheus/common v0.44.0
|
github.com/prometheus/common v0.45.0
|
||||||
github.com/prometheus/procfs v0.12.0
|
github.com/prometheus/procfs v0.12.0
|
||||||
github.com/rs/cors v1.10.1
|
github.com/rs/cors v1.10.1
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/rs/zerolog v1.31.0
|
||||||
|
@ -173,7 +173,7 @@ require (
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||||
github.com/miekg/dns v1.1.55 // indirect
|
github.com/miekg/dns v1.1.55 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -540,8 +540,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||||
|
@ -671,8 +671,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9
|
||||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||||
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
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/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
|
77
integration/authentication_test.go
Normal file
77
integration/authentication_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-jose/go-jose/v3/jwt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/integration/flows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRouteSessions(t *testing.T) {
|
||||||
|
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
|
defer clearTimeout()
|
||||||
|
|
||||||
|
client := getClient(t)
|
||||||
|
|
||||||
|
// Sign in to access one route.
|
||||||
|
url1 := mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain")
|
||||||
|
res, err := flows.Authenticate(ctx, client, url1, flows.WithEmail("user1@dogs.test"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for httpdetails")
|
||||||
|
|
||||||
|
// Now request a different route. This should not require signing in again,
|
||||||
|
// but will redirect through the authenticate service if using the
|
||||||
|
// stateless authentication flow.
|
||||||
|
client.CheckRedirect = nil
|
||||||
|
url2 := mustParseURL("https://restricted-httpdetails.localhost.pomerium.io/by-domain")
|
||||||
|
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url2.String(), nil)
|
||||||
|
res, err = client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for restricted-httpdetails")
|
||||||
|
|
||||||
|
// Now examine the session cookies saved for each route.
|
||||||
|
claims1 := getSessionCookieJWTClaims(t, client, url1)
|
||||||
|
claims2 := getSessionCookieJWTClaims(t, client, url2)
|
||||||
|
|
||||||
|
if AuthenticateFlow == "stateless" {
|
||||||
|
// Under the stateless authenticate flow, each route should have its
|
||||||
|
// own session.
|
||||||
|
assert.NotEqual(t, claims1.ID, claims2.ID)
|
||||||
|
} else {
|
||||||
|
// Under the stateful authenticate flow, the two routes should share
|
||||||
|
// the same session.
|
||||||
|
assert.Equal(t, claims1.ID, claims2.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSessionCookieJWTClaims(t *testing.T, client *http.Client, u *url.URL) *jwt.Claims {
|
||||||
|
t.Helper()
|
||||||
|
cookie := getSessionCookie(t, client, u)
|
||||||
|
|
||||||
|
token, err := jwt.ParseSigned(cookie.Value)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var claims jwt.Claims
|
||||||
|
err = token.UnsafeClaimsWithoutVerification(&claims)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return &claims
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSessionCookie(t *testing.T, client *http.Client, u *url.URL) *http.Cookie {
|
||||||
|
t.Helper()
|
||||||
|
for _, c := range client.Jar.Cookies(u) {
|
||||||
|
if c.Name == "_pomerium" {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatalf("no session cookie found for URL %q", u.String())
|
||||||
|
return nil
|
||||||
|
}
|
981
integration/clusters/multi-stateless/compose.yml
Normal file
981
integration/clusters/multi-stateless/compose.yml
Normal file
File diff suppressed because one or more lines are too long
793
integration/clusters/single-stateless/compose.yml
Normal file
793
integration/clusters/single-stateless/compose.yml
Normal file
File diff suppressed because one or more lines are too long
|
@ -23,7 +23,7 @@ import (
|
||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var IDP, ClusterType string
|
var IDP, ClusterType, AuthenticateFlow string
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
@ -44,7 +44,7 @@ func TestMain(m *testing.M) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setIDPAndClusterType(ctx)
|
setClusterInfo(ctx)
|
||||||
|
|
||||||
status := m.Run()
|
status := m.Run()
|
||||||
os.Exit(status)
|
os.Exit(status)
|
||||||
|
@ -169,9 +169,10 @@ func waitForHealthy(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIDPAndClusterType(ctx context.Context) {
|
func setClusterInfo(ctx context.Context) {
|
||||||
IDP = "oidc"
|
IDP = "oidc"
|
||||||
ClusterType = "single"
|
ClusterType = "single"
|
||||||
|
AuthenticateFlow = "stateful"
|
||||||
|
|
||||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -185,14 +186,19 @@ func setIDPAndClusterType(ctx context.Context) {
|
||||||
}
|
}
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
for _, name := range container.Names {
|
for _, name := range container.Names {
|
||||||
parts := regexp.MustCompile(`^/(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name)
|
parts := regexp.MustCompile(`^/(\w+?)-(\w+?)[-_]pomerium.*$`).FindStringSubmatch(name)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 3 {
|
||||||
ClusterType = parts[1]
|
ClusterType = parts[1]
|
||||||
|
AuthenticateFlow = parts[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Str("idp", IDP).Str("cluster-type", ClusterType).Send()
|
log.Info().
|
||||||
|
Str("idp", IDP).
|
||||||
|
Str("cluster-type", ClusterType).
|
||||||
|
Str("authenticate-flow", AuthenticateFlow).
|
||||||
|
Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustParseURL(str string) *url.URL {
|
func mustParseURL(str string) *url.URL {
|
||||||
|
|
|
@ -74,7 +74,7 @@ local KubernetesService(name) =
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
local Environment(mode, idp, dns_suffix) =
|
local Environment(mode, idp, authentication_flow, dns_suffix) =
|
||||||
{
|
{
|
||||||
AUTHENTICATE_SERVICE_URL: 'https://authenticate.localhost.pomerium.io',
|
AUTHENTICATE_SERVICE_URL: 'https://authenticate.localhost.pomerium.io',
|
||||||
CERTIFICATE: std.base64(importstr '../files/trusted.pem'),
|
CERTIFICATE: std.base64(importstr '../files/trusted.pem'),
|
||||||
|
@ -98,13 +98,19 @@ local Environment(mode, idp, dns_suffix) =
|
||||||
SHARED_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=',
|
SHARED_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=',
|
||||||
SIGNING_KEY: std.base64(importstr '../files/signing-key.pem'),
|
SIGNING_KEY: std.base64(importstr '../files/signing-key.pem'),
|
||||||
SIGNING_KEY_ALGORITHM: 'ES256',
|
SIGNING_KEY_ALGORITHM: 'ES256',
|
||||||
} + if mode == 'multi' then {
|
} + (
|
||||||
AUTHENTICATE_INTERNAL_SERVICE_URL: 'https://pomerium-authenticate',
|
if mode == 'multi' then {
|
||||||
AUTHORIZE_SERVICE_URL: 'https://pomerium-authorize:5443',
|
AUTHENTICATE_INTERNAL_SERVICE_URL: 'https://pomerium-authenticate',
|
||||||
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
|
AUTHORIZE_SERVICE_URL: 'https://pomerium-authorize:5443',
|
||||||
GRPC_ADDRESS: ':5443',
|
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
|
||||||
GRPC_INSECURE: 'false',
|
GRPC_ADDRESS: ':5443',
|
||||||
} else {};
|
GRPC_INSECURE: 'false',
|
||||||
|
} else {}
|
||||||
|
) + (
|
||||||
|
if authentication_flow == 'stateless' then {
|
||||||
|
DEBUG_FORCE_AUTHENTICATE_FLOW: 'stateless',
|
||||||
|
} else {}
|
||||||
|
);
|
||||||
|
|
||||||
local ComposeService(name, definition, additionalAliases=[]) =
|
local ComposeService(name, definition, additionalAliases=[]) =
|
||||||
utils.ComposeService(name, definition {
|
utils.ComposeService(name, definition {
|
||||||
|
@ -128,10 +134,10 @@ local ComposeService(name, definition, additionalAliases=[]) =
|
||||||
},
|
},
|
||||||
}, additionalAliases);
|
}, additionalAliases);
|
||||||
|
|
||||||
function(mode, idp, dns_suffix='') {
|
function(mode, idp, authentication_flow, dns_suffix='') {
|
||||||
local name = 'pomerium',
|
local name = 'pomerium',
|
||||||
local image = 'pomerium/pomerium:${POMERIUM_TAG:-main}',
|
local image = 'pomerium/pomerium:${POMERIUM_TAG:-main}',
|
||||||
local environment = Environment(mode, idp, dns_suffix),
|
local environment = Environment(mode, idp, authentication_flow, dns_suffix),
|
||||||
|
|
||||||
compose: {
|
compose: {
|
||||||
services: if mode == 'multi' then
|
services: if mode == 'multi' then
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
(import '../../deployments/multi.libsonnet')('oidc', 'stateful')
|
|
@ -0,0 +1 @@
|
||||||
|
(import '../../deployments/multi.libsonnet')('oidc', 'stateless')
|
|
@ -1 +0,0 @@
|
||||||
(import '../../deployments/multi.libsonnet')('oidc')
|
|
|
@ -0,0 +1 @@
|
||||||
|
(import '../../deployments/single.libsonnet')('oidc', 'stateful')
|
|
@ -0,0 +1 @@
|
||||||
|
(import '../../deployments/single.libsonnet')('oidc', 'stateless')
|
|
@ -1 +0,0 @@
|
||||||
(import '../../deployments/single.libsonnet')('oidc')
|
|
|
@ -6,7 +6,7 @@ function(idp) utils.Merge([
|
||||||
(import '../backends/fortio.libsonnet')().kubernetes +
|
(import '../backends/fortio.libsonnet')().kubernetes +
|
||||||
(import '../backends/httpdetails.libsonnet')().kubernetes +
|
(import '../backends/httpdetails.libsonnet')().kubernetes +
|
||||||
(import '../backends/mock-idp.libsonnet')(idp).kubernetes +
|
(import '../backends/mock-idp.libsonnet')(idp).kubernetes +
|
||||||
(import '../backends/pomerium.libsonnet')('single', idp, '.default.svc.cluster.local').kubernetes +
|
(import '../backends/pomerium.libsonnet')('single', idp, 'stateful', '.default.svc.cluster.local').kubernetes +
|
||||||
(import '../backends/postgres.libsonnet')().kubernetes +
|
(import '../backends/postgres.libsonnet')().kubernetes +
|
||||||
(import '../backends/verify.libsonnet')('single').kubernetes +
|
(import '../backends/verify.libsonnet')('single').kubernetes +
|
||||||
(import '../backends/websocket-echo.libsonnet')().kubernetes
|
(import '../backends/websocket-echo.libsonnet')().kubernetes
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
local utils = import '../utils.libsonnet';
|
local utils = import '../utils.libsonnet';
|
||||||
|
|
||||||
function(idp) utils.Merge([
|
function(idp, authentication_flow) utils.Merge([
|
||||||
(import '../backends/fortio.libsonnet')().compose,
|
(import '../backends/fortio.libsonnet')().compose,
|
||||||
(import '../backends/httpdetails.libsonnet')().compose,
|
(import '../backends/httpdetails.libsonnet')().compose,
|
||||||
(import '../backends/mock-idp.libsonnet')(idp).compose,
|
(import '../backends/mock-idp.libsonnet')(idp).compose,
|
||||||
(import '../backends/pomerium.libsonnet')('multi', idp).compose,
|
(import '../backends/pomerium.libsonnet')('multi', idp, authentication_flow).compose,
|
||||||
(import '../backends/postgres.libsonnet')().compose,
|
(import '../backends/postgres.libsonnet')().compose,
|
||||||
(import '../backends/verify.libsonnet')('multi').compose,
|
(import '../backends/verify.libsonnet')('multi').compose,
|
||||||
(import '../backends/websocket-echo.libsonnet')().compose,
|
(import '../backends/websocket-echo.libsonnet')().compose,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
local utils = import '../utils.libsonnet';
|
local utils = import '../utils.libsonnet';
|
||||||
|
|
||||||
function(idp) utils.Merge([
|
function(idp, authentication_flow) utils.Merge([
|
||||||
(import '../backends/fortio.libsonnet')().compose,
|
(import '../backends/fortio.libsonnet')().compose,
|
||||||
(import '../backends/httpdetails.libsonnet')().compose,
|
(import '../backends/httpdetails.libsonnet')().compose,
|
||||||
(import '../backends/mock-idp.libsonnet')(idp).compose,
|
(import '../backends/mock-idp.libsonnet')(idp).compose,
|
||||||
(import '../backends/pomerium.libsonnet')('single', idp).compose,
|
(import '../backends/pomerium.libsonnet')('single', idp, authentication_flow).compose,
|
||||||
(import '../backends/postgres.libsonnet')().compose,
|
(import '../backends/postgres.libsonnet')().compose,
|
||||||
(import '../backends/verify.libsonnet')('single').compose,
|
(import '../backends/verify.libsonnet')('single').compose,
|
||||||
(import '../backends/websocket-echo.libsonnet')().compose,
|
(import '../backends/websocket-echo.libsonnet')().compose,
|
||||||
|
|
|
@ -33,7 +33,7 @@ type inMemoryKey struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New constructs a new registry tracking service that operates in RAM
|
// New constructs a new registry tracking service that operates in RAM
|
||||||
// as such, it is not usable for multi-node deployment where REDIS or other alternative should be used
|
// as such, it is not usable for multi-node deployment.
|
||||||
func New(ctx context.Context, ttl time.Duration) registry.Interface {
|
func New(ctx context.Context, ttl time.Duration) registry.Interface {
|
||||||
srv := &inMemoryServer{
|
srv := &inMemoryServer{
|
||||||
ttl: ttl,
|
ttl: ttl,
|
||||||
|
|
|
@ -114,8 +114,12 @@ func newProxyStateFromConfig(cfg *config.Config) (*proxyState, error) {
|
||||||
|
|
||||||
state.programmaticRedirectDomainWhitelist = cfg.Options.ProgrammaticRedirectDomainWhitelist
|
state.programmaticRedirectDomainWhitelist = cfg.Options.ProgrammaticRedirectDomainWhitelist
|
||||||
|
|
||||||
state.authenticateFlow, err = authenticateflow.NewStateless(
|
if cfg.Options.UseStatelessAuthenticateFlow() {
|
||||||
cfg, state.sessionStore, nil, nil, nil)
|
state.authenticateFlow, err = authenticateflow.NewStateless(
|
||||||
|
cfg, state.sessionStore, nil, nil, nil)
|
||||||
|
} else {
|
||||||
|
state.authenticateFlow, err = authenticateflow.NewStateful(cfg, state.sessionStore)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue