Documentation
¶
Overview ¶
Package tunnel implements secure access tunneling for plexd mesh nodes.
Index ¶
- Constants
- func GenerateHostKey() (ssh.Signer, error)
- func HandleSSHSessionSetup(mgr *SessionManager, reporter SessionActivityReporter) api.EventHandler
- func HandleSessionRevoked(mgr *SessionManager) api.EventHandler
- func LoadOrGenerateHostKey(dataDir string, logger *slog.Logger) (ssh.Signer, error)
- func TerminatedByFromReason(reason string) string
- type ClosedSessionInfo
- type Config
- type Ed25519JWTVerifier
- type JWTVerifier
- type K8sProxy
- type MeshServer
- type SSHServer
- type SSHServerConfig
- type Session
- type SessionActivityReporter
- type SessionManager
- func (m *SessionManager) ActiveCount() int
- func (m *SessionManager) CloseSession(sessionID, reason string) *ClosedSessionInfo
- func (m *SessionManager) CreateSession(ctx context.Context, setup api.SSHSessionSetup) (string, error)
- func (m *SessionManager) SetOnClosed(fn func(sessionID, reason string, info *ClosedSessionInfo))
- func (m *SessionManager) Shutdown()
Constants ¶
const DefaultMaxSSHSessions = 10
DefaultMaxSSHSessions is the default maximum number of concurrent SSH sessions.
const DefaultMaxSessions = 10
DefaultMaxSessions is the default maximum number of concurrent tunnel sessions.
const DefaultSSHIdleTimeout = 30 * time.Minute
DefaultSSHIdleTimeout is the default idle timeout for SSH connections.
const DefaultTimeout = 30 * time.Minute
DefaultTimeout is the default session timeout.
Variables ¶
This section is empty.
Functions ¶
func GenerateHostKey ¶
GenerateHostKey generates a new Ed25519 keypair and returns it as an ssh.Signer.
func HandleSSHSessionSetup ¶
func HandleSSHSessionSetup(mgr *SessionManager, reporter SessionActivityReporter) api.EventHandler
HandleSSHSessionSetup returns an api.EventHandler for ssh_session_setup events. It parses the SSE payload, creates a tunnel session via the SessionManager, and reports the session_started row via the SessionActivityReporter.
func HandleSessionRevoked ¶
func HandleSessionRevoked(mgr *SessionManager) api.EventHandler
HandleSessionRevoked returns an api.EventHandler for session_revoked events. It looks up the session by ID and closes it with reason "revoked"; the session_ended row is emitted by the manager's on-closed callback, keeping a single reporting path for every close reason. Revoking a non-existent session is a no-op.
func LoadOrGenerateHostKey ¶
LoadOrGenerateHostKey loads an existing Ed25519 host key from dataDir, or generates and persists a new one if none exists.
func TerminatedByFromReason ¶ added in v0.2.0
TerminatedByFromReason maps an internal session close reason to the wire terminated_by enum reported on a session_ended row: "expired" becomes ttl_expired, "revoked" becomes operator_revoke, and any other reason (including a local plexd-initiated close) becomes plexd_close. api.TerminatedByIdleTimeout is reserved and never produced here — the tunnel subsystem has no idle timer.
Types ¶
type ClosedSessionInfo ¶
type ClosedSessionInfo struct {
Duration time.Duration
TargetHost string
TargetPort int
BytesIn int64
BytesOut int64
}
ClosedSessionInfo contains metadata about a session that was closed.
type Config ¶
type Config struct {
// Enabled controls whether tunneling is active.
// Default: true (set by ApplyDefaults).
Enabled bool `yaml:"enabled"`
// MaxSessions is the maximum number of concurrent tunnel sessions.
// Default: 10
MaxSessions int `yaml:"max_sessions"`
// DefaultTimeout is the default/maximum session timeout.
// Default: 30m
DefaultTimeout time.Duration `yaml:"default_timeout"`
// SSHListenAddr is the address for the SSH mesh server to listen on.
// If empty, the SSH server is not started.
SSHListenAddr string `yaml:"ssh_listen_addr"`
// HostKeyDir is the directory for storing the SSH host key.
// If empty, a transient key is generated (not persisted).
HostKeyDir string `yaml:"host_key_dir"`
}
Config holds the configuration for secure access tunneling.
func (*Config) ApplyDefaults ¶
func (c *Config) ApplyDefaults()
ApplyDefaults sets default values for zero-valued fields. On a zero-valued Config, Enabled defaults to true. To disable tunneling, set Enabled=false before or after calling ApplyDefaults.
type Ed25519JWTVerifier ¶
type Ed25519JWTVerifier struct {
// contains filtered or unexported fields
}
Ed25519JWTVerifier verifies compact JWS tokens (alg=EdDSA) using an Ed25519 public key. It validates the signature and checks the "exp" claim without relying on any external JWT library.
func NewEd25519JWTVerifier ¶
func NewEd25519JWTVerifier(publicKey ed25519.PublicKey) *Ed25519JWTVerifier
NewEd25519JWTVerifier creates a new verifier with the given Ed25519 public key.
func (*Ed25519JWTVerifier) Verify ¶
func (v *Ed25519JWTVerifier) Verify(token string) error
Verify validates a compact JWS token (header.payload.signature). It checks the Ed25519 signature over the signing input and verifies the "exp" claim has not elapsed.
type JWTVerifier ¶
JWTVerifier validates JWT tokens for SSH authentication.
type K8sProxy ¶
type K8sProxy struct {
// contains filtered or unexported fields
}
K8sProxy is a reverse proxy that forwards HTTP requests to a Kubernetes API server.
func NewK8sProxy ¶
NewK8sProxy creates a K8sProxy targeting the given API server URL. If tlsConfig is non-nil, it is used for TLS connections to the API server.
type MeshServer ¶
type MeshServer struct {
// contains filtered or unexported fields
}
MeshServer composes the SSH mesh server with the existing SessionManager, managing their lifecycle together.
func NewMeshServer ¶
func NewMeshServer(cfg Config, hostKey ssh.Signer, verifier JWTVerifier, logger *slog.Logger) *MeshServer
NewMeshServer creates a MeshServer that manages the SSH server and session manager. If cfg.SSHListenAddr is empty, the SSH server is not created.
func (*MeshServer) SSHServer ¶
func (m *MeshServer) SSHServer() *SSHServer
SSHServer returns the underlying SSH server, or nil if not configured.
func (*MeshServer) SessionManager ¶
func (m *MeshServer) SessionManager() *SessionManager
SessionManager returns the underlying session manager.
func (*MeshServer) Shutdown ¶
func (m *MeshServer) Shutdown() error
Shutdown gracefully stops the mesh server. Ordering: close SSH listener first, then drain session manager.
type SSHServer ¶
type SSHServer struct {
// contains filtered or unexported fields
}
SSHServer is a mesh-facing SSH server that authenticates clients via JWT and provides direct-tcpip channel forwarding.
func NewSSHServer ¶
func NewSSHServer(cfg SSHServerConfig, hostKey ssh.Signer, verifier JWTVerifier, logger *slog.Logger) *SSHServer
NewSSHServer creates a new SSHServer with the given configuration.
type SSHServerConfig ¶
type SSHServerConfig struct {
// MaxSessions is the maximum number of concurrent SSH sessions.
// Default: 10
MaxSessions int
// IdleTimeout is the idle timeout for SSH connections.
// Default: 30m
IdleTimeout time.Duration
// ListenAddr is the address to listen on (mesh IP + port).
// Required.
ListenAddr string
}
SSHServerConfig holds the configuration for the SSH mesh server.
func (*SSHServerConfig) ApplyDefaults ¶
func (c *SSHServerConfig) ApplyDefaults()
ApplyDefaults sets default values for zero-valued fields.
func (*SSHServerConfig) Validate ¶
func (c *SSHServerConfig) Validate() error
Validate checks that configuration values are within acceptable ranges.
type Session ¶
type Session struct {
SessionID string
TargetHost string
TargetPort int
MeshIP string
// contains filtered or unexported fields
}
Session represents an active tunnel session with a local TCP listener that forwards connections to a target host through the mesh.
func NewSession ¶
func NewSession(sessionID, targetHost string, targetPort int, meshIP string, expiresAt time.Time, logger *slog.Logger) *Session
NewSession creates a Session with the given parameters.
func (*Session) Counters ¶ added in v0.2.0
Counters returns the bytes forwarded in each direction: in is client -> target (operator to target), out is target -> client (target to operator).
func (*Session) ListenAddr ¶
ListenAddr returns the listener address or empty string if not started.
type SessionActivityReporter ¶ added in v0.2.0
type SessionActivityReporter interface {
ReportSessionStarted(ctx context.Context, sessionID, targetHost string, targetPort int)
}
SessionActivityReporter reports the tcp-phase session_started row to the control plane once the listener is up. The matching session_ended row is emitted from the SessionManager's on-closed callback, not through here.
type SessionManager ¶
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager manages the lifecycle of tunnel sessions.
func NewSessionManager ¶
func NewSessionManager(cfg Config, meshIP string, logger *slog.Logger) *SessionManager
NewSessionManager creates a new SessionManager with default config applied.
func (*SessionManager) ActiveCount ¶
func (m *SessionManager) ActiveCount() int
ActiveCount returns the number of active sessions.
func (*SessionManager) CloseSession ¶
func (m *SessionManager) CloseSession(sessionID, reason string) *ClosedSessionInfo
CloseSession closes and removes a session by ID. Returns session metadata if the session existed, or nil if not found.
func (*SessionManager) CreateSession ¶
func (m *SessionManager) CreateSession(ctx context.Context, setup api.SSHSessionSetup) (string, error)
CreateSession creates and starts a new tunnel session.
func (*SessionManager) SetOnClosed ¶ added in v0.2.0
func (m *SessionManager) SetOnClosed(fn func(sessionID, reason string, info *ClosedSessionInfo))
SetOnClosed registers a callback invoked after a session is successfully closed and removed, for every close reason including "shutdown". The callback carries the close reason and the session's final metadata, and is the single path by which a session_ended activity row — TTL expiry, operator revoke, and node shutdown alike — reaches the control plane. Because it is the only carrier of the session's byte counters, skipping it on shutdown would leave the control plane's audit record for every live session without bytes_in, bytes_out, or terminated_by.
func (*SessionManager) Shutdown ¶
func (m *SessionManager) Shutdown()
Shutdown closes all active sessions, reporting each one through the on-closed callback with reason "shutdown" so its byte counters and a plexd_close terminated_by reach the control plane before the node goes offline.
The on-closed callback performs a blocking, bounded report, so the sessions are closed concurrently: total shutdown latency is then the single slowest report rather than their sum. Closing serially instead would let a slow or unreachable control plane stretch teardown to MaxSessions times the per-report bound, overrunning a typical orchestrator termination grace period.