@@ -2,19 +2,16 @@ package cli
2
2
3
3
import (
4
4
"context"
5
- cryptotls "crypto/tls"
6
- "errors"
7
5
"fmt"
8
6
"log/slog"
9
7
"os"
10
8
"os/signal"
11
- "path/filepath"
12
9
"strings"
13
10
"syscall"
14
- "time"
15
11
12
+ "github.com/coder/jail"
16
13
"github.com/coder/jail/audit"
17
- "github.com/coder/jail/network "
14
+ "github.com/coder/jail/namespace "
18
15
"github.com/coder/jail/proxy"
19
16
"github.com/coder/jail/rules"
20
17
"github.com/coder/jail/tls"
@@ -23,10 +20,8 @@ import (
23
20
24
21
// Config holds all configuration for the CLI
25
22
type Config struct {
26
- AllowStrings []string
27
- NoTLSIntercept bool
28
- LogLevel string
29
- NoJailCleanup bool
23
+ AllowStrings []string
24
+ LogLevel string
30
25
}
31
26
32
27
// NewCommand creates and returns the root serpent command
@@ -56,13 +51,6 @@ Examples:
56
51
Description : "Allow rule (can be specified multiple times). Format: 'pattern' or 'METHOD[,METHOD] pattern'." ,
57
52
Value : serpent .StringArrayOf (& config .AllowStrings ),
58
53
},
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
- },
66
54
{
67
55
Name : "log-level" ,
68
56
Flag : "log-level" ,
@@ -71,14 +59,6 @@ Examples:
71
59
Default : "warn" ,
72
60
Value : serpent .StringOf (& config .LogLevel ),
73
61
},
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
- },
82
62
},
83
63
Handler : func (inv * serpent.Invocation ) error {
84
64
return Run (config , inv .Args )
@@ -124,95 +104,82 @@ func Run(config Config, args []string) error {
124
104
logger .Warn ("No allow rules specified; all network traffic will be denied by default" )
125
105
}
126
106
107
+ // Parse allow rules
127
108
allowRules , err := rules .ParseAllowSpecs (config .AllowStrings )
128
109
if err != nil {
129
110
logger .Error ("Failed to parse allow rules" , "error" , err )
130
111
return fmt .Errorf ("failed to parse allow rules: %v" , err )
131
112
}
132
113
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
-
137
114
// Create rule engine
138
- ruleEngine := rules .NewRuleEngine (ruleList , logger )
115
+ ruleEngine := rules .NewRuleEngine (allowRules , logger )
139
116
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 )
146
119
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 ,
150
124
}
151
125
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 )
190
131
}
191
132
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 )
198
138
}
199
139
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 ()
202
143
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 )
205
146
}
206
147
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
208
175
sigChan := make (chan os.Signal , 1 )
209
176
signal .Notify (sigChan , syscall .SIGINT , syscall .SIGTERM )
210
177
211
178
// Handle signals immediately in background
212
179
go func () {
213
180
sig := <- sigChan
214
181
logger .Info ("Received signal during setup, cleaning up..." , "signal" , sig )
215
- err := networkInstance . Cleanup ()
182
+ err := jailInstance . Close ()
216
183
if err != nil {
217
184
logger .Error ("Emergency cleanup failed" , "error" , err )
218
185
}
@@ -222,55 +189,29 @@ func Run(config Config, args []string) error {
222
189
// Ensure cleanup happens no matter what
223
190
defer func () {
224
191
logger .Debug ("Starting cleanup process" )
225
- err := networkInstance . Cleanup ()
192
+ err := jailInstance . Close ()
226
193
if err != nil {
227
- logger .Error ("Failed to cleanup network jail" , "error" , err )
194
+ logger .Error ("Failed to cleanup jail" , "error" , err )
228
195
} else {
229
196
logger .Debug ("Cleanup completed successfully" )
230
197
}
231
198
}()
232
199
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 ( )
235
202
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 )
251
205
}
252
206
253
- proxyServer := proxy .NewProxyServer (proxyConfig )
254
-
255
207
// Create context for graceful shutdown
256
208
ctx , cancel := context .WithCancel (context .Background ())
257
209
defer cancel ()
258
210
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
271
212
go func () {
272
213
defer cancel ()
273
- err := networkInstance . Execute (args , extraEnv )
214
+ err := jailInstance . Command (args ). Run ( )
274
215
if err != nil {
275
216
logger .Error ("Command execution failed" , "error" , err )
276
217
}
@@ -283,12 +224,7 @@ func Run(config Config, args []string) error {
283
224
cancel ()
284
225
case <- ctx .Done ():
285
226
// 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..." )
292
228
}
293
229
294
230
return nil
0 commit comments