Files
teleport/internal/server/forward_test.go
T
s1d3sw1ped_bot 2d46d313d1
CI / check-and-test (pull_request) Successful in 11s
Encrypt TCP tunnel payload with AES-GCM.
TCP forwardData copied plaintext after the handshake. Frame each chunk as uint32 length plus AES-GCM ciphertext in both client and server, both directions.
2026-09-01 03:28:48 +00:00

119 lines
2.9 KiB
Go

package server
import (
"bytes"
"encoding/binary"
"io"
"net"
"testing"
"time"
"teleport/pkg/config"
"teleport/pkg/encryption"
)
func testServer(t *testing.T) *TeleportServer {
t.Helper()
return NewTeleportServer(&config.Config{
EncryptionKey: "a0e3dd20a761b118ca234160dd8b87230a001e332a97c9cfe3b8b9c99efaae03",
})
}
func TestForwardDataHidesHTTPPlaintextOnTunnel(t *testing.T) {
ts := testServer(t)
defer ts.Stop()
targetA, targetB := net.Pipe()
tunnelA, tunnelB := net.Pipe()
defer targetA.Close()
defer targetB.Close()
defer tunnelA.Close()
defer tunnelB.Close()
// target -> tunnel (encrypt)
go ts.forwardData(targetA, tunnelA, true)
resp := []byte("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello")
writeErr := make(chan error, 1)
go func() {
_, err := targetB.Write(resp)
writeErr <- err
}()
tunnelB.SetReadDeadline(time.Now().Add(5 * time.Second))
var length uint32
if err := binary.Read(tunnelB, binary.BigEndian, &length); err != nil {
t.Fatalf("failed to read frame length from tunnel: %v", err)
}
ciphertext := make([]byte, length)
if _, err := io.ReadFull(tunnelB, ciphertext); err != nil {
t.Fatalf("failed to read ciphertext from tunnel: %v", err)
}
if bytes.Contains(ciphertext, []byte("HTTP/1.1")) ||
bytes.Contains(ciphertext, []byte("Content-Type")) ||
bytes.Contains(ciphertext, []byte("hello")) {
t.Fatal("HTTP plaintext visible on server->client tunnel")
}
var frame bytes.Buffer
if err := binary.Write(&frame, binary.BigEndian, length); err != nil {
t.Fatal(err)
}
frame.Write(ciphertext)
plain, err := encryption.ReadEncryptedFrame(&frame, ts.derivedKey)
if err != nil {
t.Fatalf("decrypt of tunneled frame failed: %v", err)
}
if !bytes.Equal(plain, resp) {
t.Errorf("decrypted payload mismatch: got %q want %q", plain, resp)
}
select {
case err := <-writeErr:
if err != nil {
t.Fatalf("write to target conn failed: %v", err)
}
case <-time.After(5 * time.Second):
t.Fatal("timed out waiting for target write")
}
}
func TestForwardDataDecryptsTunnelToTarget(t *testing.T) {
ts := testServer(t)
defer ts.Stop()
tunnelA, tunnelB := net.Pipe()
targetA, targetB := net.Pipe()
defer tunnelA.Close()
defer tunnelB.Close()
defer targetA.Close()
defer targetB.Close()
go ts.forwardData(tunnelA, targetA, false)
req := []byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
writeErr := make(chan error, 1)
go func() {
writeErr <- encryption.WriteEncryptedFrame(tunnelB, req, ts.derivedKey)
}()
targetB.SetReadDeadline(time.Now().Add(5 * time.Second))
got := make([]byte, len(req))
if _, err := io.ReadFull(targetB, got); err != nil {
t.Fatalf("failed to read decrypted payload: %v", err)
}
if !bytes.Equal(got, req) {
t.Errorf("got %q want %q", got, req)
}
select {
case err := <-writeErr:
if err != nil {
t.Fatalf("WriteEncryptedFrame failed: %v", err)
}
case <-time.After(5 * time.Second):
t.Fatal("timed out waiting for tunnel write")
}
}