Skip to content

Commit 2ed7c35

Browse files
authored
chore: start linting in CI (#53)
* chore: start linting in CI * handle errors * use action * fix * fix
1 parent d3a2bb7 commit 2ed7c35

File tree

8 files changed

+171
-43
lines changed

8 files changed

+171
-43
lines changed

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,41 @@ on:
77
branches: [ main ]
88

99
jobs:
10+
lint:
11+
name: Lint
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Check out code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v5
19+
with:
20+
go-version: '1.25'
21+
check-latest: true
22+
23+
- name: Cache Go modules
24+
uses: actions/cache@v4
25+
with:
26+
path: |
27+
~/.cache/go-build
28+
~/go/pkg/mod
29+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
30+
restore-keys: |
31+
${{ runner.os }}-go-
32+
33+
- name: Download and verify dependencies
34+
run: make deps
35+
36+
- name: Install golangci-lint
37+
run: |
38+
# binary will be $(go env GOPATH)/bin/golangci-lint
39+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.4.0
40+
golangci-lint --version
41+
42+
- name: Run linting
43+
run: make lint
44+
1045
test:
1146
name: Test
1247
strategy:

boundary

-14.1 MB
Binary file not shown.

cmd/boundary/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
// Version information injected at build time
1111
var (
12+
//nolint:unused
1213
version = "dev" // Set via -ldflags "-X main.version=v1.0.0"
1314
)
1415

jail/linux.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ options timeout:2 attempts:2
197197
func (l *LinuxJail) setupIptables() error {
198198
// Enable IP forwarding
199199
cmd := exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1")
200-
cmd.Run() // Ignore error
200+
_ = cmd.Run() // Ignore error
201201

202202
// NAT rules for outgoing traffic (MASQUERADE for return traffic)
203203
cmd = exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE")
@@ -222,11 +222,19 @@ func (l *LinuxJail) setupIptables() error {
222222
func (l *LinuxJail) cleanupIptables() error {
223223
// Remove comprehensive TCP redirect rule
224224
cmd := exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", l.vethHost, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort))
225-
cmd.Run() // Ignore errors during cleanup
225+
err := cmd.Run()
226+
if err != nil {
227+
l.logger.Error("Failed to remove TCP redirect rule", "error", err)
228+
// Continue with other cleanup even if this fails
229+
}
226230

227231
// Remove NAT rule
228232
cmd = exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE")
229-
cmd.Run() // Ignore errors during cleanup
233+
err = cmd.Run()
234+
if err != nil {
235+
l.logger.Error("Failed to remove NAT rule", "error", err)
236+
// Continue with other cleanup even if this fails
237+
}
230238

231239
return nil
232240
}
@@ -262,4 +270,4 @@ func (l *LinuxJail) removeNamespace() error {
262270
return fmt.Errorf("failed to remove namespace: %v", err)
263271
}
264272
return nil
265-
}
273+
}

jail/macos.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,12 @@ func (n *MacOSJail) setupPFRules() error {
266266
cmd := exec.Command("pfctl", "-a", pfAnchorName, "-f", n.pfRulesPath)
267267
err = cmd.Run()
268268
if err != nil {
269-
n.logger.Error("Failed to load PF rules", "error", err, "rules_file", n.pfRulesPath)
270269
return fmt.Errorf("failed to load PF rules: %v", err)
271270
}
272271

273272
// Enable PF if not already enabled
274273
cmd = exec.Command("pfctl", "-E")
275-
cmd.Run() // Ignore error as PF might already be enabled
274+
_ = cmd.Run() // Ignore error as PF might already be enabled
276275

277276
// Create and load main ruleset that includes our anchor
278277
mainRules := fmt.Sprintf(`# Temporary main ruleset to include boundary anchor
@@ -318,17 +317,26 @@ anchor "%s"
318317
func (n *MacOSJail) removePFRules() error {
319318
// Flush the anchor
320319
cmd := exec.Command("pfctl", "-a", pfAnchorName, "-F", "all")
321-
cmd.Run() // Ignore errors during cleanup
320+
err := cmd.Run()
321+
if err != nil {
322+
return fmt.Errorf("failed to flush PF anchor: %v", err)
323+
}
322324

323325
return nil
324326
}
325327

326328
// cleanupTempFiles removes temporary rule files
327329
func (n *MacOSJail) cleanupTempFiles() {
328330
if n.pfRulesPath != "" {
329-
os.Remove(n.pfRulesPath)
331+
err := os.Remove(n.pfRulesPath)
332+
if err != nil {
333+
n.logger.Error("Failed to remove temporary PF rules file", "file", n.pfRulesPath, "error", err)
334+
}
330335
}
331336
if n.mainRulesPath != "" {
332-
os.Remove(n.mainRulesPath)
337+
err := os.Remove(n.mainRulesPath)
338+
if err != nil {
339+
n.logger.Error("Failed to remove temporary main PF rules file", "file", n.mainRulesPath, "error", err)
340+
}
333341
}
334-
}
342+
}

proxy/proxy.go

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ func (p *Server) Start(ctx context.Context) error {
7171
if err != nil {
7272
select {
7373
case <-ctx.Done():
74-
listener.Close()
74+
err = listener.Close()
75+
if err != nil {
76+
p.logger.Error("Failed to close listener", "error", err)
77+
}
7578
return
7679
default:
7780
p.logger.Error("Failed to accept connection", "error", err)
@@ -194,7 +197,7 @@ func (p *Server) forwardRequest(w http.ResponseWriter, r *http.Request, https bo
194197
http.Error(w, fmt.Sprintf("Failed to make request: %v", err), http.StatusBadGateway)
195198
return
196199
}
197-
defer resp.Body.Close()
200+
defer func() { _ = resp.Body.Close() }()
198201

199202
p.logger.Debug("Received response", "status", resp.StatusCode, "target", targetURL.String())
200203

@@ -238,7 +241,7 @@ func (p *Server) writeBlockedResponse(w http.ResponseWriter, r *http.Request) {
238241
host = r.Host
239242
}
240243

241-
fmt.Fprintf(w, `🚫 Request Blocked by Boundary
244+
_, _ = fmt.Fprintf(w, `🚫 Request Blocked by Boundary
242245
243246
Request: %s %s
244247
Host: %s
@@ -290,7 +293,12 @@ func (p *Server) handleConnect(w http.ResponseWriter, r *http.Request) {
290293
p.logger.Error("Failed to hijack connection", "error", err)
291294
return
292295
}
293-
defer conn.Close()
296+
defer func() {
297+
err := conn.Close()
298+
if err != nil {
299+
p.logger.Error("Failed to close connection", "error", err)
300+
}
301+
}()
294302

295303
// Send 200 Connection established response manually
296304
_, err = conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
@@ -416,7 +424,12 @@ func (p *Server) handleDecryptedHTTPS(w http.ResponseWriter, r *http.Request) {
416424

417425
// handleConnectionWithTLSDetection detects TLS vs HTTP and handles appropriately
418426
func (p *Server) handleConnectionWithTLSDetection(conn net.Conn) {
419-
defer conn.Close()
427+
defer func() {
428+
err := conn.Close()
429+
if err != nil {
430+
p.logger.Error("Failed to close connection", "error", err)
431+
}
432+
}()
420433

421434
// Peek at first byte to detect protocol
422435
buf := make([]byte, 1)
@@ -442,15 +455,25 @@ func (p *Server) handleConnectionWithTLSDetection(conn net.Conn) {
442455
p.logger.Debug("TLS handshake successful")
443456
// Use HTTP server with TLS connection
444457
listener := newSingleConnectionListener(tlsConn)
445-
defer listener.Close()
458+
defer func() {
459+
err := listener.Close()
460+
if err != nil {
461+
p.logger.Error("Failed to close connection", "error", err)
462+
}
463+
}()
446464
err = http.Serve(listener, http.HandlerFunc(p.handleDecryptedHTTPS))
447465
p.logger.Debug("http.Serve completed for HTTPS", "error", err)
448466
} else {
449467
p.logger.Debug("Detected HTTP request, handling normally")
450468
// Use HTTP server with regular connection
451469
p.logger.Debug("About to call http.Serve for HTTP connection")
452470
listener := newSingleConnectionListener(connWrapper)
453-
defer listener.Close()
471+
defer func() {
472+
err := listener.Close()
473+
if err != nil {
474+
p.logger.Error("Failed to close connection", "error", err)
475+
}
476+
}()
454477
err = http.Serve(listener, http.HandlerFunc(p.handleHTTP))
455478
p.logger.Debug("http.Serve completed", "error", err)
456479
}
@@ -519,7 +542,10 @@ func (sl *singleConnectionListener) Close() error {
519542
}
520543

521544
if sl.conn != nil {
522-
sl.conn.Close()
545+
err := sl.conn.Close()
546+
if err != nil {
547+
return fmt.Errorf("failed to close connection: %w", err)
548+
}
523549
sl.conn = nil
524550
}
525551
return nil
@@ -613,9 +639,9 @@ func (p *Server) constructFullURL(req *http.Request, hostname string) string {
613639

614640
// writeBlockedResponseStreaming writes a blocked response directly to the TLS connection
615641
func (p *Server) writeBlockedResponseStreaming(tlsConn *tls.Conn, req *http.Request) {
616-
response := fmt.Sprintf("HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n🚫 Request Blocked by Boundary\n\nRequest: %s %s\nHost: %s\n\nTo allow this request, restart boundary with:\n --allow \"%s\"\n",
642+
response := fmt.Sprintf("HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n🚫 Request Blocked by Boundary\n\nRequest: %s %s\nHost: %s\n\nTo allow this request, restart boundary with:\n --allow \"%s\"\n",
617643
req.Method, req.URL.Path, req.Host, req.Host)
618-
tlsConn.Write([]byte(response))
644+
_, _ = tlsConn.Write([]byte(response))
619645
}
620646

621647
// streamRequestToTarget streams the HTTP request (including body) to the target server
@@ -625,60 +651,94 @@ func (p *Server) streamRequestToTarget(clientConn *tls.Conn, bufReader *bufio.Re
625651
if err != nil {
626652
return fmt.Errorf("failed to connect to target %s: %v", hostname, err)
627653
}
628-
defer targetConn.Close()
654+
defer func() {
655+
err := targetConn.Close()
656+
if err != nil {
657+
p.logger.Error("Failed to close target connection", "error", err)
658+
}
659+
}()
629660

630661
// Send HTTP request headers to target
631662
reqLine := fmt.Sprintf("%s %s %s\r\n", req.Method, req.URL.RequestURI(), req.Proto)
632-
targetConn.Write([]byte(reqLine))
663+
_, err = targetConn.Write([]byte(reqLine))
664+
if err != nil {
665+
return fmt.Errorf("failed to write request line to target: %v", err)
666+
}
633667

634668
// Send headers
635669
for name, values := range req.Header {
636670
for _, value := range values {
637671
headerLine := fmt.Sprintf("%s: %s\r\n", name, value)
638-
targetConn.Write([]byte(headerLine))
672+
_, err = targetConn.Write([]byte(headerLine))
673+
if err != nil {
674+
return fmt.Errorf("failed to write header to target: %v", err)
675+
}
639676
}
640677
}
641-
targetConn.Write([]byte("\r\n")) // End of headers
678+
_, err = targetConn.Write([]byte("\r\n")) // End of headers
679+
if err != nil {
680+
return fmt.Errorf("failed to write headers to target: %v", err)
681+
}
642682

643683
// Stream request body and response bidirectionally
644684
go func() {
645685
// Stream request body: client -> target
646-
io.Copy(targetConn, bufReader)
686+
_, err := io.Copy(targetConn, bufReader)
687+
if err != nil {
688+
p.logger.Error("Error copying request body to target", "error", err)
689+
}
647690
}()
648691

649692
// Stream response: target -> client
650-
io.Copy(clientConn, targetConn)
693+
_, err = io.Copy(clientConn, targetConn)
694+
if err != nil {
695+
p.logger.Error("Error copying response from target to client", "error", err)
696+
}
697+
651698
return nil
652699
}
653700

654701
// handleConnectStreaming handles CONNECT requests with streaming TLS termination
655702
func (p *Server) handleConnectStreaming(tlsConn *tls.Conn, req *http.Request, hostname string) {
656703
p.logger.Debug("Handling CONNECT request with streaming", "hostname", hostname)
657-
704+
658705
// For CONNECT, we need to establish a tunnel but still maintain TLS termination
659706
// This is the tricky part - we're already inside a TLS connection from the client
660707
// The client is asking us to CONNECT to another server, but we want to intercept that too
661-
708+
662709
// Send CONNECT response
663710
response := "HTTP/1.1 200 Connection established\r\n\r\n"
664-
tlsConn.Write([]byte(response))
665-
711+
_, err := tlsConn.Write([]byte(response))
712+
if err != nil {
713+
p.logger.Error("Failed to send CONNECT response", "error", err)
714+
return
715+
}
716+
666717
// Now the client will try to do TLS handshake for the target server
667718
// But we want to intercept and terminate it
668719
// This means we need to do another level of TLS termination
669-
720+
670721
// For now, let's create a simple tunnel and log that we're not inspecting
671722
p.logger.Warn("CONNECT tunnel established - content not inspected", "hostname", hostname)
672-
723+
673724
// Create connection to real target
674725
targetConn, err := net.Dial("tcp", req.Host)
675726
if err != nil {
676727
p.logger.Error("Failed to connect to CONNECT target", "target", req.Host, "error", err)
677728
return
678729
}
679-
defer targetConn.Close()
680-
730+
defer func() { _ = targetConn.Close() }()
731+
681732
// Bidirectional copy
682-
go io.Copy(targetConn, tlsConn)
683-
io.Copy(tlsConn, targetConn)
684-
}
733+
go func() {
734+
_, err := io.Copy(targetConn, tlsConn)
735+
if err != nil {
736+
p.logger.Error("Error copying from client to target", "error", err)
737+
}
738+
}()
739+
_, err = io.Copy(tlsConn, targetConn)
740+
if err != nil {
741+
p.logger.Error("Error copying from target to client", "error", err)
742+
}
743+
p.logger.Debug("CONNECT tunnel closed", "hostname", hostname)
744+
}

rules/rules.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func newAllowRule(spec string) (Rule, error) {
185185
right := strings.TrimSpace(s[idx:])
186186
// methods part is valid if it only contains letters and commas
187187
valid := left != "" && strings.IndexFunc(left, func(r rune) bool {
188-
return !(r == ',' || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z'))
188+
return r != ',' && (r < 'A' || r > 'Z') && (r < 'a' || r > 'z')
189189
}) == -1
190190
if valid {
191191
methods = make(map[string]bool)

0 commit comments

Comments
 (0)