-
Notifications
You must be signed in to change notification settings - Fork 50
/
init.go
134 lines (117 loc) · 4.01 KB
/
init.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package sandbox
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"github.com/ossf/package-analysis/internal/log"
)
const (
ipBin = "/usr/sbin/ip"
iptablesLoadBin = "/usr/sbin/iptables-restore"
iptablesRules = "/usr/local/etc/iptables.rules"
dummyInterface = "cnidummy0"
// bridgeInterface is the name of the podman bridge defined in
// tools/network/podman-analysis.conflist. This bridge is used by the
// sandbox during analysis to separate the sandbox traffic from the host.
bridgeInterface = "cni-analysis"
)
const (
// NetworkInterface is the name of a network interface that has access to
// the sandbox network traffic.
NetworkInterface = bridgeInterface
)
func loadIptablesRules(ctx context.Context) error {
slog.DebugContext(ctx, "Loading iptable rules")
// Open the iptables-restore configuration
f, err := os.Open(iptablesRules)
if err != nil {
return err
}
defer f.Close()
logOut := log.NewWriter(ctx, slog.Default(), slog.LevelInfo)
defer logOut.Close()
logErr := log.NewWriter(ctx, slog.Default(), slog.LevelWarn)
defer logErr.Close()
cmd := exec.CommandContext(ctx, iptablesLoadBin)
cmd.Stdout = logOut
cmd.Stderr = logErr
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
defer stdin.Close()
if err := cmd.Start(); err != nil {
return err
}
// Send the iptables rules to the command via stdin
if _, err := io.Copy(stdin, f); err != nil {
return err
}
stdin.Close()
return cmd.Wait()
}
// createBridgeNetwork ensures that NetworkInterface and the bridge network
// exists prior to the sandbox.
//
// podman would create this bridge interface anyway, but doing it early allows
// a packet capture to be started on the interface prior to the sandbox
// starting.
func createBridgeNetwork(ctx context.Context) error {
slog.DebugContext(ctx, "Creating bridge network")
// Create the bridge
cmd := exec.CommandContext(ctx, ipBin, "link", "add", "name", bridgeInterface, "type", "bridge")
if err := cmd.Run(); err != nil {
// If the error is not an ExitError, or the Exit Code is not 2, then abort.
if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != 2 {
return fmt.Errorf("failed to add bridge interface: %w", err)
}
}
// Bring the bridge up.
cmd = exec.CommandContext(ctx, ipBin, "link", "set", bridgeInterface, "up")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to bring up bridge interface: %w", err)
}
// Add a dummy device so the bridge stays up
cmd = exec.CommandContext(ctx, ipBin, "link", "add", "dev", dummyInterface, "type", "dummy")
if err := cmd.Run(); err != nil {
// If the error is not an ExitError, or the Exit Code is not 2, then abort.
if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != 2 {
return fmt.Errorf("failed to create dummy inteface: %w", err)
}
}
// Add the dummy device to the bridge network
cmd = exec.CommandContext(ctx, ipBin, "link", "set", "dev", dummyInterface, "master", bridgeInterface)
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to add dummy interface to bridge: %w", err)
}
// Bring the dummy device up.
cmd = exec.CommandContext(ctx, ipBin, "link", "set", dummyInterface, "up")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to bring up dummy interface: %w", err)
}
return nil
}
// InitNetwork initializes the host for sandbox network connections
//
// It will ensure that the network interface exists, and any firewall
// rules are configured.
//
// This function is idempotent and is safe to be called more than once.
//
// This function must be called after logging is complete, and may exit if
// any of the commands fail.
func InitNetwork(ctx context.Context) {
// Create the bridge network
if err := createBridgeNetwork(ctx); err != nil {
slog.ErrorContext(ctx, "Failed to create bridge network", "error", err)
os.Exit(1)
}
// Load iptables rules to further isolate the sandbox
if err := loadIptablesRules(ctx); err != nil {
slog.ErrorContext(ctx, "Failed restoring iptables rules", "error", err)
os.Exit(1)
}
}