Skip to content

Commit 84939d4

Browse files
authored
Merge pull request #3 from coder/blink/httpjail-go-implementation
Rename netjail to network, simplify rules to allow-only
2 parents 7f83353 + 7b60fd0 commit 84939d4

File tree

12 files changed

+234
-186
lines changed

12 files changed

+234
-186
lines changed

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
name: Test
10+
strategy:
11+
fail-fast: false
12+
matrix:
13+
os: [ubuntu-latest, macos-latest]
14+
runs-on: ${{ matrix.os }}
15+
16+
steps:
17+
- name: Check out code
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: '1.25'
24+
check-latest: true
25+
26+
- name: Cache Go modules
27+
uses: actions/cache@v4
28+
with:
29+
path: |
30+
~/.cache/go-build
31+
~/go/pkg/mod
32+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
33+
restore-keys: |
34+
${{ runner.os }}-go-
35+
36+
- name: Download dependencies
37+
run: go mod download
38+
39+
- name: Verify dependencies
40+
run: go mod verify
41+
42+
- name: Run tests
43+
run: go test -v -race ./...
44+
45+
- name: Run build
46+
run: go build -v ./...

main.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"syscall"
1313
"time"
1414

15-
"github.com/coder/jail/netjail"
15+
"github.com/coder/jail/network"
1616
"github.com/coder/jail/proxy"
1717
"github.com/coder/jail/rules"
1818
"github.com/coder/jail/tls"
@@ -181,15 +181,15 @@ func runJail(inv *serpent.Invocation) error {
181181
}
182182

183183
// Create network jail configuration
184-
netjailConfig := netjail.Config{
184+
networkConfig := network.JailConfig{
185185
HTTPPort: 8040,
186186
HTTPSPort: 8043,
187187
NetJailName: "jail",
188188
SkipCleanup: noJailCleanup,
189189
}
190190

191191
// Create network jail
192-
netjailInstance, err := netjail.NewNetJail(netjailConfig, logger)
192+
networkInstance, err := network.NewJail(networkConfig, logger)
193193
if err != nil {
194194
logger.Error("Failed to create network jail", "error", err)
195195
return fmt.Errorf("failed to create network jail: %v", err)
@@ -203,7 +203,7 @@ func runJail(inv *serpent.Invocation) error {
203203
go func() {
204204
sig := <-sigChan
205205
logger.Info("Received signal during setup, cleaning up...", "signal", sig)
206-
if err := netjailInstance.Cleanup(); err != nil {
206+
if err := networkInstance.Cleanup(); err != nil {
207207
logger.Error("Emergency cleanup failed", "error", err)
208208
}
209209
os.Exit(1)
@@ -212,23 +212,23 @@ func runJail(inv *serpent.Invocation) error {
212212
// Ensure cleanup happens no matter what
213213
defer func() {
214214
logger.Debug("Starting cleanup process")
215-
if err := netjailInstance.Cleanup(); err != nil {
215+
if err := networkInstance.Cleanup(); err != nil {
216216
logger.Error("Failed to cleanup network jail", "error", err)
217217
} else {
218218
logger.Debug("Cleanup completed successfully")
219219
}
220220
}()
221221

222222
// Setup network jail
223-
if err := netjailInstance.Setup(netjailConfig.HTTPPort, netjailConfig.HTTPSPort); err != nil {
223+
if err := networkInstance.Setup(networkConfig.HTTPPort, networkConfig.HTTPSPort); err != nil {
224224
logger.Error("Failed to setup network jail", "error", err)
225225
return fmt.Errorf("failed to setup network jail: %v", err)
226226
}
227227

228228
// Create proxy server
229229
proxyConfig := proxy.Config{
230-
HTTPPort: netjailConfig.HTTPPort,
231-
HTTPSPort: netjailConfig.HTTPSPort,
230+
HTTPPort: networkConfig.HTTPPort,
231+
HTTPSPort: networkConfig.HTTPSPort,
232232
RuleEngine: ruleEngine,
233233
Logger: logger,
234234
TLSConfig: tlsConfig,
@@ -253,7 +253,7 @@ func runJail(inv *serpent.Invocation) error {
253253
// Execute command in network jail
254254
go func() {
255255
defer cancel()
256-
if err := netjailInstance.Execute(args, extraEnv); err != nil {
256+
if err := networkInstance.Execute(args, extraEnv); err != nil {
257257
logger.Error("Command execution failed", "error", err)
258258
}
259259
}()

netjail/linux_stub.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

netjail/macos_stub.go

Lines changed: 0 additions & 10 deletions
This file was deleted.

netjail/linux.go renamed to network/linux.go

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//go:build linux
22

3-
package netjail
3+
package network
44

55
import (
66
"fmt"
@@ -11,27 +11,31 @@ import (
1111
"time"
1212
)
1313

14-
// LinuxNetJail implements NetJail using Linux network namespaces
15-
type LinuxNetJail struct {
16-
config Config
14+
const (
15+
namespacePrefix = "coder_jail"
16+
)
17+
18+
// LinuxJail implements NetJail using Linux network namespaces
19+
type LinuxJail struct {
20+
config JailConfig
1721
namespace string
1822
logger *slog.Logger
1923
}
2024

21-
// newLinuxNetJail creates a new Linux network jail instance
22-
func newLinuxNetJail(config Config, logger *slog.Logger) (*LinuxNetJail, error) {
25+
// newLinuxJail creates a new Linux network jail instance
26+
func newLinuxJail(config JailConfig, logger *slog.Logger) (*LinuxJail, error) {
2327
// Generate unique namespace name
24-
namespace := fmt.Sprintf("boundary_%d", time.Now().UnixNano()%10000000)
28+
namespace := fmt.Sprintf("%s_%d", namespacePrefix, time.Now().UnixNano()%10000000)
2529

26-
return &LinuxNetJail{
30+
return &LinuxJail{
2731
config: config,
2832
namespace: namespace,
2933
logger: logger,
3034
}, nil
3135
}
3236

3337
// Setup creates network namespace and configures iptables rules
34-
func (l *LinuxNetJail) Setup(httpPort, httpsPort int) error {
38+
func (l *LinuxJail) Setup(httpPort, httpsPort int) error {
3539
l.logger.Debug("Setup called", "httpPort", httpPort, "httpsPort", httpsPort)
3640
l.config.HTTPPort = httpPort
3741
l.config.HTTPSPort = httpsPort
@@ -70,7 +74,7 @@ func (l *LinuxNetJail) Setup(httpPort, httpsPort int) error {
7074
}
7175

7276
// Execute runs a command within the network namespace
73-
func (l *LinuxNetJail) Execute(command []string, extraEnv map[string]string) error {
77+
func (l *LinuxJail) Execute(command []string, extraEnv map[string]string) error {
7478
l.logger.Debug("Execute called", "command", command)
7579
if len(command) == 0 {
7680
return fmt.Errorf("no command specified")
@@ -81,7 +85,7 @@ func (l *LinuxNetJail) Execute(command []string, extraEnv map[string]string) err
8185
cmdArgs := []string{"ip", "netns", "exec", l.namespace}
8286
cmdArgs = append(cmdArgs, command...)
8387
l.logger.Debug("Full command args", "args", cmdArgs)
84-
88+
8589
cmd := exec.Command("ip", cmdArgs[1:]...)
8690

8791
// Set up environment
@@ -124,7 +128,7 @@ func (l *LinuxNetJail) Execute(command []string, extraEnv map[string]string) err
124128
}
125129

126130
// Cleanup removes the network namespace and iptables rules
127-
func (l *LinuxNetJail) Cleanup() error {
131+
func (l *LinuxJail) Cleanup() error {
128132
if l.config.SkipCleanup {
129133
return nil
130134
}
@@ -152,7 +156,7 @@ func (l *LinuxNetJail) Cleanup() error {
152156
}
153157

154158
// createNamespace creates a new network namespace
155-
func (l *LinuxNetJail) createNamespace() error {
159+
func (l *LinuxJail) createNamespace() error {
156160
cmd := exec.Command("ip", "netns", "add", l.namespace)
157161
if err := cmd.Run(); err != nil {
158162
return fmt.Errorf("failed to create namespace: %v", err)
@@ -161,12 +165,12 @@ func (l *LinuxNetJail) createNamespace() error {
161165
}
162166

163167
// setupNetworking configures networking within the namespace
164-
func (l *LinuxNetJail) setupNetworking() error {
168+
func (l *LinuxJail) setupNetworking() error {
165169
// Create veth pair with short names (Linux interface names limited to 15 chars)
166170
// Generate unique ID to avoid conflicts
167171
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano()%10000000) // 7 digits max
168-
vethHost := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
169-
vethNetJail := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
172+
vethHost := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
173+
vethNetJail := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
170174

171175
cmd := exec.Command("ip", "link", "add", vethHost, "type", "veth", "peer", "name", vethNetJail)
172176
if err := cmd.Run(); err != nil {
@@ -218,7 +222,7 @@ func (l *LinuxNetJail) setupNetworking() error {
218222
// setupDNS configures DNS resolution for the namespace
219223
// This ensures reliable DNS resolution by using public DNS servers
220224
// instead of relying on the host's potentially complex DNS configuration
221-
func (l *LinuxNetJail) setupDNS() error {
225+
func (l *LinuxJail) setupDNS() error {
222226
// Always create namespace-specific resolv.conf with reliable public DNS servers
223227
// This avoids issues with systemd-resolved, Docker DNS, and other complex setups
224228
netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
@@ -228,7 +232,7 @@ func (l *LinuxNetJail) setupDNS() error {
228232

229233
// Write custom resolv.conf with multiple reliable public DNS servers
230234
resolvConfPath := fmt.Sprintf("%s/resolv.conf", netnsEtc)
231-
dnsConfig := `# Custom DNS for boundary namespace
235+
dnsConfig := `# Custom DNS for network namespace
232236
nameserver 8.8.8.8
233237
nameserver 8.8.4.4
234238
nameserver 1.1.1.1
@@ -244,7 +248,7 @@ options timeout:2 attempts:2
244248
}
245249

246250
// setupIptables configures iptables rules for traffic redirection
247-
func (l *LinuxNetJail) setupIptables() error {
251+
func (l *LinuxJail) setupIptables() error {
248252
// Enable IP forwarding
249253
cmd := exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1")
250254
cmd.Run() // Ignore error
@@ -273,7 +277,7 @@ func (l *LinuxNetJail) setupIptables() error {
273277
}
274278

275279
// removeIptables removes iptables rules
276-
func (l *LinuxNetJail) removeIptables() error {
280+
func (l *LinuxJail) removeIptables() error {
277281
// Remove NAT rule
278282
cmd := exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE")
279283
cmd.Run() // Ignore errors during cleanup
@@ -282,10 +286,10 @@ func (l *LinuxNetJail) removeIptables() error {
282286
}
283287

284288
// removeNamespace removes the network namespace
285-
func (l *LinuxNetJail) removeNamespace() error {
289+
func (l *LinuxJail) removeNamespace() error {
286290
cmd := exec.Command("ip", "netns", "del", l.namespace)
287291
if err := cmd.Run(); err != nil {
288292
return fmt.Errorf("failed to remove namespace: %v", err)
289293
}
290294
return nil
291-
}
295+
}

network/linux_stub.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !linux
2+
3+
package network
4+
5+
import (
6+
"fmt"
7+
"log/slog"
8+
)
9+
10+
// newLinuxJail is not available on non-Linux platforms
11+
func newLinuxJail(_ JailConfig, _ *slog.Logger) (Jail, error) {
12+
return nil, fmt.Errorf("linux network jail not supported on this platform")
13+
}

netjail/macos.go renamed to network/macos.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
//go:build darwin
22

3-
package netjail
3+
package network
44

55
import (
66
"fmt"
7-
"io/ioutil"
87
"log/slog"
98
"os"
109
"os/exec"
@@ -14,21 +13,21 @@ import (
1413
)
1514

1615
const (
17-
PF_ANCHOR_NAME = "boundary"
18-
GROUP_NAME = "boundary"
16+
PF_ANCHOR_NAME = "network"
17+
GROUP_NAME = "network"
1918
)
2019

2120
// MacOSNetJail implements network jail using macOS PF (Packet Filter) and group-based isolation
2221
type MacOSNetJail struct {
23-
config Config
22+
config JailConfig
2423
groupID int
2524
pfRulesPath string
2625
mainRulesPath string
2726
logger *slog.Logger
2827
}
2928

30-
// newMacOSNetJail creates a new macOS network jail instance
31-
func newMacOSNetJail(config Config, logger *slog.Logger) (*MacOSNetJail, error) {
29+
// newMacOSJail creates a new macOS network jail instance
30+
func newMacOSJail(config JailConfig, logger *slog.Logger) (*MacOSNetJail, error) {
3231
pfRulesPath := fmt.Sprintf("/tmp/%s.pf", config.NetJailName)
3332
mainRulesPath := fmt.Sprintf("/tmp/%s_main.pf", config.NetJailName)
3433

@@ -266,7 +265,7 @@ func (m *MacOSNetJail) setupPFRules() error {
266265
}
267266

268267
// Write rules to temp file
269-
if err := ioutil.WriteFile(m.pfRulesPath, []byte(rules), 0644); err != nil {
268+
if err := os.WriteFile(m.pfRulesPath, []byte(rules), 0644); err != nil {
270269
return fmt.Errorf("failed to write PF rules file: %v", err)
271270
}
272271

@@ -297,7 +296,7 @@ anchor "%s"
297296
`, PF_ANCHOR_NAME, PF_ANCHOR_NAME)
298297

299298
// Write and load the main ruleset
300-
if err := ioutil.WriteFile(m.mainRulesPath, []byte(mainRules), 0644); err != nil {
299+
if err := os.WriteFile(m.mainRulesPath, []byte(mainRules), 0644); err != nil {
301300
return fmt.Errorf("failed to write main PF rules: %v", err)
302301
}
303302

@@ -335,4 +334,4 @@ func (m *MacOSNetJail) cleanupTempFiles() {
335334
if m.mainRulesPath != "" {
336335
os.Remove(m.mainRulesPath)
337336
}
338-
}
337+
}

network/macos_stub.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//go:build !darwin
2+
3+
package network
4+
5+
import "log/slog"
6+
7+
// newMacOSJail is not available on non-macOS platforms
8+
func newMacOSJail(config JailConfig, logger *slog.Logger) (Jail, error) {
9+
panic("macOS network jail not available on this platform")
10+
}

0 commit comments

Comments
 (0)