Skip to content

Commit cc51a42

Browse files
committed
merge conflict resolution
2 parents ce6bc97 + eaf3370 commit cc51a42

File tree

11 files changed

+434
-407
lines changed

11 files changed

+434
-407
lines changed

cli/cli.go

Lines changed: 64 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,16 @@ package cli
22

33
import (
44
"context"
5-
cryptotls "crypto/tls"
6-
"errors"
75
"fmt"
86
"log/slog"
97
"os"
108
"os/signal"
11-
"path/filepath"
129
"strings"
1310
"syscall"
14-
"time"
1511

12+
"github.com/coder/jail"
1613
"github.com/coder/jail/audit"
17-
"github.com/coder/jail/network"
14+
"github.com/coder/jail/namespace"
1815
"github.com/coder/jail/proxy"
1916
"github.com/coder/jail/rules"
2017
"github.com/coder/jail/tls"
@@ -23,10 +20,8 @@ import (
2320

2421
// Config holds all configuration for the CLI
2522
type Config struct {
26-
AllowStrings []string
27-
NoTLSIntercept bool
28-
LogLevel string
29-
NoJailCleanup bool
23+
AllowStrings []string
24+
LogLevel string
3025
}
3126

3227
// NewCommand creates and returns the root serpent command
@@ -56,13 +51,6 @@ Examples:
5651
Description: "Allow rule (can be specified multiple times). Format: 'pattern' or 'METHOD[,METHOD] pattern'.",
5752
Value: serpent.StringArrayOf(&config.AllowStrings),
5853
},
59-
{
60-
Name: "no-tls-intercept",
61-
Flag: "no-tls-intercept",
62-
Env: "JAIL_NO_TLS_INTERCEPT",
63-
Description: "Disable HTTPS interception.",
64-
Value: serpent.BoolOf(&config.NoTLSIntercept),
65-
},
6654
{
6755
Name: "log-level",
6856
Flag: "log-level",
@@ -71,14 +59,6 @@ Examples:
7159
Default: "warn",
7260
Value: serpent.StringOf(&config.LogLevel),
7361
},
74-
{
75-
Name: "no-jail-cleanup",
76-
Flag: "no-jail-cleanup",
77-
Env: "JAIL_NO_JAIL_CLEANUP",
78-
Description: "Skip jail cleanup (hidden flag for testing).",
79-
Value: serpent.BoolOf(&config.NoJailCleanup),
80-
Hidden: true,
81-
},
8262
},
8363
Handler: func(inv *serpent.Invocation) error {
8464
return Run(config, inv.Args)
@@ -124,95 +104,82 @@ func Run(config Config, args []string) error {
124104
logger.Warn("No allow rules specified; all network traffic will be denied by default")
125105
}
126106

107+
// Parse allow rules
127108
allowRules, err := rules.ParseAllowSpecs(config.AllowStrings)
128109
if err != nil {
129110
logger.Error("Failed to parse allow rules", "error", err)
130111
return fmt.Errorf("failed to parse allow rules: %v", err)
131112
}
132113

133-
// Implicit final deny-all is handled by the RuleEngine default behavior when no rules match.
134-
// Build final rules slice in order: user allows only.
135-
ruleList := allowRules
136-
137114
// Create rule engine
138-
ruleEngine := rules.NewRuleEngine(ruleList, logger)
115+
ruleEngine := rules.NewRuleEngine(allowRules, logger)
139116

140-
// Get configuration directory
141-
configDir, err := tls.GetConfigDir()
142-
if err != nil {
143-
logger.Error("Failed to get config directory", "error", err)
144-
return fmt.Errorf("failed to get config directory: %v", err)
145-
}
117+
// Create auditor
118+
auditor := audit.NewLoggingAuditor(logger)
146119

147-
if configDir == "" {
148-
logger.Error("Config dir received was the empty string")
149-
return errors.New("config dir received was the empty string")
120+
// Create network namespace configuration
121+
nsConfig := namespace.Config{
122+
HTTPPort: 8040,
123+
HTTPSPort: 8043,
150124
}
151125

152-
// Create certificate manager (if TLS interception is enabled)
153-
var certManager *tls.CertificateManager
154-
var tlsConfig *cryptotls.Config
155-
var extraEnv map[string]string = make(map[string]string)
156-
157-
if !config.NoTLSIntercept {
158-
certManager, err = tls.NewCertificateManager(configDir, logger)
159-
if err != nil {
160-
logger.Error("Failed to create certificate manager", "error", err)
161-
return fmt.Errorf("failed to create certificate manager: %v", err)
162-
}
163-
164-
tlsConfig = certManager.GetTLSConfig()
165-
166-
// Get CA certificate for environment
167-
caCertPEM, err := certManager.GetCACertPEM()
168-
if err != nil {
169-
logger.Error("Failed to get CA certificate", "error", err)
170-
return fmt.Errorf("failed to get CA certificate: %v", err)
171-
}
172-
173-
// Write CA certificate to a temporary file for tools that need a file path
174-
caCertPath := filepath.Join(configDir, "ca-cert.pem")
175-
err = os.WriteFile(caCertPath, caCertPEM, 0644)
176-
if err != nil {
177-
logger.Error("Failed to write CA certificate file", "error", err)
178-
return fmt.Errorf("failed to write CA certificate file: %v", err)
179-
}
180-
181-
// Set standard CA certificate environment variables for common tools
182-
// This makes tools like curl, git, etc. trust our dynamically generated CA
183-
extraEnv["SSL_CERT_FILE"] = caCertPath // OpenSSL/LibreSSL-based tools
184-
extraEnv["SSL_CERT_DIR"] = configDir // OpenSSL certificate directory
185-
extraEnv["CURL_CA_BUNDLE"] = caCertPath // curl
186-
extraEnv["GIT_SSL_CAINFO"] = caCertPath // Git
187-
extraEnv["REQUESTS_CA_BUNDLE"] = caCertPath // Python requests
188-
extraEnv["NODE_EXTRA_CA_CERTS"] = caCertPath // Node.js
189-
extraEnv["JAIL_CA_CERT"] = string(caCertPEM) // Keep for backward compatibility
126+
// Create commander
127+
commander, err := namespace.New(nsConfig, logger)
128+
if err != nil {
129+
logger.Error("Failed to create network namespace", "error", err)
130+
return fmt.Errorf("failed to create network namespace: %v", err)
190131
}
191132

192-
// Create network jail configuration
193-
networkConfig := network.JailConfig{
194-
HTTPPort: 8040,
195-
HTTPSPort: 8043,
196-
NetJailName: "jail",
197-
SkipCleanup: config.NoJailCleanup,
133+
// Create certificate manager
134+
certManager, err := tls.NewCertificateManager(logger)
135+
if err != nil {
136+
logger.Error("Failed to create certificate manager", "error", err)
137+
return fmt.Errorf("failed to create certificate manager: %v", err)
198138
}
199139

200-
// Create network jail
201-
networkInstance, err := network.NewJail(networkConfig, logger)
140+
// Setup TLS config and write CA certificate to file
141+
var caCertPath, configDir string
142+
tlsConfig, caCertPath, configDir, err := certManager.SetupTLSAndWriteCACert()
202143
if err != nil {
203-
logger.Error("Failed to create network jail", "error", err)
204-
return fmt.Errorf("failed to create network jail: %v", err)
144+
logger.Error("Failed to setup TLS and CA certificate", "error", err)
145+
return fmt.Errorf("failed to setup TLS and CA certificate: %v", err)
205146
}
206147

207-
// Setup signal handling BEFORE any network setup
148+
// Set standard CA certificate environment variables for common tools
149+
// This makes tools like curl, git, etc. trust our dynamically generated CA
150+
commander.SetEnv("SSL_CERT_FILE", caCertPath) // OpenSSL/LibreSSL-based tools
151+
commander.SetEnv("SSL_CERT_DIR", configDir) // OpenSSL certificate directory
152+
commander.SetEnv("CURL_CA_BUNDLE", caCertPath) // curl
153+
commander.SetEnv("GIT_SSL_CAINFO", caCertPath) // Git
154+
commander.SetEnv("REQUESTS_CA_BUNDLE", caCertPath) // Python requests
155+
commander.SetEnv("NODE_EXTRA_CA_CERTS", caCertPath) // Node.js
156+
157+
// Create proxy server
158+
proxyServer := proxy.NewProxyServer(proxy.Config{
159+
HTTPPort: 8040,
160+
HTTPSPort: 8043,
161+
RuleEngine: ruleEngine,
162+
Auditor: auditor,
163+
Logger: logger,
164+
TLSConfig: tlsConfig,
165+
})
166+
167+
// Create jail instance
168+
jailInstance := jail.New(jail.Config{
169+
Commander: commander,
170+
ProxyServer: proxyServer,
171+
Logger: logger,
172+
})
173+
174+
// Setup signal handling BEFORE any setup
208175
sigChan := make(chan os.Signal, 1)
209176
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
210177

211178
// Handle signals immediately in background
212179
go func() {
213180
sig := <-sigChan
214181
logger.Info("Received signal during setup, cleaning up...", "signal", sig)
215-
err := networkInstance.Cleanup()
182+
err := jailInstance.Close()
216183
if err != nil {
217184
logger.Error("Emergency cleanup failed", "error", err)
218185
}
@@ -222,55 +189,29 @@ func Run(config Config, args []string) error {
222189
// Ensure cleanup happens no matter what
223190
defer func() {
224191
logger.Debug("Starting cleanup process")
225-
err := networkInstance.Cleanup()
192+
err := jailInstance.Close()
226193
if err != nil {
227-
logger.Error("Failed to cleanup network jail", "error", err)
194+
logger.Error("Failed to cleanup jail", "error", err)
228195
} else {
229196
logger.Debug("Cleanup completed successfully")
230197
}
231198
}()
232199

233-
// Setup network jail
234-
err = networkInstance.Setup(networkConfig.HTTPPort, networkConfig.HTTPSPort)
200+
// Open jail (starts network namespace and proxy server)
201+
err = jailInstance.Open()
235202
if err != nil {
236-
logger.Error("Failed to setup network jail", "error", err)
237-
return fmt.Errorf("failed to setup network jail: %v", err)
238-
}
239-
240-
// Create auditor
241-
auditor := audit.NewLoggingAuditor(logger)
242-
243-
// Create proxy server
244-
proxyConfig := proxy.Config{
245-
HTTPPort: networkConfig.HTTPPort,
246-
HTTPSPort: networkConfig.HTTPSPort,
247-
RuleEngine: ruleEngine,
248-
Auditor: auditor,
249-
Logger: logger,
250-
TLSConfig: tlsConfig,
203+
logger.Error("Failed to open jail", "error", err)
204+
return fmt.Errorf("failed to open jail: %v", err)
251205
}
252206

253-
proxyServer := proxy.NewProxyServer(proxyConfig)
254-
255207
// Create context for graceful shutdown
256208
ctx, cancel := context.WithCancel(context.Background())
257209
defer cancel()
258210

259-
// Start proxy server in background
260-
go func() {
261-
err := proxyServer.Start(ctx)
262-
if err != nil {
263-
logger.Error("Proxy server error", "error", err)
264-
}
265-
}()
266-
267-
// Give proxy time to start
268-
time.Sleep(100 * time.Millisecond)
269-
270-
// Execute command in network jail
211+
// Execute command in jail
271212
go func() {
272213
defer cancel()
273-
err := networkInstance.Execute(args, extraEnv)
214+
err := jailInstance.Command(args).Run()
274215
if err != nil {
275216
logger.Error("Command execution failed", "error", err)
276217
}
@@ -283,12 +224,7 @@ func Run(config Config, args []string) error {
283224
cancel()
284225
case <-ctx.Done():
285226
// Context cancelled by command completion
286-
}
287-
288-
// Stop proxy server
289-
err = proxyServer.Stop()
290-
if err != nil {
291-
logger.Error("Failed to stop proxy server", "error", err)
227+
logger.Info("Command completed, shutting down...")
292228
}
293229

294230
return nil

jail.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package jail
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"os/exec"
8+
"time"
9+
10+
"github.com/coder/jail/proxy"
11+
)
12+
13+
type Commander interface {
14+
Open() error
15+
SetEnv(key string, value string)
16+
Command(command []string) *exec.Cmd
17+
Close() error
18+
}
19+
20+
type Config struct {
21+
Commander Commander
22+
ProxyServer *proxy.ProxyServer
23+
Logger *slog.Logger
24+
}
25+
26+
type Jail struct {
27+
commandExecutor Commander
28+
proxyServer *proxy.ProxyServer
29+
logger *slog.Logger
30+
cancel context.CancelFunc
31+
ctx context.Context
32+
}
33+
34+
func New(config Config) *Jail {
35+
ctx, cancel := context.WithCancel(context.Background())
36+
37+
return &Jail{
38+
commandExecutor: config.Commander,
39+
proxyServer: config.ProxyServer,
40+
logger: config.Logger,
41+
ctx: ctx,
42+
cancel: cancel,
43+
}
44+
}
45+
46+
func (j *Jail) Open() error {
47+
// Open the command executor (network namespace)
48+
err := j.commandExecutor.Open()
49+
if err != nil {
50+
return fmt.Errorf("failed to open command executor: %v", err)
51+
}
52+
53+
// Start proxy server in background
54+
go func() {
55+
err := j.proxyServer.Start(j.ctx)
56+
if err != nil {
57+
j.logger.Error("Proxy server error", "error", err)
58+
}
59+
}()
60+
61+
// Give proxy time to start
62+
time.Sleep(100 * time.Millisecond)
63+
64+
return nil
65+
}
66+
67+
func (j *Jail) Command(command []string) *exec.Cmd {
68+
return j.commandExecutor.Command(command)
69+
}
70+
71+
func (j *Jail) Close() error {
72+
// Cancel context to stop proxy server
73+
if j.cancel != nil {
74+
j.cancel()
75+
}
76+
77+
// Stop proxy server
78+
if j.proxyServer != nil {
79+
err := j.proxyServer.Stop()
80+
if err != nil {
81+
j.logger.Error("Failed to stop proxy server", "error", err)
82+
}
83+
}
84+
85+
// Close command executor
86+
return j.commandExecutor.Close()
87+
}

0 commit comments

Comments
 (0)