Files
Paul Lorenz d7076430c9 Make ER/T terminator create failures diagnosable. Fixes #4193
- reports edge router policy denials with an access-denied error naming the missing policy,
  replacing the session error reused on the sessionless ER/T and create-circuit-v3 paths
- adds EdgeRouterManager.GetEdgeRouterAccess, which reports which of the two required policy
  links (identity-to-edge-router, service-to-edge-router) is absent, and removes the boolean
  IsAccessToEdgeRouterAllowed it replaces
- logs the controller's rejection on the router at warn level, since it is recoverable and
  retried by the periodic scan; the router previously discarded the error code and message
- delays a new terminator's first create attempt by a fixed 2s so config applied in quick
  succession settles before the router asks, avoiding a 2-3 minute wait for the retry scan; the
  delay is a deliberate stopgap until edge router policy visibility lands in the router data model
- propagates the controller's error code and retry hint to SDK clients on the dial paths, which
  dropped the code and left every refusal classified as unknown
- adds the retry hint header to controller error replies, grouped with the other error-reply
  headers rather than the create-circuit-v3 request headers
- notes that the sync strategy headers alias the edge namespace's 1013-1015 ids and stay
  disjoint only by message content type
- tests the per-policy denial reporting, the error code carried with and without a retry hint,
  the controller-to-SDK error code mapping at both dial relay sites, and the terminator settle
  gate
- waits for terminator establishment in the tunneler dataflow tests instead of a fixed sleep, so
  they no longer race the settle delay
- restores the tproxy multiple-lanIf and multiple-resolver changelog entries with keep markers,
  which regeneration drops because their commits reference pull requests rather than issues
2026-08-03 15:43:30 -04:00

238 lines
5.4 KiB
Go

/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package handler_edge_ctrl
import (
"errors"
"github.com/openziti/sdk-golang/v2/ziti/edge"
"github.com/openziti/ziti/v2/common/pb/edge_ctrl_pb"
)
type controllerError interface {
error
ErrorCode() uint32
GetRetryHint() edge.RetryHint
}
func retryHintToResult(hint edge.RetryHint) edge_ctrl_pb.CreateTerminatorResult {
switch hint {
case edge.RetryTooBusy:
return edge_ctrl_pb.CreateTerminatorResult_FailedBusy
default:
return edge_ctrl_pb.CreateTerminatorResult_FailedOther
}
}
func internalError(err error) controllerError {
if err == nil {
return nil
}
var ctrlErr controllerError
if errors.As(err, &ctrlErr) {
return ctrlErr
}
return internalErrorWrapper{error: err}
}
func nonRetriableError(err error) controllerError {
if err == nil {
return nil
}
return nonRetriableErrorWrapper{error: err}
}
type internalErrorWrapper struct {
error
}
func (internalErrorWrapper) ErrorCode() uint32 {
return edge.ErrorCodeInternal
}
func (internalErrorWrapper) GetRetryHint() edge.RetryHint {
return edge.RetryDefault
}
func busyError(err error) controllerError {
if err == nil {
return nil
}
return &genericControllerError{
Message: err.Error(),
Code: edge.ErrorCodeInternal,
RetryHint: edge.RetryTooBusy,
}
}
type nonRetriableErrorWrapper struct {
error
}
func (nonRetriableErrorWrapper) ErrorCode() uint32 {
return edge.ErrorCodeInternal
}
func (nonRetriableErrorWrapper) GetRetryHint() edge.RetryHint {
return edge.RetryNotRetriable
}
type InvalidApiSessionError struct{}
func (InvalidApiSessionError) Error() string {
return "invalid api session"
}
func (self InvalidApiSessionError) ErrorCode() uint32 {
return edge.ErrorCodeInvalidApiSession
}
func (InvalidApiSessionError) GetRetryHint() edge.RetryHint {
return edge.RetryStartOver
}
type InvalidSessionError struct{}
func (InvalidSessionError) Error() string {
return "invalid session"
}
func (self InvalidSessionError) ErrorCode() uint32 {
return edge.ErrorCodeInvalidSession
}
func (InvalidSessionError) GetRetryHint() edge.RetryHint {
return edge.RetryStartOver
}
type WrongSessionTypeError struct{}
func (WrongSessionTypeError) Error() string {
return "incorrect session type"
}
func (self WrongSessionTypeError) ErrorCode() uint32 {
return edge.ErrorCodeWrongSessionType
}
func (WrongSessionTypeError) GetRetryHint() edge.RetryHint {
return edge.RetryStartOver
}
type InvalidEdgeRouterForSessionError struct{}
func (InvalidEdgeRouterForSessionError) Error() string {
return "invalid edge router for session"
}
func (self InvalidEdgeRouterForSessionError) ErrorCode() uint32 {
return edge.ErrorCodeInvalidEdgeRouterForSession
}
func (InvalidEdgeRouterForSessionError) GetRetryHint() edge.RetryHint {
return edge.RetryStartOver
}
// edgeRouterAccessDenied reports that an edge router is not linked by policy to the identity
// and/or the service it is trying to act on. It is used where no edge session is involved,
// such as an edge router tunneler hosting or dialing a service on its own behalf.
func edgeRouterAccessDenied(msg string) controllerError {
return &genericControllerError{
Message: msg,
Code: edge.ErrorCodeAccessDenied,
RetryHint: edge.RetryDefault,
}
}
type InvalidServiceError struct{}
func (InvalidServiceError) Error() string {
return "invalid service"
}
func (self InvalidServiceError) ErrorCode() uint32 {
return edge.ErrorCodeInvalidService
}
func (InvalidServiceError) GetRetryHint() edge.RetryHint {
return edge.RetryNotRetriable
}
type TunnelingNotEnabledError struct{}
func (TunnelingNotEnabledError) Error() string {
return "tunneling not enabled"
}
func (self TunnelingNotEnabledError) ErrorCode() uint32 {
return edge.ErrorCodeTunnelingNotEnabled
}
func (TunnelingNotEnabledError) GetRetryHint() edge.RetryHint {
return edge.RetryNotRetriable
}
func invalidTerminator(msg string) controllerError {
return &genericControllerError{
Message: msg,
Code: edge.ErrorCodeInvalidTerminator,
RetryHint: edge.RetryNotRetriable,
}
}
func invalidCost(msg string) controllerError {
return &genericControllerError{
Message: msg,
Code: edge.ErrorCodeInvalidCost,
RetryHint: edge.RetryNotRetriable,
}
}
func invalidPrecedence(msg string) controllerError {
return &genericControllerError{
Message: msg,
Code: edge.ErrorCodeInvalidPrecedence,
RetryHint: edge.RetryNotRetriable,
}
}
func encryptionDataMissing(msg string) controllerError {
return &genericControllerError{
Message: msg,
Code: edge.ErrorCodeEncryptionDataMissing,
RetryHint: edge.RetryStartOver,
}
}
type genericControllerError struct {
Message string
Code uint32
RetryHint edge.RetryHint
}
func (self *genericControllerError) Error() string {
return self.Message
}
func (self *genericControllerError) ErrorCode() uint32 {
return self.Code
}
func (self *genericControllerError) GetRetryHint() edge.RetryHint {
return self.RetryHint
}