Documentation
¶
Overview ¶
Package rtunnels owns reverse-port-forward authorization and relay state.
Index ¶
- Constants
- Variables
- func CloseAuthorization(sessionID string, authorizationID AuthorizationID) int
- func CloseLocalIfActive(tunnel *RTunnel) (bool, error)
- func CloseRemoteIfActive(tunnel *RTunnel) bool
- func CloseSession(sessionID string) int
- func RemoveRTunnelIf(id uint64, expected *RTunnel) bool
- func TryAddRTunnel(tun *RTunnel) bool
- type Authorization
- type AuthorizationID
- type AuthorizationState
- type Broker
- type ContextDialer
- type DialContextFunc
- type RTunnel
- func (c *RTunnel) AuthorizationID() AuthorizationID
- func (c *RTunnel) Close()
- func (c *RTunnel) Done() <-chan struct{}
- func (c *RTunnel) IncReadSequence()
- func (c *RTunnel) IncWriteSequence()
- func (c *RTunnel) MarkPeerClose(sequence uint64) (bool, error)
- func (c *RTunnel) PeerClosePending() bool
- func (c *RTunnel) PeerCloseReady() bool
- func (c *RTunnel) PeerTeardownPending() bool
- func (c *RTunnel) ProcessInbound(sequence uint64, data []byte, write func([]byte) error) (int, error)
- func (c *RTunnel) QueueOutbound(send func(uint64) error) error
- func (c *RTunnel) ReadSequence() uint64
- func (c *RTunnel) SetPeerCloseNotifier(notifier func(uint64) error)
- func (c *RTunnel) StartPeerCloseDeadline(timeout time.Duration, expired func())
- func (c *RTunnel) WriteSequence() uint64
- type Registry
- func (registry *Registry) Activate(sessionID string, id AuthorizationID, listenerID uint32) error
- func (registry *Registry) ActivateProtocol(sessionID string, id AuthorizationID, listenerID uint32, ...) error
- func (registry *Registry) Begin(sessionID string, address string, keepAlive int32) (AuthorizationID, error)
- func (registry *Registry) BeginSpec(sessionID string, bindAddress string, address string, keepAlive int32) (AuthorizationID, error)
- func (registry *Registry) List(sessionID string) []Authorization
- func (registry *Registry) Lookup(sessionID string, id AuthorizationID) (Authorization, bool)
- func (registry *Registry) LookupListener(sessionID string, listenerID uint32) (Authorization, bool)
- func (registry *Registry) Revoke(sessionID string, id AuthorizationID) bool
- func (registry *Registry) RevokeListener(sessionID string, listenerID uint32) bool
- func (registry *Registry) RevokeSession(sessionID string) int
Constants ¶
const DefaultDialTimeout = 10 * time.Second
DefaultDialTimeout bounds an authorized reverse-port-forward connection attempt.
Variables ¶
var ( ErrInvalidSessionID = errors.New("invalid reverse port forward session ID") ErrInvalidForwardAddress = errors.New("invalid reverse port forward address") ErrUnknownAuthorization = errors.New("unknown reverse port forward authorization") ErrAuthorizationSession = errors.New("reverse port forward authorization belongs to another session") ErrAuthorizationRevoked = errors.New("reverse port forward authorization is revoked") ErrAuthorizationActive = errors.New("reverse port forward authorization is already active") ErrAuthorizationIDRequired = errors.New("reverse port forward authorization ID is required") ErrAuthorizationConnectionLimit = errors.New("reverse port forward authorization connection limit reached") ErrSessionConnectionLimit = errors.New("reverse port forward session connection limit reached") ErrGlobalConnectionLimit = errors.New("reverse port forward global connection limit reached") ErrAuthorizationReservation = errors.New("invalid reverse port forward connection reservation") ErrDuplicateListenerID = errors.New("reverse port forward listener ID is already registered") ErrAuthorizationIDGeneration = errors.New("failed to generate a unique reverse port forward authorization ID") )
ErrInvalidSessionID and the related errors report rejected authorizations.
var ( DefaultRegistry = NewRegistry() DefaultBroker = NewBroker(DefaultRegistry, nil, DefaultDialTimeout) )
DefaultRegistry and DefaultBroker are the production reverse port forward authorization components. Tests should prefer instance-owned registries.
var ( ErrReverseTunnelFrameTooLarge = errors.New("reverse tunnel frame exceeds the size limit") ErrReverseTunnelWindow = errors.New("reverse tunnel sequence exceeds the pending window") ErrReverseTunnelPendingBytes = errors.New("reverse tunnel pending data exceeds the byte limit") ErrReverseTunnelClosed = errors.New("reverse tunnel is closed") ErrReverseTunnelIngressLimit = errors.New("reverse tunnel inbound concurrency limit reached") ErrReverseTunnelAuthBudget = errors.New("reverse tunnel authorization pending-data budget reached") ErrReverseTunnelSessionBudget = errors.New("reverse tunnel session pending-data budget reached") ErrReverseTunnelGlobalBudget = errors.New("reverse tunnel global pending-data budget reached") ErrReverseTunnelTerminal = errors.New("reverse tunnel terminal sequence is invalid") )
ErrReverseTunnelFrameTooLarge and the related errors report rejected relay operations.
var ErrDuplicateTunnelID = errors.New("reverse tunnel ID is already registered")
ErrDuplicateTunnelID rejects reuse of an active reverse tunnel ID.
var ErrNilDialConnection = errors.New("reverse port forward dialer returned a nil connection")
ErrNilDialConnection rejects dialers that return success without a connection.
Functions ¶
func CloseAuthorization ¶ added in v1.7.7
func CloseAuthorization(sessionID string, authorizationID AuthorizationID) int
CloseAuthorization atomically detaches and closes active relays created by a specific authorization. It never closes another session's tunnel.
func CloseLocalIfActive ¶ added in v1.7.7
CloseLocalIfActive selects local-close semantics only while this exact generation is still published. The terminal state is established before the registry entry disappears, so retained writers cannot enqueue after detach.
func CloseRemoteIfActive ¶ added in v1.7.7
CloseRemoteIfActive closes an exact peer-terminated generation without an echo and removes it only after its closed state is visible to retained work.
func CloseSession ¶ added in v1.7.7
CloseSession atomically detaches and closes only tunnels owned by sessionID. It is safe to call repeatedly during disconnect cleanup.
func RemoveRTunnelIf ¶ added in v1.7.7
RemoveRTunnelIf removes ID only when it still points to expected. This keeps stale cleanup from deleting a different tunnel that reused the same ID.
func TryAddRTunnel ¶ added in v1.7.7
TryAddRTunnel atomically rejects duplicate tunnel IDs instead of replacing and orphaning an existing tunnel.
Types ¶
type Authorization ¶ added in v1.7.7
type Authorization struct {
AuthorizationID AuthorizationID
SessionID string
BindAddress string
Address string
KeepAlive int32
State AuthorizationState
ImplantListenerID uint32
HasListenerID bool
RequiresAuthorizationID bool
}
Authorization is an immutable snapshot of a server-owned reverse port forward authorization. Mutating a snapshot cannot affect the registry.
type AuthorizationID ¶ added in v1.7.7
type AuthorizationID string
AuthorizationID is an opaque, teamserver-generated capability identifying a reverse port forward authorization. Implants must not be able to choose it.
func (AuthorizationID) String ¶ added in v1.7.7
func (id AuthorizationID) String() string
type AuthorizationState ¶ added in v1.7.7
type AuthorizationState uint8
AuthorizationState describes the server-owned lifecycle of a reverse port forward listener authorization.
const ( AuthorizationStarting AuthorizationState = iota + 1 AuthorizationActive AuthorizationRevoked )
AuthorizationStarting and the related states describe authorization lifecycle.
func (AuthorizationState) String ¶ added in v1.7.7
func (state AuthorizationState) String() string
type Broker ¶ added in v1.7.7
type Broker struct {
// contains filtered or unexported fields
}
Broker resolves server-owned authorizations and opens their immutable dial plans. A caller can provide an implant address only for legacy lookup; that value is never passed to the dialer.
func NewBroker ¶ added in v1.7.7
func NewBroker(registry *Registry, dialer ContextDialer, dialTimeout time.Duration) *Broker
NewBroker creates an outbound reverse port forward broker. Nil dependencies use production-safe defaults, and non-positive timeouts use DefaultDialTimeout.
func (*Broker) Open ¶ added in v1.7.7
func (broker *Broker) Open(ctx context.Context, sessionID string, id AuthorizationID, legacyAddress string) (net.Conn, AuthorizationID, error)
Open resolves an authorization, dials only its stored operator destination, applies keepalive policy, and tracks the connection until it is closed or its authorization is revoked. Authorization revocation cancels pending dials and closes committed connections. The destination-bearing plan remains private; callers receive only the resolved capability for tunnel association.
type ContextDialer ¶ added in v1.7.7
type ContextDialer interface {
DialContext(ctx context.Context, network string, address string) (net.Conn, error)
}
ContextDialer is the only dependency allowed to create outbound reverse port forward connections. It is injectable so authorization-to-dial behavior can be tested without opening a real socket.
type DialContextFunc ¶ added in v1.7.7
DialContextFunc adapts a function into a ContextDialer for focused tests.
func (DialContextFunc) DialContext ¶ added in v1.7.7
func (dial DialContextFunc) DialContext(ctx context.Context, network string, address string) (net.Conn, error)
DialContext invokes the adapted dial function.
type RTunnel ¶
type RTunnel struct {
ID uint64
SessionID string
// Reader io.ReadCloser
Readers []io.ReadCloser
Writer io.WriteCloser
// contains filtered or unexported fields
}
RTunnel - Duplex byte read/write
func NewAuthorizedRTunnel ¶ added in v1.7.7
func NewAuthorizedRTunnel(id uint64, sessionID string, authorizationID AuthorizationID, writer io.WriteCloser, readers ...io.ReadCloser) *RTunnel
NewAuthorizedRTunnel associates a reverse tunnel with the authorization that created its outbound connection. NewRTunnel remains available for callers that do not use reverse port forward authorization.
func NewRTunnel ¶
func NewRTunnel(id uint64, sID string, writer io.WriteCloser, readers ...io.ReadCloser) *RTunnel
func (*RTunnel) AuthorizationID ¶ added in v1.7.7
func (c *RTunnel) AuthorizationID() AuthorizationID
AuthorizationID returns the server-owned authorization associated with the tunnel, or an empty ID for non-reverse tunnels.
func (*RTunnel) Done ¶ added in v1.7.7
func (c *RTunnel) Done() <-chan struct{}
Done is closed when the relay is revoked or otherwise closed. Writers use it to abandon a blocked implant send without leaking a goroutine.
func (*RTunnel) IncReadSequence ¶
func (c *RTunnel) IncReadSequence()
func (*RTunnel) IncWriteSequence ¶
func (c *RTunnel) IncWriteSequence()
func (*RTunnel) MarkPeerClose ¶ added in v1.7.7
MarkPeerClose records the exclusive last sequence expected from the implant. Legacy zero-sequence closes after data retain their immediate-close behavior.
func (*RTunnel) PeerClosePending ¶ added in v1.7.7
PeerClosePending reports whether the peer supplied a terminal sequence.
func (*RTunnel) PeerCloseReady ¶ added in v1.7.7
PeerCloseReady reports whether all frames preceding the peer terminal arrived.
func (*RTunnel) PeerTeardownPending ¶ added in v1.7.7
PeerTeardownPending reports peer-owned teardown without pretending that a sequenced terminal has been installed. Inbound ordering consults only PeerClosePending; notifier failure suppression uses this combined state.
func (*RTunnel) ProcessInbound ¶ added in v1.7.7
func (c *RTunnel) ProcessInbound(sequence uint64, data []byte, write func([]byte) error) (int, error)
ProcessInbound serializes one relay's inbound stream, bounds out-of-order buffering, and drains contiguous frames through write. Pending data is owned by this tunnel instance, so stale cleanup for a reused numeric ID cannot delete another generation's cache.
func (*RTunnel) QueueOutbound ¶ added in v1.7.7
QueueOutbound serializes server-to-implant frames with terminal close. The sequence advances only after the transport accepted the frame.
func (*RTunnel) ReadSequence ¶
func (*RTunnel) SetPeerCloseNotifier ¶ added in v1.7.7
SetPeerCloseNotifier installs the bounded transport callback used when the server is the side that first closes this relay. It must be set before the tunnel is published.
func (*RTunnel) StartPeerCloseDeadline ¶ added in v1.7.7
StartPeerCloseDeadline runs expired if the terminal sequence remains incomplete.
func (*RTunnel) WriteSequence ¶
type Registry ¶ added in v1.7.7
type Registry struct {
// contains filtered or unexported fields
}
Registry owns all reverse port forward authorizations. Its indexes contain only server-generated IDs and destinations derived from operator requests.
func NewRegistry ¶ added in v1.7.7
func NewRegistry() *Registry
NewRegistry creates an empty reverse port forward authorization registry.
func (*Registry) Activate ¶ added in v1.7.7
func (registry *Registry) Activate(sessionID string, id AuthorizationID, listenerID uint32) error
Activate binds an implant listener ID to a Starting authorization. A duplicate implant-selected listener ID never replaces or orphans the existing record: the rejected candidate is revoked atomically.
func (*Registry) ActivateProtocol ¶ added in v1.7.7
func (registry *Registry) ActivateProtocol(sessionID string, id AuthorizationID, listenerID uint32, requiresAuthorizationID bool) error
ActivateProtocol binds an implant listener ID and records whether the implant proved support for authorization IDs by echoing the issued ID in its start response. Legacy address lookup is disabled for capable listeners, so a new-implant protocol regression fails closed while older implants retain exact-address compatibility.
func (*Registry) Begin ¶ added in v1.7.7
func (registry *Registry) Begin(sessionID string, address string, keepAlive int32) (AuthorizationID, error)
Begin creates a Starting authorization for an operator-selected destination. Prefer BeginSpec when the operator's bind address is available.
func (*Registry) BeginSpec ¶ added in v1.7.7
func (registry *Registry) BeginSpec(sessionID string, bindAddress string, address string, keepAlive int32) (AuthorizationID, error)
BeginSpec creates a Starting authorization before the start request is sent to the implant. The destination and bind address are immutable thereafter.
func (*Registry) List ¶ added in v1.7.7
func (registry *Registry) List(sessionID string) []Authorization
List returns server-authoritative Starting and Active listeners for a session.
func (*Registry) Lookup ¶ added in v1.7.7
func (registry *Registry) Lookup(sessionID string, id AuthorizationID) (Authorization, bool)
Lookup returns an immutable snapshot for an authorization.
func (*Registry) LookupListener ¶ added in v1.7.7
func (registry *Registry) LookupListener(sessionID string, listenerID uint32) (Authorization, bool)
LookupListener returns an immutable snapshot for an implant listener ID.
func (*Registry) Revoke ¶ added in v1.7.7
func (registry *Registry) Revoke(sessionID string, id AuthorizationID) bool
Revoke prevents new acquisitions and closes pending or active broker-owned connections for an authorization. It is idempotent.
func (*Registry) RevokeListener ¶ added in v1.7.7
RevokeListener revokes by the implant's listener ID. It does not depend on a successful implant stop response and is idempotent.
func (*Registry) RevokeSession ¶ added in v1.7.7
RevokeSession revokes every authorization for a disconnected session, closes its broker-owned connections, and removes authorization tombstones. It is idempotent and never affects another session.