mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
[public-api] Measure incoming JWT Sessions - WEB-102 (#17345)
* retest * retest * [installer] Add cookie name to config * Fix * retest * [installer] Add cookie name to config * [public-api] Measure incoming JWT Sessions * fix * Fix * Fix * fix * retest
This commit is contained in:
parent
3a3d99edd3
commit
0dc46c5bcc
@ -11,6 +11,7 @@ const (
|
||||
OIDCServiceEnabledFlag = "oidcServiceEnabled"
|
||||
SupervisorPersistServerAPIChannelWhenStartFlag = "supervisor_persist_serverapi_channel_when_start"
|
||||
SupervisorUsePublicAPIFlag = "supervisor_experimental_publicapi"
|
||||
JWTSessionsEnabledFlag = "jwtSessionCookieEnabled"
|
||||
)
|
||||
|
||||
func IsPersonalAccessTokensEnabled(ctx context.Context, client Client, attributes Attributes) bool {
|
||||
@ -28,3 +29,7 @@ func SupervisorPersistServerAPIChannelWhenStart(ctx context.Context, client Clie
|
||||
func SupervisorUsePublicAPI(ctx context.Context, client Client, attributes Attributes) bool {
|
||||
return client.GetBoolValue(ctx, SupervisorUsePublicAPIFlag, false, attributes)
|
||||
}
|
||||
|
||||
func JWTSessionsEnabled(ctx context.Context, client Client, attributes Attributes) bool {
|
||||
return client.GetBoolValue(ctx, JWTSessionsEnabledFlag, false, attributes)
|
||||
}
|
||||
|
||||
28
components/public-api-server/pkg/auth/metrics.go
Normal file
28
components/public-api-server/pkg/auth/metrics.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
|
||||
// Licensed under the GNU Affero General Public License (AGPL).
|
||||
// See License.AGPL.txt in the project root for license information.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func reportRequestWithJWT(jwtPresent bool) {
|
||||
requestsWithJWTSessionsTotal.WithLabelValues(strconv.FormatBool(jwtPresent)).Inc()
|
||||
}
|
||||
|
||||
var (
|
||||
requestsWithJWTSessionsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "gitpod",
|
||||
Subsystem: "public_api",
|
||||
Name: "requests_with_jwt_sessions_total",
|
||||
Help: "Count of sessions with, or without JWT sessions",
|
||||
}, []string{"with_jwt"})
|
||||
)
|
||||
|
||||
func RegisterMetrics(registry *prometheus.Registry) {
|
||||
registry.MustRegister(requestsWithJWTSessionsTotal)
|
||||
}
|
||||
@ -5,9 +5,14 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/gitpod-io/gitpod/common-go/experiments"
|
||||
"github.com/gitpod-io/gitpod/common-go/log"
|
||||
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
@ -41,3 +46,58 @@ func VerifySessionJWT(token string, verifier jws.Verifier, expectedIssuer string
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func NewJWTCookieInterceptor(exp experiments.Client, cookieName string, expectedIssuer string, verifier jws.Verifier) connect.UnaryInterceptorFunc {
|
||||
interceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
|
||||
|
||||
return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
||||
validJWT := false
|
||||
defer func() {
|
||||
reportRequestWithJWT(validJWT)
|
||||
}()
|
||||
|
||||
if req.Spec().IsClient {
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
token, err := TokenFromContext(ctx)
|
||||
if err != nil {
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
if !experiments.JWTSessionsEnabled(ctx, exp, experiments.Attributes{}) {
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
if token.Type != CookieTokenType {
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
jwtSessionCookie, err := cookieFromString(token.Value, cookieName)
|
||||
if err != nil {
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
claims, err := VerifySessionJWT(jwtSessionCookie.Value, verifier, expectedIssuer)
|
||||
if err != nil {
|
||||
log.Extract(ctx).WithError(err).Warnf("Failed to verify JWT session token")
|
||||
return next(ctx, req)
|
||||
}
|
||||
|
||||
validJWT = claims != nil
|
||||
|
||||
return next(ctx, req)
|
||||
})
|
||||
}
|
||||
return connect.UnaryInterceptorFunc(interceptor)
|
||||
}
|
||||
|
||||
func cookieFromString(rawCookieHeader, name string) (*http.Cookie, error) {
|
||||
// To access the cookie as an http.Cookie, we sadly have to construct a request with the appropriate header such
|
||||
// that we can then extract the cookie.
|
||||
header := http.Header{}
|
||||
header.Add("Cookie", rawCookieHeader)
|
||||
req := http.Request{Header: header}
|
||||
|
||||
return req.Cookie(name)
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup JWS Keyset: %w", err)
|
||||
}
|
||||
_, err = jws.NewRSA256(keyset)
|
||||
rsa256, err := jws.NewRSA256(keyset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup jws.RSA256: %w", err)
|
||||
}
|
||||
@ -146,6 +146,8 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
|
||||
cipher: cipherSet,
|
||||
oidcService: oidcService,
|
||||
idpService: idpService,
|
||||
authCfg: cfg.Auth,
|
||||
sessionVerifier: rsa256,
|
||||
}); registerErr != nil {
|
||||
return fmt.Errorf("failed to register services: %w", registerErr)
|
||||
}
|
||||
@ -165,10 +167,14 @@ type registerDependencies struct {
|
||||
cipher db.Cipher
|
||||
oidcService *oidc.Service
|
||||
idpService *identityprovider.Service
|
||||
|
||||
sessionVerifier jws.SignerVerifier
|
||||
authCfg config.AuthConfiguration
|
||||
}
|
||||
|
||||
func register(srv *baseserver.Server, deps *registerDependencies) error {
|
||||
proxy.RegisterMetrics(srv.MetricsRegistry())
|
||||
auth.RegisterMetrics(srv.MetricsRegistry())
|
||||
|
||||
connectMetrics := NewConnectMetrics()
|
||||
err := connectMetrics.Register(srv.MetricsRegistry())
|
||||
@ -186,6 +192,7 @@ func register(srv *baseserver.Server, deps *registerDependencies) error {
|
||||
NewLogInterceptor(log.Log),
|
||||
auth.NewServerInterceptor(),
|
||||
origin.NewInterceptor(),
|
||||
auth.NewJWTCookieInterceptor(deps.expClient, deps.authCfg.Session.Cookie.Name, deps.authCfg.Session.Issuer, deps.sessionVerifier),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user