Compare commits

...

15 Commits

Author SHA1 Message Date
Andrei Kvapil
9c4507b58a Release v0.40.7 (#1994)
This PR prepares the release `v0.40.7`.
2026-02-06 08:56:22 +01:00
cozystack-bot
b82604718c Prepare release v0.40.7
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-02-06 01:40:42 +00:00
Andrei Kvapil
82435cda9c [Backport release-0.40] [dashboard] Verify JWT token (#1984)
# Description
Backport of #1980 to `release-0.40`.
2026-02-05 09:39:21 +01:00
Timofei Larkin
fc5de97dc0 [dashboard] Verify JWT token
## What this PR does

When OIDC is disabled, the dashboard's token-proxy now properly
validates bearer tokens against the k8s API's JWKS url.

### Release note

```release-note
[dashboard] Verify bearer tokens against the issuer's JWKS url.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
(cherry picked from commit 23e399bd9a)
2026-02-04 15:11:15 +00:00
Andrei Kvapil
85b8ad3984 Release v0.40.6 (#1965)
This PR prepares the release `v0.40.6`.
2026-02-03 08:35:40 +01:00
cozystack-bot
d1746a5f81 Prepare release v0.40.6
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-02-03 01:36:43 +00:00
Andrei Kvapil
d94f5c24b4 [Backport release-0.40] fix manifests for kubernetes deployment (#1944)
# Description
Backport of #1943 to `release-0.40`.
2026-02-02 22:07:54 +01:00
IvanHunters
3595eaea13 fix manifests for kubernetes deployment
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
(cherry picked from commit 281715b365)
2026-02-02 09:33:24 +00:00
Andrei Kvapil
37e84be573 Release v0.40.5 (#1925)
This PR prepares the release `v0.40.5`.
2026-01-29 10:08:30 +01:00
cozystack-bot
7e58850fb1 Prepare release v0.40.5
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-29 01:39:21 +00:00
Andrei Kvapil
ef00445322 Update cozyhr v1.6.1
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-01-27 23:10:58 +01:00
Andrei Kvapil
8bd3861164 [Backport release-0.40] [dashboard] Improve dashboard session params (#1919)
# Description
Backport of #1913 to `release-0.40`.
2026-01-27 18:56:31 +01:00
Timofei Larkin
d11dc983b8 [dashboard] Improve dashboard session params
## What this PR does

This patch enables the `offline_access` scope for the dashbord keycloak
client, so that users get a refresh token which gatekeeper can use to
automatically refresh an expiring access token. Also session timeouts
were increased.

### Release note

```release-note
[dashboard] Increase session timeouts, add the offline_access scope,
enable refresh tokens to improve the overall user experience when
working with the dashboard.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
(cherry picked from commit 7cebafbafd)
2026-01-27 17:18:42 +00:00
Andrei Kvapil
5e9bff0e50 Release v0.40.4 (#1890)
This PR prepares the release `v0.40.4`.
2026-01-20 03:31:21 +01:00
cozystack-bot
cfbe41d8e0 Prepare release v0.40.4
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-20 01:34:53 +00:00
30 changed files with 300 additions and 77 deletions

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:6f2b1d6b0b2bdc66f1cbb30c59393369cbf070cb8f5fec748f176952273483cc
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:598331326f0c2aac420187f0cc3a49fedcb22ed5de4afe50c6ccf8e05d9fa537

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0b208ed506dd8c453426761d93ec3d42c9d1b791ba6c91b01c6386dcb1b02442

View File

@@ -292,6 +292,12 @@ metadata:
{{- end }}
spec:
clusterName: {{ $.Release.Name }}
replicas: 2
strategy:
rollingUpdate:
maxSurge: {{ $group.maxReplicas }}
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
cluster.x-k8s.io/cluster-name: {{ $.Release.Name }}
@@ -326,6 +332,7 @@ metadata:
namespace: {{ $.Release.Namespace }}
spec:
clusterName: {{ $.Release.Name }}
maxUnhealthy: 0
nodeStartupTimeout: 10m
selector:
matchLabels:

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:aca403030ff5d831415d72367866fdf291fab73ee2cfddbe4c93c2915a316ab1
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570

View File

@@ -28,7 +28,7 @@ RUN go mod download
FROM alpine:3.22
RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.5.0
RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.6.1
RUN apk add --no-cache make kubectl helm coreutils git jq openssl

View File

@@ -1,2 +1,2 @@
cozystack:
image: ghcr.io/cozystack/cozystack/installer:v0.40.3@sha256:4581c0a80fd31db2988411d96afc77afd315726348468e495c9f5c21b2b2a664
image: ghcr.io/cozystack/cozystack/installer:v0.40.7@sha256:6cf3f2439acab9599c00c788f8aabeaf4b4f515a4534bc21e20c95facb542e27

View File

@@ -1,2 +1,2 @@
assets:
image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.3@sha256:48e63ec92d473038e29ea2375f57a449ed9b9295aced53ae27d3944507c076c4
image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.7@sha256:2409c3ce21fa43b7b864564be0a8b7249603b1caa506020db0d13dd7dfa38b8c

View File

@@ -3,7 +3,7 @@ FROM ubuntu:22.04
ARG KUBECTL_VERSION=1.33.2
ARG TALOSCTL_VERSION=1.10.4
ARG HELM_VERSION=3.18.3
ARG COZYHR_VERSION=1.5.0
ARG COZYHR_VERSION=1.6.1
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,2 +1,2 @@
e2e:
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.3@sha256:fde7616aacaf5939388bbe74eb6d946147ce855b9cceb47092f620b75ba2c98a
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.7@sha256:eac71ef0de3450fce96255629e77903630c63ade62b81e7055f1a689f92ee153

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/matchbox:v0.40.3@sha256:d9674696eb6c59e4564127bb919582ea66bf5b267504ce11ef4f9431db3a247f
ghcr.io/cozystack/cozystack/matchbox:v0.40.7@sha256:55f1c6a324dc60d3a813777e26135110891a71b1643be6470fb7cd6b5c91d9e5

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.3@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.7@sha256:3e61975b8bb04d0e1d993a08b49ce8d5265e029a21ad6d57b44d4f93243aff4c

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:79463ccddbbd2a4048863944a858b2aced3f6e1c2d77f7ef4fa57e30624f6e35
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:5bc30fcdc14b289c7eeca3c53388270e3a56d10a3e611fe6c9099afa02661cf4

View File

@@ -1,5 +1,5 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.3@sha256:1b5109bc4149178084941cf1cb505110c581c9326dbb1ef94ffdcdb026dff0e8
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.7@sha256:ac3598860e3a41b466d240c54b5eafceccfa2be5aa084d872e06f5ce7e4054e3
localK8sAPIEndpoint:
enabled: true
replicas: 2

View File

@@ -1,6 +1,6 @@
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.3@sha256:2b4ad67116c03cd001c495001f9032e6d944199f14a2d80a24d697e2293650c3
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.7@sha256:0f746b1fa9be8743c249629cff9f457d63ca4a48875161d2ab598f7e69aa0457
debug: false
disableTelemetry: false
cozystackVersion: "v0.40.3"
cozystackVersion: "v0.40.7"
cozystackAPIKind: "DaemonSet"

View File

@@ -3,6 +3,21 @@ module token-proxy
go 1.24.0
require (
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/gorilla/securecookie v1.1.2
github.com/lestrrat-go/httprc/v3 v3.0.2
github.com/lestrrat-go/jwx/v3 v3.0.13
)
require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/dsig v1.0.0 // indirect
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/valyala/fastjson v1.6.7 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/sys v0.39.0 // indirect
)

View File

@@ -1,6 +1,43 @@
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
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=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc/v3 v3.0.2 h1:7u4HUaD0NQbf2/n5+fyp+T10hNCsAnwKfqn4A4Baif0=
github.com/lestrrat-go/httprc/v3 v3.0.2/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk=
github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU=
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,6 +1,9 @@
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"flag"
@@ -13,10 +16,13 @@ import (
"os"
"path"
"strings"
"sync"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/securecookie"
"github.com/lestrrat-go/httprc/v3"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwt"
)
/* ----------------------------- flags ------------------------------------ */
@@ -26,7 +32,9 @@ var (
cookieName, cookieSecretB64 string
cookieSecure bool
cookieRefresh time.Duration
tokenCheckURL string
jwksURL string
saTokenPath string
saCACertPath string
)
func init() {
@@ -38,7 +46,70 @@ func init() {
flag.StringVar(&cookieSecretB64, "cookie-secret", "", "Base64-encoded cookie secret")
flag.BoolVar(&cookieSecure, "cookie-secure", false, "Set Secure flag on cookie")
flag.DurationVar(&cookieRefresh, "cookie-refresh", 0, "Cookie refresh interval (e.g. 1h)")
flag.StringVar(&tokenCheckURL, "token-check-url", "", "URL for external token validation")
flag.StringVar(&jwksURL, "jwks-url", "https://kubernetes.default.svc/openid/v1/jwks", "JWKS URL for token verification")
flag.StringVar(&saTokenPath, "sa-token-path", "/var/run/secrets/kubernetes.io/serviceaccount/token", "Path to service account token")
flag.StringVar(&saCACertPath, "sa-ca-cert-path", "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", "Path to service account CA certificate")
flag.Parse()
// Initialize jwkCache
ctx := context.Background()
// Load CA certificate
caCert, err := os.ReadFile(saCACertPath)
if err != nil {
jwkCacheErr := fmt.Errorf("failed to read CA cert: %w", err)
panic(jwkCacheErr)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
jwkCacheErr := fmt.Errorf("failed to parse CA cert")
panic(jwkCacheErr)
}
// Create transport with SA token injection
transport := &saTokenTransport{
base: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
tokenPath: saTokenPath,
}
transport.startRefresh(ctx, 5*time.Minute)
httpClient := &http.Client{
Transport: transport,
Timeout: 10 * time.Second,
}
// Create httprc client with custom HTTP client
httprcClient := httprc.NewClient(
httprc.WithHTTPClient(httpClient),
)
// Create JWK cache
jwkCache, err = jwk.NewCache(ctx, httprcClient)
if err != nil {
jwkCacheErr := fmt.Errorf("failed to create JWK cache: %w", err)
panic(jwkCacheErr)
}
// Register the JWKS URL with refresh settings
if err := jwkCache.Register(ctx, jwksURL,
jwk.WithMinInterval(5*time.Minute),
jwk.WithMaxInterval(15*time.Minute),
); err != nil {
jwkCacheErr := fmt.Errorf("failed to register JWKS URL: %w", err)
panic(jwkCacheErr)
}
// Perform initial fetch to ensure the JWKS is available
if _, err := jwkCache.Refresh(ctx, jwksURL); err != nil {
jwkCacheErr := fmt.Errorf("failed to fetch initial JWKS: %w", err)
panic(jwkCacheErr)
}
log.Printf("JWK cache initialized with JWKS URL: %s", jwksURL)
}
/* ----------------------------- templates -------------------------------- */
@@ -117,42 +188,94 @@ var loginTmpl = template.Must(template.New("login").Parse(`
</body>
</html>`))
/* ----------------------------- helpers ---------------------------------- */
/* ----------------------------- JWK cache -------------------------------- */
func decodeJWT(raw string) jwt.MapClaims {
if raw == "" {
return jwt.MapClaims{}
}
tkn, _, err := new(jwt.Parser).ParseUnverified(raw, jwt.MapClaims{})
if err != nil || tkn == nil {
return jwt.MapClaims{}
}
if c, ok := tkn.Claims.(jwt.MapClaims); ok {
return c
}
return jwt.MapClaims{}
var (
jwkCache *jwk.Cache
)
// saTokenTransport adds the service account token to requests and refreshes it periodically.
type saTokenTransport struct {
base http.RoundTripper
tokenPath string
mu sync.RWMutex
token string
}
func externalTokenCheck(raw string) error {
if tokenCheckURL == "" {
func (t *saTokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
t.mu.RLock()
token := t.token
t.mu.RUnlock()
if token != "" {
req = req.Clone(req.Context())
req.Header.Set("Authorization", "Bearer "+token)
}
return t.base.RoundTrip(req)
}
func (t *saTokenTransport) refreshToken() {
data, err := os.ReadFile(t.tokenPath)
if err != nil {
log.Printf("warning: failed to read SA token: %v", err)
return
}
t.mu.Lock()
t.token = string(data)
t.mu.Unlock()
}
func (t *saTokenTransport) startRefresh(ctx context.Context, interval time.Duration) {
t.refreshToken()
go func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
t.refreshToken()
}
}
}()
}
/* ----------------------------- helpers ---------------------------------- */
// verifyAndParseJWT verifies the token signature and returns the parsed token.
func verifyAndParseJWT(ctx context.Context, raw string) (jwt.Token, error) {
if raw == "" {
return nil, fmt.Errorf("empty token")
}
keySet, err := jwkCache.Lookup(ctx, jwksURL)
if err != nil {
return nil, fmt.Errorf("failed to get JWKS: %w", err)
}
token, err := jwt.Parse([]byte(raw), jwt.WithKeySet(keySet))
if err != nil {
return nil, fmt.Errorf("failed to verify token: %w", err)
}
return token, nil
}
// getClaim extracts a claim value from a verified token.
func getClaim(token jwt.Token, key string) any {
if token == nil {
return nil
}
req, _ := http.NewRequest(http.MethodGet, tokenCheckURL, nil)
req.Header.Set("Authorization", "Bearer "+raw)
cli := &http.Client{Timeout: 5 * time.Second}
resp, err := cli.Do(req)
if err != nil {
return err
var val any
if err := token.Get(key, &val); err != nil {
return nil
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("status %d", resp.StatusCode)
}
return nil
return val
}
func encodeSession(sc *securecookie.SecureCookie, token string, exp, issued int64) (string, error) {
v := map[string]interface{}{
v := map[string]any{
"access_token": token,
"expires": exp,
"issued": issued,
@@ -166,7 +289,6 @@ func encodeSession(sc *securecookie.SecureCookie, token string, exp, issued int6
/* ----------------------------- main ------------------------------------- */
func main() {
flag.Parse()
if upstream == "" {
log.Fatal("--upstream is required")
}
@@ -214,7 +336,11 @@ func main() {
}{Action: signIn, Err: "Token required"})
return
}
if err := externalTokenCheck(token); err != nil {
// Verify token signature using JWKS
verifiedToken, err := verifyAndParseJWT(r.Context(), token)
if err != nil {
log.Printf("token verification failed: %v", err)
_ = loginTmpl.Execute(w, struct {
Action string
Err string
@@ -223,9 +349,8 @@ func main() {
}
exp := time.Now().Add(24 * time.Hour).Unix()
claims := decodeJWT(token)
if v, ok := claims["exp"].(float64); ok {
exp = int64(v)
if expTime, ok := verifiedToken.Expiration(); ok && !expTime.IsZero() {
exp = expTime.Unix()
}
session, _ := encodeSession(sc, token, exp, time.Now().Unix())
http.SetCookie(w, &http.Cookie{
@@ -264,7 +389,7 @@ func main() {
return
}
var token string
var sess map[string]interface{}
var sess map[string]any
if sc != nil {
if err := sc.Decode(cookieName, c.Value, &sess); err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
@@ -273,19 +398,25 @@ func main() {
token, _ = sess["access_token"].(string)
} else {
token = c.Value
sess = map[string]interface{}{
sess = map[string]any{
"expires": time.Now().Add(24 * time.Hour).Unix(),
"issued": time.Now().Unix(),
}
}
claims := decodeJWT(token)
out := map[string]interface{}{
// Re-verify the token to ensure it's still valid
verifiedToken, err := verifyAndParseJWT(r.Context(), token)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
out := map[string]any{
"token": token,
"sub": claims["sub"],
"email": claims["email"],
"preferred_username": claims["preferred_username"],
"groups": claims["groups"],
"sub": getClaim(verifiedToken, "sub"),
"email": getClaim(verifiedToken, "email"),
"preferred_username": getClaim(verifiedToken, "preferred_username"),
"groups": getClaim(verifiedToken, "groups"),
"expires": sess["expires"],
"issued": sess["issued"],
"cookie_refresh_enable": cookieRefresh > 0,
@@ -303,7 +434,7 @@ func main() {
return
}
var token string
var sess map[string]interface{}
var sess map[string]any
if sc != nil {
if err := sc.Decode(cookieName, c.Value, &sess); err != nil {
http.Redirect(w, r, signIn, http.StatusFound)
@@ -312,7 +443,7 @@ func main() {
token, _ = sess["access_token"].(string)
} else {
token = c.Value
sess = map[string]interface{}{
sess = map[string]any{
"expires": time.Now().Add(24 * time.Hour).Unix(),
"issued": time.Now().Unix(),
}

View File

@@ -1,6 +1,6 @@
{{- $brandingConfig := .Values._cluster.branding | default dict }}
{{- $tenantText := "v0.40.3" }}
{{- $tenantText := "v0.40.7" }}
{{- $footerText := "Cozystack" }}
{{- $titleText := "Cozystack Dashboard" }}
{{- $logoText := "" }}

View File

@@ -2,3 +2,30 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: incloud-web-gatekeeper
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
{{- if ne $oidcEnabled "true" }}
---
# ClusterRole to allow token-proxy to fetch JWKS for JWT verification
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: incloud-web-gatekeeper-jwks
rules:
- nonResourceURLs:
- /openid/v1/jwks
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: incloud-web-gatekeeper-jwks
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: incloud-web-gatekeeper-jwks
subjects:
- kind: ServiceAccount
name: incloud-web-gatekeeper
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -64,6 +64,7 @@ spec:
- --cookie-secure=true
- --cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET)
- --skip-provider-button
- --scope=openid email profile offline_access
env:
- name: OAUTH2_PROXY_CLIENT_ID
value: dashboard
@@ -88,7 +89,6 @@ spec:
- --cookie-name=kc-access
- --cookie-secure=true
- --cookie-secret=$(TOKEN_PROXY_COOKIE_SECRET)
- --token-check-url=http://incloud-web-nginx.{{ .Release.Namespace }}.svc:8080/api/clusters/default/k8s/apis/core.cozystack.io/v1alpha1/tenantnamespaces
env:
- name: TOKEN_PROXY_COOKIE_SECRET
valueFrom:

View File

@@ -66,6 +66,12 @@ spec:
defaultClientScopes:
- groups
- kubernetes-client
optionalClientScopes:
- offline_access
attributes:
post.logout.redirect.uris: "+"
client.session.idle.timeout: "86400"
client.session.max.lifespan: "604800"
redirectUris:
- "https://dashboard.{{ $host }}/oauth2/callback/*"
{{- range $i, $v := $extraRedirectUris }}

View File

@@ -1,6 +1,6 @@
openapiUI:
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.3@sha256:9666e61fdf7d4ddfeb9084f49ee9c1297b67a4159c475fe62680a0d84ea4050c
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.7@sha256:7ec24b3ecf1e72a6203f337101fb8b326bd319c540bea2f34c5458b836d46867
openapiUIK8sBff:
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.3@sha256:c905f98598526820bd71e3997fdc62868b9c3d7a4e9700b01d73b017b0953257
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.7@sha256:d33583995dc81a47c1dcbe45dbd866fa9097f88f4b6eb78b408dca432f15bd38
tokenProxy:
image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.3@sha256:73887f80d96e7e3c16f1cebab521b05b4308bf4662ccc6724e6a8a9745ed8254
image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.7@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc

View File

@@ -3,7 +3,7 @@ kamaji:
deploy: false
image:
pullPolicy: IfNotPresent
tag: v0.40.3@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7
tag: v0.40.7@sha256:fe9b6bb548edfc26be8aaac65801d598a4e2f9884ddf748083b9e509fa00259e
repository: ghcr.io/cozystack/cozystack/kamaji
resources:
limits:
@@ -13,4 +13,4 @@ kamaji:
cpu: 100m
memory: 100Mi
extraArgs:
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.3@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.7@sha256:fe9b6bb548edfc26be8aaac65801d598a4e2f9884ddf748083b9e509fa00259e

View File

@@ -1,4 +1,4 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.3@sha256:864a75eb6fb07eedef3b9ff94e6bb6b02e94fe73c1e03bf360bc785fbfbc1170
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.7@sha256:34e603d6d527ad07d0fd6f969ff4b4f2a98abfdbfacc020ccd9eb61b8394b1c1
ovnCentralName: ovn-central

View File

@@ -1,3 +1,3 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.3@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.7@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29

View File

@@ -1,3 +1,3 @@
storageClass: replicated
csiDriver:
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0b208ed506dd8c453426761d93ec3d42c9d1b791ba6c91b01c6386dcb1b02442

View File

@@ -1,5 +1,5 @@
lineageControllerWebhook:
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.3@sha256:3556a8ca2ee280dff3991519a5f536ec6ea0a4e5f9be0572f23adb8f59466e56
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.7@sha256:1680ba3634c81691c2cf346759c7746cf700ce59fd752fce862e5e89f3a6fdb5
debug: false
localK8sAPIEndpoint:
enabled: true

View File

@@ -1,7 +1,7 @@
piraeusServer:
image:
repository: ghcr.io/cozystack/cozystack/piraeus-server
tag: 1.32.3@sha256:1138c8dc0a117360ef70e2e2ab97bc2696419b63f46358f7668c7e01a96c419b
tag: 1.32.3@sha256:3d1b4348c665fb88f8bead09a1fa68547e6872172ed0168449cb232c4467ad84
linstor:
autoDiskful:
enabled: true
@@ -10,4 +10,4 @@ linstor:
linstorCSI:
image:
repository: ghcr.io/cozystack/cozystack/linstor-csi
tag: v1.10.5@sha256:68465f120cfeec3d7ccbb389dd9bdbf7df1675da3ab9ba91c3feff21a799bc36
tag: v1.10.5@sha256:6e6cf48cb994f3918df946e02ec454ac64916678b3e60d78c136b431f1a26155

View File

@@ -1,3 +1,3 @@
objectstorage:
controller:
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.3@sha256:76756860145d49abe1585c8ca600267a07fcdbb0b359bc9c127baf9efb8e006b"
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.7@sha256:85c9dd60b4a5e2a9ce7e5675f6a62bb4d3bb95656c7bcf6b64c87e9a56aadf9d"

View File

@@ -177,7 +177,7 @@ seaweedfs:
bucketClassName: "seaweedfs"
region: ""
sidecar:
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.3@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc"
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.7@sha256:3e61975b8bb04d0e1d993a08b49ce8d5265e029a21ad6d57b44d4f93243aff4c"
certificates:
commonName: "SeaweedFS CA"
ipAddresses: []