9.7 KiB
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
netpackage 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.Builderfor 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.Builderand 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
wgcommand - 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:
- Better Type Safety: Compile-time error checking prevents runtime issues
- Improved Error Handling: Structured error handling with context
- Enhanced Validation: More comprehensive input validation
- Better Performance: Native key generation and optimized operations
- Easier Maintenance: Structured code with clear separation of concerns
- Cross-Platform: Single binary with no external dependencies
- Better Security: Cryptographically secure operations
- 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.