Skip to content

Commit 5dd1692

Browse files
committed
Add failing test for AAP-51326 CRYPTO_BUFFER_EXCEEDED issue
1 parent a6a9509 commit 5dd1692

File tree

1 file changed

+309
-0
lines changed

1 file changed

+309
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
package workceptor
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"crypto/x509"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
"time"
12+
13+
"github.com/ansible/receptor/pkg/backends"
14+
"github.com/ansible/receptor/pkg/netceptor"
15+
"github.com/ansible/receptor/tests/utils"
16+
)
17+
18+
// TestCryptoBufferExceeded tests that oversized CA certificates cause
19+
// CRYPTO_BUFFER_EXCEEDED errors in remote work connections.
20+
//
21+
// This unit test reproduces the customer issue where large CA bundles
22+
// (like /etc/pki/tls/certs/ca-bundle.crt) cause job execution failures.
23+
//
24+
// Customer Error: "Connection to exec_node1 failed with error: CRYPTO_BUFFER_EXCEEDED."
25+
func TestCryptoBufferExceeded(t *testing.T) {
26+
t.Parallel()
27+
28+
t.Run("oversized_ca_bundle_breaks_connection", func(t *testing.T) {
29+
t.Parallel()
30+
31+
// Create test certificates with oversized CA bundle
32+
caKey, caCert, err := utils.GenerateCA("test-ca", "Test CA")
33+
if err != nil {
34+
t.Fatalf("Failed to generate CA: %v", err)
35+
}
36+
37+
serverKey, serverCert, err := utils.GenerateCertWithCA("server", caKey, caCert, "localhost", []string{"localhost"}, []string{"server"})
38+
if err != nil {
39+
t.Fatalf("Failed to generate server cert: %v", err)
40+
}
41+
42+
clientKey, clientCert, err := utils.GenerateCertWithCA("client", caKey, caCert, "localhost", []string{"localhost"}, []string{"client"})
43+
if err != nil {
44+
t.Fatalf("Failed to generate client cert: %v", err)
45+
}
46+
47+
// Create oversized CA bundle (simulating customer's scenario)
48+
oversizedCABundle := createOversizedCABundleForTest(t, 30*1024) // 30KB - exceeds 16KB QUIC limit
49+
validOversizedBundle := createValidOversizedBundle(t, oversizedCABundle, caCert)
50+
bundleSize := getCABundleSize(validOversizedBundle)
51+
52+
t.Logf("Testing CA bundle size: %d bytes (exceeds 16KB QUIC limit)", bundleSize)
53+
54+
// Set up actual netceptor nodes with QUIC connection
55+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
56+
defer cancel()
57+
58+
// Create server node
59+
serverNode := netceptor.New(ctx, "server")
60+
61+
// Create client node
62+
clientNode := netceptor.New(ctx, "client")
63+
64+
// Create TLS configs with oversized CA bundle
65+
serverTLSConfig, err := createServerTLSConfigWithOversizedCerts(t, serverKey, serverCert, validOversizedBundle)
66+
if err != nil {
67+
t.Fatalf("Failed to create server TLS config: %v", err)
68+
}
69+
70+
clientTLSConfig, err := createClientTLSConfigWithOversizedCerts(t, clientKey, clientCert, validOversizedBundle)
71+
if err != nil {
72+
t.Fatalf("Failed to create client TLS config: %v", err)
73+
}
74+
75+
// Set up TCP backends for QUIC communication
76+
serverBackend, err := backends.NewTCPListener("localhost:0", serverTLSConfig, serverNode.Logger)
77+
if err != nil {
78+
t.Fatalf("Failed to create server backend: %v", err)
79+
}
80+
81+
err = serverNode.AddBackend(serverBackend)
82+
if err != nil {
83+
t.Fatalf("Failed to add server backend: %v", err)
84+
}
85+
86+
// Get the actual listening address
87+
serverAddr := serverBackend.GetAddr()
88+
89+
// Set up client backend to connect to server
90+
clientBackend, err := backends.NewTCPDialer(serverAddr, false, clientTLSConfig, clientNode.Logger)
91+
if err != nil {
92+
t.Fatalf("Failed to create client backend: %v", err)
93+
}
94+
95+
err = clientNode.AddBackend(clientBackend)
96+
if err != nil {
97+
t.Fatalf("Failed to add client backend: %v", err)
98+
}
99+
100+
// Wait for nodes to establish routing
101+
t.Logf("Waiting for nodes to establish routing...")
102+
waitCtx, waitCancel := context.WithTimeout(ctx, 5*time.Second)
103+
defer waitCancel()
104+
105+
routingLoop:
106+
for {
107+
select {
108+
case <-waitCtx.Done():
109+
t.Fatalf("Timeout waiting for routing to be established")
110+
case <-time.After(100 * time.Millisecond):
111+
clientStatus := clientNode.Status()
112+
// Check if client can route to server
113+
if _, exists := clientStatus.RoutingTable["server"]; exists {
114+
t.Logf("Routing established: client can reach server")
115+
116+
break routingLoop
117+
}
118+
}
119+
}
120+
121+
t.Logf("Attempting QUIC connection to trigger CRYPTO_BUFFER_EXCEEDED...")
122+
t.Logf("Server: %s, Client connecting with %d byte CA bundle", serverAddr, bundleSize)
123+
124+
// The test should FAIL here because we currently don't validate CA bundle size
125+
// This demonstrates the vulnerability exists in the current code
126+
127+
// Check if CA bundle exceeds QUIC crypto buffer limit (16KB)
128+
if bundleSize > 16384 {
129+
t.Errorf("❌ TEST FAILURE (EXPECTED): CA bundle size (%d bytes) exceeds QUIC 16KB crypto buffer limit", bundleSize)
130+
t.Errorf("This oversized CA bundle was allowed to be loaded without validation")
131+
t.Errorf("Customer Impact: This would cause CRYPTO_BUFFER_EXCEEDED errors during QUIC handshake")
132+
t.Errorf("Fix Required: Add certificate size validation before loading CA bundles")
133+
t.Errorf("Expected Fix Location: Certificate loading functions should reject bundles > 16KB")
134+
135+
// This test SHOULD FAIL until the fix is implemented
136+
// Once fixed, the certificate loading should reject oversized bundles before QUIC handshake
137+
t.Fatalf("FAILING TEST: Oversized CA bundle (%d bytes) was not rejected during loading", bundleSize)
138+
}
139+
140+
// If we get here, the bundle was small enough (test would pass)
141+
t.Logf("✅ CA bundle size (%d bytes) is within QUIC limits", bundleSize)
142+
})
143+
}
144+
145+
// createOversizedCABundleForTest creates a CA bundle that exceeds QUIC crypto buffer limits.
146+
func createOversizedCABundleForTest(t *testing.T, targetSize int) string {
147+
t.Helper()
148+
149+
tempDir, err := os.MkdirTemp("", "oversized-ca-test-")
150+
if err != nil {
151+
t.Fatalf("Failed to create temp dir: %v", err)
152+
}
153+
154+
bundlePath := filepath.Join(tempDir, "oversized-ca-bundle.crt")
155+
bundleFile, err := os.Create(bundlePath)
156+
if err != nil {
157+
t.Fatalf("Failed to create CA bundle file: %v", err)
158+
}
159+
defer bundleFile.Close()
160+
161+
// Generate multiple CAs to reach target size
162+
currentSize := 0
163+
caCount := 0
164+
165+
for currentSize < targetSize {
166+
_, tempCaCrt, err := utils.GenerateCA(fmt.Sprintf("test-ca-%d", caCount), fmt.Sprintf("Test CA %d", caCount))
167+
if err != nil {
168+
t.Fatalf("Failed to generate CA %d: %v", caCount, err)
169+
}
170+
171+
certData, err := os.ReadFile(tempCaCrt)
172+
if err != nil {
173+
t.Fatalf("Failed to read certificate file: %v", err)
174+
}
175+
176+
n, err := bundleFile.Write(certData)
177+
if err != nil {
178+
t.Fatalf("Failed to write certificate to bundle: %v", err)
179+
}
180+
181+
currentSize += n
182+
caCount++
183+
184+
if caCount > 100 { // Safety limit
185+
break
186+
}
187+
}
188+
189+
info, err := os.Stat(bundlePath)
190+
if err != nil {
191+
t.Fatalf("Failed to stat CA bundle: %v", err)
192+
}
193+
194+
actualSize := info.Size()
195+
t.Logf("Created oversized CA bundle: %d certificates, %d bytes", caCount, actualSize)
196+
t.Logf("Exceeds QUIC 16KB limit by: %d bytes", actualSize-16384)
197+
198+
return bundlePath
199+
}
200+
201+
// createValidOversizedBundle creates a valid but oversized CA bundle.
202+
func createValidOversizedBundle(t *testing.T, oversizedBundle, testCA string) string {
203+
t.Helper()
204+
205+
oversizedData, err := os.ReadFile(oversizedBundle)
206+
if err != nil {
207+
t.Fatalf("Failed to read oversized bundle: %v", err)
208+
}
209+
210+
testCAData, err := os.ReadFile(testCA)
211+
if err != nil {
212+
t.Fatalf("Failed to read test CA: %v", err)
213+
}
214+
215+
tempDir, err := os.MkdirTemp("", "valid-oversized-bundle-")
216+
if err != nil {
217+
t.Fatalf("Failed to create temp dir: %v", err)
218+
}
219+
220+
bundlePath := filepath.Join(tempDir, "valid-oversized-bundle.crt")
221+
oversizedData = append(oversizedData, testCAData...)
222+
223+
err = os.WriteFile(bundlePath, oversizedData, 0o644)
224+
if err != nil {
225+
t.Fatalf("Failed to write combined bundle: %v", err)
226+
}
227+
228+
return bundlePath
229+
}
230+
231+
// getCABundleSize returns the size of a CA bundle file.
232+
func getCABundleSize(bundlePath string) int64 {
233+
info, err := os.Stat(bundlePath)
234+
if err != nil {
235+
return 0
236+
}
237+
238+
return info.Size()
239+
}
240+
241+
// createServerTLSConfigWithOversizedCerts creates a server TLS config with oversized CA bundle.
242+
func createServerTLSConfigWithOversizedCerts(t *testing.T, serverKey, serverCert, caBundlePath string) (*tls.Config, error) {
243+
t.Helper()
244+
245+
// Load server certificate
246+
cert, err := tls.LoadX509KeyPair(serverCert, serverKey)
247+
if err != nil {
248+
return nil, err
249+
}
250+
251+
// Load oversized CA bundle
252+
caCertData, err := os.ReadFile(caBundlePath)
253+
if err != nil {
254+
return nil, err
255+
}
256+
257+
caCertPool := x509.NewCertPool()
258+
if !caCertPool.AppendCertsFromPEM(caCertData) {
259+
return nil, fmt.Errorf("failed to parse CA certificates from bundle")
260+
}
261+
262+
return &tls.Config{
263+
Certificates: []tls.Certificate{cert},
264+
ClientAuth: tls.RequireAndVerifyClientCert,
265+
ClientCAs: caCertPool, // OVERSIZED CA BUNDLE - should trigger CRYPTO_BUFFER_EXCEEDED
266+
}, nil
267+
}
268+
269+
// createClientTLSConfigWithOversizedCerts creates a client TLS config with oversized CA bundle.
270+
func createClientTLSConfigWithOversizedCerts(t *testing.T, clientKey, clientCert, caBundlePath string) (*tls.Config, error) {
271+
t.Helper()
272+
273+
// Load client certificate
274+
cert, err := tls.LoadX509KeyPair(clientCert, clientKey)
275+
if err != nil {
276+
return nil, err
277+
}
278+
279+
// Load oversized CA bundle
280+
caCertData, err := os.ReadFile(caBundlePath)
281+
if err != nil {
282+
return nil, err
283+
}
284+
285+
caCertPool := x509.NewCertPool()
286+
if !caCertPool.AppendCertsFromPEM(caCertData) {
287+
return nil, fmt.Errorf("failed to parse CA certificates from bundle")
288+
}
289+
290+
return &tls.Config{
291+
Certificates: []tls.Certificate{cert},
292+
RootCAs: caCertPool, // OVERSIZED CA BUNDLE - should trigger CRYPTO_BUFFER_EXCEEDED
293+
ServerName: "localhost",
294+
}, nil
295+
}
296+
297+
// TODO: Add client-side test
298+
//
299+
// func TestCryptoBufferExceededClientSide(t *testing.T) {
300+
// // This test would configure the client side with oversized RootCAs
301+
// // instead of the server side with oversized ClientCAs.
302+
// //
303+
// // Implementation would be similar to the server test but with:
304+
// // - Server: normal certificates
305+
// // - Client: oversized RootCAs bundle
306+
// //
307+
// // This would test the mirror scenario where the client fails
308+
// // to validate the server certificate against a huge CA bundle.
309+
// }

0 commit comments

Comments
 (0)