2020-03-24 13:15:01 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-11-09 10:30:15 +00:00
|
|
|
"crypto/tls"
|
2022-02-14 16:22:30 +00:00
|
|
|
"fmt"
|
2020-03-24 13:15:01 +00:00
|
|
|
"net/http"
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
"slices"
|
2020-07-08 11:56:37 +00:00
|
|
|
"strings"
|
2020-03-24 13:15:01 +00:00
|
|
|
|
feat: protos refactoring
* start with user
* user first try done in all services
* user, org, idp for discussion
* remove unused stuff
* bla
* dockerbuild
* rename search, get multiple to list...
* add annotation
* update proto dependencies
* update proto dependencies
* change proto imports
* replace all old imports
* fix go out
* remove unused lines
* correct protoc flags
* grpc and openapi flags
* go out source path relative
* -p
* remove dead code
* sourcepath relative
* ls
* is onenapi the problem?
* hobla
* authoption output
* wrong field name
* gopf
* correct option, add correct flags
* small improvments
* SIMPLYFY
* relative path
* gopf bin ich en tubel
* correct path
* default policies in admin
* grpc generation in one file
* remove non ascii
* metadata on manipulations
* correct auth_option import
* fixes
* larry
* idp provider to idp
* fix generate
* admin and auth nearly done
* admin and auth nearly done
* gen
* healthz
* imports
* deleted too much imports
* fix org
* add import
* imports
* import
* naming
* auth_opt
* gopf
* management
* imports
* _TYPE_UNSPECIFIED
* improts
* auth opts
* management policies
* imports
* passwordlessType to MFAType
* auth_opt
* add user grant calls
* add missing messages
* result
* fix option
* improvements
* ids
* fix http
* imports
* fixes
* fields
* body
* add fields
* remove wrong member query
* fix request response
* fixes
* add copy files
* variable versions
* generate all files
* improvements
* add dependencies
* factors
* user session
* oidc information, iam
* remove unused file
* changes
* enums
* dockerfile
* fix build
* remove unused folder
* update readme for build
* move old server impl
* add event type to change
* some changes
* start admin
* remove wrong field
* admin only list calls missing
* fix proto numbers
* surprisingly it compiles
* service ts changes
* admin mgmt
* mgmt
* auth manipulation and gets done, lists missing
* validations and some field changes
* validations
* enum validations
* remove todo
* move proto files to proto/zitadel
* change proto path in dockerfile
* it compiles!
* add validate import
* remove duplicate import
* fix protos
* fix import
* tests
* cleanup
* remove unimplemented methods
* iam member multiple queries
* all auth and admin calls
* add initial password on crate human
* message names
* management user server
* machine done
* fix: todos (#1346)
* fix: pub sub in new eventstore
* fix: todos
* fix: todos
* fix: todos
* fix: todos
* fix: todos
* fix tests
* fix: search method domain
* admin service, user import type typescript
* admin changes
* admin changes
* fix: search method domain
* more user grpc and begin org, fix configs
* fix: return object details
* org grpc
* remove creation date add details
* app
* fix: return object details
* fix: return object details
* mgmt service, project members
* app
* fix: convert policies
* project, members, granted projects, searches
* fix: convert usergrants
* fix: convert usergrants
* auth user detail, user detail, mfa, second factor, auth
* fix: convert usergrants
* mfa, memberships, password, owned proj detail
* fix: convert usergrants
* project grant
* missing details
* changes, userview
* idp table, keys
* org list and user table filter
* unify rest paths (#1381)
* unify rest paths
* post for all searches,
mfa to multi_factor,
secondfactor to second_factor
* remove v1
* fix tests
* rename api client key to app key
* machine keys, age policy
* user list, machine keys, changes
* fix: org states
* add default flag to policy
* second factor to type
* idp id
* app type
* unify ListQuery, ListDetails, ObjectDetails field names
* user grants, apps, memberships
* fix type params
* metadata to detail, linke idps
* api create, membership, app detail, create
* idp, app, policy
* queries, multi -> auth factors and missing fields
* update converters
* provider to user, remove old mgmt refs
* temp remove authfactor dialog, build finish
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
2021-03-09 09:30:11 +00:00
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
2023-04-11 13:37:42 +00:00
|
|
|
"github.com/zitadel/logging"
|
feat: protos refactoring
* start with user
* user first try done in all services
* user, org, idp for discussion
* remove unused stuff
* bla
* dockerbuild
* rename search, get multiple to list...
* add annotation
* update proto dependencies
* update proto dependencies
* change proto imports
* replace all old imports
* fix go out
* remove unused lines
* correct protoc flags
* grpc and openapi flags
* go out source path relative
* -p
* remove dead code
* sourcepath relative
* ls
* is onenapi the problem?
* hobla
* authoption output
* wrong field name
* gopf
* correct option, add correct flags
* small improvments
* SIMPLYFY
* relative path
* gopf bin ich en tubel
* correct path
* default policies in admin
* grpc generation in one file
* remove non ascii
* metadata on manipulations
* correct auth_option import
* fixes
* larry
* idp provider to idp
* fix generate
* admin and auth nearly done
* admin and auth nearly done
* gen
* healthz
* imports
* deleted too much imports
* fix org
* add import
* imports
* import
* naming
* auth_opt
* gopf
* management
* imports
* _TYPE_UNSPECIFIED
* improts
* auth opts
* management policies
* imports
* passwordlessType to MFAType
* auth_opt
* add user grant calls
* add missing messages
* result
* fix option
* improvements
* ids
* fix http
* imports
* fixes
* fields
* body
* add fields
* remove wrong member query
* fix request response
* fixes
* add copy files
* variable versions
* generate all files
* improvements
* add dependencies
* factors
* user session
* oidc information, iam
* remove unused file
* changes
* enums
* dockerfile
* fix build
* remove unused folder
* update readme for build
* move old server impl
* add event type to change
* some changes
* start admin
* remove wrong field
* admin only list calls missing
* fix proto numbers
* surprisingly it compiles
* service ts changes
* admin mgmt
* mgmt
* auth manipulation and gets done, lists missing
* validations and some field changes
* validations
* enum validations
* remove todo
* move proto files to proto/zitadel
* change proto path in dockerfile
* it compiles!
* add validate import
* remove duplicate import
* fix protos
* fix import
* tests
* cleanup
* remove unimplemented methods
* iam member multiple queries
* all auth and admin calls
* add initial password on crate human
* message names
* management user server
* machine done
* fix: todos (#1346)
* fix: pub sub in new eventstore
* fix: todos
* fix: todos
* fix: todos
* fix: todos
* fix: todos
* fix tests
* fix: search method domain
* admin service, user import type typescript
* admin changes
* admin changes
* fix: search method domain
* more user grpc and begin org, fix configs
* fix: return object details
* org grpc
* remove creation date add details
* app
* fix: return object details
* fix: return object details
* mgmt service, project members
* app
* fix: convert policies
* project, members, granted projects, searches
* fix: convert usergrants
* fix: convert usergrants
* auth user detail, user detail, mfa, second factor, auth
* fix: convert usergrants
* mfa, memberships, password, owned proj detail
* fix: convert usergrants
* project grant
* missing details
* changes, userview
* idp table, keys
* org list and user table filter
* unify rest paths (#1381)
* unify rest paths
* post for all searches,
mfa to multi_factor,
secondfactor to second_factor
* remove v1
* fix tests
* rename api client key to app key
* machine keys, age policy
* user list, machine keys, changes
* fix: org states
* add default flag to policy
* second factor to type
* idp id
* app type
* unify ListQuery, ListDetails, ObjectDetails field names
* user grants, apps, memberships
* fix type params
* metadata to detail, linke idps
* api create, membership, app detail, create
* idp, app, policy
* queries, multi -> auth factors and missing fields
* update converters
* provider to user, remove old mgmt refs
* temp remove authfactor dialog, build finish
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
2021-03-09 09:30:11 +00:00
|
|
|
"google.golang.org/grpc"
|
2024-06-07 07:30:04 +00:00
|
|
|
"google.golang.org/grpc/codes"
|
2023-11-09 10:30:15 +00:00
|
|
|
"google.golang.org/grpc/credentials"
|
2022-02-14 16:22:30 +00:00
|
|
|
"google.golang.org/grpc/credentials/insecure"
|
2023-04-11 13:37:42 +00:00
|
|
|
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
2024-06-07 07:30:04 +00:00
|
|
|
"google.golang.org/grpc/status"
|
2021-04-19 15:20:18 +00:00
|
|
|
"google.golang.org/protobuf/encoding/protojson"
|
2023-05-04 08:50:19 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
2022-02-14 16:22:30 +00:00
|
|
|
|
2022-04-26 23:01:45 +00:00
|
|
|
client_middleware "github.com/zitadel/zitadel/internal/api/grpc/client/middleware"
|
2024-07-04 09:37:23 +00:00
|
|
|
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
2022-04-26 23:01:45 +00:00
|
|
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
2024-07-04 09:37:23 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/telemetry/metrics"
|
2020-03-24 13:15:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-02-14 16:22:30 +00:00
|
|
|
mimeWildcard = "*/*"
|
2020-03-24 13:15:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2022-02-14 16:22:30 +00:00
|
|
|
customHeaders = []string{
|
|
|
|
"x-zitadel-",
|
|
|
|
}
|
|
|
|
jsonMarshaler = &runtime.JSONPb{
|
2021-04-19 15:20:18 +00:00
|
|
|
UnmarshalOptions: protojson.UnmarshalOptions{
|
|
|
|
DiscardUnknown: true,
|
|
|
|
},
|
|
|
|
}
|
2020-03-24 13:15:01 +00:00
|
|
|
|
2024-06-07 07:30:04 +00:00
|
|
|
httpErrorHandler = runtime.RoutingErrorHandlerFunc(
|
|
|
|
func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
|
|
|
|
if httpStatus != http.StatusMethodNotAllowed {
|
|
|
|
runtime.DefaultRoutingErrorHandler(ctx, mux, marshaler, w, r, httpStatus)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use HTTPStatusError to customize the DefaultHTTPErrorHandler status code
|
|
|
|
err := &runtime.HTTPStatusError{
|
|
|
|
HTTPStatus: httpStatus,
|
|
|
|
Err: status.Error(codes.Unimplemented, http.StatusText(httpStatus)),
|
|
|
|
}
|
|
|
|
|
|
|
|
runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
serveMuxOptions = func(hostHeaders []string) []runtime.ServeMuxOption {
|
|
|
|
return []runtime.ServeMuxOption{
|
|
|
|
runtime.WithMarshalerOption(jsonMarshaler.ContentType(nil), jsonMarshaler),
|
|
|
|
runtime.WithMarshalerOption(mimeWildcard, jsonMarshaler),
|
|
|
|
runtime.WithMarshalerOption(runtime.MIMEWildcard, jsonMarshaler),
|
|
|
|
runtime.WithIncomingHeaderMatcher(headerMatcher(hostHeaders)),
|
|
|
|
runtime.WithOutgoingHeaderMatcher(runtime.DefaultHeaderMatcher),
|
|
|
|
runtime.WithForwardResponseOption(responseForwarder),
|
|
|
|
runtime.WithRoutingErrorHandler(httpErrorHandler),
|
|
|
|
}
|
2020-07-08 11:56:37 +00:00
|
|
|
}
|
|
|
|
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
headerMatcher = func(hostHeaders []string) runtime.HeaderMatcherFunc {
|
|
|
|
customHeaders = slices.Compact(append(customHeaders, hostHeaders...))
|
|
|
|
return func(header string) (string, bool) {
|
2020-07-08 11:56:37 +00:00
|
|
|
for _, customHeader := range customHeaders {
|
|
|
|
if strings.HasPrefix(strings.ToLower(header), customHeader) {
|
|
|
|
return header, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return runtime.DefaultHeaderMatcher(header)
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-04 08:50:19 +00:00
|
|
|
|
|
|
|
responseForwarder = func(ctx context.Context, w http.ResponseWriter, resp proto.Message) error {
|
|
|
|
t, ok := resp.(CustomHTTPResponse)
|
|
|
|
if ok {
|
|
|
|
// TODO: find a way to return a location header if needed w.Header().Set("location", t.Location())
|
|
|
|
w.WriteHeader(t.CustomHTTPCode())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-24 13:15:01 +00:00
|
|
|
)
|
|
|
|
|
2023-04-11 13:37:42 +00:00
|
|
|
type Gateway struct {
|
2023-05-15 06:51:02 +00:00
|
|
|
mux *runtime.ServeMux
|
|
|
|
connection *grpc.ClientConn
|
|
|
|
accessInterceptor *http_mw.AccessInterceptor
|
2020-03-24 13:15:01 +00:00
|
|
|
}
|
|
|
|
|
2023-04-11 13:37:42 +00:00
|
|
|
func (g *Gateway) Handler() http.Handler {
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
return addInterceptors(g.mux, g.accessInterceptor)
|
2023-04-11 13:37:42 +00:00
|
|
|
}
|
|
|
|
|
2023-05-04 08:50:19 +00:00
|
|
|
type CustomHTTPResponse interface {
|
|
|
|
CustomHTTPCode() int
|
|
|
|
}
|
|
|
|
|
2023-04-11 13:37:42 +00:00
|
|
|
type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error
|
2020-03-24 13:15:01 +00:00
|
|
|
|
2023-05-11 07:24:44 +00:00
|
|
|
func CreateGatewayWithPrefix(
|
|
|
|
ctx context.Context,
|
|
|
|
g WithGatewayPrefix,
|
|
|
|
port uint16,
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
hostHeaders []string,
|
2023-05-15 06:51:02 +00:00
|
|
|
accessInterceptor *http_mw.AccessInterceptor,
|
2023-11-09 10:30:15 +00:00
|
|
|
tlsConfig *tls.Config,
|
2023-05-11 07:24:44 +00:00
|
|
|
) (http.Handler, string, error) {
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
runtimeMux := runtime.NewServeMux(serveMuxOptions(hostHeaders)...)
|
2020-11-20 06:57:39 +00:00
|
|
|
opts := []grpc.DialOption{
|
2023-11-09 10:30:15 +00:00
|
|
|
grpc.WithTransportCredentials(grpcCredentials(tlsConfig)),
|
2023-11-28 15:56:29 +00:00
|
|
|
grpc.WithChainUnaryInterceptor(
|
|
|
|
client_middleware.DefaultTracingClient(),
|
|
|
|
client_middleware.UnaryActivityClientInterceptor(),
|
|
|
|
),
|
2020-11-20 06:57:39 +00:00
|
|
|
}
|
2023-04-11 13:37:42 +00:00
|
|
|
connection, err := dial(ctx, port, opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
err = g.RegisterGateway()(ctx, runtimeMux, connection)
|
2022-02-14 16:22:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, "", fmt.Errorf("failed to register grpc gateway: %w", err)
|
2020-03-24 13:15:01 +00:00
|
|
|
}
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
return addInterceptors(runtimeMux, accessInterceptor), g.GatewayPathPrefix(), nil
|
2020-03-24 13:15:01 +00:00
|
|
|
}
|
|
|
|
|
2023-11-09 10:30:15 +00:00
|
|
|
func CreateGateway(
|
|
|
|
ctx context.Context,
|
|
|
|
port uint16,
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
hostHeaders []string,
|
2023-11-09 10:30:15 +00:00
|
|
|
accessInterceptor *http_mw.AccessInterceptor,
|
|
|
|
tlsConfig *tls.Config,
|
|
|
|
) (*Gateway, error) {
|
2023-04-11 13:37:42 +00:00
|
|
|
connection, err := dial(ctx,
|
|
|
|
port,
|
|
|
|
[]grpc.DialOption{
|
2023-11-09 10:30:15 +00:00
|
|
|
grpc.WithTransportCredentials(grpcCredentials(tlsConfig)),
|
2023-11-28 15:56:29 +00:00
|
|
|
grpc.WithChainUnaryInterceptor(
|
|
|
|
client_middleware.DefaultTracingClient(),
|
|
|
|
client_middleware.UnaryActivityClientInterceptor(),
|
|
|
|
),
|
2023-04-11 13:37:42 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
feat: trusted (instance) domains (#8369)
# Which Problems Are Solved
ZITADEL currently selects the instance context based on a HTTP header
(see https://github.com/zitadel/zitadel/issues/8279#issue-2399959845 and
checks it against the list of instance domains. Let's call it instance
or API domain.
For any context based URL (e.g. OAuth, OIDC, SAML endpoints, links in
emails, ...) the requested domain (instance domain) will be used. Let's
call it the public domain.
In cases of proxied setups, all exposed domains (public domains) require
the domain to be managed as instance domain.
This can either be done using the "ExternalDomain" in the runtime config
or via system API, which requires a validation through CustomerPortal on
zitadel.cloud.
# How the Problems Are Solved
- Two new headers / header list are added:
- `InstanceHostHeaders`: an ordered list (first sent wins), which will
be used to match the instance.
(For backward compatibility: the `HTTP1HostHeader`, `HTTP2HostHeader`
and `forwarded`, `x-forwarded-for`, `x-forwarded-host` are checked
afterwards as well)
- `PublicHostHeaders`: an ordered list (first sent wins), which will be
used as public host / domain. This will be checked against a list of
trusted domains on the instance.
- The middleware intercepts all requests to the API and passes a
`DomainCtx` object with the hosts and protocol into the context
(previously only a computed `origin` was passed)
- HTTP / GRPC server do not longer try to match the headers to instances
themself, but use the passed `http.DomainContext` in their interceptors.
- The `RequestedHost` and `RequestedDomain` from authz.Instance are
removed in favor of the `http.DomainContext`
- When authenticating to or signing out from Console UI, the current
`http.DomainContext(ctx).Origin` (already checked by instance
interceptor for validity) is used to compute and dynamically add a
`redirect_uri` and `post_logout_redirect_uri`.
- Gateway passes all configured host headers (previously only did
`x-zitadel-*`)
- Admin API allows to manage trusted domain
# Additional Changes
None
# Additional Context
- part of #8279
- open topics:
- "single-instance" mode
- Console UI
2024-07-31 15:00:38 +00:00
|
|
|
runtimeMux := runtime.NewServeMux(append(serveMuxOptions(hostHeaders), runtime.WithHealthzEndpoint(healthpb.NewHealthClient(connection)))...)
|
2023-04-11 13:37:42 +00:00
|
|
|
return &Gateway{
|
2023-05-15 06:51:02 +00:00
|
|
|
mux: runtimeMux,
|
|
|
|
connection: connection,
|
|
|
|
accessInterceptor: accessInterceptor,
|
2023-04-11 13:37:42 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func RegisterGateway(ctx context.Context, gateway *Gateway, server Server) error {
|
|
|
|
err := server.RegisterGateway()(ctx, gateway.mux, gateway.connection)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to register grpc gateway: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func dial(ctx context.Context, port uint16, opts []grpc.DialOption) (*grpc.ClientConn, error) {
|
|
|
|
endpoint := fmt.Sprintf("localhost:%d", port)
|
|
|
|
conn, err := grpc.Dial(endpoint, opts...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
if cerr := conn.Close(); cerr != nil {
|
|
|
|
logging.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
<-ctx.Done()
|
|
|
|
if cerr := conn.Close(); cerr != nil {
|
|
|
|
logging.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
return conn, nil
|
|
|
|
}
|
|
|
|
|
2023-05-11 07:24:44 +00:00
|
|
|
func addInterceptors(
|
|
|
|
handler http.Handler,
|
2023-05-15 06:51:02 +00:00
|
|
|
accessInterceptor *http_mw.AccessInterceptor,
|
2023-05-11 07:24:44 +00:00
|
|
|
) http.Handler {
|
2023-02-27 21:36:43 +00:00
|
|
|
handler = http_mw.CallDurationHandler(handler)
|
2022-06-03 12:44:04 +00:00
|
|
|
handler = http_mw.CORSInterceptor(handler)
|
2023-05-05 08:25:02 +00:00
|
|
|
handler = http_mw.RobotsTagHandler(handler)
|
2020-12-02 07:50:59 +00:00
|
|
|
handler = http_mw.DefaultTelemetryHandler(handler)
|
2023-11-28 15:56:29 +00:00
|
|
|
handler = http_mw.ActivityHandler(handler)
|
2023-05-11 07:24:44 +00:00
|
|
|
// For some non-obvious reason, the exhaustedCookieInterceptor sends the SetCookie header
|
|
|
|
// only if it follows the http_mw.DefaultTelemetryHandler
|
2024-01-17 10:16:48 +00:00
|
|
|
handler = exhaustedCookieInterceptor(handler, accessInterceptor)
|
2024-07-04 09:37:23 +00:00
|
|
|
handler = http_mw.MetricsHandler([]metrics.MetricType{
|
|
|
|
metrics.MetricTypeTotalCount,
|
|
|
|
metrics.MetricTypeStatusCode,
|
|
|
|
}, http_utils.Probes...)(handler)
|
2023-05-11 07:24:44 +00:00
|
|
|
return handler
|
2022-06-03 12:44:04 +00:00
|
|
|
}
|
|
|
|
|
2023-05-11 07:24:44 +00:00
|
|
|
func exhaustedCookieInterceptor(
|
|
|
|
next http.Handler,
|
2023-05-15 06:51:02 +00:00
|
|
|
accessInterceptor *http_mw.AccessInterceptor,
|
2023-05-11 07:24:44 +00:00
|
|
|
) http.Handler {
|
|
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
next.ServeHTTP(&cookieResponseWriter{
|
2023-05-15 06:51:02 +00:00
|
|
|
ResponseWriter: writer,
|
|
|
|
accessInterceptor: accessInterceptor,
|
|
|
|
request: request,
|
2023-05-11 07:24:44 +00:00
|
|
|
}, request)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type cookieResponseWriter struct {
|
|
|
|
http.ResponseWriter
|
2023-05-15 06:51:02 +00:00
|
|
|
accessInterceptor *http_mw.AccessInterceptor
|
|
|
|
request *http.Request
|
2024-01-17 10:16:48 +00:00
|
|
|
headerWritten bool
|
2023-05-11 07:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *cookieResponseWriter) WriteHeader(status int) {
|
|
|
|
if status >= 200 && status < 300 {
|
2023-05-19 05:12:31 +00:00
|
|
|
r.accessInterceptor.DeleteExhaustedCookie(r.ResponseWriter)
|
2023-05-11 07:24:44 +00:00
|
|
|
}
|
|
|
|
if status == http.StatusTooManyRequests {
|
2023-05-15 06:51:02 +00:00
|
|
|
r.accessInterceptor.SetExhaustedCookie(r.ResponseWriter, r.request)
|
2023-05-11 07:24:44 +00:00
|
|
|
}
|
2024-01-17 10:16:48 +00:00
|
|
|
r.headerWritten = true
|
2023-05-11 07:24:44 +00:00
|
|
|
r.ResponseWriter.WriteHeader(status)
|
|
|
|
}
|
2023-11-09 10:30:15 +00:00
|
|
|
|
2024-01-17 10:16:48 +00:00
|
|
|
func (r *cookieResponseWriter) Write(bytes []byte) (int, error) {
|
|
|
|
if !r.headerWritten {
|
|
|
|
// If no header was written before the data, the status code is 200 and we can delete the cookie
|
|
|
|
r.accessInterceptor.DeleteExhaustedCookie(r.ResponseWriter)
|
|
|
|
}
|
|
|
|
return r.ResponseWriter.Write(bytes)
|
|
|
|
}
|
|
|
|
|
2023-11-09 10:30:15 +00:00
|
|
|
func grpcCredentials(tlsConfig *tls.Config) credentials.TransportCredentials {
|
|
|
|
creds := insecure.NewCredentials()
|
|
|
|
if tlsConfig != nil {
|
|
|
|
tlsConfigClone := tlsConfig.Clone()
|
|
|
|
// We don't want to verify the certificate of the internal grpc server
|
|
|
|
// That's up to the client who called the gRPC gateway
|
|
|
|
tlsConfigClone.InsecureSkipVerify = true
|
|
|
|
creds = credentials.NewTLS(tlsConfigClone)
|
|
|
|
}
|
|
|
|
return creds
|
|
|
|
}
|