Files
zeekay 241625e7d9 rename: ziti → zt across all paths
- Repo names: ziti-* → zt-*
- Go modules: github.com/hanzozt/ziti → github.com/hanzozt/zt
- Docker images: hanzozt/ziti-* → hanzozt/zt-*
- C/Java/JS symbols: ziti_* → zt_*
- File and directory names updated
2026-02-18 18:34:19 -08:00

165 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 xgress_sdk
import (
"fmt"
"net"
"sync/atomic"
"github.com/michaelquigley/pfxlog"
"github.com/hanzozt/channel/v4/protobufs"
"github.com/hanzozt/foundation/v2/concurrenz"
"github.com/hanzozt/sdk-golang/xgress"
"github.com/hanzozt/sdk-golang/zt"
"github.com/hanzozt/sdk-golang/zt/edge"
"github.com/hanzozt/secretstream/kx"
"github.com/hanzozt/zt/v2/common"
"github.com/hanzozt/zt/v2/common/ctrl_msg"
"github.com/hanzozt/zt/v2/common/pb/edge_ctrl_pb"
"github.com/hanzozt/zt/v2/router/env"
"github.com/hanzozt/zt/v2/router/posture"
"github.com/hanzozt/zt/v2/router/xgress_common"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func NewFabric(env env.RouterEnv, options *xgress.Options) (Fabric, error) {
result := &fabricImpl{
env: env,
options: options,
}
env.MarkRouterDataModelRequired()
if err := env.GetRouterDataModel().SubscribeToIdentityChanges(env.GetRouterId().Token, result, true); err != nil {
return nil, err
}
return result, nil
}
type fabricImpl struct {
env env.RouterEnv
options *xgress.Options
currentIdentity atomic.Pointer[common.IdentityState]
servicesByName concurrenz.AtomicValue[map[string]*common.IdentityService]
}
func (self *fabricImpl) updateIdentityState(state *common.IdentityState) {
self.currentIdentity.Store(state)
servicesByName := map[string]*common.IdentityService{}
if state != nil {
for _, v := range state.Services {
servicesByName[v.Service.Name] = v
}
}
self.servicesByName.Store(servicesByName)
}
func (self *fabricImpl) NotifyIdentityEvent(state *common.IdentityState, eventType common.IdentityEventType) {
self.updateIdentityState(state)
pfxlog.Logger().Infof("identity %s event %s", state.Identity.Name, eventType.String())
if eventType == common.IdentityFullStateState {
for _, service := range state.Services {
pfxlog.Logger().Infof("identity %s gained access to %s", state.Identity.Name, service.GetName())
}
}
}
func (self *fabricImpl) NotifyServiceChange(state *common.IdentityState, oldService, newService *common.IdentityService, eventType common.ServiceEventType) {
self.updateIdentityState(state)
if eventType == common.ServiceAccessGainedEvent {
pfxlog.Logger().Infof("identity %s gained access to %s", state.Identity.Name, newService.GetName())
} else if eventType == common.ServiceAccessLostEvent {
pfxlog.Logger().Infof("identity %s lost access to %s", state.Identity.Name, oldService.GetName())
}
}
func (self *fabricImpl) TunnelWithOptions(serviceName string, options *zt.DialOptions, conn net.Conn, halfClose bool) error {
service := self.servicesByName.Load()[serviceName]
if service == nil {
return fmt.Errorf("service %s not found", serviceName)
}
keyPair, err := kx.NewKeyPair()
if err != nil {
return err
}
log := logrus.WithField("service", serviceName)
peerData := make(map[uint32][]byte)
if service.Service.EncryptionRequired {
peerData[uint32(edge.PublicKeyHeader)] = keyPair.Public()
}
if len(options.AppData) > 0 {
peerData[uint32(edge.AppDataHeader)] = options.AppData
}
peerData[uint32(ctrl_msg.InitiatorLocalAddressHeader)] = []byte(conn.LocalAddr().String())
peerData[uint32(ctrl_msg.InitiatorRemoteAddressHeader)] = []byte(conn.RemoteAddr().String())
ctrlCh := self.env.GetNetworkControllers().AnyChannel()
if ctrlCh == nil {
errStr := "no controller available, cannot create circuit"
log.Error(errStr)
return errors.New(errStr)
}
log = log.WithField("ctrlId", ctrlCh.Id())
rdm := self.env.GetRouterDataModel()
if policy, err := posture.HasAccess(rdm, self.env.GetRouterId().Token, service.GetId(), nil, edge_ctrl_pb.PolicyType_DialPolicy); err != nil && policy != nil {
return fmt.Errorf("router does not have access to service '%s' (%w)", serviceName, err)
}
request := &edge_ctrl_pb.CreateTunnelCircuitV2Request{
ServiceName: serviceName,
TerminatorInstanceId: options.Identity,
PeerData: peerData,
}
responseMsg, err := protobufs.MarshalTyped(request).WithTimeout(options.GetConnectTimeout()).SendForReply(ctrlCh)
response := &edge_ctrl_pb.CreateTunnelCircuitV2Response{}
if err = xgress_common.GetResultOrFailure(responseMsg, err, response); err != nil {
log.WithError(err).Warn("failed to dial fabric")
return err
}
peerKey, peerKeyFound := response.PeerData[uint32(edge.PublicKeyHeader)]
if service.Service.EncryptionRequired && !peerKeyFound {
return errors.New("service requires encryption, but public key header not returned")
}
xgConn := xgress_common.NewXgressConn(conn, halfClose, false)
if peerKeyFound {
if err = xgConn.SetupClientCrypto(keyPair, peerKey); err != nil {
return err
}
}
x := xgress.NewXgress(response.CircuitId, ctrlCh.Id(), xgress.Address(response.Address), xgConn, xgress.Initiator, self.options, response.Tags)
self.env.GetXgressBindHandler().HandleXgressBind(x)
x.Start()
return nil
}