mirror of
https://github.com/openziti/ziti.git
synced 2026-09-10 16:55:41 +00:00
948735d86c
* fixes openziti/ziti#4094 accept first-party certs issued by a separate edge signing CA - adds FirstPartyX509CertValidation and ThirdPartyX509CertValidation usages and an intermediates field to the router data model public keys, deprecating ClientX509CertValidation - publishes config CA bundle roots as first-party anchors with their intermediates and Ca store entries as third-party anchors; controller certs carry JWT validation only, since a controller identity is never a CA and anchors no client cert chains - builds router first-party and client cert trust pools from the published usages, falling back to the deprecated usage against older controllers - propagates the full signing cert chain between controllers via a new mesh SigningCertChainHeader and persists whole chains in controller records - removes the orphaned InstantStrategy.AddPublicKey, dead since public key sync moved to controller list data - gives each command dispatcher its own decoder registry so multiple in-process controllers no longer decode into the last-started controller's managers - adds a three-controller in-process HA test harness with a split signing PKI, cluster formation and first-party cert integration tests - trusts the edge signing CA when verifying router control channel certs - adds a variadic additionalRoots parameter to VerifyLeafCertChain, applied to a clone of the caller's pool so an identity's live tls.Configs are unaffected - passes the edge enrollment signing CA bundle as additional roots when admitting a router control channel connection, so a deployment whose signing CA sits outside the controller's own trust bundle no longer has every router refused - leaves the fingerprint check bound to the verified leaf, so the wider anchor set changes which chains verify, not which routers are admitted - covers the split-root case and the caller-pool guarantee in common/cert tests
168 lines
4.7 KiB
Go
168 lines
4.7 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 command
|
|
|
|
import (
|
|
"reflect"
|
|
"sync"
|
|
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/channel/v5"
|
|
"github.com/openziti/foundation/v2/debugz"
|
|
"github.com/openziti/foundation/v2/rate"
|
|
"github.com/openziti/ziti/v2/common/pb/ctrl_pb"
|
|
"github.com/openziti/ziti/v2/controller/change"
|
|
"github.com/openziti/ziti/v2/controller/storage/boltz"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Command instances represent actions to be taken by the fabric controller. They are serializable,
|
|
// so they can be shipped from one controller for RAFT coordination
|
|
type Command interface {
|
|
// Apply runs the commands
|
|
Apply(ctx boltz.MutateContext) error
|
|
|
|
// GetChangeContext returns the change context associated with the command
|
|
GetChangeContext() *change.Context
|
|
|
|
// Encode returns a serialized representation of the command
|
|
Encode() ([]byte, error)
|
|
}
|
|
|
|
// CriticalCommand marks commands that establish base state (e.g. a snapshot restore). A failed apply
|
|
// of one must halt the node rather than log-and-advance the raft index, which would leave the node
|
|
// caught up on index but missing data. Ordinary commands are logged and skipped on failure.
|
|
type CriticalCommand interface {
|
|
Command
|
|
IsCriticalCommand()
|
|
}
|
|
|
|
// Validatable instances can be validated. Command instances which implement Validable will be validated
|
|
// before Command.Apply is called
|
|
type Validatable interface {
|
|
Validate() error
|
|
}
|
|
|
|
// Dispatcher instances will take a command and either send it to the leader to be applied, or if the current
|
|
// system is the leader, apply it locally
|
|
type Dispatcher interface {
|
|
Dispatch(command Command) error
|
|
IsLeaderOrLeaderless() bool
|
|
IsLeaderless() bool
|
|
IsLeader() bool
|
|
GetPeers() map[string]channel.Channel
|
|
GetRateLimiter() rate.RateLimiter
|
|
Bootstrap() error
|
|
CtrlAddresses() (uint64, []string, []*ctrl_pb.CtrlDetail)
|
|
|
|
// GetDecoders returns the command decoder registry used to decode commands dispatched
|
|
// through this dispatcher. Each dispatcher owns its own registry so multiple controllers
|
|
// in one process don't decode each other's commands.
|
|
GetDecoders() Decoders
|
|
}
|
|
|
|
// LocalDispatcher should be used when running a non-clustered system
|
|
type LocalDispatcher struct {
|
|
EncodeDecodeCommands bool
|
|
Limiter rate.RateLimiter
|
|
|
|
decodersInit sync.Once
|
|
decoders Decoders
|
|
}
|
|
|
|
func (self *LocalDispatcher) GetDecoders() Decoders {
|
|
self.decodersInit.Do(func() {
|
|
self.decoders = NewDecoders()
|
|
})
|
|
return self.decoders
|
|
}
|
|
|
|
func (self *LocalDispatcher) Bootstrap() error {
|
|
return nil
|
|
}
|
|
|
|
func (self *LocalDispatcher) IsLeader() bool {
|
|
return true
|
|
}
|
|
|
|
func (self *LocalDispatcher) IsLeaderOrLeaderless() bool {
|
|
return true
|
|
}
|
|
|
|
func (self *LocalDispatcher) IsLeaderless() bool {
|
|
return false
|
|
}
|
|
|
|
func (self *LocalDispatcher) GetPeers() map[string]channel.Channel {
|
|
return nil
|
|
}
|
|
|
|
func (self *LocalDispatcher) GetRateLimiter() rate.RateLimiter {
|
|
return self.Limiter
|
|
}
|
|
|
|
func (self *LocalDispatcher) CtrlAddresses() (uint64, []string, []*ctrl_pb.CtrlDetail) {
|
|
return 0, nil, nil
|
|
}
|
|
|
|
func (self *LocalDispatcher) Dispatch(command Command) error {
|
|
defer func() {
|
|
if p := recover(); p != nil {
|
|
pfxlog.Logger().
|
|
WithField(logrus.ErrorKey, p).
|
|
WithField("cmdType", reflect.TypeOf(command)).
|
|
Error("error while dispatching command of type")
|
|
debugz.DumpLocalStack()
|
|
panic(p)
|
|
}
|
|
}()
|
|
|
|
changeCtx := command.GetChangeContext()
|
|
if changeCtx == nil {
|
|
changeCtx = change.New().SetSourceType("unattributed").SetChangeAuthorType(change.AuthorTypeUnattributed)
|
|
}
|
|
|
|
if self.EncodeDecodeCommands {
|
|
bytes, err := command.Encode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd, err := self.GetDecoders().Decode(bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
command = cmd
|
|
}
|
|
|
|
return self.Limiter.RunRateLimited(func() error {
|
|
ctx := changeCtx.NewMutateContext()
|
|
return command.Apply(ctx)
|
|
})
|
|
}
|
|
|
|
// Decoder instances know how to decode encoded commands
|
|
type Decoder interface {
|
|
Decode(commandType int32, data []byte) (Command, error)
|
|
}
|
|
|
|
// DecoderF is a function version of the Decoder interface
|
|
type DecoderF func(commandType int32, data []byte) (Command, error)
|
|
|
|
func (self DecoderF) Decode(commandType int32, data []byte) (Command, error) {
|
|
return self(commandType, data)
|
|
}
|