Files
wgtool/GO_VS_BASH_ANALYSIS.md
2026-03-22 00:54:58 -07:00

9.7 KiB

Go vs Bash Implementation Analysis

Overview

This document compares the bash implementation (wireguard_setup.sh) with the Go implementation (wireguard_setup.go) and identifies key improvements and advantages of the Go version.

Key Improvements in Go Version

1. Type Safety and Error Handling

Bash Version:

# Basic string validation
if [ -z "$NODE_NAME" ]; then
    print_error "Node name cannot be empty"
    exit 1
fi

Go Version:

// Strong typing with structured validation
func validateHostname(hostname string) error {
    if hostname == "" {
        return fmt.Errorf("hostname cannot be empty")
    }
    if len(hostname) > 63 {
        return fmt.Errorf("hostname too long (max 63 characters)")
    }
    hostnameRegex := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$`)
    if !hostnameRegex.MatchString(hostname) {
        return fmt.Errorf("invalid hostname format. Use alphanumeric characters and hyphens only")
    }
    return nil
}

Advantages:

  • Compile-time error checking
  • Structured error handling with context
  • Type safety prevents runtime errors
  • Better validation with regex patterns

2. Configuration Management

Bash Version:

# Hardcoded values scattered throughout
ZION_PUBLIC_KEY="2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg="
ZION_ENDPOINT="ugh.im:51820"
ZION_ALLOWED_IPS="10.8.0.0/24"

Go Version:

// Structured configuration with type safety
type ZionConfig struct {
    PublicKey string            `json:"public_key"`
    Endpoint  string            `json:"endpoint"`
    Peers     map[string]string `json:"peers"` // name -> public_key
}

var zionConfig = ZionConfig{
    PublicKey: "2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=",
    Endpoint:  "ugh.im:51820",
    Peers: map[string]string{
        "Cth":      "NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=",
        "Aza":      "qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=",
        // ... more peers
    },
}

Advantages:

  • Centralized configuration management
  • JSON serialization support
  • Type-safe access to configuration
  • Easy to extend and maintain

3. IP Address Validation

Bash Version:

# Basic regex validation
validate_ip() {
    local ip=$1
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
        return 0
    else
        return 1
    fi
}

Go Version:

func validateIP(ip string) error {
    if ip == "" {
        return fmt.Errorf("IP address cannot be empty")
    }

    // Check if it's in the expected range
    if !ipRegex.MatchString(ip) {
        return fmt.Errorf("IP should be in 10.8.0.x range for NextGen network")
    }

    // Validate IP format using net package
    parsedIP := net.ParseIP(ip)
    if parsedIP == nil {
        return fmt.Errorf("invalid IP address format")
    }

    // Check for conflicts with existing peers
    for peerName, peerIP := range map[string]string{
        "Zion": "10.8.0.1",
        "Cth":  "10.8.0.10",
        // ... more peers
    } {
        if ip == peerIP {
            return fmt.Errorf("IP %s is already in use by %s", ip, peerName)
        }
    }

    return nil
}

Advantages:

  • Uses Go's net package for proper IP validation
  • Checks for IP conflicts with existing peers
  • More comprehensive validation
  • Better error messages

4. Key Generation

Bash Version:

# Relies on external wg command
PRIVATE_KEY=$(wg genkey)
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | wg pubkey)

Go Version:

func generateWireGuardKeys() (string, string, error) {
    // Generate private key
    privateKeyBytes := make([]byte, 32)
    if _, err := rand.Read(privateKeyBytes); err != nil {
        return "", "", err
    }

    // Ensure the key is valid for curve25519
    privateKeyBytes[0] &= 248
    privateKeyBytes[31] &= 127
    privateKeyBytes[31] |= 64

    // Generate public key using curve25519
    var publicKeyBytes [32]byte
    curve25519.ScalarBaseMult(&publicKeyBytes, (*[32]byte)(privateKeyBytes))

    privateKey := base64.StdEncoding.EncodeToString(privateKeyBytes[:])
    publicKey := base64.StdEncoding.EncodeToString(publicKeyBytes[:])

    return privateKey, publicKey, nil
}

Advantages:

  • No external dependencies for key generation
  • Uses cryptographically secure random number generator
  • Proper curve25519 key generation
  • Better error handling

5. File Operations

Bash Version:

# Basic file operations
cat > "$CONFIG_FILE" << EOF
[Interface]
PrivateKey = $PRIVATE_KEY
Address = $IP_ADDRESS
EOF

Go Version:

// Structured file operations with error handling
func generateConfig(hostname, ipAddress, privateKey, routingMode string) string {
    var config strings.Builder

    // Interface section
    config.WriteString("[Interface]\n")
    config.WriteString(fmt.Sprintf("Address = %s/24\n", ipAddress))
    config.WriteString(fmt.Sprintf("PrivateKey = %s\n", privateKey))

    // Add ListenPort for static servers
    if port, exists := staticServers[ipAddress]; exists {
        config.WriteString(fmt.Sprintf("ListenPort = %s\n", port))
    }

    // Add DNS for full tunnel mode
    if routingMode == "full_tunnel" {
        config.WriteString("DNS = 1.1.1.1, 8.8.8.8\n")
    }

    // Zion peer (always enabled)
    config.WriteString("\n# Zion (central server)\n")
    config.WriteString("[Peer]\n")
    config.WriteString(fmt.Sprintf("PublicKey = %s\n", zionConfig.PublicKey))
    // ... more configuration

    return config.String()
}

Advantages:

  • Uses strings.Builder for efficient string concatenation
  • Structured configuration generation
  • Better memory efficiency
  • Type-safe operations

6. User Input Handling

Bash Version:

# Basic input with limited validation
read -p "Enter node name (e.g., aza, cth, galaxy): " NODE_NAME
if [ -z "$NODE_NAME" ]; then
    print_error "Node name cannot be empty"
    exit 1
fi

Go Version:

func getUserInput(prompt string, validator func(string) error) string {
    reader := bufio.NewReader(os.Stdin)
    for {
        fmt.Print(prompt)
        input, _ := reader.ReadString('\n')
        input = strings.TrimSpace(input)

        if err := validator(input); err != nil {
            printError(err.Error())
            fmt.Print("Continue anyway? (y/N): ")
            response, _ := reader.ReadString('\n')
            response = strings.TrimSpace(strings.ToLower(response))
            if response == "y" || response == "yes" {
                return input
            }
            continue
        }
        return input
    }
}

Advantages:

  • Function-based validation
  • Better error recovery
  • Consistent input handling
  • More flexible validation logic

7. JSON Configuration Export

Go Version Only:

type WGConfig struct {
    Hostname    string `json:"hostname"`
    IPAddress   string `json:"ip_address"`
    PrivateKey  string `json:"private_key"`
    PublicKey   string `json:"public_key"`
    RoutingMode string `json:"routing_mode"`
    Generated   string `json:"generated"`
    ScriptVer   string `json:"script_version"`
    RunningRoot bool   `json:"running_as_root"`
    ZionPeer    bool   `json:"zion_peer_added"`
}

// Automatic JSON serialization
infoData, err := json.MarshalIndent(wgConfig, "", "  ")
if err != nil {
    printError(fmt.Sprintf("Failed to marshal config: %v", err))
    os.Exit(1)
}

Advantages:

  • Structured data export
  • Easy integration with other tools
  • Machine-readable configuration
  • Better debugging and logging

Performance Comparison

Memory Usage

  • Bash: Higher memory usage due to string operations and external commands
  • Go: More efficient memory usage with strings.Builder and structured data

Execution Speed

  • Bash: Slower due to external command calls (wg genkey, wg pubkey)
  • Go: Faster with native key generation and optimized string operations

Dependencies

  • Bash: Requires external tools (wg, wg-quick, bash)
  • Go: Single binary with minimal runtime dependencies

Maintainability

Code Organization

  • Bash: Functions scattered throughout, harder to maintain
  • Go: Structured with clear separation of concerns

Error Handling

  • Bash: Basic error checking with exit codes
  • Go: Comprehensive error handling with context

Testing

  • Bash: Difficult to unit test
  • Go: Easy to unit test with structured functions

Security Improvements

Key Generation

  • Bash: Relies on external wg command
  • Go: Uses cryptographically secure random number generator

Input Validation

  • Bash: Basic regex validation
  • Go: Comprehensive validation with multiple checks

File Operations

  • Bash: Basic file operations
  • Go: Proper error handling and atomic operations

Cross-Platform Compatibility

Distribution

  • Bash: Requires bash shell and external tools
  • Go: Single binary that works on any platform

Dependencies

  • Bash: Platform-specific package managers
  • Go: No external dependencies

Conclusion

The Go implementation provides significant improvements over the bash version:

  1. Better Type Safety: Compile-time error checking prevents runtime issues
  2. Improved Error Handling: Structured error handling with context
  3. Enhanced Validation: More comprehensive input validation
  4. Better Performance: Native key generation and optimized operations
  5. Easier Maintenance: Structured code with clear separation of concerns
  6. Cross-Platform: Single binary with no external dependencies
  7. Better Security: Cryptographically secure operations
  8. Structured Output: JSON configuration export for integration

The Go version is more suitable for production use, especially in environments where reliability, security, and maintainability are important.