diff --git a/.gitignore b/.gitignore index 91ff0b1..5a0497e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -gerbil \ No newline at end of file +gerbil +.DS_Store +bin/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index daeac10..286241d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /gerbil # Start a new stage from scratch FROM ubuntu:22.04 AS runner +RUN apt-get update && apt-get install -y iptables iproute2 && rm -rf /var/lib/apt/lists/* + # Copy the pre-built binary file from the previous stage and the entrypoint script COPY --from=builder /gerbil /usr/local/bin/ COPY entrypoint.sh / diff --git a/Makefile b/Makefile index dadb9b3..a1d6aeb 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,11 @@ test: local: CGO_ENABLED=0 GOOS=linux go build -o gerbil +<<<<<<< HEAD go-build-release: +======= +release: +>>>>>>> bab8630756f5b243feec8a9e8952086498808a11 CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/gerbil_linux_arm64 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/gerbil_linux_amd64 diff --git a/main.go b/main.go index c4b1764..bb705eb 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "os" + "os/exec" "strconv" "strings" "sync" @@ -188,11 +189,17 @@ func main() { wgconfig.PrivateKey = key.String() } } else { - wgconfig, err = loadRemoteConfig(remoteConfigURL, key, reachableAt) - if err != nil { - logger.Fatal("Failed to load configuration: %v", err) + // loop until we get the config + for wgconfig.PrivateKey == "" { + logger.Info("Fetching remote config from %s", remoteConfigURL) + wgconfig, err = loadRemoteConfig(remoteConfigURL, key, reachableAt) + if err != nil { + logger.Error("Failed to load configuration: %v", err) + time.Sleep(5 * time.Second) + continue + } + wgconfig.PrivateKey = key.String() } - wgconfig.PrivateKey = key.String() } wgClient, err = wgctrl.New() @@ -338,6 +345,10 @@ func ensureWireguardInterface(wgconfig WgConfig) error { return fmt.Errorf("failed to bring up interface: %v", err) } + if err := ensureMSSClamping(); err != nil { + logger.Warn("Failed to ensure MSS clamping: %v", err) + } + logger.Info("WireGuard interface %s created and configured", interfaceName) return nil @@ -415,6 +426,94 @@ func ensureWireguardPeers(peers []Peer) error { return nil } +func ensureMSSClamping() error { + // Calculate MSS value (MTU - 40 for IPv4 header (20) and TCP header (20)) + mssValue := mtuInt - 40 + + // Rules to be managed - just the chains, we'll construct the full command separately + chains := []string{"INPUT", "OUTPUT", "FORWARD"} + + // First, try to delete any existing rules + for _, chain := range chains { + deleteCmd := exec.Command("/usr/sbin/iptables", + "-t", "mangle", + "-D", chain, + "-p", "tcp", + "--tcp-flags", "SYN,RST", "SYN", + "-j", "TCPMSS", + "--set-mss", fmt.Sprintf("%d", mssValue)) + + logger.Info("Attempting to delete existing MSS clamping rule for chain %s", chain) + + // Try deletion multiple times to handle multiple existing rules + for i := 0; i < 3; i++ { + out, err := deleteCmd.CombinedOutput() + if err != nil { + // Convert exit status 1 to string for better logging + if exitErr, ok := err.(*exec.ExitError); ok { + logger.Debug("Deletion stopped for chain %s: %v (output: %s)", + chain, exitErr.String(), string(out)) + } + break // No more rules to delete + } + logger.Info("Deleted MSS clamping rule for chain %s (attempt %d)", chain, i+1) + } + } + + // Then add the new rules + var errors []error + for _, chain := range chains { + addCmd := exec.Command("/usr/sbin/iptables", + "-t", "mangle", + "-A", chain, + "-p", "tcp", + "--tcp-flags", "SYN,RST", "SYN", + "-j", "TCPMSS", + "--set-mss", fmt.Sprintf("%d", mssValue)) + + logger.Info("Adding MSS clamping rule for chain %s", chain) + + if out, err := addCmd.CombinedOutput(); err != nil { + errMsg := fmt.Sprintf("Failed to add MSS clamping rule for chain %s: %v (output: %s)", + chain, err, string(out)) + logger.Error(errMsg) + errors = append(errors, fmt.Errorf(errMsg)) + continue + } + + // Verify the rule was added + checkCmd := exec.Command("/usr/sbin/iptables", + "-t", "mangle", + "-C", chain, + "-p", "tcp", + "--tcp-flags", "SYN,RST", "SYN", + "-j", "TCPMSS", + "--set-mss", fmt.Sprintf("%d", mssValue)) + + if out, err := checkCmd.CombinedOutput(); err != nil { + errMsg := fmt.Sprintf("Rule verification failed for chain %s: %v (output: %s)", + chain, err, string(out)) + logger.Error(errMsg) + errors = append(errors, fmt.Errorf(errMsg)) + continue + } + + logger.Info("Successfully added and verified MSS clamping rule for chain %s", chain) + } + + // If we encountered any errors, return them combined + if len(errors) > 0 { + var errMsgs []string + for _, err := range errors { + errMsgs = append(errMsgs, err.Error()) + } + return fmt.Errorf("MSS clamping setup encountered errors:\n%s", + strings.Join(errMsgs, "\n")) + } + + return nil +} + func handlePeer(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: