Encrypt TCP tunnel payload with AES-GCM.
CI / check-and-test (pull_request) Successful in 11s

TCP forwardData copied plaintext after the handshake. Frame each chunk as uint32 length plus AES-GCM ciphertext in both client and server, both directions.
This commit is contained in:
s1d3sw1ped_bot
2026-09-01 03:28:48 +00:00
parent 1fef0b6a70
commit 2d46d313d1
6 changed files with 524 additions and 22 deletions
+42 -12
View File
@@ -28,6 +28,7 @@ type TeleportClient struct {
cancel context.CancelFunc
connectionPool chan net.Conn
maxPoolSize int
derivedKey []byte
}
// NewTeleportClient creates a new teleport client
@@ -49,6 +50,7 @@ func NewTeleportClient(config *config.Config) *TeleportClient {
cancel: cancel,
connectionPool: make(chan net.Conn, maxPoolSize),
maxPoolSize: maxPoolSize,
derivedKey: encryption.DeriveKey(config.EncryptionKey),
}
}
@@ -650,23 +652,34 @@ func (tc *TeleportClient) handleTCPConnection(clientConn net.Conn, rule config.P
var wg sync.WaitGroup
wg.Add(2)
// Forward data from client to server
// Encrypt local plaintext onto the tunnel
go func() {
defer wg.Done()
tc.forwardData(clientConn, serverConn)
tc.forwardData(clientConn, serverConn, true)
}()
// Forward data from server to client
// Decrypt tunnel frames to the local connection
go func() {
defer wg.Done()
tc.forwardData(serverConn, clientConn)
tc.forwardData(serverConn, clientConn, false)
}()
wg.Wait()
}
// forwardData forwards data from src to dst
func (tc *TeleportClient) forwardData(src, dst net.Conn) {
// forwardData copies between a local/plaintext conn and the tunnel.
// When toTunnel is true, plaintext is AES-GCM encrypted and written as
// uint32 length + ciphertext. When false, framed ciphertext is decrypted
// and written as plaintext.
func (tc *TeleportClient) forwardData(src, dst net.Conn, toTunnel bool) {
if toTunnel {
tc.forwardPlainToTunnel(src, dst)
return
}
tc.forwardTunnelToPlain(src, dst)
}
func (tc *TeleportClient) forwardPlainToTunnel(src, dst net.Conn) {
buffer := make([]byte, 4096)
for {
select {
@@ -675,14 +688,32 @@ func (tc *TeleportClient) forwardData(src, dst net.Conn) {
default:
n, err := src.Read(buffer)
if err != nil {
// Close the destination connection when source closes
dst.Close()
return
}
if n == 0 {
continue
}
if err := encryption.WriteEncryptedFrame(dst, buffer[:n], tc.derivedKey); err != nil {
src.Close()
return
}
}
}
}
_, err = dst.Write(buffer[:n])
func (tc *TeleportClient) forwardTunnelToPlain(src, dst net.Conn) {
for {
select {
case <-tc.ctx.Done():
return
default:
plain, err := encryption.ReadEncryptedFrame(src, tc.derivedKey)
if err != nil {
// Close the source connection when destination closes
dst.Close()
return
}
if _, err := dst.Write(plain); err != nil {
src.Close()
return
}
@@ -703,9 +734,8 @@ func (tc *TeleportClient) sendRequestToConnection(conn net.Conn, request types.P
return err
}
// Encrypt the data
key := encryption.DeriveKey(tc.config.EncryptionKey)
encryptedData, err := encryption.EncryptData(data, key)
// Encrypt the data (key derived once at process start)
encryptedData, err := encryption.EncryptData(data, tc.derivedKey)
if err != nil {
logger.WithFields(map[string]interface{}{
"error": err,