fix: exclude db connection error details (#7785)

* fix: exclude db connection error details

* remove potential recursive error

(cherry picked from commit cc0c06f225e21911e935111af06a67a67a010a71)
This commit is contained in:
Livio Spring 2024-04-23 10:35:25 +02:00
parent 24e42cdb7b
commit 61e3718622
No known key found for this signature in database
GPG Key ID: 26BB1C2FA5952CF0
7 changed files with 59 additions and 31 deletions

View File

@ -357,7 +357,7 @@ func startAPIs(
http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))),
)
limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig)
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, limitingAccessInterceptor)
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, config.ExternalDomain, limitingAccessInterceptor)
if err != nil {
return fmt.Errorf("error creating api %w", err)
}
@ -404,7 +404,7 @@ func startAPIs(
if err := apis.RegisterService(ctx, execution_v3_alpha.CreateServer(commands, queries)); err != nil {
return err
}
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...)
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, config.ExternalDomain, login.IgnoreInstanceEndpoints...)
assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge)
apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle))

View File

@ -49,7 +49,7 @@ func New(
queries *query.Queries,
verifier internal_authz.APITokenVerifier,
authZ internal_authz.Config,
tlsConfig *tls.Config, http2HostName, http1HostName string,
tlsConfig *tls.Config, http2HostName, http1HostName, externalDomain string,
accessInterceptor *http_mw.AccessInterceptor,
) (_ *API, err error) {
api := &API{
@ -62,7 +62,7 @@ func New(
accessInterceptor: accessInterceptor,
}
api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, tlsConfig, accessInterceptor.AccessService())
api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, externalDomain, tlsConfig, accessInterceptor.AccessService())
api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor, tlsConfig)
if err != nil {
return nil, err

View File

@ -2,6 +2,7 @@ package assets
import (
"context"
"errors"
"fmt"
"io"
"net/http"
@ -12,14 +13,17 @@ import (
"github.com/gabriel-vasile/mimetype"
"github.com/gorilla/mux"
"github.com/zitadel/logging"
"golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http"
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/id"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/static"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
@ -73,19 +77,29 @@ type Downloader interface {
type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, defaultCode int)
func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
logging.WithFields("uri", r.RequestURI).WithError(err).Warn("error occurred on asset api")
code, ok := http_util.ZitadelErrorToHTTPStatusCode(err)
if !ok {
code = defaultCode
func DefaultErrorHandler(translator *i18n.Translator) func(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
return func(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
logging.WithFields("uri", r.RequestURI).WithError(err).Warn("error occurred on asset api")
code, ok := http_util.ZitadelErrorToHTTPStatusCode(err)
if !ok {
code = defaultCode
}
zErr := new(zerrors.ZitadelError)
if errors.As(err, &zErr) {
zErr.SetMessage(translator.LocalizeFromCtx(r.Context(), zErr.GetMessage(), nil))
zErr.Parent = nil // ensuring we don't leak any unwanted information
err = zErr
}
http.Error(w, err.Error(), code)
}
http.Error(w, err.Error(), code)
}
func NewHandler(commands *command.Commands, verifier authz.APITokenVerifier, authConfig authz.Config, idGenerator id.Generator, storage static.Storage, queries *query.Queries, callDurationInterceptor, instanceInterceptor, assetCacheInterceptor, accessInterceptor func(handler http.Handler) http.Handler) http.Handler {
translator, err := i18n.NewZitadelTranslator(language.English)
logging.OnError(err).Panic("unable to get translator")
h := &Handler{
commands: commands,
errorHandler: DefaultErrorHandler,
errorHandler: DefaultErrorHandler(translator),
authInterceptor: http_mw.AuthorizationInterceptor(verifier, authConfig),
idGenerator: idGenerator,
storage: storage,

View File

@ -2,6 +2,7 @@ package gerrors
import (
"errors"
"strings"
"github.com/zitadel/logging"
"google.golang.org/grpc/codes"
@ -35,6 +36,9 @@ func ExtractZITADELError(err error) (c codes.Code, msg, id string, ok bool) {
if err == nil {
return codes.OK, "", "", false
}
if strings.Contains(err.Error(), "failed to connect to") { // version of pgx does not yet export the error type
return codes.Internal, "db connection error", "", true
}
zitadelErr := new(zerrors.ZitadelError)
if ok := errors.As(err, &zitadelErr); !ok {
return codes.Unknown, err.Error(), "", false

View File

@ -14,6 +14,7 @@ import (
"google.golang.org/grpc/status"
"github.com/zitadel/zitadel/internal/api/authz"
zitadel_http "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
@ -23,15 +24,15 @@ const (
HTTP1Host = "x-zitadel-http1-host"
)
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
translator, err := i18n.NewZitadelTranslator(language.English)
logging.OnError(err).Panic("unable to get translator")
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
return setInstance(ctx, req, info, handler, verifier, headerName, translator, explicitInstanceIdServices...)
return setInstance(ctx, req, info, handler, verifier, headerName, externalDomain, translator, explicitInstanceIdServices...)
}
}
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName, externalDomain string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }()
for _, service := range idFromRequestsServices {
@ -55,18 +56,21 @@ func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
return handler(authz.WithInstance(ctx, instance), req)
}
}
host, err := hostFromContext(interceptorCtx, headerName)
if err != nil {
return nil, status.Error(codes.NotFound, err.Error())
}
instance, err := verifier.InstanceByHost(interceptorCtx, host)
if err != nil {
notFoundErr := new(zerrors.NotFoundError)
if errors.As(err, &notFoundErr) {
notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil)
origin := zitadel_http.ComposedOrigin(ctx)
logging.WithFields("origin", origin, "externalDomain", externalDomain).WithError(err).Error("unable to set instance")
zErr := new(zerrors.ZitadelError)
if errors.As(err, &zErr) {
zErr.SetMessage(translator.LocalizeFromCtx(ctx, zErr.GetMessage(), nil))
zErr.Parent = err
return nil, status.Error(codes.NotFound, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s): %s", origin, externalDomain, zErr))
}
return nil, status.Error(codes.NotFound, err.Error())
return nil, status.Error(codes.NotFound, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s)", origin, externalDomain))
}
span.End()
return handler(authz.WithInstance(ctx, instance), req)

View File

@ -39,6 +39,7 @@ func CreateServer(
authConfig authz.Config,
queries *query.Queries,
hostHeaderName string,
externalDomain string,
tlsConfig *tls.Config,
accessSvc *logstore.Service[*record.AccessLog],
) *grpc.Server {
@ -50,7 +51,7 @@ func CreateServer(
middleware.DefaultTracingServer(),
middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
middleware.NoCacheInterceptor(),
middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
middleware.InstanceInterceptor(queries, hostHeaderName, externalDomain, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
middleware.AccessStorageInterceptor(accessSvc),
middleware.ErrorHandler(),
middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName),

View File

@ -19,16 +19,17 @@ import (
)
type instanceInterceptor struct {
verifier authz.InstanceVerifier
headerName string
ignoredPrefixes []string
translator *i18n.Translator
verifier authz.InstanceVerifier
headerName, externalDomain string
ignoredPrefixes []string
translator *i18n.Translator
}
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, ignoredPrefixes ...string) *instanceInterceptor {
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, ignoredPrefixes ...string) *instanceInterceptor {
return &instanceInterceptor{
verifier: verifier,
headerName: headerName,
externalDomain: externalDomain,
ignoredPrefixes: ignoredPrefixes,
translator: newZitadelTranslator(),
}
@ -55,11 +56,15 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
}
ctx, err := setInstance(r, a.verifier, a.headerName)
if err != nil {
caosErr := new(zerrors.NotFoundError)
if errors.As(err, &caosErr) {
caosErr.Message = a.translator.LocalizeFromRequest(r, caosErr.GetMessage(), nil)
origin := zitadel_http.ComposedOrigin(r.Context())
logging.WithFields("origin", origin, "externalDomain", a.externalDomain).WithError(err).Error("unable to set instance")
zErr := new(zerrors.ZitadelError)
if errors.As(err, &zErr) {
zErr.SetMessage(a.translator.LocalizeFromRequest(r, zErr.GetMessage(), nil))
http.Error(w, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s): %s", origin, a.externalDomain, zErr), http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusNotFound)
http.Error(w, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s)", origin, a.externalDomain), http.StatusNotFound)
return
}
r = r.WithContext(ctx)
@ -68,13 +73,13 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
func setInstance(r *http.Request, verifier authz.InstanceVerifier, headerName string) (_ context.Context, err error) {
ctx := r.Context()
authCtx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }()
host, err := HostFromRequest(r, headerName)
if err != nil {
return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.Instance.NotFound")
return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.IAM.NotFound")
}
instance, err := verifier.InstanceByHost(authCtx, host)