Skip to content

Commit 54e5559

Browse files
authored
Propagate DIFC proxy TLS CA trust to git/gh/curl via process environment (#4042)
`gh repo clone` / `git clone` failed through the DIFC TLS proxy (`https://localhost:18443/...`) because clients could not validate the proxy’s generated certificate chain. This change propagates the generated proxy CA to the toolchains that perform HTTPS validation in agent workflows. - **Proxy startup: propagate CA trust to common clients** - On `awmg proxy --tls`, set process env vars to the generated `ca.crt`: - `NODE_EXTRA_CA_CERTS` - `SSL_CERT_FILE` - `GIT_SSL_CAINFO` - `CURL_CA_BUNDLE` - `REQUESTS_CA_BUNDLE` - This covers `gh`, `git`, `curl`, and other TLS consumers that honor standard CA env settings. - **No `GITHUB_ENV` dependency** - TLS trust propagation no longer reads or writes `GITHUB_ENV`. - Proxy startup relies only on process environment exports for trust configuration. - **Operator-facing output and docs** - Proxy connection hints include git-relevant exports (`SSL_CERT_FILE`, `GIT_SSL_CAINFO`). - `docs/PROXY_MODE.md` documents CA setup for `gh` + `git` without `GITHUB_ENV` automation. - **Focused unit coverage** - Added/updated tests for: - env var propagation - no reliance on `GITHUB_ENV` (including asserting the file remains unchanged) - rejection of newline-containing CA paths ```go var tlsTrustEnvKeys = []string{ "NODE_EXTRA_CA_CERTS", "SSL_CERT_FILE", "GIT_SSL_CAINFO", "CURL_CA_BUNDLE", "REQUESTS_CA_BUNDLE", } ``` > [!WARNING] > >
2 parents f6df700 + a2d0e31 commit 54e5559

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

docs/PROXY_MODE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ awmg proxy \
2222
# Trust the generated CA and point gh at the proxy
2323
export GH_HOST=localhost:8443
2424
export NODE_EXTRA_CA_CERTS=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
25+
export SSL_CERT_FILE=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
26+
export GIT_SSL_CAINFO=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
2527
gh issue list -R org/repo
2628
```
2729

@@ -167,6 +169,8 @@ docker run --rm -p 8443:8443 \
167169
# Trust the CA cert from the mounted log volume
168170
export GH_HOST=localhost:8443
169171
export NODE_EXTRA_CA_CERTS=/tmp/proxy-logs/proxy-tls/ca.crt
172+
export SSL_CERT_FILE=/tmp/proxy-logs/proxy-tls/ca.crt
173+
export GIT_SSL_CAINFO=/tmp/proxy-logs/proxy-tls/ca.crt
170174
gh issue list -R org/repo
171175
```
172176

@@ -219,6 +223,8 @@ When `--tls` is enabled, the proxy writes to `--tls-dir` (default: `<log-dir>/pr
219223
**gh CLI / Node.js**:
220224
```bash
221225
export NODE_EXTRA_CA_CERTS=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
226+
export SSL_CERT_FILE=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
227+
export GIT_SSL_CAINFO=/tmp/gh-aw/mcp-logs/proxy-tls/ca.crt
222228
```
223229

224230
**System-wide (Ubuntu)**:

internal/cmd/proxy.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"os/signal"
1212
"path/filepath"
13+
"strings"
1314
"syscall"
1415
"time"
1516

@@ -24,6 +25,14 @@ import (
2425

2526
var logProxyCmd = logger.New("cmd:proxy")
2627

28+
var tlsTrustEnvKeys = []string{
29+
"NODE_EXTRA_CA_CERTS",
30+
"SSL_CERT_FILE",
31+
"GIT_SSL_CAINFO",
32+
"CURL_CA_BUNDLE",
33+
"REQUESTS_CA_BUNDLE",
34+
}
35+
2736
// Proxy subcommand flag variables
2837
var (
2938
proxyGuardWasm string
@@ -223,6 +232,9 @@ func runProxy(cmd *cobra.Command, args []string) error {
223232
if err != nil {
224233
return fmt.Errorf("failed to generate TLS certificates: %w", err)
225234
}
235+
if err := configureTLSTrustEnvironment(tlsCfg.CACertPath); err != nil {
236+
return err
237+
}
226238
logger.LogInfo("startup", "TLS certificates generated: ca=%s", tlsCfg.CACertPath)
227239
}
228240

@@ -268,6 +280,8 @@ func runProxy(cmd *cobra.Command, args []string) error {
268280
fmt.Fprintf(os.Stderr, "\nConnect with:\n")
269281
fmt.Fprintf(os.Stderr, " export GH_HOST=%s\n", clientAddr(actualAddr))
270282
fmt.Fprintf(os.Stderr, " export NODE_EXTRA_CA_CERTS=%s\n", tlsCfg.CACertPath)
283+
fmt.Fprintf(os.Stderr, " export SSL_CERT_FILE=%s\n", tlsCfg.CACertPath)
284+
fmt.Fprintf(os.Stderr, " export GIT_SSL_CAINFO=%s\n", tlsCfg.CACertPath)
271285
fmt.Fprintf(os.Stderr, " gh issue list -R org/repo\n\n")
272286
} else {
273287
fmt.Fprintf(os.Stderr, "\nConnect with:\n")
@@ -302,3 +316,16 @@ func clientAddr(addr string) string {
302316
}
303317
return addr
304318
}
319+
320+
func configureTLSTrustEnvironment(caCertPath string) error {
321+
if strings.ContainsAny(caCertPath, "\r\n") {
322+
return fmt.Errorf("invalid TLS CA cert path contains newline")
323+
}
324+
325+
for _, key := range tlsTrustEnvKeys {
326+
if err := os.Setenv(key, caCertPath); err != nil {
327+
return fmt.Errorf("failed to set %s: %w", key, err)
328+
}
329+
}
330+
return nil
331+
}

internal/cmd/proxy_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,49 @@ func TestClientAddr(t *testing.T) {
409409
})
410410
}
411411
}
412+
413+
func TestConfigureTLSTrustEnvironment(t *testing.T) {
414+
caPath := "/tmp/proxy-tls/ca.crt"
415+
416+
t.Run("sets trust environment variables in process", func(t *testing.T) {
417+
assert := assert.New(t)
418+
t.Setenv("GITHUB_ENV", "")
419+
for _, key := range tlsTrustEnvKeys {
420+
t.Setenv(key, "")
421+
}
422+
423+
err := configureTLSTrustEnvironment(caPath)
424+
require.NoError(t, err)
425+
426+
for _, key := range tlsTrustEnvKeys {
427+
assert.Equal(caPath, os.Getenv(key), "expected %s to be set", key)
428+
}
429+
})
430+
431+
t.Run("does not rely on GITHUB_ENV", func(t *testing.T) {
432+
assert := assert.New(t)
433+
githubEnvFile := t.TempDir() + "/github_env"
434+
const original = "UNCHANGED=1\n"
435+
require.NoError(t, os.WriteFile(githubEnvFile, []byte(original), 0o644))
436+
t.Setenv("GITHUB_ENV", githubEnvFile)
437+
for _, key := range tlsTrustEnvKeys {
438+
t.Setenv(key, "")
439+
}
440+
441+
require.NoError(t, configureTLSTrustEnvironment(caPath))
442+
443+
for _, key := range tlsTrustEnvKeys {
444+
assert.Equal(caPath, os.Getenv(key), "expected %s to be set", key)
445+
}
446+
447+
content, err := os.ReadFile(githubEnvFile)
448+
require.NoError(t, err)
449+
assert.Equal(original, string(content))
450+
})
451+
452+
t.Run("rejects CA cert path with newline", func(t *testing.T) {
453+
err := configureTLSTrustEnvironment("/tmp/ca.crt\nMALICIOUS=1")
454+
require.Error(t, err)
455+
assert.Contains(t, err.Error(), "contains newline")
456+
})
457+
}

0 commit comments

Comments
 (0)