Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions contrib/local-environment/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# oauth2-proxy: local-environment

Run `make up` to deploy local dex, etcd and oauth2-proxy instances in Docker containers. Review the [`Makefile`](Makefile) for additional deployment options.

## Reproducing issue #3057

The script `reproduce-3057.sh` runs a local Keycloak environment with Redis session storage. Use this to observe how sessions are not cleared when a refresh fails.

```bash
# Start the environment
./reproduce-3057.sh up

# When finished
./reproduce-3057.sh down
```

After starting, login at `http://oauth2-proxy.localtest.me:4180`. Then delete the session for that user in the Keycloak admin console (`http://keycloak.localtest.me:9080`). Wait about 30 seconds (the configured `cookie_refresh`), refresh the protected page and note that the session remains active.
68 changes: 68 additions & 0 deletions contrib/local-environment/docker-compose-keycloak-redis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# This docker-compose file can be used to reproduce session clearing issue 3057
# It extends the Keycloak example with a Redis backend for session storage.
# Access http://oauth2-proxy.localtest.me:4180 to start a login.
version: '3.0'
services:
oauth2-proxy:
container_name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.9.0
command: --config /oauth2-proxy.cfg
hostname: oauth2-proxy
volumes:
- "./oauth2-proxy-keycloak-redis.cfg:/oauth2-proxy.cfg"
restart: unless-stopped
ports:
- 4180:4180/tcp
networks:
keycloak: {}
httpbin: {}
oauth2-proxy: {}
depends_on:
- httpbin
- keycloak
- redis

httpbin:
container_name: httpbin
image: kennethreitz/httpbin:latest
hostname: httpbin
ports:
- 8080:80/tcp
networks:
httpbin:
aliases:
- httpbin.localtest.me

keycloak:
container_name: keycloak
image: keycloak/keycloak:25.0
hostname: keycloak
command:
- 'start-dev'
- '--http-port=9080'
- '--import-realm'
volumes:
- ./keycloak:/opt/keycloak/data/import
environment:
KC_HTTP_PORT: 9080
KEYCLOAK_ADMIN: [email protected]
KEYCLOAK_ADMIN_PASSWORD: password
ports:
- 9080:9080/tcp
networks:
keycloak:
aliases:
- keycloak.localtest.me

redis:
container_name: redis
image: redis:7
ports:
- 6379:6379/tcp
networks:
oauth2-proxy: {}

networks:
httpbin: {}
keycloak: {}
oauth2-proxy: {}
23 changes: 23 additions & 0 deletions contrib/local-environment/oauth2-proxy-keycloak-redis.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
http_address="0.0.0.0:4180"
cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
email_domains="example.com"
cookie_secure="false"
upstreams="http://httpbin"
cookie_domains=["oauth2-proxy.localtest.me:4080", "httpbin.localtest.me:8080", "keycloak.localtest.me:9080"]
whitelist_domains=[".localtest.me"]

# keycloak provider
client_secret="72341b6d-7065-4518-a0e4-50ee15025608"
client_id="oauth2-proxy"
redirect_url="http://oauth2-proxy.localtest.me:4180/oauth2/callback"

# in this case oauth2-proxy is going to visit
# http://keycloak.localtest.me:9080/realms/oauth2-proxy/.well-known/openid-configuration for configuration
oidc_issuer_url="http://keycloak.localtest.me:9080/realms/oauth2-proxy"
provider="oidc"
provider_display_name="Keycloak"

cookie_refresh="30s"
cookie_expire="2m"
session_store_type="redis"
redis_connection_url="redis://redis:6379/0"
25 changes: 25 additions & 0 deletions contrib/local-environment/reproduce-3057.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Helper script to start the Keycloak environment with Redis to reproduce issue #3057
set -euo pipefail

cd "$(dirname "$0")"

if ! command -v docker > /dev/null; then
echo "docker is required" >&2
exit 1
fi

COMPOSE_FILE="docker-compose-keycloak-redis.yaml"

# bring up or down the environment based on first argument
cmd=${1:-up}

docker compose -f "$COMPOSE_FILE" $cmd

if [ "$cmd" = "up" ]; then
echo "\nEnvironment started."
echo "1. Visit http://oauth2-proxy.localtest.me:4180 and log in with [email protected] / password."
echo "2. Open Keycloak admin at http://keycloak.localtest.me:9080/ and remove the active session."
echo "3. Wait about 30 seconds (cookie_refresh). Send another request to http://oauth2-proxy.localtest.me:4180/."
echo "If the session remains active despite the refresh failure, the bug is reproduced."
fi
32 changes: 32 additions & 0 deletions pkg/middleware/stored_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,38 @@ var _ = Describe("Stored Session Suite", func() {
}),
)

It("clears the session from the store when refresh fails (issue 3057)", func() {
stored := &sessionsapi.SessionState{
RefreshToken: "RefreshError",
CreatedAt: &createdPast,
ExpiresOn: &createdFuture,
}
store := &fakeSessionStore{
LoadFunc: func(*http.Request) (*sessionsapi.SessionState, error) { return stored, nil },
ClearFunc: func(http.ResponseWriter, *http.Request) error { stored = nil; return nil },
}

scope := &middlewareapi.RequestScope{}
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("Cookie", "_oauth2_proxy=RefreshError")
req = middlewareapi.AddRequestScope(req, scope)
rw := httptest.NewRecorder()

opts := &StoredSessionLoaderOptions{
SessionStore: store,
RefreshPeriod: 1 * time.Minute,
RefreshSession: func(context.Context, *sessionsapi.SessionState) (bool, error) {
return false, errors.New("refresh failed")
},
ValidateSession: func(context.Context, *sessionsapi.SessionState) bool { return true },
}

handler := NewStoredSessionLoader(opts)(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
handler.ServeHTTP(rw, req)

Expect(stored).To(BeNil())
})

type storedSessionLoaderConcurrentTableInput struct {
existingSession *sessionsapi.SessionState
refreshPeriod time.Duration
Expand Down