diff --git a/cmd/start/start.go b/cmd/start/start.go index e671aadf1..a19bec304 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -360,7 +360,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 nil, fmt.Errorf("error creating api %w", err) } @@ -410,7 +410,7 @@ func startAPIs( if err := apis.RegisterService(ctx, execution_v3_alpha.CreateServer(commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil { return nil, 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)) diff --git a/docs/docs/apis/introduction.mdx b/docs/docs/apis/introduction.mdx index 7ad38525b..7502b20be 100644 --- a/docs/docs/apis/introduction.mdx +++ b/docs/docs/apis/introduction.mdx @@ -268,6 +268,8 @@ ZITADEL hosts everything under a single domain: `{instance}.zitadel.cloud` or yo The domain is used as the OIDC issuer and as the base url for the gRPC and REST APIs, the Login and Console UI, which you'll find under `{your_domain}/ui/console/`. +Are you self-hosting and having troubles with *Instance not found* errors? [Check out this page](https://zitadel.com/docs/self-hosting/manage/custom-domain). + ## API path prefixes If you run ZITADEL on a custom domain, you may want to reuse that domain for other applications. diff --git a/internal/api/api.go b/internal/api/api.go index c4ae07f3f..2b94e1e28 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -74,7 +74,8 @@ 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{ @@ -87,7 +88,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 diff --git a/internal/api/grpc/server/middleware/instance_interceptor.go b/internal/api/grpc/server/middleware/instance_interceptor.go index 31f5e2c16..f5965beb3 100644 --- a/internal/api/grpc/server/middleware/instance_interceptor.go +++ b/internal/api/grpc/server/middleware/instance_interceptor.go @@ -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,16 +56,18 @@ 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, ¬FoundErr) { - notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil) + err = fmt.Errorf("unable to set instance using origin %s (ExternalDomain is %s): %w", zitadel_http.ComposedOrigin(ctx), externalDomain, err) + zErr := new(zerrors.ZitadelError) + if errors.As(err, &zErr) { + zErr.SetMessage(translator.LocalizeFromCtx(ctx, zErr.GetMessage(), nil)) + zErr.Parent = err + err = zErr } return nil, status.Error(codes.NotFound, err.Error()) } diff --git a/internal/api/grpc/server/middleware/instance_interceptor_test.go b/internal/api/grpc/server/middleware/instance_interceptor_test.go index 5de348c8d..132ced4e6 100644 --- a/internal/api/grpc/server/middleware/instance_interceptor_test.go +++ b/internal/api/grpc/server/middleware/instance_interceptor_test.go @@ -137,7 +137,7 @@ func Test_setInstance(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, nil) + got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, "", nil) if (err != nil) != tt.res.err { t.Errorf("setInstance() error = %v, wantErr %v", err, tt.res.err) return diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index c957dda02..4f0a6140b 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -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), diff --git a/internal/api/http/middleware/instance_interceptor.go b/internal/api/http/middleware/instance_interceptor.go index ac944278b..4b315d5a7 100644 --- a/internal/api/http/middleware/instance_interceptor.go +++ b/internal/api/http/middleware/instance_interceptor.go @@ -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,9 +56,12 @@ 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) + err = fmt.Errorf("unable to set instance using origin %s (ExternalDomain is %s): %w", zitadel_http.ComposedOrigin(r.Context()), a.externalDomain, err) + zErr := new(zerrors.ZitadelError) + if errors.As(err, &zErr) { + zErr.SetMessage(a.translator.LocalizeFromRequest(r, zErr.GetMessage(), nil)) + zErr.Parent = err + err = zErr } http.Error(w, err.Error(), http.StatusNotFound) return @@ -68,13 +72,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) diff --git a/internal/query/instance.go b/internal/query/instance.go index e528f3bcb..8bc83b2d4 100644 --- a/internal/query/instance.go +++ b/internal/query/instance.go @@ -6,6 +6,7 @@ import ( _ "embed" "encoding/json" "errors" + "fmt" "strings" "time" @@ -196,7 +197,12 @@ var ( func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Instance, err error) { ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() + defer func() { + if err != nil { + err = fmt.Errorf("unable to get instance by host %s: %w", host, err) + } + span.EndWithError(err) + }() domain := strings.Split(host, ":")[0] // remove possible port instance, scan := scanAuthzInstance(host, domain) diff --git a/internal/static/i18n/bg.yaml b/internal/static/i18n/bg.yaml index 85975c4f5..566f4c207 100644 --- a/internal/static/i18n/bg.yaml +++ b/internal/static/i18n/bg.yaml @@ -330,7 +330,7 @@ Errors: NotActive: Грантът по проекта не е активен NotInactive: Грантът по проекта не е неактивен IAM: - NotFound: Екземплярът не е намерен. Вижте https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM не е намерен. Уверете се, че сте получили правилния домейн. Вижте https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Ролите не са сменени MemberInvalid: Членът е невалиден diff --git a/internal/static/i18n/cs.yaml b/internal/static/i18n/cs.yaml index dfb7f75ab..619371c33 100644 --- a/internal/static/i18n/cs.yaml +++ b/internal/static/i18n/cs.yaml @@ -327,7 +327,7 @@ Errors: NotActive: Grant projektu není aktivní NotInactive: Grant projektu není neaktivní IAM: - NotFound: Instance nenalezena. Podívejte se na https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: Instance nebyla nalezena. Ujistěte se, že jste získali správnou doménu. Podívejte se na https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Role nebyly změněny MemberInvalid: Člen je neplatný diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 5876a5c67..7e2474091 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -328,7 +328,7 @@ Errors: NotActive: Projekt Grant ist nicht aktiv NotInactive: Projekt Grant ist nicht inaktiv IAM: - NotFound: Instanz nicht gefunden. Schau dir https://zitadel.com/docs/self-hosting/manage/custom-domain an + NotFound: Instanz nicht gefunden. Stelle sicher, dass Du die richtige Domain hast. Schau unter https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Rollen wurden nicht verändert MemberInvalid: Member ist ungültig diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 5a20617cb..f7a7ad40c 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -328,7 +328,7 @@ Errors: NotActive: Project grant is not active NotInactive: Project grant is not inactive IAM: - NotFound: Instance not found. Check out https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: Instance not found. Make sure you got the domain right. Check out https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Roles have not been changed MemberInvalid: Member is invalid diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index deddad8a3..87609eed9 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -328,7 +328,7 @@ Errors: NotActive: La concesión del proyecto no está activa NotInactive: La concesión del proyecto no está inactiva IAM: - NotFound: Instancia no encontrada. Consulta https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: Instancia no encontrada. Asegúrate de que tienes el dominio correcto. Consulta https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Los roles no han cambiado MemberInvalid: El miembro no es válido diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index 688994eae..66f48348f 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -328,7 +328,7 @@ Errors: NotActive: La subvention de projet n'est pas active NotInactive: La subvention du projet n'est pas inactive IAM: - NotFound: Instance non trouvée. Consultez https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM non trouvé. Assurez-vous que vous avez la bonne organisation. Vérifiez https://zitadel.com/docs/apis/introduction#organizations Member: RolesNotChanged: Les rôles n'ont pas été modifiés MemberInvalid: Le membre n'est pas valide diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 81ff5e51e..279bab293 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -329,7 +329,7 @@ Errors: NotActive: Grant del progetto non è attivo NotInactive: Grant del progetto non è inattivo IAM: - NotFound: Istanza non trovata. Controlla https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM non trovato. Assicurati di avere il dominio corretto. Guarda su https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: I ruoli non sono stati cambiati MemberInvalid: Il membro non è valido diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index 22ad9a7c6..7a0442e04 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -317,7 +317,7 @@ Errors: NotActive: プロジェクトグラントはアクティブではありません NotInactive: プロジェクトグラントは非アクティブではありません IAM: - NotFound: インスタンスが見つかりません https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAMが見つかりません。正しいドメインを持っていることを確認してください。 https://zitadel.com/docs/apis/introduction#domains を参照してください Member: RolesNotChanged: ロールは変更されていません MemberInvalid: 無効なメンバーです diff --git a/internal/static/i18n/mk.yaml b/internal/static/i18n/mk.yaml index 59d40a57e..44ea7a59f 100644 --- a/internal/static/i18n/mk.yaml +++ b/internal/static/i18n/mk.yaml @@ -327,7 +327,7 @@ Errors: NotActive: Овластувањето за проектот не е активно NotInactive: Овластувањето за проектот не е неактивно IAM: - NotFound: Инстанцата не е пронајдена. Проверете https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM не е пронајден. Проверете дали имате точен домен. Погледнете на https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Улогите не се променети MemberInvalid: Членот е невалиден diff --git a/internal/static/i18n/nl.yaml b/internal/static/i18n/nl.yaml index eea39453e..8a8fc78dd 100644 --- a/internal/static/i18n/nl.yaml +++ b/internal/static/i18n/nl.yaml @@ -328,7 +328,7 @@ Errors: NotActive: Projecttoekenning is niet actief NotInactive: Projecttoekenning is niet gedeactiveerd IAM: - NotFound: Instantie niet gevonden. Bekijk https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM niet gevonden. Zorg ervoor dat u het juiste domein heeft. Kijk op https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Rollen zijn niet veranderd MemberInvalid: Lid is ongeldig diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index ebcd9284b..fc2d7f620 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -328,7 +328,7 @@ Errors: NotActive: Grant projektu jest nieaktywny NotInactive: Grant projektu nie jest nieaktywny IAM: - NotFound: Instancja nie znaleziona. Sprawdź https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM nie znaleziony. Upewnij się, że masz poprawną domenę. Sprawdź https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Role nie zmienione MemberInvalid: Członek jest nieprawidłowy diff --git a/internal/static/i18n/pt.yaml b/internal/static/i18n/pt.yaml index b5cc0f6dd..3ea7cb1b3 100644 --- a/internal/static/i18n/pt.yaml +++ b/internal/static/i18n/pt.yaml @@ -326,7 +326,7 @@ Errors: NotActive: A concessão do projeto não está ativa NotInactive: A concessão do projeto não está inativa IAM: - NotFound: Instância não encontrada. Confira https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM não encontrado. Verifique se você tem o domínio correto. Consulte https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: As funções não foram alteradas MemberInvalid: O membro é inválido diff --git a/internal/static/i18n/ru.yaml b/internal/static/i18n/ru.yaml index 60a8f3568..76ce4ecb0 100644 --- a/internal/static/i18n/ru.yaml +++ b/internal/static/i18n/ru.yaml @@ -321,7 +321,7 @@ Errors: NotActive: Грант проекта не активен NotInactive: Грант проекта не неактивен IAM: - NotFound: Экземпляр не найден. Проверьте https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM не найден. Убедитесь, что у вас есть правильный домен. Смотрите на https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: Роли не изменились MemberInvalid: Участник недействителен diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index 4d30c732c..71cb5ee63 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -328,7 +328,7 @@ Errors: NotActive: 项目授权不是启用状态 NotInactive: 项目授权不是停用状态 IAM: - NotFound: 实例未找到。查看 https://zitadel.com/docs/self-hosting/manage/custom-domain + NotFound: IAM 未找到。确保您有正确的域。查看 https://zitadel.com/docs/apis/introduction#domains Member: RolesNotChanged: 角色没有改变 MemberInvalid: 成员无效