1
1
package cli
2
2
3
3
import (
4
- "context"
5
- cryptotls "crypto/tls"
6
4
"fmt"
7
5
"log/slog"
8
6
"os"
9
- "os/signal"
10
- "path/filepath"
11
- "strings"
12
- "syscall"
13
- "time"
14
7
15
- "github.com/coder/jail/audit"
16
- "github.com/coder/jail/network"
17
- "github.com/coder/jail/proxy"
18
- "github.com/coder/jail/rules"
19
- "github.com/coder/jail/tls"
8
+ "github.com/coder/jail"
20
9
"github.com/coder/serpent"
21
10
)
22
11
@@ -49,32 +38,24 @@ Examples:
49
38
# Block everything by default (implicit)` ,
50
39
Options : serpent.OptionSet {
51
40
{
52
- Name : "allow" ,
53
41
Flag : "allow" ,
54
- Env : "JAIL_ALLOW" ,
55
- Description : "Allow rule (can be specified multiple times). Format: 'pattern' or 'METHOD[,METHOD] pattern'." ,
42
+ Description : "Allow rule (repeatable). Format: \" pattern\" or \" METHOD[,METHOD] pattern\" " ,
56
43
Value : serpent .StringArrayOf (& config .AllowStrings ),
57
44
},
58
45
{
59
- Name : "no-tls-intercept" ,
60
- Flag : "no-tls-intercept" ,
61
- Env : "JAIL_NO_TLS_INTERCEPT" ,
62
- Description : "Disable HTTPS interception." ,
63
- Value : serpent .BoolOf (& config .NoTLSIntercept ),
64
- },
65
- {
66
- Name : "log-level" ,
67
46
Flag : "log-level" ,
68
- Env : "JAIL_LOG_LEVEL" ,
69
- Description : "Set log level (error, warn, info, debug)." ,
47
+ Description : "Set log level (error, warn, info, debug)" ,
70
48
Default : "warn" ,
71
49
Value : serpent .StringOf (& config .LogLevel ),
72
50
},
73
51
{
74
- Name : "no-jail-cleanup" ,
52
+ Flag : "no-tls-intercept" ,
53
+ Description : "Disable HTTPS interception" ,
54
+ Value : serpent .BoolOf (& config .NoTLSIntercept ),
55
+ },
56
+ {
75
57
Flag : "no-jail-cleanup" ,
76
- Env : "JAIL_NO_JAIL_CLEANUP" ,
77
- Description : "Skip jail cleanup (hidden flag for testing)." ,
58
+ Description : "Disable jail cleanup (for debugging)" ,
78
59
Value : serpent .BoolOf (& config .NoJailCleanup ),
79
60
Hidden : true ,
80
61
},
@@ -85,31 +66,7 @@ Examples:
85
66
}
86
67
}
87
68
88
- // setupLogging creates a slog logger with the specified level
89
- func setupLogging (logLevel string ) * slog.Logger {
90
- var level slog.Level
91
- switch strings .ToLower (logLevel ) {
92
- case "error" :
93
- level = slog .LevelError
94
- case "warn" :
95
- level = slog .LevelWarn
96
- case "info" :
97
- level = slog .LevelInfo
98
- case "debug" :
99
- level = slog .LevelDebug
100
- default :
101
- level = slog .LevelWarn // Default to warn if invalid level
102
- }
103
-
104
- // Create a standard slog logger with the appropriate level
105
- handler := slog .NewTextHandler (os .Stderr , & slog.HandlerOptions {
106
- Level : level ,
107
- })
108
-
109
- return slog .New (handler )
110
- }
111
-
112
- // Run executes the jail command with the given configuration and arguments
69
+ // Run executes the jail with the given configuration and command arguments
113
70
func Run (config Config , args []string ) error {
114
71
logger := setupLogging (config .LogLevel )
115
72
@@ -118,172 +75,46 @@ func Run(config Config, args []string) error {
118
75
return fmt .Errorf ("no command specified" )
119
76
}
120
77
121
- // Parse allow list; default to deny-all if none provided
78
+ // Warn if no allow rules specified
122
79
if len (config .AllowStrings ) == 0 {
123
80
logger .Warn ("No allow rules specified; all network traffic will be denied by default" )
124
81
}
125
82
126
- allowRules , err := rules .ParseAllowSpecs (config .AllowStrings )
127
- if err != nil {
128
- logger .Error ("Failed to parse allow rules" , "error" , err )
129
- return fmt .Errorf ("failed to parse allow rules: %v" , err )
83
+ // Create jail configuration
84
+ jailConfig := jail.Config {
85
+ AllowRules : config .AllowStrings ,
86
+ NoTLSIntercept : config .NoTLSIntercept ,
87
+ Logger : logger ,
88
+ SkipCleanup : config .NoJailCleanup ,
130
89
}
131
90
132
- // Implicit final deny-all is handled by the RuleEngine default behavior when no rules match.
133
- // Build final rules slice in order: user allows only.
134
- ruleList := allowRules
135
-
136
- // Create rule engine
137
- ruleEngine := rules .NewRuleEngine (ruleList , logger )
138
-
139
- // Get configuration directory
140
- configDir , err := tls .GetConfigDir ()
91
+ // Create jail instance
92
+ j , err := jail .New (jailConfig )
141
93
if err != nil {
142
- logger .Error ("Failed to get config directory" , "error" , err )
143
- return fmt .Errorf ("failed to get config directory: %v" , err )
144
- }
145
-
146
- // Create certificate manager (if TLS interception is enabled)
147
- var certManager * tls.CertificateManager
148
- var tlsConfig * cryptotls.Config
149
- var extraEnv map [string ]string = make (map [string ]string )
150
-
151
- if ! config .NoTLSIntercept {
152
- certManager , err = tls .NewCertificateManager (configDir , logger )
153
- if err != nil {
154
- logger .Error ("Failed to create certificate manager" , "error" , err )
155
- return fmt .Errorf ("failed to create certificate manager: %v" , err )
156
- }
157
-
158
- tlsConfig = certManager .GetTLSConfig ()
159
-
160
- // Get CA certificate for environment
161
- caCertPEM , err := certManager .GetCACertPEM ()
162
- if err != nil {
163
- logger .Error ("Failed to get CA certificate" , "error" , err )
164
- return fmt .Errorf ("failed to get CA certificate: %v" , err )
165
- }
166
-
167
- // Write CA certificate to a temporary file for tools that need a file path
168
- caCertPath := filepath .Join (configDir , "ca-cert.pem" )
169
- err = os .WriteFile (caCertPath , caCertPEM , 0644 )
170
- if err != nil {
171
- logger .Error ("Failed to write CA certificate file" , "error" , err )
172
- return fmt .Errorf ("failed to write CA certificate file: %v" , err )
173
- }
174
-
175
- // Set standard CA certificate environment variables for common tools
176
- // This makes tools like curl, git, etc. trust our dynamically generated CA
177
- extraEnv ["SSL_CERT_FILE" ] = caCertPath // OpenSSL/LibreSSL-based tools
178
- extraEnv ["SSL_CERT_DIR" ] = configDir // OpenSSL certificate directory
179
- extraEnv ["CURL_CA_BUNDLE" ] = caCertPath // curl
180
- extraEnv ["GIT_SSL_CAINFO" ] = caCertPath // Git
181
- extraEnv ["REQUESTS_CA_BUNDLE" ] = caCertPath // Python requests
182
- extraEnv ["NODE_EXTRA_CA_CERTS" ] = caCertPath // Node.js
183
- extraEnv ["JAIL_CA_CERT" ] = string (caCertPEM ) // Keep for backward compatibility
94
+ return fmt .Errorf ("failed to create jail: %v" , err )
184
95
}
185
96
186
- // Create network jail configuration
187
- networkConfig := network.JailConfig {
188
- HTTPPort : 8040 ,
189
- HTTPSPort : 8043 ,
190
- NetJailName : "jail" ,
191
- SkipCleanup : config .NoJailCleanup ,
192
- }
193
-
194
- // Create network jail
195
- networkInstance , err := network .NewJail (networkConfig , logger )
196
- if err != nil {
197
- logger .Error ("Failed to create network jail" , "error" , err )
198
- return fmt .Errorf ("failed to create network jail: %v" , err )
199
- }
200
-
201
- // Setup signal handling BEFORE any network setup
202
- sigChan := make (chan os.Signal , 1 )
203
- signal .Notify (sigChan , syscall .SIGINT , syscall .SIGTERM )
204
-
205
- // Handle signals immediately in background
206
- go func () {
207
- sig := <- sigChan
208
- logger .Info ("Received signal during setup, cleaning up..." , "signal" , sig )
209
- err := networkInstance .Cleanup ()
210
- if err != nil {
211
- logger .Error ("Emergency cleanup failed" , "error" , err )
212
- }
213
- os .Exit (1 )
214
- }()
215
-
216
- // Ensure cleanup happens no matter what
217
- defer func () {
218
- logger .Debug ("Starting cleanup process" )
219
- err := networkInstance .Cleanup ()
220
- if err != nil {
221
- logger .Error ("Failed to cleanup network jail" , "error" , err )
222
- } else {
223
- logger .Debug ("Cleanup completed successfully" )
224
- }
225
- }()
226
-
227
- // Setup network jail
228
- err = networkInstance .Setup (networkConfig .HTTPPort , networkConfig .HTTPSPort )
229
- if err != nil {
230
- logger .Error ("Failed to setup network jail" , "error" , err )
231
- return fmt .Errorf ("failed to setup network jail: %v" , err )
232
- }
233
-
234
- // Create auditor
235
- auditor := audit .NewLoggingAuditor (logger )
236
-
237
- // Create proxy server
238
- proxyConfig := proxy.Config {
239
- HTTPPort : networkConfig .HTTPPort ,
240
- HTTPSPort : networkConfig .HTTPSPort ,
241
- RuleEngine : ruleEngine ,
242
- Auditor : auditor ,
243
- Logger : logger ,
244
- TLSConfig : tlsConfig ,
245
- }
246
-
247
- proxyServer := proxy .NewProxyServer (proxyConfig )
248
-
249
- // Create context for graceful shutdown
250
- ctx , cancel := context .WithCancel (context .Background ())
251
- defer cancel ()
252
-
253
- // Start proxy server in background
254
- go func () {
255
- err := proxyServer .Start (ctx )
256
- if err != nil {
257
- logger .Error ("Proxy server error" , "error" , err )
258
- }
259
- }()
260
-
261
- // Give proxy time to start
262
- time .Sleep (100 * time .Millisecond )
263
-
264
- // Execute command in network jail
265
- go func () {
266
- defer cancel ()
267
- err := networkInstance .Execute (args , extraEnv )
268
- if err != nil {
269
- logger .Error ("Command execution failed" , "error" , err )
270
- }
271
- }()
272
-
273
- // Wait for signal or context cancellation
274
- select {
275
- case sig := <- sigChan :
276
- logger .Info ("Received signal, shutting down..." , "signal" , sig )
277
- cancel ()
278
- case <- ctx .Done ():
279
- // Context cancelled by command completion
280
- }
97
+ // Run the command in the jail
98
+ return j .Run (args , nil )
99
+ }
281
100
282
- // Stop proxy server
283
- err = proxyServer .Stop ()
284
- if err != nil {
285
- logger .Error ("Failed to stop proxy server" , "error" , err )
101
+ // setupLogging configures and returns a logger based on the log level
102
+ func setupLogging (level string ) * slog.Logger {
103
+ var slogLevel slog.Level
104
+ switch level {
105
+ case "debug" :
106
+ slogLevel = slog .LevelDebug
107
+ case "info" :
108
+ slogLevel = slog .LevelInfo
109
+ case "warn" :
110
+ slogLevel = slog .LevelWarn
111
+ case "error" :
112
+ slogLevel = slog .LevelError
113
+ default :
114
+ slogLevel = slog .LevelWarn
286
115
}
287
116
288
- return nil
117
+ return slog .New (slog .NewTextHandler (os .Stderr , & slog.HandlerOptions {
118
+ Level : slogLevel ,
119
+ }))
289
120
}
0 commit comments