Skip to content

Commit f6f5285

Browse files
Prerare integration with claude-code module (#70)
* fix: update user mapping * feat: reimplement proxy * ci: run ci * fix: https and ignore redirects * test: skip CONNECT test * feat: block disallowed HTTP requests * tmp commit: seems working * fix: close connection * feat: add audit * fix: proxy logging * fix: logger * refactor: minor improvement * refactor: minor improvement * refactor: minor improvement * refactor: minor improvements * refactor: minor refactoring * fix: linter * minor fix * fix: linter * minor fix * minor fix * tmp commit * tmp commit * tmp commit * 8087 * debug log * more debug logging * more debug logging * remove unnecessary logging * revert allow everything policy * fix logger * make port configurable * remove invalid comment * don't log any sensitive information * fix bug with HTTP 2.0
1 parent 7d83c3e commit f6f5285

File tree

5 files changed

+70
-10
lines changed

5 files changed

+70
-10
lines changed

audit/log_auditor.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ func (a *LogAuditor) AuditRequest(req Request) {
2020
a.logger.Info("ALLOW",
2121
"method", req.Method,
2222
"url", req.URL,
23+
"host", req.Host,
2324
"rule", req.Rule)
2425
} else {
2526
a.logger.Warn("DENY",
2627
"method", req.Method,
27-
"url", req.URL)
28+
"url", req.URL,
29+
"host", req.Host,
30+
)
2831
}
2932
}

audit/request.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type Auditor interface {
88
type Request struct {
99
Method string
1010
URL string
11+
Host string
1112
Allowed bool
1213
Rule string // The rule that matched (if any)
1314
}

boundary.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Config struct {
2020
TLSConfig *tls.Config
2121
Logger *slog.Logger
2222
Jailer jail.Jailer
23+
ProxyPort int
2324
}
2425

2526
type Boundary struct {
@@ -34,7 +35,7 @@ type Boundary struct {
3435
func New(ctx context.Context, config Config) (*Boundary, error) {
3536
// Create proxy server
3637
proxyServer := proxy.NewProxyServer(proxy.Config{
37-
HTTPPort: 8080,
38+
HTTPPort: config.ProxyPort,
3839
RuleEngine: config.RuleEngine,
3940
Auditor: config.Auditor,
4041
Logger: config.Logger,

cli/cli.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Config struct {
2828
LogLevel string
2929
LogDir string
3030
Unprivileged bool
31+
ProxyPort int64
3132
}
3233

3334
// NewCommand creates and returns the root serpent command
@@ -86,6 +87,13 @@ func BaseCommand() *serpent.Command {
8687
Description: "Run in unprivileged mode (no network isolation, uses proxy environment variables).",
8788
Value: serpent.BoolOf(&config.Unprivileged),
8889
},
90+
{
91+
Flag: "proxy-port",
92+
Env: "PROXY_PORT",
93+
Description: "Set a port for HTTP proxy.",
94+
Default: "8080",
95+
Value: serpent.Int64Of(&config.ProxyPort),
96+
},
8997
},
9098
Handler: func(inv *serpent.Invocation) error {
9199
args := inv.Args
@@ -100,15 +108,20 @@ func isChild() bool {
100108

101109
// Run executes the boundary command with the given configuration and arguments
102110
func Run(ctx context.Context, config Config, args []string) error {
111+
logger, err := setupLogging(config)
112+
if err != nil {
113+
return fmt.Errorf("could not set up logging: %v", err)
114+
}
115+
103116
if isChild() {
104-
log.Printf("boundary CHILD process is started")
117+
logger.Info("boundary CHILD process is started")
105118

106119
vethNetJail := os.Getenv("VETH_JAIL_NAME")
107120
err := jail.SetupChildNetworking(vethNetJail)
108121
if err != nil {
109122
return fmt.Errorf("failed to setup child networking: %v", err)
110123
}
111-
log.Printf("child networking is successfully configured")
124+
logger.Info("child networking is successfully configured")
112125

113126
// Program to run
114127
bin := args[0]
@@ -130,10 +143,6 @@ func Run(ctx context.Context, config Config, args []string) error {
130143
ctx, cancel := context.WithCancel(ctx)
131144
defer cancel()
132145

133-
logger, err := setupLogging(config)
134-
if err != nil {
135-
return fmt.Errorf("could not set up logging: %v", err)
136-
}
137146
username, uid, gid, homeDir, configDir := util.GetUserInfo()
138147

139148
// Get command arguments
@@ -180,7 +189,7 @@ func Run(ctx context.Context, config Config, args []string) error {
180189
// Create jailer with cert path from TLS setup
181190
jailer, err := createJailer(jail.Config{
182191
Logger: logger,
183-
HttpProxyPort: 8080,
192+
HttpProxyPort: int(config.ProxyPort),
184193
Username: username,
185194
Uid: uid,
186195
Gid: gid,
@@ -199,6 +208,7 @@ func Run(ctx context.Context, config Config, args []string) error {
199208
TLSConfig: tlsConfig,
200209
Logger: logger,
201210
Jailer: jailer,
211+
ProxyPort: int(config.ProxyPort),
202212
})
203213
if err != nil {
204214
return fmt.Errorf("failed to create boundary instance: %v", err)

proxy/proxy.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ func (p *Server) handleHTTPConnection(conn net.Conn) {
187187
p.auditor.AuditRequest(audit.Request{
188188
Method: req.Method,
189189
URL: req.URL.String(),
190+
Host: req.Host,
190191
Allowed: result.Allowed,
191192
Rule: result.Rule,
192193
})
@@ -237,6 +238,7 @@ func (p *Server) handleTLSConnection(conn net.Conn) {
237238
p.auditor.AuditRequest(audit.Request{
238239
Method: req.Method,
239240
URL: req.URL.String(),
241+
Host: req.Host,
240242
Allowed: result.Allowed,
241243
Rule: result.Rule,
242244
})
@@ -270,6 +272,23 @@ func (p *Server) forwardRequest(conn net.Conn, req *http.Request, https bool) {
270272
Path: req.URL.Path,
271273
RawQuery: req.URL.RawQuery,
272274
}
275+
276+
//var requestBodyBytes []byte
277+
//{
278+
// var err error
279+
// requestBodyBytes, err = io.ReadAll(req.Body)
280+
// if err != nil {
281+
// p.logger.Error("can't read response body", "error", err)
282+
// return
283+
// }
284+
// err = req.Body.Close()
285+
// if err != nil {
286+
// p.logger.Error("Failed to close HTTP response body", "error", err)
287+
// return
288+
// }
289+
// req.Body = io.NopCloser(bytes.NewBuffer(requestBodyBytes))
290+
//}
291+
273292
var body = req.Body
274293
if req.Method == http.MethodGet || req.Method == http.MethodHead {
275294
body = nil
@@ -300,6 +319,16 @@ func (p *Server) forwardRequest(conn net.Conn, req *http.Request, https bool) {
300319

301320
p.logger.Debug("🔒 HTTPS Response", "status code", resp.StatusCode, "status", resp.Status)
302321

322+
p.logger.Debug("Forwarded Request",
323+
"method", newReq.Method,
324+
"host", newReq.Host,
325+
//"requestBodyBytes", string(requestBodyBytes),
326+
"URL", newReq.URL,
327+
)
328+
//for hKey, hVal := range newReq.Header {
329+
// p.logger.Debug("Forwarded Request Header", hKey, hVal)
330+
//}
331+
303332
// Read the body and explicitly set Content-Length header, otherwise client can hung up on the request.
304333
bodyBytes, err := io.ReadAll(resp.Body)
305334
if err != nil {
@@ -315,10 +344,26 @@ func (p *Server) forwardRequest(conn net.Conn, req *http.Request, https bool) {
315344
}
316345
resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
317346

347+
// The downstream client (Claude) always communicates over HTTP/1.1.
348+
// However, Go's default HTTP client may negotiate an HTTP/2 connection
349+
// with the upstream server via ALPN during TLS handshake.
350+
// This can cause the response's Proto field to be set to "HTTP/2.0",
351+
// which would produce an invalid response for an HTTP/1.1 client.
352+
// To prevent this mismatch, we explicitly normalize the response
353+
// to HTTP/1.1 before writing it back to the client.
354+
resp.Proto = "HTTP/1.1"
355+
resp.ProtoMajor = 1
356+
resp.ProtoMinor = 1
357+
318358
// Copy response back to client
319359
err = resp.Write(conn)
320360
if err != nil {
321-
p.logger.Error("Failed to forward HTTP request", "error", err)
361+
p.logger.Error("Failed to forward back HTTP response",
362+
"error", err,
363+
"host", req.Host,
364+
"method", req.Method,
365+
//"bodyBytes", string(bodyBytes),
366+
)
322367
return
323368
}
324369

0 commit comments

Comments
 (0)