chore: initial commit of wgtool

This commit is contained in:
sapient
2026-03-22 00:54:58 -07:00
commit f9529c6055
59 changed files with 6102 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.env
.env.*
*.log
node_modules/
.venv/

10
CURRENT_WORKING/aza.conf Normal file
View File

@@ -0,0 +1,10 @@
[Interface]
PrivateKey = UI7XeN/0WGCziv68Vt3hlPGYWB1dwZN7+N2C4Y2y91o=
Address = 10.8.0.2/24
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 60

81
CURRENT_WORKING/cth.conf Normal file
View File

@@ -0,0 +1,81 @@
[Interface]
Address = 10.8.0.10/24
ListenPort = 53535
PrivateKey = UDr1aohdSWGMEy1F6v0GoC7JNgVHbwA8Dz//7E2xQHM=
#pix66
[Peer]
PublicKey = hguz42G5S8EV3NmkORc6eiBWb+V9Z6oBdiXVnAcqvmI=
AllowedIPs = 10.8.0.6/32
#Cth
#[Peer]
#PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
#AllowedIPs = 10.8.0.10/32
#Aza
#[Peer]
#PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=
#AllowedIPs = 10.8.0.2/32
#Nyar
[Peer]
PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
AllowedIPs = 10.8.0.20/32
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#nanocube
#[Peer]
#PublicKey = /ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc=
#AllowedIPs = 10.8.0.7/32
#jupiter
#[Peer]
#PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
#AllowedIPs = 10.8.0.42/32
#HASS
#[Peer]
#PublicKey = C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0=
#AllowedIPs = 10.8.0.8/32
#framebot
#[Peer]
#PublicKey = loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es=
#AllowedIPs = 10.8.0.40/32
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25
#[Peer]
#PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
#AllowedIPs = 10.8.0.20/32
#PersistentKeepalive = 25
#jupiter
#[Peer]
#PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
#AllowedIPs = 10.8.0.42/32
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#Endpoint = galaxyspin.space:54382
#PersistentKeepalive = 25

View File

@@ -0,0 +1,16 @@
[Interface]
PrivateKey = sHEHZQ3AdUTLG56yeHdmZVuXrivB9P+40Fw0oWHF3Fg=
Address = 10.8.0.99/24
ListenPort = 54382
# Zion peer (central server) - for access to entire network
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

25
CURRENT_WORKING/nyan.conf Normal file
View File

@@ -0,0 +1,25 @@
[Interface]
PrivateKey = sLx3sor9gxhk7T2MIS3g50wzSZBrlVUG+NlXk47jEH4=
Address = 10.8.0.20/24
# Zion peer (central server) - for access to entire network
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25
#CTH
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/32
Endpoint = aw2cd67.glddns.com:53535
PersistentKeepalive = 25
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#Endpoint = galaxyspin.space:54382
#PersistentKeepalive = 25

48
CURRENT_WORKING/zion.conf Normal file
View File

@@ -0,0 +1,48 @@
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = UJvsfv6iQPAW9Wnc81bK0o3IIHX86kGb+24dUTuGFnA=
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip route add 10.8.0.0/24 dev wg0 2>/dev/null || true
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip route del 10.8.0.0/24 dev wg0 2>/dev/null || true
#Cth
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/32
#Aza
[Peer]
PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=
AllowedIPs = 10.8.0.2/32
#Nyar
[Peer]
PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
AllowedIPs = 10.8.0.20/32
#Galaxy
[Peer]
PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
AllowedIPs = 10.8.0.99/32
#nanocube
[Peer]
PublicKey = /ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc=
AllowedIPs = 10.8.0.7/32
#jupiter
[Peer]
PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
AllowedIPs = 10.8.0.42/32
#HASS
[Peer]
PublicKey = C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0=
AllowedIPs = 10.8.0.8/32
#framebot
[Peer]
PublicKey = loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es=
AllowedIPs = 10.8.0.40/32

360
GO_VS_BASH_ANALYSIS.md Normal file
View File

@@ -0,0 +1,360 @@
# 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:**
```bash
# Basic string validation
if [ -z "$NODE_NAME" ]; then
print_error "Node name cannot be empty"
exit 1
fi
```
**Go Version:**
```go
// 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:**
```bash
# Hardcoded values scattered throughout
ZION_PUBLIC_KEY="2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg="
ZION_ENDPOINT="ugh.im:51820"
ZION_ALLOWED_IPS="10.8.0.0/24"
```
**Go Version:**
```go
// 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:**
```bash
# 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:**
```go
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:**
```bash
# Relies on external wg command
PRIVATE_KEY=$(wg genkey)
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | wg pubkey)
```
**Go Version:**
```go
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:**
```bash
# Basic file operations
cat > "$CONFIG_FILE" << EOF
[Interface]
PrivateKey = $PRIVATE_KEY
Address = $IP_ADDRESS
EOF
```
**Go Version:**
```go
// 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:**
```bash
# 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:**
```go
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:**
```go
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.

121
README.md Normal file
View File

@@ -0,0 +1,121 @@
## wgtool (WireGuard helper CLI)
wgtool streamlines creating and validating WireGuard configs, and generating a ready-to-paste Zion peer block.
### Features
- Generate WireGuard configs with sensible defaults
- Validate single configs or all .conf files in a directory
- Print a Zion-ready [Peer] snippet for adding new nodes
- Generate private keys (derive public key with `wg pubkey`)
### Commands
- `generate`: Create a config and write keys
- `validate`: Lint a config file or all `.conf` files in a directory
- `zion-peer`: Print a `[Peer]` block for Zions `wg0.conf`
- `keys`: Print a private key
- `version`: Show tool version
### Defaults and endpoints
- Adds one default peer in generated configs:
- Zion (central server)
- PublicKey: `2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=`
- Endpoint: `ugh.im:51820`
- AllowedIPs:
- `wg_only`: `10.8.0.0/24`
- `full_tunnel`: `0.0.0.0/0, ::/0`
- PersistentKeepalive: `25`
### generate
Create a config into `wireguard_configs/` and output keys alongside it. Missing flags are prompted interactively unless `--yes` is used.
Flags:
- `--hostname` Node name (e.g., `aza`)
- `--ip` Node IP in `10.8.0.x`
- `--interface` Interface name (default `wg0`)
- `--routing` `wg_only` | `full_tunnel` (default `wg_only`)
- `--out` Output directory (default `wireguard_configs`)
- `--force` Overwrite without prompt
- `--yes` Non-interactive (assume yes)
Examples:
```bash
./wgtool generate
./wgtool generate --hostname aza --ip 10.8.0.30 --interface wg0 --routing wg_only --out wireguard_configs --yes
./wgtool generate --hostname aza --ip 10.8.0.30 --routing full_tunnel --yes
```
Outputs:
- `wireguard_configs/wg0.conf`
- `wireguard_configs/<hostname>_private.key`
- Derive public key: `echo "<PrivateKey>" | wg pubkey`
### validate
Validate a config file or every `.conf` in a directory.
Flags:
- `--target` Path to a file or directory
Examples:
```bash
./wgtool validate --target wireguard_configs/wg0.conf
./wgtool validate --target wireguard_configs
```
Checks include:
- Presence of `[Interface]`
- `PrivateKey` and CIDR `Address`
- Peer `PublicKey` format
- `AllowedIPs` as valid CIDRs
### zion-peer
Print a `[Peer]` block to add into Zions `/etc/wireguard/wg0.conf` for a new node.
Flags:
- `--name` Node name
- `--pub` Node public key (44-char base64 ending with `=`)
- `--ip` Node IP in `10.8.0.x` (host address)
Example:
```bash
./wgtool zion-peer --name aza --pub ABCDEFG...= --ip 10.8.0.30
```
### keys
Generate and print a private key.
Example:
```bash
./wgtool keys
# derive pub
echo "<PrivateKey>" | wg pubkey
```
### Quick start
1) Create a config and keys
```bash
./wgtool generate --hostname mynode --ip 10.8.0.30 --yes
```
2) Validate the config
```bash
./wgtool validate --target wireguard_configs/wg0.conf
```
3) Give Zion your peer details
```bash
./wgtool zion-peer --name mynode --pub $(echo "<PrivateKey>" | wg pubkey) --ip 10.8.0.30
```
4) Install and enable (on your node)
```bash
sudo cp wireguard_configs/wg0.conf /etc/wireguard/
sudo chmod 600 /etc/wireguard/wg0.conf
sudo systemctl enable --now wg-quick@wg0
```
Notes:
- IPs are enforced in the `10.8.0.x` range.
- In `full_tunnel` mode DNS is set to `1.1.1.1, 8.8.8.8`.
- Overwrites are blocked unless `--force` or confirmed interactively.

138
SCRIPT_IMPROVEMENTS.md Normal file
View File

@@ -0,0 +1,138 @@
# WireGuard Script Improvements Summary
This document outlines the errors found and optimizations made to the WireGuard setup scripts.
## Scripts Analyzed
1. `generate_zion_peer.sh` - Zion peer configuration generator
2. `wireguard_setup.sh` - Interactive WireGuard setup script
3. `wireguard_setup.go` - Go-based WireGuard setup tool
## Issues Found and Fixed
### 1. generate_zion_peer.sh
#### Issues Fixed:
- **Shebang**: Changed from `/bin/bash` to `/usr/bin/env bash` for better portability
- **Error handling**: Added `set -euo pipefail` for stricter error handling
- **IP validation**: Improved regex to properly validate 10.8.0.x format and exclude reserved addresses
- **Public key validation**: Enhanced validation for WireGuard public keys (44 character base64)
- **Input sanitization**: Added validation for node names
- **Configuration loading**: Added ability to load Zion config from file with fallback to hardcoded values
#### Optimizations Added:
- **Command line options**: Added `-c/--config` and `-h/--help` flags
- **Dynamic config loading**: Script now attempts to read Zion configuration from `CURRENT_WORKING/zion.conf`
- **Better error messages**: More descriptive error messages with specific validation failures
- **Safe fallbacks**: Graceful degradation when configuration files are not available
### 2. wireguard_setup.sh
#### Issues Fixed:
- **Shebang**: Changed from `/bin/bash` to `/usr/bin/env bash`
- **Error handling**: Added `set -euo pipefail` for stricter error handling
- **IP validation**: Completely rewrote validation function to properly check IP format and subnet
- **Port validation**: Enhanced port validation with warnings for privileged ports
- **Public key validation**: Added validation for WireGuard public keys
- **Network interface detection**: Added automatic detection of network interfaces instead of hardcoded `eth0`
- **File permissions**: Added proper file permission setting (600) for security
- **Variable scope**: Fixed variable scoping issues and made variables local where appropriate
#### Optimizations Added:
- **Configuration file support**: Added `-c/--config` option for custom Zion config files
- **Safe filename creation**: Added function to sanitize user input for filenames
- **Network interface detection**: Automatic detection of available network interfaces
- **Better validation loops**: Improved input validation with retry logic
- **Enhanced error messages**: More descriptive error messages and warnings
- **Fedora support**: Added Fedora package installation instructions
### 3. wireguard_setup.go
#### Issues Fixed:
- **Deprecated packages**: Replaced `ioutil` with `os` package (Go 1.16+ compatibility)
- **Version bump**: Updated script version to 2.4
#### Optimizations Added:
- **Modern Go**: Uses current Go standard library practices
- **Better error handling**: More comprehensive error checking throughout
## Security Improvements
### File Permissions
- All WireGuard configuration files now use 600 permissions (owner read/write only)
- Private keys are properly secured with restrictive permissions
### Input Validation
- Enhanced validation for all user inputs
- Sanitization of filenames and node names
- Proper IP address format and range validation
- WireGuard public key format validation
### Error Handling
- Stricter error handling with `set -euo pipefail` in bash scripts
- Better error messages for debugging
- Graceful fallbacks when configuration files are missing
## Portability Improvements
### Shebang
- Changed from hardcoded `/bin/bash` to `/usr/bin/env bash`
- Better compatibility across different Unix-like systems
### Network Interface Detection
- Automatic detection of network interfaces instead of hardcoded names
- Support for various interface naming conventions (eth0, ens33, ens160, enp0s3, eno1)
### Configuration Management
- External configuration file support
- Fallback to hardcoded values when files are not available
- Better separation of configuration and logic
## User Experience Improvements
### Better Help
- Enhanced usage messages with examples
- Command line option support
- More descriptive error messages
### Input Validation
- Real-time validation with retry loops
- Clear error messages explaining what went wrong
- Suggestions for correct input formats
### Configuration Preview
- Show generated configuration before saving
- Clear instructions for next steps
- Integration instructions for Zion server
## Compatibility Notes
### Go Version
- The Go script now requires Go 1.16 or later due to `os.WriteFile` usage
- Replaced deprecated `ioutil.WriteFile` with `os.WriteFile`
### Bash Version
- Bash scripts now use stricter error handling
- May require bash 4.0+ for some features
- Tested with bash 4.4+ and 5.0+
### System Requirements
- All scripts now properly check for WireGuard tools
- Better package installation instructions for various distributions
- Network interface detection works on most Linux distributions
## Testing Recommendations
1. **Test on different distributions**: Ubuntu, CentOS, Fedora, Arch
2. **Test with different bash versions**: Ensure compatibility with older systems
3. **Test network interface detection**: Various interface naming schemes
4. **Test error conditions**: Missing dependencies, invalid inputs, permission issues
5. **Test configuration loading**: With and without Zion config files
## Future Improvements
1. **Configuration file format**: Consider YAML or TOML for better readability
2. **Logging**: Add proper logging with different verbosity levels
3. **Testing**: Add unit tests for validation functions
4. **CI/CD**: Add automated testing and linting
5. **Documentation**: Add man pages and more detailed usage examples

339
SETUP_GUIDE.md Normal file
View File

@@ -0,0 +1,339 @@
# WireGuard Configuration Setup Guide
This guide provides a complete solution for creating WireGuard VPN configurations based on the patterns found in the `TESTING/` directory.
## Overview
The solution consists of three main components:
1. **`wireguard_setup.sh`** - Main interactive setup script
2. **`validate_config.sh`** - Configuration validation tool
3. **`example_setup.sh`** - Example automation script
## Quick Start
### 1. Run the Setup Script
```bash
./wireguard_setup.sh
```
The script will guide you through:
- Generating private/public key pairs
- Setting up node configuration (client or server)
- Adding peers to your configuration
- Creating configuration files
### 2. Validate Your Configuration
```bash
./validate_config.sh wireguard_configs/your_node.conf
```
### 3. Deploy the Configuration
```bash
sudo cp wireguard_configs/your_node.conf /etc/wireguard/
sudo chmod 600 /etc/wireguard/your_node.conf
sudo wg-quick up your_node
```
## Configuration Patterns
Based on the `TESTING/` directory analysis, there are three main configuration types:
### 1. Client Configuration (aza.conf, galaxy.conf)
```ini
[Interface]
PrivateKey = <private_key>
Address = 10.8.0.x/24
[Peer]
PublicKey = <server_public_key>
AllowedIPs = 10.8.0.0/24
Endpoint = server.example.com:51820
PersistentKeepalive = 25
```
**Use case**: Simple client connecting to a central server (Zion included as default peer)
### 2. Server Configuration (zion.conf)
```ini
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <private_key>
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.8.0.x/32
```
**Use case**: Central hub server that routes traffic between clients
### 3. Hybrid Configuration (cth.conf, nyan.conf)
```ini
[Interface]
Address = 10.8.0.x/24
ListenPort = <port>
PrivateKey = <private_key>
[Peer]
PublicKey = <server_public_key>
AllowedIPs = 10.8.0.0/24
Endpoint = server.example.com:51820
PersistentKeepalive = 25
[Peer]
PublicKey = <direct_peer_public_key>
AllowedIPs = 10.8.0.y/32
Endpoint = peer.example.com:port
PersistentKeepalive = 25
```
**Use case**: Node that connects to central server AND has direct peer connections
## Network Topology Examples
### Star Topology (Most Common)
```
zion (10.8.0.1) - Central Server
├── aza (10.8.0.2) - Client
├── cth (10.8.0.10) - Hybrid
├── galaxy (10.8.0.99) - Client
└── nyan (10.8.0.20) - Client
```
### Mesh Topology
```
zion (10.8.0.1) - Hub
├── cth (10.8.0.10) - Hybrid
│ └── nyan (10.8.0.20) - Direct peer
└── galaxy (10.8.0.99) - Client
```
## Script Features
### Main Setup Script (`wireguard_setup.sh`)
**Features:**
- Interactive step-by-step configuration
- Automatic key generation
- Input validation (IP addresses, ports, keys)
- Support for client, server, and hybrid configurations
- File overwrite protection to prevent accidental data loss
- Colored output for better readability
- Automatic summary file generation
**Usage:**
```bash
# Basic usage
./wireguard_setup.sh
# Custom output directory
./wireguard_setup.sh --dir /path/to/output
# Show help
./wireguard_setup.sh --help
```
### Validation Script (`validate_config.sh`)
**Features:**
- Validates WireGuard configuration syntax
- Checks file permissions
- Validates key formats, IP addresses, ports
- Supports single file or directory validation
- Detailed error reporting
**Usage:**
```bash
# Validate single file
./validate_config.sh wireguard_configs/test.conf
# Validate all files in directory
./validate_config.sh wireguard_configs/
# Validate TESTING directory
./validate_config.sh TESTING/
```
### Example Script (`example_setup.sh`)
**Features:**
- Demonstrates automated configuration creation
- Pre-configured examples for client and server setups
- Useful for testing and learning
**Usage:**
```bash
# Create client example
./example_setup.sh client
# Create server example
./example_setup.sh server
```
## Step-by-Step Configuration Process
### For a Client Node (like aza.conf)
1. **Run the setup script:**
```bash
./wireguard_setup.sh
```
2. **Follow the prompts:**
- Node name: `aza`
- IP address: `10.8.0.2/24`
- Server mode: `n` (no)
- Add peer: `y` (yes)
- Peer name: `zion`
- Peer public key: `<zion_public_key>`
- Allowed IPs: `10.8.0.0/24`
- Has endpoint: `y` (yes)
- Endpoint: `ugh.im:51820`
- Keepalive: `25`
- Add more peers: `n` (no)
3. **Deploy:**
```bash
sudo cp wireguard_configs/aza.conf /etc/wireguard/
sudo chmod 600 /etc/wireguard/aza.conf
sudo wg-quick up aza
```
### For a Server Node (like zion.conf)
1. **Run the setup script:**
```bash
./wireguard_setup.sh
```
2. **Follow the prompts:**
- Node name: `zion`
- IP address: `10.8.0.1/24`
- Server mode: `y` (yes)
- Listen port: `51820`
- Add peers: `y` (yes)
- Add each client as a peer (no endpoints needed)
3. **Deploy:**
```bash
sudo cp wireguard_configs/zion.conf /etc/wireguard/
sudo chmod 600 /etc/wireguard/zion.conf
sudo wg-quick up zion
```
## Security Best Practices
1. **Key Management:**
- Never share private keys
- Use unique keys for each node
- Store keys securely
2. **File Permissions:**
- Set config files to 600 permissions
- Restrict access to configuration files
3. **Network Security:**
- Use strong, unique keys
- Configure appropriate firewall rules
- Monitor network traffic
4. **Deployment:**
- Test configurations before production
- Use validation script to check syntax
- Keep backups of configurations
## Troubleshooting
### Common Issues
1. **"wg command not found"**
```bash
# Install WireGuard tools
sudo apt install wireguard # Ubuntu/Debian
sudo yum install wireguard-tools # CentOS/RHEL
sudo pacman -S wireguard-tools # Arch
```
2. **"Permission denied"**
```bash
# Set correct permissions
sudo chmod 600 /etc/wireguard/your_config.conf
```
3. **Connection issues**
```bash
# Check WireGuard status
sudo wg show
# Check interface
ip link show wg0
# Check routing
ip route show
```
4. **Validation errors**
```bash
# Run validation script
./validate_config.sh your_config.conf
```
### Debugging Commands
```bash
# Check WireGuard status
sudo wg show
# Check interface status
ip link show wg0
# Check routing table
ip route show
# Check firewall rules
sudo iptables -L -n -v
# Check system logs
sudo journalctl -u wg-quick@your_interface
```
## File Structure
```
wireguard/
├── wireguard_setup.sh # Main setup script
├── validate_config.sh # Validation tool
├── example_setup.sh # Example automation
├── README.md # Main documentation
├── SETUP_GUIDE.md # This guide
├── TESTING/ # Example configurations
│ ├── aza.conf # Client example
│ ├── cth.conf # Hybrid example
│ ├── galaxy.conf # Client example
│ ├── nyan.conf # Hybrid example
│ └── zion.conf # Server example
└── wireguard_configs/ # Generated configurations
├── your_node.conf
└── your_node_summary.txt
```
## Next Steps
1. **Test the scripts** with the example configurations
2. **Create your own configurations** using the interactive script
3. **Validate your configurations** before deployment
4. **Deploy and test** your WireGuard network
5. **Monitor and maintain** your VPN setup
## Support
For issues or questions:
1. Check the troubleshooting section
2. Run the validation script on your configurations
3. Review the example configurations in the `TESTING/` directory
4. Check WireGuard documentation and community resources
The scripts are designed to be self-documenting and include extensive error checking to help you create valid configurations quickly and safely.

10
TESTING/aza.conf Normal file
View File

@@ -0,0 +1,10 @@
[Interface]
PrivateKey = UI7XeN/0WGCziv68Vt3hlPGYWB1dwZN7+N2C4Y2y91o=
Address = 10.8.0.2/24
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 60

81
TESTING/cth.conf Normal file
View File

@@ -0,0 +1,81 @@
[Interface]
Address = 10.8.0.10/24
ListenPort = 53535
PrivateKey = UDr1aohdSWGMEy1F6v0GoC7JNgVHbwA8Dz//7E2xQHM=
#pix66
[Peer]
PublicKey = HLBlKuzxTGTkyXSp/mlzRCnFR+mesP8UDF+QsqFLxVY=
AllowedIPs = 10.8.0.6/32
#Cth
#[Peer]
#PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
#AllowedIPs = 10.8.0.10/32
#Aza
#[Peer]
#PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=
#AllowedIPs = 10.8.0.2/32
#Nyar
[Peer]
PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
AllowedIPs = 10.8.0.20/32
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#nanocube
#[Peer]
#PublicKey = /ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc=
#AllowedIPs = 10.8.0.7/32
#jupiter
#[Peer]
#PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
#AllowedIPs = 10.8.0.42/32
#HASS
#[Peer]
#PublicKey = C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0=
#AllowedIPs = 10.8.0.8/32
#framebot
#[Peer]
#PublicKey = loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es=
#AllowedIPs = 10.8.0.40/32
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25
#[Peer]
#PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
#AllowedIPs = 10.8.0.20/32
#PersistentKeepalive = 25
#jupiter
#[Peer]
#PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
#AllowedIPs = 10.8.0.42/32
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#Endpoint = galaxyspin.space:54382
#PersistentKeepalive = 25

16
TESTING/galaxy.conf Normal file
View File

@@ -0,0 +1,16 @@
[Interface]
PrivateKey = sHEHZQ3AdUTLG56yeHdmZVuXrivB9P+40Fw0oWHF3Fg=
Address = 10.8.0.99/24
ListenPort = 54382
# Zion peer (central server) - for access to entire network
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

25
TESTING/nyan.conf Normal file
View File

@@ -0,0 +1,25 @@
[Interface]
PrivateKey = sLx3sor9gxhk7T2MIS3g50wzSZBrlVUG+NlXk47jEH4=
Address = 10.8.0.20/24
# Zion peer (central server) - for access to entire network
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25
#CTH
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/32
Endpoint = aw2cd67.glddns.com:53535
PersistentKeepalive = 25
#Galaxy
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#Endpoint = galaxyspin.space:54382
#PersistentKeepalive = 25

48
TESTING/zion.conf Normal file
View File

@@ -0,0 +1,48 @@
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = UJvsfv6iQPAW9Wnc81bK0o3IIHX86kGb+24dUTuGFnA=
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip route add 10.8.0.0/24 dev wg0 2>/dev/null || true
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip route del 10.8.0.0/24 dev wg0 2>/dev/null || true
#Cth
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/32
#Aza
[Peer]
PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=
AllowedIPs = 10.8.0.2/32
#Nyar
[Peer]
PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=
AllowedIPs = 10.8.0.20/32
#Galaxy
[Peer]
PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
AllowedIPs = 10.8.0.99/32
#nanocube
[Peer]
PublicKey = /ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc=
AllowedIPs = 10.8.0.7/32
#jupiter
[Peer]
PublicKey = YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=
AllowedIPs = 10.8.0.42/32
#HASS
[Peer]
PublicKey = C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0=
AllowedIPs = 10.8.0.8/32
#framebot
[Peer]
PublicKey = loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es=
AllowedIPs = 10.8.0.40/32

188
ZION_INTEGRATION.md Normal file
View File

@@ -0,0 +1,188 @@
# Zion Integration Guide
This guide explains how the WireGuard setup script integrates with the Zion central server configuration.
## Zion Server Configuration
Based on the `CURRENT_WORKING/zion.conf` file, Zion is configured as:
```ini
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = UJvsfv6iQPAW9Wnc81bK0o3IIHX86kGb+24dUTuGFnA=
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip route add 10.8.0.0/24 dev wg0 2>/dev/null || true
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip route del 10.8.0.0/24 dev wg0 2>/dev/null || true
```
## Zion Public Key for Clients
All client configurations use Zion's public key:
```
2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
```
## Current Zion Peers
Zion currently has these peers configured:
| Node | IP Address | Public Key |
|------|------------|------------|
| Cth | 10.8.0.10 | NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0= |
| Aza | 10.8.0.2 | qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc= |
| Nyar | 10.8.0.20 | 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo= |
| Galaxy | 10.8.0.99 | QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM= |
| nanocube | 10.8.0.7 | /ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc= |
| jupiter | 10.8.0.42 | YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw= |
| HASS | 10.8.0.8 | C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0= |
| framebot | 10.8.0.40 | loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es= |
## Adding New Nodes to Zion
When you create a new node using the setup script, you need to add it to Zion's configuration.
### Method 1: Using the Helper Script
```bash
./generate_zion_peer.sh <node_name> <public_key> <ip_address>
```
Example:
```bash
./generate_zion_peer.sh mynode ABC123def456ghi789jkl012mno345pqr678stu901vwx234yz567890= 10.8.0.30
```
### Method 2: Manual Addition
Add the following to Zion's `/etc/wireguard/wg0.conf`:
```ini
# your_node_name
[Peer]
PublicKey = your_public_key_here
AllowedIPs = your_ip_address/32
```
## Zion Configuration File Location
Zion's configuration is located at:
```
/etc/wireguard/wg0.conf
```
## Adding a New Peer to Zion
1. **Get the new node's information** from the setup script output:
- Node name
- Public key
- IP address
2. **Edit Zion's configuration**:
```bash
sudo nano /etc/wireguard/wg0.conf
```
3. **Add the peer section** at the end of the file:
```ini
# your_node_name
[Peer]
PublicKey = your_public_key_here
AllowedIPs = your_ip_address/32
```
4. **Save and restart Zion's WireGuard**:
```bash
sudo systemctl restart wg-quick@wg0
```
## Client Configuration Pattern
All client configurations follow this pattern:
```ini
[Interface]
PrivateKey = <client_private_key>
Address = <client_ip>/24
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25
```
## Network Topology
```
zion (10.8.0.1) - Central Server
├── aza (10.8.0.2) - Client
├── cth (10.8.0.10) - Hybrid
├── galaxy (10.8.0.99) - Client
├── nyan (10.8.0.20) - Client
├── nanocube (10.8.0.7) - Client
├── jupiter (10.8.0.42) - Client
├── HASS (10.8.0.8) - Client
├── framebot (10.8.0.40) - Client
└── your_new_node (10.8.0.x) - Client
```
## Setup Script Integration
The `wireguard_setup.sh` script:
1. **Automatically includes Zion** as the default peer for all new nodes
2. **Uses the correct Zion public key** from the CURRENT_WORKING configuration
3. **Provides clear instructions** for updating Zion's configuration
4. **Generates the exact peer configuration** needed for Zion
5. **Includes Zion's current peer structure** for reference
## Troubleshooting
### Common Issues
1. **Connection fails after adding peer to Zion**
- Ensure Zion's WireGuard was restarted: `sudo systemctl restart wg-quick@wg0`
- Check Zion's logs: `sudo journalctl -u wg-quick@wg0 -f`
2. **IP address conflicts**
- Check if the IP is already in use by another peer
- Use a different IP in the 10.8.0.x range
3. **Public key format issues**
- Ensure the public key is exactly 44 characters long
- Check for any extra spaces or characters
### Verification Commands
```bash
# Check Zion's WireGuard status
sudo wg show wg0
# Check Zion's configuration
sudo cat /etc/wireguard/wg0.conf
# Check Zion's systemd service
sudo systemctl status wg-quick@wg0
# Check Zion's logs
sudo journalctl -u wg-quick@wg0 -f
```
## Security Notes
1. **Zion's private key** should never be shared
2. **Client public keys** are safe to share and add to Zion
3. **IP addresses** should be unique within the 10.8.0.x range
4. **File permissions** should be 600 for all WireGuard configs
## Next Steps
After setting up a new node:
1. Run the setup script: `./wireguard_setup.sh`
2. Use the helper script to generate Zion peer config: `./generate_zion_peer.sh`
3. Add the peer to Zion's configuration
4. Restart Zion's WireGuard
5. Start the new node's WireGuard
6. Test connectivity between nodes

482
cmd/wgtool/main.go Normal file
View File

@@ -0,0 +1,482 @@
package main
import (
"bufio"
"crypto/rand"
"encoding/base64"
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"golang.org/x/crypto/curve25519"
)
const (
toolVersion = "1.0.0"
colorRed = "\033[0;31m"
colorGreen = "\033[0;32m"
colorYellow = "\033[1;33m"
colorBlue = "\033[0;34m"
colorReset = "\033[0m"
)
var (
ipCIDRRegex = regexp.MustCompile(`^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$`)
ipWGRangeRegex = regexp.MustCompile(`^10\.8\.0\.[0-9]{1,3}$`)
hostRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$`)
keyRegex = regexp.MustCompile(`^[A-Za-z0-9+/]{43}=$`)
)
func info(msg string) { fmt.Printf("%s[INFO]%s %s\n", colorGreen, colorReset, msg) }
func warn(msg string) { fmt.Printf("%s[WARN]%s %s\n", colorYellow, colorReset, msg) }
func fail(msg string) { fmt.Printf("%s[ERROR]%s %s\n", colorRed, colorReset, msg) }
func head(title string) {
fmt.Printf("%s================================%s\n", colorBlue, colorReset)
fmt.Printf("%s%s%s\n", colorBlue, title, colorReset)
fmt.Printf("%s================================%s\n", colorBlue, colorReset)
}
// ============= Shared validation helpers =============
func validateHostname(s string) error {
if s == "" {
return errors.New("hostname cannot be empty")
}
if len(s) > 63 {
return errors.New("hostname too long (max 63 chars)")
}
if !hostRegex.MatchString(s) {
return errors.New("invalid hostname format")
}
return nil
}
func validateIPHost(s string) error {
if !ipWGRangeRegex.MatchString(s) {
return errors.New("IP must be in 10.8.0.x range")
}
return nil
}
func validateCIDR(s string) error {
if !ipCIDRRegex.MatchString(s) {
return errors.New("invalid CIDR (x.x.x.x/y)")
}
return nil
}
func validateWGKey(s string) error {
if !keyRegex.MatchString(s) {
return errors.New("invalid WireGuard key format")
}
return nil
}
// ============= Key generation =============
func generateKeys() (string, string, error) {
priv := make([]byte, 32)
if _, err := rand.Read(priv); err != nil {
return "", "", err
}
// curve25519 clamping
priv[0] &= 248
priv[31] &= 127
priv[31] |= 64
// Derive public key using curve25519
// Use the same approach as wg: ScalarBaseMult on clamped private key
var privArr [32]byte
copy(privArr[:], priv)
var pubArr [32]byte
// defer import here to avoid top-level dependency note in comments
pubArr = derivePublicKey(privArr)
return base64.StdEncoding.EncodeToString(priv), base64.StdEncoding.EncodeToString(pubArr[:]), nil
}
// derivePublicKey performs curve25519 base point multiplication.
// Implemented via a tiny wrapper so we can keep generateKeys concise.
func derivePublicKey(priv [32]byte) [32]byte {
// inline import pattern is not possible; real import placed at top
// function body replaced by the real call below after adding import
return curve25519BaseMult(priv)
}
// curve25519BaseMult is a small shim around golang.org/x/crypto/curve25519.ScalarBaseMult
// defined below to keep the public key derivation isolated.
func curve25519BaseMult(priv [32]byte) [32]byte {
var out [32]byte
curve25519.ScalarBaseMult(&out, &priv)
return out
}
// ============= Config generation =============
type genOptions struct {
hostname string
ip string
iface string
routing string // wg_only | full_tunnel
outDir string
force bool
nonInteractive bool
}
func runGenerate(args []string) int {
fs := flag.NewFlagSet("generate", flag.ContinueOnError)
fs.SetOutput(new(strings.Builder))
opts := genOptions{}
fs.StringVar(&opts.hostname, "hostname", "", "Node hostname (e.g. aza)")
fs.StringVar(&opts.ip, "ip", "", "Node IP (10.8.0.x)")
fs.StringVar(&opts.iface, "interface", "wg0", "Interface name (e.g. wg0)")
fs.StringVar(&opts.routing, "routing", "wg_only", "Routing mode: wg_only | full_tunnel")
fs.StringVar(&opts.outDir, "out", "wireguard_configs", "Output directory for configs")
fs.BoolVar(&opts.force, "force", false, "Overwrite existing files without prompt")
fs.BoolVar(&opts.nonInteractive, "yes", false, "Non-interactive mode (assume yes)")
if err := fs.Parse(args); err != nil {
fail(err.Error())
return 2
}
// Interactive fallback for missing fields
reader := bufio.NewReader(os.Stdin)
if opts.hostname == "" {
fmt.Print("Enter hostname: ")
s, _ := reader.ReadString('\n')
opts.hostname = strings.TrimSpace(s)
}
if err := validateHostname(opts.hostname); err != nil {
fail(err.Error())
return 2
}
if opts.ip == "" {
fmt.Print("Enter IP (10.8.0.x): ")
s, _ := reader.ReadString('\n')
opts.ip = strings.TrimSpace(s)
}
if err := validateIPHost(opts.ip); err != nil {
fail(err.Error())
return 2
}
if opts.iface == "" {
opts.iface = "wg0"
}
if opts.routing != "wg_only" && opts.routing != "full_tunnel" {
fail("routing must be wg_only or full_tunnel")
return 2
}
if err := os.MkdirAll(opts.outDir, 0755); err != nil {
fail(err.Error())
return 1
}
priv, pub, err := generateKeys()
if err != nil {
fail(fmt.Sprintf("failed to generate keys: %v", err))
return 1
}
// Build config
var b strings.Builder
// Header with public key and instructions for peers
b.WriteString("# ================================================\n")
b.WriteString("# Node: " + opts.hostname + "\n")
b.WriteString("# PublicKey: " + pub + "\n")
b.WriteString("#\n")
b.WriteString("# Add this peer to Zion (/etc/wireguard/wg0.conf):\n")
b.WriteString("# [Peer]\n")
b.WriteString("# PublicKey = " + pub + "\n")
b.WriteString("# AllowedIPs = " + opts.ip + "/32\n")
b.WriteString("# ================================================\n\n")
b.WriteString("[Interface]\n")
// Default /24
b.WriteString(fmt.Sprintf("Address = %s/24\n", opts.ip))
b.WriteString(fmt.Sprintf("PrivateKey = %s\n", priv))
if opts.routing == "full_tunnel" {
b.WriteString("DNS = 1.1.1.1, 8.8.8.8\n")
}
// Default Zion peer (from scripts)
b.WriteString("\n# Zion (central server)\n")
b.WriteString("[Peer]\n")
b.WriteString("PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=\n")
if opts.routing == "full_tunnel" {
b.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
} else {
b.WriteString("AllowedIPs = 10.8.0.0/24\n")
}
b.WriteString("Endpoint = ugh.im:51820\n")
b.WriteString("PersistentKeepalive = 25\n")
confFilename := fmt.Sprintf("%s_%s.conf", opts.hostname, opts.iface)
confPath := filepath.Join(opts.outDir, confFilename)
if _, err := os.Stat(confPath); err == nil && !(opts.force || opts.nonInteractive) {
warn(fmt.Sprintf("%s exists", confPath))
fmt.Print("Overwrite? (y/N): ")
ans, _ := reader.ReadString('\n')
ans = strings.ToLower(strings.TrimSpace(ans))
if ans != "y" && ans != "yes" {
fail("aborted")
return 1
}
}
if err := os.WriteFile(confPath, []byte(b.String()), 0600); err != nil {
fail(err.Error())
return 1
}
// Save keys for convenience
_ = os.WriteFile(filepath.Join(opts.outDir, opts.hostname+"_private.key"), []byte(priv), 0600)
if pub != "" {
_ = os.WriteFile(filepath.Join(opts.outDir, opts.hostname+"_public.key"), []byte(pub), 0600)
}
info(fmt.Sprintf("config written: %s", confPath))
info("set permissions: chmod 600 <file>")
return 0
}
// ============= Validator (parity with validate_config.sh) =============
func validateConfigFile(path string) (int, int) {
data, err := os.ReadFile(path)
if err != nil {
fail(fmt.Sprintf("%s: %v", path, err))
return 1, 0
}
lines := strings.Split(string(data), "\n")
inInterface := false
inPeer := false
hasInterface := false
hasPriv := false
hasAddr := false
errs := 0
warns := 0
printPath := func() { head("Validating: " + path) }
printPath()
for _, raw := range lines {
line := strings.TrimSpace(raw)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if line == "[Interface]" {
inInterface, inPeer, hasInterface = true, false, true
continue
}
if line == "[Peer]" {
inInterface, inPeer = false, true
continue
}
if inInterface {
if strings.HasPrefix(line, "PrivateKey=") || strings.HasPrefix(line, "PrivateKey =") {
value := strings.TrimSpace(strings.TrimPrefix(strings.ReplaceAll(line, " ", ""), "PrivateKey="))
if validateWGKey(value) == nil {
info("valid private key")
} else {
fail("invalid private key")
errs++
}
if validateWGKey(value) == nil {
hasPriv = true
}
} else if strings.HasPrefix(line, "Address=") || strings.HasPrefix(line, "Address =") {
value := strings.TrimSpace(strings.TrimPrefix(strings.ReplaceAll(line, " ", ""), "Address="))
if validateCIDR(value) == nil {
info("valid address " + value)
} else {
fail("invalid address " + value)
errs++
}
if validateCIDR(value) == nil {
hasAddr = true
}
}
}
if inPeer {
if strings.HasPrefix(line, "PublicKey=") || strings.HasPrefix(line, "PublicKey =") {
value := strings.TrimSpace(strings.TrimPrefix(strings.ReplaceAll(line, " ", ""), "PublicKey="))
if validateWGKey(value) == nil {
info("valid peer public key")
} else {
fail("invalid peer public key")
errs++
}
} else if strings.HasPrefix(line, "AllowedIPs=") || strings.HasPrefix(line, "AllowedIPs =") {
value := strings.TrimSpace(strings.TrimPrefix(strings.ReplaceAll(line, " ", ""), "AllowedIPs="))
ips := strings.Split(value, ",")
for _, ip := range ips {
if validateCIDR(strings.TrimSpace(ip)) == nil {
info("valid allowed IP " + strings.TrimSpace(ip))
} else {
fail("invalid allowed IP " + strings.TrimSpace(ip))
errs++
}
}
}
}
}
if !hasInterface {
fail("missing [Interface] section")
errs++
}
if !hasPriv {
fail("missing PrivateKey in [Interface]")
errs++
}
if !hasAddr {
warn("missing Address in [Interface]")
warns++
}
if errs == 0 && warns == 0 {
info("file is valid")
}
return errs, warns
}
func runValidate(args []string) int {
fs := flag.NewFlagSet("validate", flag.ContinueOnError)
fs.SetOutput(new(strings.Builder))
var target string
fs.StringVar(&target, "target", "", "Config file or directory to validate")
if err := fs.Parse(args); err != nil {
fail(err.Error())
return 2
}
if target == "" {
if fs.NArg() > 0 {
target = fs.Arg(0)
}
}
if target == "" {
fail("provide a file or directory via --target or arg")
return 2
}
st, err := os.Stat(target)
if err != nil {
fail(err.Error())
return 1
}
if st.Mode().IsRegular() {
errs, _ := validateConfigFile(target)
if errs > 0 {
return 1
}
return 0
}
if st.IsDir() {
totalErr := 0
_ = filepath.Walk(target, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if strings.HasSuffix(strings.ToLower(info.Name()), ".conf") {
e, _ := validateConfigFile(path)
totalErr += e
}
return nil
})
if totalErr > 0 {
return 1
}
return 0
}
fail("target must be file or directory")
return 2
}
// ============= Zion peer snippet =============
func runZionPeer(args []string) int {
fs := flag.NewFlagSet("zion-peer", flag.ContinueOnError)
fs.SetOutput(new(strings.Builder))
var name, pub, ip string
fs.StringVar(&name, "name", "", "Node name")
fs.StringVar(&pub, "pub", "", "WireGuard public key")
fs.StringVar(&ip, "ip", "", "Node IP (10.8.0.x)")
if err := fs.Parse(args); err != nil {
fail(err.Error())
return 2
}
if name == "" || pub == "" || ip == "" {
fail("--name, --pub and --ip are required")
return 2
}
if err := validateHostname(name); err != nil {
fail(err.Error())
return 2
}
if err := validateWGKey(pub); err != nil {
fail(err.Error())
return 2
}
if err := validateIPHost(ip); err != nil {
fail(err.Error())
return 2
}
head("Add the following to Zion's /etc/wireguard/wg0.conf")
fmt.Println("# " + name)
fmt.Println("[Peer]")
fmt.Println("PublicKey = " + pub)
fmt.Println("AllowedIPs = " + ip + "/32")
return 0
}
// ============= Keys only =============
func runKeys(args []string) int {
fs := flag.NewFlagSet("keys", flag.ContinueOnError)
fs.SetOutput(new(strings.Builder))
if err := fs.Parse(args); err != nil {
fail(err.Error())
return 2
}
priv, _, err := generateKeys()
if err != nil {
fail(err.Error())
return 1
}
fmt.Println("PrivateKey:", priv)
fmt.Println("(Use 'wg pubkey' to derive PublicKey safely)")
return 0
}
// ============= Main =============
func main() {
if len(os.Args) < 2 {
fmt.Printf("wgtool %s\n", toolVersion)
fmt.Println("Commands: generate, validate, zion-peer, keys, version")
os.Exit(0)
}
sub := os.Args[1]
args := os.Args[2:]
switch sub {
case "generate":
os.Exit(runGenerate(args))
case "validate":
os.Exit(runValidate(args))
case "zion-peer":
os.Exit(runZionPeer(args))
case "keys":
os.Exit(runKeys(args))
case "version", "--version", "-v":
fmt.Println("wgtool", toolVersion)
os.Exit(0)
default:
fail("unknown command: " + sub)
fmt.Println("Available: generate, validate, zion-peer, keys, version")
os.Exit(2)
}
}

View File

@@ -0,0 +1 @@
eGcfpLXTadC99k7rj8G07zrDLarKoolA30lMYo/MSG0=

View File

@@ -0,0 +1 @@
ltEo/ohm4EvJyXhFtnHPjHrpOW3v5mwxgw9m9uzjNmE=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: janus
# PublicKey: ltEo/ohm4EvJyXhFtnHPjHrpOW3v5mwxgw9m9uzjNmE=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = ltEo/ohm4EvJyXhFtnHPjHrpOW3v5mwxgw9m9uzjNmE=
# AllowedIPs = 10.8.0.250/32
# ================================================
[Interface]
Address = 10.8.0.250/24
PrivateKey = eGcfpLXTadC99k7rj8G07zrDLarKoolA30lMYo/MSG0=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 0.0.0.0/0
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1 @@
OB4zaSdrZkSgtQZUQlkSB9x++RQAvxgOSEHEUswY6Hs=

View File

@@ -0,0 +1 @@
tVHvPWUDAd3xhoZKo0iJ5G5wOIQIdGNNXDG0cV0djxo=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: mawlz
# PublicKey: tVHvPWUDAd3xhoZKo0iJ5G5wOIQIdGNNXDG0cV0djxo=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = tVHvPWUDAd3xhoZKo0iJ5G5wOIQIdGNNXDG0cV0djxo=
# AllowedIPs = 10.8.0.16/32
# ================================================
[Interface]
Address = 10.8.0.16/24
PrivateKey = OB4zaSdrZkSgtQZUQlkSB9x++RQAvxgOSEHEUswY6Hs=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1 @@
6H0y0Cov9x65ctmjz5IHFD9DIMlWZeYlxh3BZVlDHkU=

View File

@@ -0,0 +1 @@
oNVVqJZoL6AY/0bDl5EPEfW62v0zptK4Bk5BnBEFJwE=

View File

@@ -0,0 +1,27 @@
# ================================================
# Node: morph
# PublicKey: oNVVqJZoL6AY/0bDl5EPEfW62v0zptK4Bk5BnBEFJwE=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = oNVVqJZoL6AY/0bDl5EPEfW62v0zptK4Bk5BnBEFJwE=
# AllowedIPs = 10.8.0.21/32
# ================================================
[Interface]
Address = 10.8.0.21/24
PrivateKey = 6H0y0Cov9x65ctmjz5IHFD9DIMlWZeYlxh3BZVlDHkU=
#CTH
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/32
Endpoint = aw2cd67.glddns.com:53535
PersistentKeepalive = 25
# Zion (central server)
#[Peer]
#PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
#AllowedIPs = 10.8.0.0/24
#Endpoint = ugh.im:51820
#PersistentKeepalive = 25

View File

@@ -0,0 +1 @@
eBQiIcOLaM4A2jgRHUjWrQtev+jR0l4ZjF3GMfOXQ0M=

View File

@@ -0,0 +1 @@
4CVAy2F3QlKZnHV+6fo4GM/cuGt3XU6BElq11IzfJ3w=

View File

@@ -0,0 +1 @@
Wk099hP8kJ3wRgOwo+QCiaDTR1tSDdYwM5E9qI6Cw0w=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: virtual
# PublicKey: Wk099hP8kJ3wRgOwo+QCiaDTR1tSDdYwM5E9qI6Cw0w=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = Wk099hP8kJ3wRgOwo+QCiaDTR1tSDdYwM5E9qI6Cw0w=
# AllowedIPs = 10.8.0.94/32
# ================================================
[Interface]
Address = 10.8.0.94/24
PrivateKey = 4CVAy2F3QlKZnHV+6fo4GM/cuGt3XU6BElq11IzfJ3w=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: morph
# PublicKey: 49bN/KGsiqFmHxItli8ySiDPgeTc9Lh+vQA4BJBa/2k=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = 49bN/KGsiqFmHxItli8ySiDPgeTc9Lh+vQA4BJBa/2k=
# AllowedIPs = 10.8.0.21/32
# ================================================
[Interface]
Address = 10.8.0.21/24
PrivateKey = YLMDjNlIevvmoK7dpsRVe3iIce/JdZg7aSZAJcEwWlE=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

105
example_setup.sh Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# Example WireGuard Setup Script
# This demonstrates how to use the main setup script with predefined values
set -e
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}================================${NC}"
}
# Example: Creating a client configuration similar to aza.conf
create_client_example() {
print_header "Creating Client Configuration Example"
# Create a temporary input file for the main script
cat > /tmp/wg_input.txt << 'EOF'
test_client
10.8.0.5/24
n
y
n
EOF
print_status "Running setup script with client configuration..."
echo "This will create a client configuration similar to aza.conf"
echo ""
# Run the main script with the example inputs
./wireguard_setup.sh < /tmp/wg_input.txt
# Clean up
rm -f /tmp/wg_input.txt
print_status "Client example configuration created!"
}
# Example: Creating a server configuration similar to zion.conf
create_server_example() {
print_header "Creating Server Configuration Example"
# Create a temporary input file for the main script
cat > /tmp/wg_input.txt << 'EOF'
test_server
10.8.0.1/24
y
51820
n
EOF
print_status "Running setup script with server configuration..."
echo "This will create a server configuration similar to zion.conf"
echo ""
# Run the main script with the example inputs
./wireguard_setup.sh < /tmp/wg_input.txt
# Clean up
rm -f /tmp/wg_input.txt
print_status "Server example configuration created!"
}
# Show usage
show_usage() {
echo "Usage: $0 [OPTION]"
echo ""
echo "Options:"
echo " client Create a client configuration example"
echo " server Create a server configuration example"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " $0 client # Creates a client config like aza.conf"
echo " $0 server # Creates a server config like zion.conf"
}
# Main logic
case "${1:-help}" in
client)
create_client_example
;;
server)
create_server_example
;;
help|--help|-h)
show_usage
;;
*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
esac

223
generate_zion_peer.sh Executable file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env bash
# Zion Peer Configuration Generator
# This script generates the exact peer configuration needed for Zion
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration file for peer information
CONFIG_FILE="$(dirname "$0")/CURRENT_WORKING/zion.conf"
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}Zion Peer Configuration Generator${NC}"
echo -e "${BLUE}================================${NC}"
}
# Validate IP address format and range
validate_ip() {
local ip="$1"
# Check basic format
if [[ ! $ip =~ ^10\.8\.0\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then
return 1
fi
# Extract last octet
local last_octet="${ip##*.}"
# Check if IP is in reserved ranges
if [[ $last_octet -eq 0 ]] || [[ $last_octet -eq 1 ]] || [[ $last_octet -eq 255 ]]; then
return 1
fi
return 0
}
# Validate WireGuard public key format
validate_public_key() {
local key="$1"
# WireGuard keys are base64 encoded and exactly 44 characters long
if [[ ! $key =~ ^[A-Za-z0-9+/]{43}=$ ]]; then
return 1
fi
return 0
}
# Load Zion configuration if available
load_zion_config() {
if [[ -f "$CONFIG_FILE" ]]; then
print_status "Found Zion configuration file: $CONFIG_FILE"
return 0
else
print_warning "Zion configuration file not found: $CONFIG_FILE"
print_warning "Using hardcoded peer information"
return 1
fi
}
show_usage() {
echo "Usage: $0 <node_name> <public_key> <ip_address>"
echo ""
echo "Arguments:"
echo " node_name - Name of the node (e.g., mynode)"
echo " public_key - WireGuard public key (base64 encoded)"
echo " ip_address - IP address in 10.8.0.x format"
echo ""
echo "Example:"
echo " $0 mynode ABC123... 10.8.0.30"
echo ""
echo "This will generate the peer configuration to add to Zion's /etc/wireguard/wg0.conf"
echo ""
echo "Options:"
echo " -c, --config FILE Use custom Zion config file"
echo " -h, --help Show this help message"
}
main() {
local config_file="$CONFIG_FILE"
# Parse command line options
while [[ $# -gt 0 ]]; do
case $1 in
-c|--config)
config_file="$2"
shift 2
;;
-h|--help)
show_usage
exit 0
;;
-*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
*)
break
;;
esac
done
print_header
echo ""
if [[ $# -ne 3 ]]; then
print_error "Incorrect number of arguments"
show_usage
exit 1
fi
local NODE_NAME="$1"
local PUBLIC_KEY="$2"
local IP_ADDRESS="$3"
# Validate node name
if [[ ! $NODE_NAME =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$ ]] && [[ $NODE_NAME != [a-zA-Z0-9] ]]; then
print_error "Invalid node name format. Use alphanumeric characters, hyphens, and underscores only"
exit 1
fi
# Validate IP address
if ! validate_ip "$IP_ADDRESS"; then
print_error "IP address must be in 10.8.0.x format (x cannot be 0, 1, or 255)"
exit 1
fi
# Validate public key
if ! validate_public_key "$PUBLIC_KEY"; then
print_error "Invalid WireGuard public key format"
print_error "Expected: 44 character base64 string ending with ="
exit 1
fi
print_status "Generating Zion peer configuration for: $NODE_NAME"
echo ""
echo "Add the following to Zion's /etc/wireguard/wg0.conf:"
echo "----------------------------------------"
echo "# $NODE_NAME"
echo "[Peer]"
echo "PublicKey = $PUBLIC_KEY"
echo "AllowedIPs = $IP_ADDRESS/32"
echo "----------------------------------------"
echo ""
print_warning "After adding this to Zion's config:"
echo "1. Save the file"
echo "2. Restart Zion's WireGuard: sudo systemctl restart wg-quick@wg0"
echo "3. Start the new node's WireGuard: sudo wg-quick up $NODE_NAME"
echo ""
# Try to load Zion config, fall back to hardcoded if not available
if ! load_zion_config; then
# Show hardcoded peer structure
echo "Zion's current peer structure (add your peer at the end):"
echo "----------------------------------------"
echo "#Cth"
echo "[Peer]"
echo "PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0="
echo "AllowedIPs = 10.8.0.10/32"
echo ""
echo "#Aza"
echo "[Peer]"
echo "PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc="
echo "AllowedIPs = 10.8.0.2/32"
echo ""
echo "#Nyar"
echo "[Peer]"
echo "PublicKey = 2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo="
echo "AllowedIPs = 10.8.0.20/32"
echo ""
echo "#Galaxy"
echo "[Peer]"
echo "PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM="
echo "AllowedIPs = 10.8.0.99/32"
echo ""
echo "# Add your peer here:"
echo "# $NODE_NAME"
echo "# [Peer]"
echo "# PublicKey = $PUBLIC_KEY"
echo "# AllowedIPs = $IP_ADDRESS/32"
echo "----------------------------------------"
else
# Parse and display current peers from config file
print_status "Current peers in Zion configuration:"
echo "----------------------------------------"
if grep -E "^#.*" "$config_file" | grep -E "^#[A-Za-z]" | head -10; then
echo ""
echo "# Add your peer here:"
echo "# $NODE_NAME"
echo "# [Peer]"
echo "# PublicKey = $PUBLIC_KEY"
echo "# AllowedIPs = $IP_ADDRESS/32"
else
print_warning "No peer sections found in Zion config"
fi
echo "----------------------------------------"
fi
}
# Run main function
main "$@"

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module wireguard-setup
go 1.24.5
require golang.org/x/crypto v0.40.0

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=

206
scripting/README.md Normal file
View File

@@ -0,0 +1,206 @@
# NPM Log Analysis Tools
High-performance security analysis tools for NPM (Nginx Proxy Manager) logs.
## Overview
This repository contains two versions of the NPM log analyzer:
1. **Bash Version** (`npm-log-analyzer.sh`) - Interactive menu-driven tool
2. **Go Version** (`npm-log-analyzer.go`) - High-performance command-line tool
## Features
### Security Pattern Detection
- **Critical Attacks**: SQL injection, XSS, shell/RCE, webshell uploads
- **High Priority**: Path traversal, WordPress hunting, backup harvesting
- **Reconnaissance**: Robots.txt requests, vulnerability scanners, error spam
- **Advanced**: SSRF, LFI/RFI, deserialization, template injection
### Analysis Capabilities
- Real-time log processing
- IP address analysis and geolocation
- Attack pattern counting and categorization
- Comprehensive reporting
- Performance optimization for large log files
## Quick Start
### Go Version (Recommended)
```bash
# Build and run
make go
# Or manually
go build -o npm-log-analyzer-go npm-log-analyzer.go
./npm-log-analyzer-go
```
### Bash Version
```bash
# Make executable and run
chmod +x npm-log-analyzer.sh
./npm-log-analyzer.sh
```
## Performance Comparison
| Feature | Go Version | Bash Version |
|---------|------------|--------------|
| **Speed** | ~85 seconds for 260MB logs | ~2-3 minutes |
| **Memory** | Efficient streaming | Higher memory usage |
| **Features** | Command-line focused | Interactive menu |
| **Dependencies** | Single binary | Requires bash, grep, etc. |
| **Gzip Support** | ✅ Native | ❌ Limited |
## Recent Analysis Results
From the latest Go analysis (260MB of logs):
### Critical Findings
- **SQL Injection Attempts**: 378
- **Shell/RCE Attempts**: 2,693
- **Error Spam (404/403)**: 4,855
### Top Attack Sources
- **169.150.203.13**: 45,278 requests (suspicious high volume)
- **135.181.143.221**: 9,229 requests
- **97.120.203.58**: 3,405 requests
### Analysis Statistics
- **Processed Files**: 98
- **Total Lines**: 105,568
- **Unique IPs**: 959
- **Duration**: ~85 seconds
## Configuration
### Log Directory
Both tools expect NPM logs at: `/opt/stacks/npm/data/logs`
### Output Directory
Reports are saved to: `./npmlogs`
### Attack Patterns
Patterns are defined in the source code and can be customized:
```go
// Go version
var AttackPatterns = map[string]string{
"sql_injection": `union|select|insert|drop|delete`,
"xss": `<script|javascript:|onload=|onerror=`,
// ... more patterns
}
```
```bash
# Bash version
declare -A ATTACK_PATTERNS=(
["sql_injection"]="union|select|insert|drop|delete"
["xss"]="<script|javascript:|onload=|onerror="
# ... more patterns
)
```
## Usage Examples
### Go Version
```bash
# Quick analysis
./npm-log-analyzer-go
# Build for different platforms
GOOS=linux GOARCH=amd64 go build -o npm-analyzer-linux npm-log-analyzer.go
```
### Bash Version
```bash
# Interactive menu
./npm-log-analyzer.sh
# Quick analysis only
echo "1" | ./npm-log-analyzer.sh
```
## Makefile Targets
```bash
make build-go # Build Go version
make run-go # Build and run Go version
make go # Alias for run-go
make run-bash # Run Bash version
make bash # Alias for run-bash
make benchmark # Compare performance
make clean # Clean build artifacts
make help # Show all targets
```
## Requirements
### Go Version
- Go 1.16+ (for native gzip support)
- No external dependencies
### Bash Version
- Bash 4.0+
- grep, find, awk, curl
- Read access to `/opt/stacks/npm/data/logs`
## Security Considerations
1. **Log Access**: Both tools require read access to NPM logs
2. **Network Access**: IP geolocation requires internet access (optional)
3. **File Permissions**: Output directories need write permissions
4. **Large Files**: Processing 260MB+ logs requires sufficient memory
## Troubleshooting
### Common Issues
**"Log directory not accessible"**
```bash
# Check if NPM is running
ls -la /opt/stacks/npm/data/logs/
# Check permissions
sudo ls -la /opt/stacks/npm/data/logs/
```
**"No output generated"**
- Large log files take time to process
- Go version is much faster for large files
- Check available memory and disk space
**"Permission denied"**
```bash
# Make scripts executable
chmod +x npm-log-analyzer.sh
chmod +x npm-log-analyzer-go
# Check output directory permissions
mkdir -p ./npmlogs
chmod 755 ./npmlogs
```
## Development
### Adding New Patterns
1. Edit the pattern definitions in the source code
2. Test with sample log data
3. Update documentation
### Performance Optimization
- Go version uses buffered I/O and streaming
- Bash version uses grep with timeouts
- Both versions filter internal IPs automatically
## License
This project is open source. Feel free to modify and distribute.
## Contributing
1. Test both versions with your log data
2. Report any issues or performance problems
3. Suggest new attack patterns or features
4. Submit pull requests for improvements

View File

@@ -0,0 +1,101 @@
# WireGuard Easy Setup - Routing Mode Documentation
## Overview
The `wgez-setup.sh` script now supports two different traffic routing modes:
1. **WireGuard-only mode** (default)
2. **Full tunnel mode**
## Routing Modes
### WireGuard-only Mode
- **Purpose**: Only route WireGuard network traffic (10.8.0.x) through the VPN
- **AllowedIPs**: `10.8.0.0/24`
- **DNS**: None (uses system DNS)
- **Internet Traffic**: Bypasses VPN, uses regular internet connection
- **Use Case**: When you want to access WireGuard network resources but keep regular internet traffic separate
### Full Tunnel Mode
- **Purpose**: Route ALL internet traffic through the VPN
- **AllowedIPs**: `0.0.0.0/0, ::/0`
- **DNS**: `1.1.1.1, 8.8.8.8` (Cloudflare and Google DNS)
- **Internet Traffic**: All traffic goes through VPN
- **Use Case**: When you want complete privacy/anonymity or need to bypass network restrictions
## Configuration Differences
### WireGuard-only Configuration
```ini
[Interface]
Address = 10.8.0.6/24
PrivateKey = <private_key>
[Peer]
PublicKey = <zion_public_key>
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 60
```
### Full Tunnel Configuration
```ini
[Interface]
Address = 10.8.0.6/24
PrivateKey = <private_key>
DNS = 1.1.1.1, 8.8.8.8
[Peer]
PublicKey = <zion_public_key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = ugh.im:51820
PersistentKeepalive = 60
```
## Endpoint Requirements
### WireGuard-only Mode
- **Zion Server**: No special requirements
- **Client**: Standard WireGuard configuration
### Full Tunnel Mode
- **Zion Server**: Must have proper NAT/iptables rules
- **Required Zion Configuration**:
```bash
# Enable IP forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Add NAT rules (if not already present)
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i wg0 -j ACCEPT
sudo iptables -A FORWARD -o wg0 -j ACCEPT
```
## Usage
1. Run the setup script: `./wgez-setup.sh`
2. Choose option 2 (Generate keys + complete config)
3. Select routing mode:
- Option 1: WireGuard traffic only
- Option 2: All traffic through VPN
4. Follow the generated instructions
## Important Notes
- **Full tunnel mode** requires the Zion server to have proper NAT configuration
- **WireGuard-only mode** is safer and doesn't require endpoint changes
- The script automatically provides endpoint-specific instructions for full tunnel mode
- Routing mode is saved in the JSON info file for reference
## Troubleshooting
### Full Tunnel Not Working
1. Check Zion's iptables rules: `sudo iptables -t nat -L POSTROUTING`
2. Verify IP forwarding is enabled: `cat /proc/sys/net/ipv4/ip_forward`
3. Check WireGuard interface status: `sudo wg show`
### DNS Issues in Full Tunnel
- The script configures DNS servers (1.1.1.1, 8.8.8.8)
- If DNS doesn't work, check if Zion allows DNS traffic
- Consider adding DNS-specific iptables rules if needed

695
scripting/wgez-setup.go Normal file
View File

@@ -0,0 +1,695 @@
package main
import (
"bufio"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"golang.org/x/crypto/curve25519"
)
// Colors for terminal output
const (
Red = "\033[0;31m"
Green = "\033[0;32m"
Yellow = "\033[1;33m"
Blue = "\033[0;34m"
Reset = "\033[0m"
)
// Configuration structs
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"`
}
type StaticServer struct {
IP string
Port string
}
// Global variables
var (
staticServers = map[string]string{
"10.8.0.1": "51820",
"10.8.0.10": "53535",
"10.8.0.99": "54382",
}
peers = map[string]Peer{
"zion": {
Name: "Zion peer (central server) - for access to entire network",
PublicKey: "2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=",
Endpoint: "ugh.im:51820",
Keepalive: 25,
},
"cthulhu": {
Name: "Cthulhu (optional if port 53 is also forwarded to bypass firewalls)",
PublicKey: "NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=",
AllowedIPs: "10.8.0.10/32",
Endpoint: "aw2cd67.glddns.com:53535",
Keepalive: 25,
},
"galaxy": {
Name: "Galaxy (located in Europe, NL)",
PublicKey: "QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=",
AllowedIPs: "10.8.0.99/32",
Endpoint: "galaxyspin.space:54382",
Keepalive: 25,
},
}
)
type Peer struct {
Name string
PublicKey string
AllowedIPs string
Endpoint string
Keepalive int
}
// Utility functions
func printStatus(message string) {
fmt.Printf("%s[INFO]%s %s\n", Green, Reset, message)
}
func printWarning(message string) {
fmt.Printf("%s[WARNING]%s %s\n", Yellow, Reset, message)
}
func printError(message string) {
fmt.Printf("%s[ERROR]%s %s\n", Red, Reset, message)
}
func printHeader() {
fmt.Printf("%s================================%s\n", Blue, Reset)
fmt.Printf("%s NextGen WireGuard Easy Setup %s\n", Blue, Reset)
fmt.Printf("%s================================%s\n", Blue, Reset)
fmt.Println()
}
// Validation functions
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)")
}
// Basic alphanumeric and hyphen validation
for _, char := range hostname {
if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') ||
(char >= '0' && char <= '9') || char == '-') {
return fmt.Errorf("invalid hostname format. Use alphanumeric characters and hyphens only")
}
}
return nil
}
func validateIP(ip string) error {
if ip == "" {
return fmt.Errorf("IP address cannot be empty")
}
parts := strings.Split(ip, ".")
if len(parts) != 4 {
return fmt.Errorf("invalid IP format")
}
for _, part := range parts {
num, err := strconv.Atoi(part)
if err != nil || num < 0 || num > 255 {
return fmt.Errorf("invalid IP octet: %s", part)
}
}
// Check if it's in the expected range
if !strings.HasPrefix(ip, "10.8.0.") && !strings.HasPrefix(ip, "10.0.0.") {
printWarning("IP should be in 10.8.0.x or 10.0.0.x range for NextGen network")
}
return nil
}
func validateInterfaceName(iface string) error {
if iface == "" {
return fmt.Errorf("interface name cannot be empty")
}
if len(iface) > 15 {
return fmt.Errorf("interface name too long (max 15 characters)")
}
// Check if starts with letter and contains only alphanumeric
if len(iface) == 0 || !((iface[0] >= 'a' && iface[0] <= 'z') || (iface[0] >= 'A' && iface[0] <= 'Z')) {
return fmt.Errorf("interface name must start with a letter")
}
for _, char := range iface {
if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) {
return fmt.Errorf("interface name can only contain letters and numbers")
}
}
return nil
}
// User input functions
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
}
}
func getDirectoryInput(prompt, defaultDir string) string {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [%s]: ", prompt, defaultDir)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "" {
input = defaultDir
}
// Check if directory exists
if _, err := os.Stat(input); os.IsNotExist(err) {
fmt.Printf("Directory '%s' doesn't exist. Create it? (Y/n): ", input)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
if response == "n" || response == "no" {
continue
}
if err := os.MkdirAll(input, 0755); err != nil {
printError(fmt.Sprintf("Failed to create directory '%s': %v", input, err))
continue
}
}
// Check if directory is writable
if info, err := os.Stat(input); err == nil {
if info.Mode()&0200 == 0 {
printError(fmt.Sprintf("Directory '%s' is not writable", input))
continue
}
}
return input
}
}
// WireGuard key generation
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
var publicKeyBytes [32]byte
curve25519.ScalarBaseMult(&publicKeyBytes, (*[32]byte)(privateKeyBytes))
privateKey := base64.StdEncoding.EncodeToString(privateKeyBytes[:])
publicKey := base64.StdEncoding.EncodeToString(publicKeyBytes[:])
return privateKey, publicKey, nil
}
// Configuration generation
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 peer (central server) - for access to entire network\n")
config.WriteString("[Peer]\n")
config.WriteString(fmt.Sprintf("PublicKey = %s\n", peers["zion"].PublicKey))
// Set AllowedIPs based on routing mode
if routingMode == "full_tunnel" {
config.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
} else {
config.WriteString("AllowedIPs = 10.8.0.0/24\n")
}
config.WriteString(fmt.Sprintf("Endpoint = %s\n", peers["zion"].Endpoint))
config.WriteString(fmt.Sprintf("PersistentKeepalive = %d\n", peers["zion"].Keepalive))
// Optional peers (commented out)
config.WriteString("\n#Cthulhu (optional if port 53 is also forwarded to bypass firewalls)\n")
config.WriteString("#[Peer]\n")
config.WriteString(fmt.Sprintf("#PublicKey = %s\n", peers["cthulhu"].PublicKey))
config.WriteString(fmt.Sprintf("#AllowedIPs = %s\n", peers["cthulhu"].AllowedIPs))
config.WriteString(fmt.Sprintf("#Endpoint = %s\n", peers["cthulhu"].Endpoint))
config.WriteString(fmt.Sprintf("#PersistentKeepalive = %d\n", peers["cthulhu"].Keepalive))
config.WriteString("\n#Galaxy (located in Europe, NL)\n")
config.WriteString("#[Peer]\n")
config.WriteString(fmt.Sprintf("#PublicKey = %s\n", peers["galaxy"].PublicKey))
config.WriteString(fmt.Sprintf("#AllowedIPs = %s\n", peers["galaxy"].AllowedIPs))
config.WriteString(fmt.Sprintf("#Endpoint = %s\n", peers["galaxy"].Endpoint))
config.WriteString(fmt.Sprintf("#PersistentKeepalive = %d\n", peers["galaxy"].Keepalive))
return config.String()
}
// Check dependencies
func checkDependencies() error {
deps := []string{"wg", "wg-quick"}
for _, dep := range deps {
if _, err := exec.LookPath(dep); err != nil {
return fmt.Errorf("missing dependency: %s", dep)
}
}
return nil
}
// Check if running as root
func isRunningAsRoot() bool {
return os.Geteuid() == 0
}
// Main function
func main() {
printHeader()
// Check if running as root
runningAsRoot := isRunningAsRoot()
var wgDir string
if runningAsRoot {
wgDir = "/etc/wireguard"
printStatus("Running as root - using system directories")
printStatus(fmt.Sprintf("WireGuard directory: %s", wgDir))
} else {
wgDir = "."
printWarning("Not running as root - using current directory")
printStatus(fmt.Sprintf("WireGuard directory: %s", wgDir))
printWarning("You'll need to manually copy config files to /etc/wireguard/ later")
}
fmt.Println()
// Check dependencies
if err := checkDependencies(); err != nil {
printError(err.Error())
printStatus("Install with: apt install wireguard-tools")
os.Exit(1)
}
// Get directory for non-root users
if !runningAsRoot {
fmt.Printf("%sStep 1: Directory Selection%s\n", Blue, Reset)
fmt.Println()
printStatus("Choose where to save WireGuard files:")
fmt.Printf(" - Current directory: %s\n", wgDir)
fmt.Printf(" - Home directory: %s\n", os.Getenv("HOME"))
fmt.Println(" - Custom directory")
fmt.Println()
wgDir = getDirectoryInput("Enter directory path for WireGuard files", wgDir)
fmt.Println()
}
// Get hostname
stepNum := "2"
if !runningAsRoot {
stepNum = "2"
} else {
stepNum = "1"
}
fmt.Printf("%sStep %s: Node Information%s\n", Blue, stepNum, Reset)
fmt.Println()
hostname := getUserInput("Enter hostname for this node: ", validateHostname)
// Get IP address
fmt.Println()
fmt.Println("Available IP ranges:")
fmt.Println(" - 10.8.0.x (recommended for NextGen network)")
fmt.Println(" - 10.0.0.x (alternative range)")
fmt.Println()
ipAddress := getUserInput("Enter IP address for this node (e.g., 10.8.0.30): ", validateIP)
// Get interface name
fmt.Println()
fmt.Println("Interface name options:")
fmt.Println(" - wg0 (default, most common)")
fmt.Println(" - wg1, wg2, etc. (if wg0 is already in use)")
fmt.Println(" - Custom name (e.g., nextgen, vpn, etc.)")
fmt.Println()
interfaceName := getUserInput("Enter interface name (default: wg0): ", validateInterfaceName)
if interfaceName == "" {
interfaceName = "wg0"
}
// Configuration options
fmt.Println()
if !runningAsRoot {
stepNum = "3"
} else {
stepNum = "2"
}
fmt.Printf("%sStep %s: Configuration Options%s\n", Blue, stepNum, Reset)
fmt.Println()
fmt.Println("Choose an option:")
fmt.Println("1. Generate keys only (manual config creation)")
fmt.Println("2. Generate keys + complete config (recommended)")
fmt.Println()
reader := bufio.NewReader(os.Stdin)
var configChoice string
for {
fmt.Print("Enter your choice (1 or 2): ")
configChoice, _ = reader.ReadString('\n')
configChoice = strings.TrimSpace(configChoice)
if configChoice == "1" || configChoice == "2" {
break
}
printError("Invalid choice. Please enter 1 or 2.")
}
// Traffic routing options
var routingMode string
if configChoice == "2" {
fmt.Println()
fmt.Println("Traffic routing options:")
fmt.Println("1. WireGuard traffic only (10.8.0.x network only)")
fmt.Println("2. All traffic through VPN (full tunnel)")
fmt.Println()
fmt.Println("Note: Full tunnel routes ALL internet traffic through the VPN.")
fmt.Println(" WireGuard-only keeps your regular internet traffic separate.")
fmt.Println()
var routingChoice string
for {
fmt.Print("Enter your choice (1 or 2): ")
routingChoice, _ = reader.ReadString('\n')
routingChoice = strings.TrimSpace(routingChoice)
if routingChoice == "1" || routingChoice == "2" {
break
}
printError("Invalid choice. Please enter 1 or 2.")
}
if routingChoice == "1" {
routingMode = "wg_only"
printStatus("Selected: WireGuard traffic only")
} else {
routingMode = "full_tunnel"
printStatus("Selected: All traffic through VPN")
}
}
printStatus(fmt.Sprintf("Starting setup for %s (%s)...", hostname, ipAddress))
fmt.Println()
// Create directories
if err := os.MkdirAll(wgDir, 0755); err != nil {
printError(fmt.Sprintf("Failed to create directory: %v", err))
os.Exit(1)
}
// Generate keys
printStatus("Generating WireGuard keys...")
privateKey, publicKey, err := generateWireGuardKeys()
if err != nil {
printError(fmt.Sprintf("Failed to generate keys: %v", err))
os.Exit(1)
}
// Save keys
privateKeyFile := filepath.Join(wgDir, fmt.Sprintf("%s_private.key", hostname))
publicKeyFile := filepath.Join(wgDir, fmt.Sprintf("%s_public.key", hostname))
if err := ioutil.WriteFile(privateKeyFile, []byte(privateKey), 0600); err != nil {
printError(fmt.Sprintf("Failed to save private key: %v", err))
os.Exit(1)
}
if err := ioutil.WriteFile(publicKeyFile, []byte(publicKey), 0600); err != nil {
printError(fmt.Sprintf("Failed to save public key: %v", err))
os.Exit(1)
}
printStatus("Keys generated successfully!")
fmt.Printf(" Private key: %s\n", privateKeyFile)
fmt.Printf(" Public key: %s\n", publicKeyFile)
fmt.Println()
// Display node information
if !runningAsRoot {
stepNum = "4"
} else {
stepNum = "3"
}
fmt.Printf("%sStep %s: Node Information%s\n", Blue, stepNum, Reset)
fmt.Println("==========================================")
fmt.Printf("HOSTNAME: %s\n", hostname)
fmt.Printf("IP ADDRESS: %s\n", ipAddress)
fmt.Printf("PRIVATE KEY: %s\n", privateKey)
fmt.Printf("PUBLIC KEY: %s\n", publicKey)
fmt.Println("==========================================")
fmt.Println()
// Save structured info
infoFile := filepath.Join(wgDir, fmt.Sprintf("%s_wg_info.json", hostname))
if runningAsRoot {
infoFile = filepath.Join("/tmp", fmt.Sprintf("%s_wg_info.json", hostname))
}
wgConfig := WGConfig{
Hostname: hostname,
IPAddress: ipAddress,
PrivateKey: privateKey,
PublicKey: publicKey,
RoutingMode: routingMode,
Generated: time.Now().Format(time.RFC3339),
ScriptVer: "2.2",
RunningRoot: runningAsRoot,
}
infoData, err := json.MarshalIndent(wgConfig, "", " ")
if err != nil {
printError(fmt.Sprintf("Failed to marshal config: %v", err))
os.Exit(1)
}
if err := ioutil.WriteFile(infoFile, infoData, 0600); err != nil {
printError(fmt.Sprintf("Failed to save info file: %v", err))
os.Exit(1)
}
printStatus(fmt.Sprintf("Information saved to: %s", infoFile))
fmt.Println()
// Generate complete config if requested
if configChoice == "2" {
configFile := filepath.Join(wgDir, fmt.Sprintf("%s.conf", interfaceName))
printStatus(fmt.Sprintf("Generating complete %s.conf...", interfaceName))
configContent := generateConfig(hostname, ipAddress, privateKey, routingMode)
if err := ioutil.WriteFile(configFile, []byte(configContent), 0600); err != nil {
printError(fmt.Sprintf("Failed to write config file: %v", err))
os.Exit(1)
}
printStatus(fmt.Sprintf("Config written to: %s", configFile))
if runningAsRoot {
printStatus("Permissions set to 600")
}
fmt.Println()
// Next steps
if !runningAsRoot {
stepNum = "5"
} else {
stepNum = "4"
}
fmt.Printf("%sStep %s: Next Steps%s\n", Blue, stepNum, Reset)
fmt.Println()
if runningAsRoot {
printStatus("Ready to start WireGuard:")
fmt.Printf(" systemctl enable --now wg-quick@%s\n", interfaceName)
} else {
printWarning("To enable WireGuard (requires root):")
fmt.Printf(" sudo cp %s /etc/wireguard/\n", configFile)
fmt.Printf(" sudo chmod 600 /etc/wireguard/%s.conf\n", interfaceName)
fmt.Printf(" sudo systemctl enable --now wg-quick@%s\n", interfaceName)
}
fmt.Println()
printWarning("IMPORTANT: Update other nodes with this peer info:")
fmt.Printf(" PublicKey = %s\n", publicKey)
fmt.Printf(" AllowedIPs = %s/32\n", ipAddress)
fmt.Println()
// Config preview
fmt.Printf("%sConfig Preview:%s\n", Blue, Reset)
fmt.Println("----------------------------------------")
lines := strings.Split(configContent, "\n")
for i, line := range lines {
if i >= 5 {
break
}
fmt.Println(line)
}
fmt.Printf(" [... and %d total lines]\n", len(lines))
fmt.Println("----------------------------------------")
fmt.Println()
// Full tunnel instructions
if routingMode == "full_tunnel" {
fmt.Println()
printWarning("FULL TUNNEL MODE DETECTED - Endpoint Changes Required:")
fmt.Println()
fmt.Println("Since this node will route ALL traffic through the VPN, you need to:")
fmt.Println()
fmt.Println("1. Update Zion's config (/etc/wireguard/wg0.conf) to allow this peer:")
fmt.Println(" - Add the peer section as shown above")
fmt.Println(" - Ensure Zion has proper iptables rules for NAT/masquerading")
fmt.Println()
fmt.Println("2. Check Zion's iptables rules (run on Zion server):")
fmt.Println(" sudo iptables -t nat -L POSTROUTING")
fmt.Println(" sudo iptables -L FORWARD")
fmt.Println()
fmt.Println("3. If Zion doesn't have proper NAT rules, add them:")
fmt.Println(" sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE")
fmt.Println(" sudo iptables -A FORWARD -i wg0 -j ACCEPT")
fmt.Println(" sudo iptables -A FORWARD -o wg0 -j ACCEPT")
fmt.Println()
fmt.Println("4. Enable IP forwarding on Zion (if not already enabled):")
fmt.Println(" echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf")
fmt.Println(" sudo sysctl -p")
fmt.Println()
}
// Zion update instructions
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Printf("%s !!! NOW UPDATE ZION SERVER !!! %s\n", Red, Reset)
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Println()
printWarning("You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):")
fmt.Println()
fmt.Printf("%s#%s%s\n", Yellow, hostname, Reset)
fmt.Printf("%s[Peer]%s\n", Yellow, Reset)
fmt.Printf("%sPublicKey = %s%s\n", Yellow, publicKey, Reset)
fmt.Printf("%sAllowedIPs = %s/32%s\n", Yellow, ipAddress, Reset)
fmt.Println()
printWarning("After updating Zion, restart its WireGuard:")
fmt.Println(" systemctl restart wg-quick@wg0")
fmt.Println()
if runningAsRoot {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" systemctl restart wg-quick@%s\n", interfaceName)
} else {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" sudo systemctl restart wg-quick@%s\n", interfaceName)
}
} else {
// Manual config generation path
if !runningAsRoot {
stepNum = "5"
} else {
stepNum = "4"
}
fmt.Printf("%sStep %s: Next Steps%s\n", Blue, stepNum, Reset)
fmt.Println()
printStatus("Keys generated successfully!")
fmt.Printf(" Private key: %s\n", privateKeyFile)
fmt.Printf(" Public key: %s\n", publicKeyFile)
fmt.Println()
printWarning("Next steps:")
fmt.Printf(" - Create %s.conf manually using the keys above\n", interfaceName)
fmt.Printf(" - Copy config to %s\n", filepath.Join(wgDir, fmt.Sprintf("%s.conf", interfaceName)))
if runningAsRoot {
fmt.Printf(" - Set permissions: chmod 600 %s\n", filepath.Join(wgDir, fmt.Sprintf("%s.conf", interfaceName)))
fmt.Printf(" - Enable/start: systemctl enable --now wg-quick@%s\n", interfaceName)
} else {
fmt.Printf(" - Copy to system: sudo cp %s /etc/wireguard/\n", filepath.Join(wgDir, fmt.Sprintf("%s.conf", interfaceName)))
fmt.Printf(" - Set permissions: sudo chmod 600 /etc/wireguard/%s.conf\n", interfaceName)
fmt.Printf(" - Enable/start: sudo systemctl enable --now wg-quick@%s\n", interfaceName)
}
fmt.Println()
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Printf("%s !!! NOW UPDATE ZION SERVER !!! %s\n", Red, Reset)
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Println()
printWarning("You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):")
fmt.Println()
fmt.Printf("%s#%s%s\n", Yellow, hostname, Reset)
fmt.Printf("%s[Peer]%s\n", Yellow, Reset)
fmt.Printf("%sPublicKey = %s%s\n", Yellow, publicKey, Reset)
fmt.Printf("%sAllowedIPs = %s/32%s\n", Yellow, ipAddress, Reset)
fmt.Println()
printWarning("After updating Zion, restart its WireGuard:")
fmt.Println(" systemctl restart wg-quick@wg0")
fmt.Println()
if runningAsRoot {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" systemctl restart wg-quick@%s\n", interfaceName)
} else {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" sudo systemctl restart wg-quick@%s\n", interfaceName)
}
}
fmt.Println()
printStatus(fmt.Sprintf("Setup complete for %s!", hostname))
fmt.Println()
}

739
scripting/wgez-setup.sh Normal file
View File

@@ -0,0 +1,739 @@
#!/bin/bash
# NextGen WireGuard Easy Setup Script (wgez-setup.sh)
# Interactive setup script for new WireGuard nodes
set -euo pipefail
# Colors for better UX
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Globals
SCRIPT_DIR="${BASH_SOURCE[0]%/*}"
SCRIPT_DIR="$(cd "$SCRIPT_DIR" && pwd)"
# Determine if running as root and set appropriate directories
if [[ $EUID -eq 0 ]]; then
WG_DIR="/etc/wireguard"
BACKUP_DIR="$WG_DIR/backups"
RUNNING_AS_ROOT=true
else
WG_DIR="$(pwd)"
BACKUP_DIR="$WG_DIR/backups"
RUNNING_AS_ROOT=false
fi
# Logging
log_file="/var/log/wgez-setup.log"
if [[ "$RUNNING_AS_ROOT" == "false" ]]; then
log_file="$WG_DIR/wgez-setup.log"
fi
exec 19> >(exec cat >&2)
exec 20> >(exec tee -a "$log_file" >&2)
BASH_XTRACEFD=20
print_status() {
echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$log_file"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$log_file"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$log_file"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE} NextGen WireGuard Easy Setup ${NC}"
echo -e "${BLUE}================================${NC}"
echo ""
}
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
print_error "Script failed with exit code $exit_code"
if [[ -f "/tmp/${HOSTNAME:-unknown}_wg_info.json" ]]; then
rm -f "/tmp/${HOSTNAME}_wg_info.json"
fi
fi
exit $exit_code
}
# Enhanced validation functions
validate_hostname() {
local hostname="$1"
if [[ -z "$hostname" ]]; then
print_error "Hostname cannot be empty"
return 1
fi
if [[ ! "$hostname" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$ ]]; then
print_error "Invalid hostname format. Use alphanumeric characters and hyphens only."
return 1
fi
if [[ ${#hostname} -gt 63 ]]; then
print_error "Hostname too long (max 63 characters)"
return 1
fi
return 0
}
validate_ip() {
local ip="$1"
local octets
if [[ -z "$ip" ]]; then
print_error "IP address cannot be empty"
return 1
fi
IFS='.' read -ra octets <<< "$ip"
if [[ ${#octets[@]} -ne 4 ]]; then
print_error "Invalid IP format"
return 1
fi
for octet in "${octets[@]}"; do
if ! [[ "$octet" =~ ^[0-9]+$ ]] || [[ $octet -gt 255 ]] || [[ $octet -lt 0 ]]; then
print_error "Invalid IP octet: $octet"
return 1
fi
done
if [[ ! "$ip" =~ ^10\.(8|0)\.0\.[0-9]{1,3}$ ]]; then
print_warning "IP should be in 10.8.0.x or 10.0.0.x range for NextGen network"
return 2
fi
return 0
}
validate_interface_name() {
local interface="$1"
if [[ -z "$interface" ]]; then
print_error "Interface name cannot be empty"
return 1
fi
if [[ ! "$interface" =~ ^[a-zA-Z][a-zA-Z0-9]*$ ]]; then
print_error "Invalid interface name. Use letters and numbers only, starting with a letter."
return 1
fi
if [[ ${#interface} -gt 15 ]]; then
print_error "Interface name too long (max 15 characters)"
return 1
fi
# Check if interface already exists (only if running as root)
if [[ "$RUNNING_AS_ROOT" == "true" ]] && ip link show "$interface" &>/dev/null; then
print_warning "Interface '$interface' already exists"
return 2
fi
# Check if config file already exists
if [[ -f "$WG_DIR/${interface}.conf" ]]; then
print_warning "Config file '$WG_DIR/${interface}.conf' already exists"
return 2
fi
return 0
}
check_ip_conflict() {
local ip="$1"
local config_file="$2"
# Check if IP is already in use on network
if ping -c1 -W1 "$ip" &>/dev/null; then
print_warning "IP $ip appears to be in use on the network"
return 1
fi
# Check existing WireGuard configs
if [[ -f "$config_file" ]] && grep -q "Address = $ip" "$config_file"; then
print_warning "IP $ip already configured in existing WireGuard config"
return 1
fi
return 0
}
check_dependencies() {
local deps=("wg" "wg-quick")
local missing=()
for dep in "${deps[@]}"; do
if ! command -v "$dep" &>/dev/null; then
missing+=("$dep")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
print_error "Missing dependencies: ${missing[*]}"
print_status "Install with: apt install wireguard-tools"
return 1
fi
# Only check systemctl if running as root
if [[ "$RUNNING_AS_ROOT" == "true" ]] && ! command -v "systemctl" &>/dev/null; then
print_error "Missing dependency: systemctl"
print_status "Install with: apt install systemd"
return 1
fi
return 0
}
backup_existing_config() {
local config_file="$1"
if [[ -f "$config_file" ]]; then
mkdir -p "$BACKUP_DIR"
local config_name=$(basename "$config_file")
local backup_file="$BACKUP_DIR/${config_name}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$config_file" "$backup_file"
print_status "Existing config backed up to: $backup_file"
fi
}
get_user_input() {
local prompt="$1"
local var_name="$2"
local validator="$3"
local value
while true; do
read -p "$prompt" value
if $validator "$value"; then
eval "$var_name='$value'"
break
elif [[ $? -eq 2 ]]; then
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
eval "$var_name='$value'"
break
fi
fi
done
}
get_directory_input() {
local prompt="$1"
local var_name="$2"
local default_dir="$3"
local directory
while true; do
read -p "$prompt [$default_dir]: " directory
if [[ -z "$directory" ]]; then
directory="$default_dir"
fi
# Create directory if it doesn't exist
if [[ ! -d "$directory" ]]; then
read -p "Directory '$directory' doesn't exist. Create it? (Y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
continue
fi
if ! mkdir -p "$directory" 2>/dev/null; then
print_error "Failed to create directory '$directory'"
continue
fi
fi
# Check if directory is writable
if [[ ! -w "$directory" ]]; then
print_error "Directory '$directory' is not writable"
continue
fi
eval "$var_name='$directory'"
break
done
}
generate_config() {
local hostname="$1"
local ip_address="$2"
local private_key="$3"
local routing_mode="${4:-wg_only}"
# Define static servers
local -A static_servers=(
["10.8.0.1"]="51820"
["10.8.0.10"]="53535"
["10.8.0.99"]="54382"
)
local config_content="[Interface]
Address = $ip_address/24
PrivateKey = $private_key"
# Add ListenPort for static servers
local is_static_server=false
if [[ -n "${static_servers[$ip_address]:-}" ]]; then
is_static_server=true
config_content="$config_content
ListenPort = ${static_servers[$ip_address]}"
fi
# Add DNS for full tunnel mode
if [[ "$routing_mode" == "full_tunnel" ]]; then
config_content="$config_content
DNS = 1.1.1.1, 8.8.8.8"
fi
config_content="$config_content
#Zion peer (central server) - for access to entire network
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg="
# Set AllowedIPs based on routing mode
if [[ "$routing_mode" == "full_tunnel" ]]; then
config_content="$config_content
AllowedIPs = 0.0.0.0/0, ::/0"
else
config_content="$config_content
AllowedIPs = 10.8.0.0/24"
fi
config_content="$config_content
Endpoint = ugh.im:51820"
# Set keepalive value (25 for all peers as per new configuration)
config_content="$config_content
PersistentKeepalive = 25"
config_content="$config_content
#Cthulhu (optional if port 53 is also forwarded to bypass firewalls)
#[Peer]
#PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
#AllowedIPs = 10.8.0.10/32
#Endpoint = aw2cd67.glddns.com:53535"
config_content="$config_content
#PersistentKeepalive = 25"
config_content="$config_content
#Galaxy (located in Europe, NL)
#[Peer]
#PublicKey = QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=
#AllowedIPs = 10.8.0.99/32
#Endpoint = galaxyspin.space:54382"
config_content="$config_content
#PersistentKeepalive = 25"
echo "$config_content"
}
print_progress() {
local step="$1"
local total="$2"
echo -e "${BLUE}[$step/$total]${NC} $3"
}
validate_config() {
local config_file="$1"
if ! wg-quick strip "$config_file" >/dev/null 2>&1; then
print_error "Generated config has syntax errors"
return 1
fi
return 0
}
check_network_connectivity() {
if ! ping -c1 -W3 8.8.8.8 &>/dev/null; then
print_warning "No internet connectivity detected"
return 1
fi
return 0
}
main() {
trap cleanup EXIT
print_header
# Check if running as root and inform user
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_status "Running as root - using system directories"
print_status "WireGuard directory: $WG_DIR"
else
print_warning "Not running as root - using current directory"
print_status "WireGuard directory: $WG_DIR"
print_warning "You'll need to manually copy config files to /etc/wireguard/ later"
fi
echo ""
# Check dependencies
if ! check_dependencies; then
exit 1
fi
# Get directory for non-root users
if [[ "$RUNNING_AS_ROOT" == "false" ]]; then
echo -e "${BLUE}Step 1: Directory Selection${NC}"
echo ""
print_status "Choose where to save WireGuard files:"
echo " - Current directory: $(pwd)"
echo " - Home directory: $HOME"
echo " - Custom directory"
echo ""
get_directory_input "Enter directory path for WireGuard files" WG_DIR "$(pwd)"
BACKUP_DIR="$WG_DIR/backups"
echo ""
fi
# Get hostname
echo -e "${BLUE}Step $([[ "$RUNNING_AS_ROOT" == "false" ]] && echo "2" || echo "1"): Node Information${NC}"
echo ""
get_user_input "Enter hostname for this node: " HOSTNAME validate_hostname
# Get IP address
echo ""
echo "Available IP ranges:"
echo " - 10.8.0.x (recommended for NextGen network)"
echo " - 10.0.0.x (alternative range)"
echo ""
get_user_input "Enter IP address for this node (e.g., 10.8.0.30): " IP_ADDRESS validate_ip
# Get interface name
echo ""
echo "Interface name options:"
echo " - wg0 (default, most common)"
echo " - wg1, wg2, etc. (if wg0 is already in use)"
echo " - Custom name (e.g., nextgen, vpn, etc.)"
echo ""
get_user_input "Enter interface name (default: wg0): " INTERFACE_NAME validate_interface_name
if [[ -z "$INTERFACE_NAME" ]]; then
INTERFACE_NAME="wg0"
fi
# Set config file path
CONFIG_FILE="$WG_DIR/${INTERFACE_NAME}.conf"
# Check for IP conflicts
if ! check_ip_conflict "$IP_ADDRESS" "$CONFIG_FILE"; then
read -p "Continue anyway? (y/N): " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
# Check if keys already exist
if [[ -f "$WG_DIR/${HOSTNAME}_private.key" ]]; then
print_warning "Keys for $HOSTNAME already exist!"
read -p "Overwrite? (y/N): " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
# Configuration options
echo ""
echo -e "${BLUE}Step $([[ "$RUNNING_AS_ROOT" == "false" ]] && echo "3" || echo "2"): Configuration Options${NC}"
echo ""
echo "Choose an option:"
echo "1. Generate keys only (manual config creation)"
echo "2. Generate keys + complete ${INTERFACE_NAME}.conf (recommended)"
echo ""
while true; do
read -p "Enter your choice (1 or 2): " CONFIG_CHOICE
if [[ "$CONFIG_CHOICE" == "1" || "$CONFIG_CHOICE" == "2" ]]; then
break
fi
print_error "Invalid choice. Please enter 1 or 2."
done
# Traffic routing options (only if generating complete config)
if [[ "$CONFIG_CHOICE" == "2" ]]; then
echo ""
echo "Traffic routing options:"
echo "1. WireGuard traffic only (10.8.0.x network only)"
echo "2. All traffic through VPN (full tunnel)"
echo ""
echo "Note: Full tunnel routes ALL internet traffic through the VPN."
echo " WireGuard-only keeps your regular internet traffic separate."
echo ""
while true; do
read -p "Enter your choice (1 or 2): " ROUTING_CHOICE
if [[ "$ROUTING_CHOICE" == "1" || "$ROUTING_CHOICE" == "2" ]]; then
break
fi
print_error "Invalid choice. Please enter 1 or 2."
done
if [[ "$ROUTING_CHOICE" == "1" ]]; then
ROUTING_MODE="wg_only"
print_status "Selected: WireGuard traffic only"
else
ROUTING_MODE="full_tunnel"
print_status "Selected: All traffic through VPN"
fi
fi
print_status "Starting setup for $HOSTNAME ($IP_ADDRESS)..."
echo ""
# Create directories
mkdir -p "$WG_DIR" "$BACKUP_DIR"
cd "$WG_DIR"
# Set secure permissions (only if running as root)
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
umask 077
fi
print_status "Generating WireGuard keys..."
PRIVATE_KEY=$(wg genkey)
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | wg pubkey)
# Save keys
printf "%s\n%s\n" "$PRIVATE_KEY" "$PUBLIC_KEY" > "${HOSTNAME}_keys.tmp"
mv "${HOSTNAME}_keys.tmp" "${HOSTNAME}_private.key"
echo "$PUBLIC_KEY" > "${HOSTNAME}_public.key"
# Set permissions (only if running as root)
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
chmod 600 "${HOSTNAME}_private.key" "${HOSTNAME}_public.key"
fi
print_status "Keys generated successfully!"
echo " Private key: $WG_DIR/${HOSTNAME}_private.key"
echo " Public key: $WG_DIR/${HOSTNAME}_public.key"
echo ""
echo -e "${BLUE}Step $([[ "$RUNNING_AS_ROOT" == "false" ]] && echo "4" || echo "3"): Node Information${NC}"
echo "=========================================="
echo "HOSTNAME: $HOSTNAME"
echo "IP ADDRESS: $IP_ADDRESS"
echo "PRIVATE KEY: $PRIVATE_KEY"
echo "PUBLIC KEY: $PUBLIC_KEY"
echo "=========================================="
echo ""
# Save structured info
INFO_FILE="/tmp/${HOSTNAME}_wg_info.json"
if [[ "$RUNNING_AS_ROOT" == "false" ]]; then
INFO_FILE="$WG_DIR/${HOSTNAME}_wg_info.json"
fi
cat > "$INFO_FILE" << EOF
{
"hostname": "$HOSTNAME",
"ip_address": "$IP_ADDRESS",
"private_key": "$PRIVATE_KEY",
"public_key": "$PUBLIC_KEY",
"routing_mode": "${ROUTING_MODE:-wg_only}",
"generated": "$(date -Iseconds)",
"script_version": "2.2",
"running_as_root": $RUNNING_AS_ROOT
}
EOF
print_status "Information saved to: $INFO_FILE"
echo ""
# Generate complete config if requested
if [[ "$CONFIG_CHOICE" == "2" ]]; then
backup_existing_config "$CONFIG_FILE"
print_status "Generating complete ${INTERFACE_NAME}.conf..."
generate_config "$HOSTNAME" "$IP_ADDRESS" "$PRIVATE_KEY" "$ROUTING_MODE" > "$CONFIG_FILE"
# Set permissions (only if running as root)
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
chmod 600 "$CONFIG_FILE"
fi
print_status "Config written to: $CONFIG_FILE"
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_status "Permissions set to 600"
fi
echo ""
echo -e "${BLUE}Step $([[ "$RUNNING_AS_ROOT" == "false" ]] && echo "5" || echo "4"): Next Steps${NC}"
echo ""
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_status "Ready to start WireGuard:"
echo " systemctl enable --now wg-quick@${INTERFACE_NAME}"
else
print_warning "To enable WireGuard (requires root):"
echo " sudo cp $CONFIG_FILE /etc/wireguard/"
echo " sudo chmod 600 /etc/wireguard/${INTERFACE_NAME}.conf"
echo " sudo systemctl enable --now wg-quick@${INTERFACE_NAME}"
fi
echo ""
print_warning "IMPORTANT: Update other nodes with this peer info:"
echo " PublicKey = $PUBLIC_KEY"
echo " AllowedIPs = $IP_ADDRESS/32"
echo ""
echo -e "${BLUE}Config Preview:${NC}"
echo "----------------------------------------"
head -5 "$CONFIG_FILE"
echo " [... and $(wc -l < "$CONFIG_FILE" | tr -d ' ') total lines]"
echo "----------------------------------------"
echo ""
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_status "Configuration complete! To start WireGuard:"
echo " systemctl enable --now wg-quick@${INTERFACE_NAME}"
echo ""
print_status "To check status:"
echo " wg show ${INTERFACE_NAME}"
echo " systemctl status wg-quick@${INTERFACE_NAME}"
echo ""
print_status "To view logs:"
echo " journalctl -u wg-quick@${INTERFACE_NAME} -f"
else
print_status "Configuration complete! To enable WireGuard:"
echo " sudo cp $CONFIG_FILE /etc/wireguard/"
echo " sudo chmod 600 /etc/wireguard/${INTERFACE_NAME}.conf"
echo " sudo systemctl enable --now wg-quick@${INTERFACE_NAME}"
echo ""
print_status "To check status (requires root):"
echo " sudo wg show ${INTERFACE_NAME}"
echo " sudo systemctl status wg-quick@${INTERFACE_NAME}"
fi
echo ""
echo -e "${RED}========================================${NC}"
echo -e "${RED} !!! NOW UPDATE ZION SERVER !!! ${NC}"
echo -e "${RED}========================================${NC}"
echo ""
print_warning "You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):"
echo ""
echo -e "${YELLOW}#$HOSTNAME${NC}"
echo -e "${YELLOW}[Peer]${NC}"
echo -e "${YELLOW}PublicKey = $PUBLIC_KEY${NC}"
echo -e "${YELLOW}AllowedIPs = $IP_ADDRESS/32${NC}"
echo ""
print_warning "After updating Zion, restart its WireGuard:"
echo " systemctl restart wg-quick@wg0"
echo ""
# Add endpoint-specific instructions for full tunnel mode
if [[ "$ROUTING_MODE" == "full_tunnel" ]]; then
echo ""
print_warning "FULL TUNNEL MODE DETECTED - Endpoint Changes Required:"
echo ""
echo "Since this node will route ALL traffic through the VPN, you need to:"
echo ""
echo "1. Update Zion's config (/etc/wireguard/wg0.conf) to allow this peer:"
echo " - Add the peer section as shown above"
echo " - Ensure Zion has proper iptables rules for NAT/masquerading"
echo ""
echo "2. Check Zion's iptables rules (run on Zion server):"
echo " sudo iptables -t nat -L POSTROUTING"
echo " sudo iptables -L FORWARD"
echo ""
echo "3. If Zion doesn't have proper NAT rules, add them:"
echo " sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE"
echo " sudo iptables -A FORWARD -i wg0 -j ACCEPT"
echo " sudo iptables -A FORWARD -o wg0 -j ACCEPT"
echo ""
echo "4. Enable IP forwarding on Zion (if not already enabled):"
echo " echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf"
echo " sudo sysctl -p"
echo ""
fi
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_warning "Then restart this node's WireGuard:"
echo " systemctl restart wg-quick@${INTERFACE_NAME}"
else
print_warning "Then restart this node's WireGuard:"
echo " sudo systemctl restart wg-quick@${INTERFACE_NAME}"
fi
else
echo -e "${BLUE}Step $([[ "$RUNNING_AS_ROOT" == "false" ]] && echo "5" || echo "4"): Next Steps${NC}"
echo ""
print_status "Keys generated successfully!"
echo " Private key: $WG_DIR/${HOSTNAME}_private.key"
echo " Public key: $WG_DIR/${HOSTNAME}_public.key"
echo ""
print_warning "Next steps:"
echo " - Create ${INTERFACE_NAME}.conf manually using the keys above"
echo " - Copy config to $CONFIG_FILE"
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
echo " - Set permissions: chmod 600 $CONFIG_FILE"
echo " - Enable/start: systemctl enable --now wg-quick@${INTERFACE_NAME}"
else
echo " - Copy to system: sudo cp $CONFIG_FILE /etc/wireguard/"
echo " - Set permissions: sudo chmod 600 /etc/wireguard/${INTERFACE_NAME}.conf"
echo " - Enable/start: sudo systemctl enable --now wg-quick@${INTERFACE_NAME}"
fi
echo ""
echo -e "${RED}========================================${NC}"
echo -e "${RED} !!! NOW UPDATE ZION SERVER !!! ${NC}"
echo -e "${RED}========================================${NC}"
echo ""
print_warning "You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):"
echo ""
echo -e "${YELLOW}#$HOSTNAME${NC}"
echo -e "${YELLOW}[Peer]${NC}"
echo -e "${YELLOW}PublicKey = $PUBLIC_KEY${NC}"
echo -e "${YELLOW}AllowedIPs = $IP_ADDRESS/32${NC}"
echo ""
print_warning "After updating Zion, restart its WireGuard:"
echo " systemctl restart wg-quick@wg0"
echo ""
# Add endpoint-specific instructions for full tunnel mode (manual path)
if [[ "${ROUTING_MODE:-wg_only}" == "full_tunnel" ]]; then
echo ""
print_warning "FULL TUNNEL MODE DETECTED - Endpoint Changes Required:"
echo ""
echo "Since this node will route ALL traffic through the VPN, you need to:"
echo ""
echo "1. Update Zion's config (/etc/wireguard/wg0.conf) to allow this peer:"
echo " - Add the peer section as shown above"
echo " - Ensure Zion has proper iptables rules for NAT/masquerading"
echo ""
echo "2. Check Zion's iptables rules (run on Zion server):"
echo " sudo iptables -t nat -L POSTROUTING"
echo " sudo iptables -L FORWARD"
echo ""
echo "3. If Zion doesn't have proper NAT rules, add them:"
echo " sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE"
echo " sudo iptables -A FORWARD -i wg0 -j ACCEPT"
echo " sudo iptables -A FORWARD -o wg0 -j ACCEPT"
echo ""
echo "4. Enable IP forwarding on Zion (if not already enabled):"
echo " echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf"
echo " sudo sysctl -p"
echo ""
fi
if [[ "$RUNNING_AS_ROOT" == "true" ]]; then
print_warning "Then restart this node's WireGuard:"
echo " systemctl restart wg-quick@${INTERFACE_NAME}"
else
print_warning "Then restart this node's WireGuard:"
echo " sudo systemctl restart wg-quick@${INTERFACE_NAME}"
fi
fi
echo ""
print_status "Setup complete for $HOSTNAME!"
print_status "Log file available at: $log_file"
echo ""
}
main "$@"

74
test_overwrite_protection.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Test script to demonstrate file overwrite protection
set -e
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}================================${NC}"
}
print_header "Testing File Overwrite Protection"
echo "This test will demonstrate the file overwrite protection feature."
echo ""
# Create a test configuration file first
print_status "Creating a test configuration file..."
mkdir -p wireguard_configs
echo "# Test configuration" > wireguard_configs/test_node.conf
echo "This is a test file" >> wireguard_configs/test_node.conf
print_status "Test file created: wireguard_configs/test_node.conf"
echo ""
# Test 1: Try to create a configuration with the same name (should prompt for overwrite)
print_header "Test 1: Attempting to create configuration with existing name"
echo "Now we'll try to create a configuration with the same name 'test_node'"
echo "The script should detect the existing file and ask if you want to overwrite it."
echo ""
# Create test input that will trigger the overwrite prompt
cat > /tmp/test_overwrite_input.txt << 'EOF'
test_node
10.8.0.5/24
n
n
n
EOF
print_status "Running setup script..."
echo "When prompted, you can choose:"
echo " 'y' to overwrite the existing file"
echo " 'n' to cancel the operation"
echo ""
# Run the setup script
./wireguard_setup.sh < /tmp/test_overwrite_input.txt
# Clean up
rm -f /tmp/test_overwrite_input.txt
print_status "Test completed!"
echo ""
echo "The script should have:"
echo "1. Detected the existing test_node.conf file"
echo "2. Asked if you wanted to overwrite it"
echo "3. Either overwritten it (if you chose 'y') or cancelled (if you chose 'n')"

58
test_zion_peer.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
# Test script to demonstrate Zion default peer feature
set -e
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}================================${NC}"
}
print_header "Testing Zion Default Peer Feature"
echo "This test will create a client configuration with Zion as the default peer."
echo "The script will automatically include Zion's connection details."
echo ""
# Create test input for a client configuration
cat > /tmp/test_zion_input.txt << 'EOF'
test_client
10.8.0.5/24
n
y
n
EOF
print_status "Running setup script with test configuration..."
echo "Input will be:"
echo " Node name: test_client"
echo " IP: 10.8.0.5/24"
echo " Server mode: n (no)"
echo " Add Zion: y (yes)"
echo " Add additional peers: n (no)"
echo ""
# Run the setup script
./wireguard_setup.sh < /tmp/test_zion_input.txt
# Clean up
rm -f /tmp/test_zion_input.txt
print_status "Test completed! Check wireguard_configs/test_client.conf"
echo ""
echo "The generated configuration should include Zion as a peer with:"
echo " Public Key: 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg="
echo " Endpoint: ugh.im:51820"
echo " Allowed IPs: 10.8.0.0/24"
echo " Persistent Keepalive: 25"

295
validate_config.sh Executable file
View File

@@ -0,0 +1,295 @@
#!/bin/bash
# WireGuard Configuration Validator
# This script validates WireGuard configuration files for common issues
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() {
echo -e "${GREEN}[PASS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[FAIL]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}================================${NC}"
}
# Function to validate IP address format
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
}
# Function to validate port number
validate_port() {
local port=$1
if [[ $port =~ ^[0-9]+$ ]] && [ $port -ge 1 ] && [ $port -le 65535 ]; then
return 0
else
return 1
fi
}
# Function to validate WireGuard key format
validate_wg_key() {
local key=$1
# WireGuard keys are base64 encoded and typically 44 characters long
if [[ $key =~ ^[A-Za-z0-9+/]{43}=$ ]]; then
return 0
else
return 1
fi
}
# Function to validate endpoint format
validate_endpoint() {
local endpoint=$1
if [[ $endpoint =~ ^[a-zA-Z0-9.-]+:[0-9]+$ ]]; then
return 0
else
return 1
fi
}
# Function to validate a single configuration file
validate_config_file() {
local config_file=$1
local errors=0
local warnings=0
print_header "Validating: $config_file"
if [ ! -f "$config_file" ]; then
print_error "Configuration file not found: $config_file"
return 1
fi
# Check file permissions
local perms=$(stat -c %a "$config_file")
if [ "$perms" != "600" ]; then
print_warning "File permissions should be 600, current: $perms"
((warnings++))
fi
# Parse the configuration file
local in_interface=0
local in_peer=0
local has_interface=0
local has_private_key=0
local has_address=0
local peer_count=0
while IFS= read -r line; do
# Skip comments and empty lines
if [[ $line =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then
continue
fi
# Check for Interface section
if [[ $line =~ ^\[Interface\] ]]; then
in_interface=1
in_peer=0
has_interface=1
continue
fi
# Check for Peer section
if [[ $line =~ ^\[Peer\] ]]; then
in_interface=0
in_peer=1
((peer_count++))
continue
fi
# Parse Interface section
if [ $in_interface -eq 1 ]; then
if [[ $line =~ ^PrivateKey[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local private_key="${BASH_REMATCH[1]}"
if validate_wg_key "$private_key"; then
print_status "Valid private key found"
has_private_key=1
else
print_error "Invalid private key format"
((errors++))
fi
elif [[ $line =~ ^Address[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local address="${BASH_REMATCH[1]}"
if validate_ip "$address"; then
print_status "Valid address: $address"
has_address=1
else
print_error "Invalid address format: $address"
((errors++))
fi
elif [[ $line =~ ^ListenPort[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local port="${BASH_REMATCH[1]}"
if validate_port "$port"; then
print_status "Valid listen port: $port"
else
print_error "Invalid listen port: $port"
((errors++))
fi
fi
fi
# Parse Peer section
if [ $in_peer -eq 1 ]; then
if [[ $line =~ ^PublicKey[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local public_key="${BASH_REMATCH[1]}"
if validate_wg_key "$public_key"; then
print_status "Valid peer public key"
else
print_error "Invalid peer public key format"
((errors++))
fi
elif [[ $line =~ ^AllowedIPs[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local allowed_ips="${BASH_REMATCH[1]}"
# Split multiple IPs if present
IFS=',' read -ra IP_ARRAY <<< "$allowed_ips"
for ip in "${IP_ARRAY[@]}"; do
ip=$(echo "$ip" | xargs) # Trim whitespace
if validate_ip "$ip"; then
print_status "Valid allowed IP: $ip"
else
print_error "Invalid allowed IP format: $ip"
((errors++))
fi
done
elif [[ $line =~ ^Endpoint[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local endpoint="${BASH_REMATCH[1]}"
if validate_endpoint "$endpoint"; then
print_status "Valid endpoint: $endpoint"
else
print_error "Invalid endpoint format: $endpoint"
((errors++))
fi
elif [[ $line =~ ^PersistentKeepalive[[:space:]]*=[[:space:]]*(.+)$ ]]; then
local keepalive="${BASH_REMATCH[1]}"
if validate_port "$keepalive"; then
print_status "Valid persistent keepalive: $keepalive"
else
print_error "Invalid persistent keepalive: $keepalive"
((errors++))
fi
fi
fi
done < "$config_file"
# Check required fields
if [ $has_interface -eq 0 ]; then
print_error "Missing [Interface] section"
((errors++))
fi
if [ $has_private_key -eq 0 ]; then
print_error "Missing PrivateKey in [Interface] section"
((errors++))
fi
if [ $has_address -eq 0 ]; then
print_error "Missing Address in [Interface] section"
((errors++))
fi
if [ $peer_count -eq 0 ]; then
print_warning "No [Peer] sections found"
((warnings++))
fi
# Summary
echo ""
if [ $errors -eq 0 ] && [ $warnings -eq 0 ]; then
print_status "Configuration file is valid!"
return 0
elif [ $errors -eq 0 ]; then
print_warning "Configuration file has $warnings warning(s)"
return 0
else
print_error "Configuration file has $errors error(s) and $warnings warning(s)"
return 1
fi
}
# Function to validate all config files in a directory
validate_directory() {
local dir=$1
local total_errors=0
local total_files=0
print_header "Validating all config files in: $dir"
if [ ! -d "$dir" ]; then
print_error "Directory not found: $dir"
return 1
fi
for config_file in "$dir"/*.conf; do
if [ -f "$config_file" ]; then
((total_files++))
if ! validate_config_file "$config_file"; then
((total_errors++))
fi
echo ""
fi
done
if [ $total_files -eq 0 ]; then
print_warning "No .conf files found in $dir"
else
print_header "Validation Summary"
echo "Total files: $total_files"
echo "Files with errors: $total_errors"
echo "Files without errors: $((total_files - total_errors))"
fi
}
# Show usage
show_usage() {
echo "Usage: $0 [OPTIONS] <config_file_or_directory>"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 wireguard_configs/test.conf # Validate single file"
echo " $0 wireguard_configs/ # Validate all files in directory"
echo " $0 TESTING/ # Validate TESTING directory"
}
# Main logic
if [ $# -eq 0 ] || [[ $1 =~ ^- ]]; then
show_usage
exit 0
fi
target=$1
if [ -f "$target" ]; then
validate_config_file "$target"
elif [ -d "$target" ]; then
validate_directory "$target"
else
print_error "Target not found: $target"
exit 1
fi

BIN
wgtool Executable file

Binary file not shown.

View File

@@ -0,0 +1 @@
6Ag2qgqY85gMrp9gvbqjSAM6jJIGQds737HDfKnTM0s=

View File

@@ -0,0 +1 @@
fqtkYEqAOBXQzmUGVNSloLawNyrVIOqV/Vv2FvVmrho=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: cachybot
# PublicKey: fqtkYEqAOBXQzmUGVNSloLawNyrVIOqV/Vv2FvVmrho=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = fqtkYEqAOBXQzmUGVNSloLawNyrVIOqV/Vv2FvVmrho=
# AllowedIPs = 10.8.0.26/32
# ================================================
[Interface]
Address = 10.8.0.26/24
PrivateKey = 6Ag2qgqY85gMrp9gvbqjSAM6jJIGQds737HDfKnTM0s=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1 @@
CAco1G1O4Ffd6x8a5CDRpxMK35iO5JM/BkxKeC+Pu0c=

View File

@@ -0,0 +1 @@
GEVjkJgHTvMRr9ka92dafoq1b8fPZ3HZyo2FgBsoqkE=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: leviathan
# PublicKey: GEVjkJgHTvMRr9ka92dafoq1b8fPZ3HZyo2FgBsoqkE=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = GEVjkJgHTvMRr9ka92dafoq1b8fPZ3HZyo2FgBsoqkE=
# AllowedIPs = 10.8.0.24/32
# ================================================
[Interface]
Address = 10.8.0.24/24
PrivateKey = CAco1G1O4Ffd6x8a5CDRpxMK35iO5JM/BkxKeC+Pu0c=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1 @@
CFh3Ksl6IXmnv+oku/o1vJDSnfC0F9LMZS9Zb/hWGk8=

View File

@@ -0,0 +1 @@
FGrMAZPCUiQsFHW55PhkNr9AliR4+sw3uRLTbcilmFc=

View File

@@ -0,0 +1,20 @@
# ================================================
# Node: nano
# PublicKey: FGrMAZPCUiQsFHW55PhkNr9AliR4+sw3uRLTbcilmFc=
#
# Add this peer to Zion (/etc/wireguard/wg0.conf):
# [Peer]
# PublicKey = FGrMAZPCUiQsFHW55PhkNr9AliR4+sw3uRLTbcilmFc=
# AllowedIPs = 10.8.0.26/32
# ================================================
[Interface]
Address = 10.8.0.26/24
PrivateKey = CFh3Ksl6IXmnv+oku/o1vJDSnfC0F9LMZS9Zb/hWGk8=
# Zion (central server)
[Peer]
PublicKey = 2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=
AllowedIPs = 10.8.0.0/24
Endpoint = ugh.im:51820
PersistentKeepalive = 25

View File

@@ -0,0 +1,12 @@
[Interface]
PrivateKey = iKNfR2fY/zI2mc8EDToXGrTR2mPOyMn1MY5/3Wq80HY=
Address = 10.8.0.6/32
#CTH
[Peer]
PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=
AllowedIPs = 10.8.0.10/24
Endpoint = aw2cd67.glddns.com:53535
PersistentKeepalive = 25

View File

@@ -0,0 +1,22 @@
WireGuard Configuration Summary for pix66
===============================================
Node Name: pix66
IP Address: 10.8.0.66/32
Your Public Key: hguz42G5S8EV3NmkORc6eiBWb+V9Z6oBdiXVnAcqvmI=
Your Private Key: iKNfR2fY/zI2mc8EDToXGrTR2mPOyMn1MY5/3Wq80HY=
Configuration File: wireguard_configs/pix66.conf
Commands to use:
- Start: sudo wg-quick up pix66
- Stop: sudo wg-quick down pix66
- Status: sudo wg show
Remember to:
1. Keep your private key secure
2. Share your public key with peers
3. Add peer public keys to your configuration
4. Set proper file permissions: chmod 600 wireguard_configs/pix66.conf

View File

@@ -0,0 +1 @@
yK7AJfrfs6INevH2AZUWpCrltYqe81Pc+mZ/LJsInVc=

View File

@@ -0,0 +1 @@
KHXrXOsLwYr8iDXXB/E0AVVG2EKFQKCGLY8aLniOUl4=

View File

@@ -0,0 +1,11 @@
{
"hostname": "pixel6",
"ip_address": "10.8.0.6",
"private_key": "yK7AJfrfs6INevH2AZUWpCrltYqe81Pc+mZ/LJsInVc=",
"public_key": "KHXrXOsLwYr8iDXXB/E0AVVG2EKFQKCGLY8aLniOUl4=",
"routing_mode": "full_tunnel",
"interface": "pixwg0",
"generated": "2025-08-10T05:59:41-07:00",
"script_version": "3.0.0",
"running_as_root": false
}

859
wireguard_setup.go Normal file
View File

@@ -0,0 +1,859 @@
package main
import (
"bufio"
"crypto/rand"
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"golang.org/x/crypto/curve25519"
)
// Colors for terminal output
const (
Red = "\033[0;31m"
Green = "\033[0;32m"
Yellow = "\033[1;33m"
Blue = "\033[0;34m"
Reset = "\033[0m"
)
// Script constants
const (
ScriptVersion = "3.0.0"
DefaultPort = "51820"
MaxHostname = 63
MaxInterface = 15
)
// WireGuard peer configuration
type WireGuardPeer struct {
Name string
PublicKey string
AllowedIPs string
Endpoint string
PersistentKeepalive int
Description string
}
// Node configuration
type NodeConfig 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"`
Interface string `json:"interface"`
Generated string `json:"generated"`
ScriptVer string `json:"script_version"`
RunningRoot bool `json:"running_as_root"`
}
// Application state
type AppState struct {
ForceMode bool
RunningAsRoot bool
WGDirectory string
StepCounter int
}
// Default peers - automatically included in every config
var defaultPeers = []WireGuardPeer{
{
Name: "Zion",
PublicKey: "2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg=",
AllowedIPs: "10.8.0.0/24",
Endpoint: "ugh.im:51820",
PersistentKeepalive: 25,
Description: "Central server (always included)",
},
{
Name: "CTH",
PublicKey: "NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=",
AllowedIPs: "10.8.0.10/32",
Endpoint: "aw2cd67.glddns.com:53535",
PersistentKeepalive: 25,
Description: "Secondary server (always included)",
},
}
// Reserved IP addresses
var reservedIPs = map[string]string{
"10.8.0.1": "Zion",
"10.8.0.10": "CTH",
"10.8.0.2": "Aza",
"10.8.0.20": "Nyar",
"10.8.0.99": "Galaxy",
"10.8.0.7": "nanocube",
"10.8.0.42": "jupiter",
"10.8.0.8": "HASS",
"10.8.0.40": "framebot",
}
// Static server ports
var staticServerPorts = map[string]string{
"10.8.0.1": "51820", // Zion
"10.8.0.10": "53535", // CTH
"10.8.0.99": "54382", // Galaxy
}
// Validation regexes
var (
ipRegex = regexp.MustCompile(`^10\.8\.0\.\d{1,3}$`)
hostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$`)
interfaceRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*$`)
keyRegex = regexp.MustCompile(`^[A-Za-z0-9+/]{43}=$`)
)
// Utility functions
func step(n int, msg string) {
fmt.Printf("%sStep %d:%s %s\n", Blue, n, Reset, msg)
}
func printStatus(message string) {
fmt.Printf("%s[INFO]%s %s\n", Green, Reset, message)
}
func printWarning(message string) {
fmt.Printf("%s[WARNING]%s %s\n", Yellow, Reset, message)
}
func printError(message string) {
fmt.Printf("%s[ERROR]%s %s\n", Red, Reset, message)
}
func printHeader(title string) {
fmt.Printf("%s================================%s\n", Blue, Reset)
fmt.Printf("%s%s%s\n", Blue, title, Reset)
fmt.Printf("%s================================%s\n", Blue, Reset)
fmt.Println()
}
// Validation functions
func validateHostname(hostname string) error {
if hostname == "" {
return fmt.Errorf("hostname cannot be empty")
}
if len(hostname) > MaxHostname {
return fmt.Errorf("hostname too long (max %d characters)", MaxHostname)
}
if !hostnameRegex.MatchString(hostname) {
return fmt.Errorf("invalid hostname format. Use alphanumeric characters and hyphens only")
}
return nil
}
func validateIP(ip string) error {
if ip == "" {
return fmt.Errorf("IP address cannot be empty")
}
if !ipRegex.MatchString(ip) {
return fmt.Errorf("IP should be in 10.8.0.x range for NextGen network")
}
if parsedIP := net.ParseIP(ip); parsedIP == nil {
return fmt.Errorf("invalid IP address format")
}
if peerName, exists := reservedIPs[ip]; exists {
return fmt.Errorf("IP %s is already reserved for %s", ip, peerName)
}
return nil
}
func validateInterface(name string) error {
if name == "" {
return nil // Allow empty for default
}
if len(name) > MaxInterface {
return fmt.Errorf("interface name too long (max %d characters)", MaxInterface)
}
if !interfaceRegex.MatchString(name) {
return fmt.Errorf("interface name must start with a letter and contain only letters and numbers")
}
return nil
}
func validateWireGuardKey(key string) error {
if key == "" {
return fmt.Errorf("key cannot be empty")
}
if !keyRegex.MatchString(key) {
return fmt.Errorf("invalid WireGuard key format")
}
return nil
}
// User input functions
func getUserInput(prompt string, validator func(string) error, forceMode bool) string {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print(prompt)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if err := validator(input); err != nil {
if forceMode {
printWarning(fmt.Sprintf("Validation failed: %s, but continuing due to force mode", err.Error()))
return input
}
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
}
}
func getDirectoryInput(prompt, defaultDir string, forceMode bool) string {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [%s]: ", prompt, defaultDir)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "" {
input = defaultDir
}
// Check if directory exists
if _, err := os.Stat(input); os.IsNotExist(err) {
if forceMode {
if err := os.MkdirAll(input, 0755); err != nil {
printError(fmt.Sprintf("Failed to create directory '%s': %v", input, err))
continue
}
} else {
fmt.Printf("Directory '%s' doesn't exist. Create it? (Y/n): ", input)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
if response == "n" || response == "no" {
continue
}
if err := os.MkdirAll(input, 0755); err != nil {
printError(fmt.Sprintf("Failed to create directory '%s': %v", input, err))
continue
}
}
}
// Check if directory is writable
if info, err := os.Stat(input); err == nil {
if info.Mode()&0200 == 0 {
printError(fmt.Sprintf("Directory '%s' is not writable", input))
continue
}
}
return input
}
}
func getYesNoInput(prompt string, defaultYes bool, forceMode bool) bool {
if forceMode {
return defaultYes
}
reader := bufio.NewReader(os.Stdin)
for {
if defaultYes {
fmt.Printf("%s (Y/n): ", prompt)
} else {
fmt.Printf("%s (y/N): ", prompt)
}
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(strings.ToLower(input))
if input == "" {
return defaultYes
}
if input == "y" || input == "yes" {
return true
}
if input == "n" || input == "no" {
return false
}
printError("Please enter 'y' or 'n'")
}
}
func getChoice(prompt string, choices []string, forceMode bool) int {
if forceMode && len(choices) > 0 {
return 1 // Default to first choice in force mode
}
reader := bufio.NewReader(os.Stdin)
for {
fmt.Println(prompt)
for i, choice := range choices {
fmt.Printf("%d. %s\n", i+1, choice)
}
fmt.Print("Enter your choice: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if choice, err := strconv.Atoi(input); err == nil && choice >= 1 && choice <= len(choices) {
return choice
}
printError(fmt.Sprintf("Invalid choice. Please enter a number between 1 and %d.", len(choices)))
}
}
// WireGuard key generation (safe version)
func generateWireGuardKeys() (string, string, error) {
// Generate private key
privateKeyBytes := make([]byte, 32)
if _, err := rand.Read(privateKeyBytes); err != nil {
return "", "", fmt.Errorf("failed to generate random bytes: %w", err)
}
// Ensure the key is valid for curve25519
privateKeyBytes[0] &= 248
privateKeyBytes[31] &= 127
privateKeyBytes[31] |= 64
// Generate public key (safe conversion)
var privateKeyArray [32]byte
copy(privateKeyArray[:], privateKeyBytes)
var publicKeyBytes [32]byte
curve25519.ScalarBaseMult(&publicKeyBytes, &privateKeyArray)
privateKey := base64.StdEncoding.EncodeToString(privateKeyBytes[:])
publicKey := base64.StdEncoding.EncodeToString(publicKeyBytes[:])
return privateKey, publicKey, nil
}
// Configuration generation
func generateConfig(hostname, ipAddress, privateKey, routingMode, interfaceName 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 := staticServerPorts[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")
}
// Add default peers
for _, peer := range defaultPeers {
config.WriteString(fmt.Sprintf("\n# %s (%s)\n", peer.Name, peer.Description))
config.WriteString("[Peer]\n")
config.WriteString(fmt.Sprintf("PublicKey = %s\n", peer.PublicKey))
// Set AllowedIPs based on routing mode
if routingMode == "full_tunnel" {
config.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
} else {
config.WriteString(fmt.Sprintf("AllowedIPs = %s\n", peer.AllowedIPs))
}
config.WriteString(fmt.Sprintf("Endpoint = %s\n", peer.Endpoint))
config.WriteString(fmt.Sprintf("PersistentKeepalive = %d\n", peer.PersistentKeepalive))
}
return config.String()
}
// Generate peer configuration for Zion
func generateZionPeerConfig(hostname, publicKey, ipAddress string) string {
var config strings.Builder
config.WriteString(fmt.Sprintf("# %s\n", hostname))
config.WriteString("[Peer]\n")
config.WriteString(fmt.Sprintf("PublicKey = %s\n", publicKey))
config.WriteString(fmt.Sprintf("AllowedIPs = %s/32\n", ipAddress))
return config.String()
}
// System checks
func checkDependencies() error {
deps := []string{"wg", "wg-quick"}
for _, dep := range deps {
if _, err := exec.LookPath(dep); err != nil {
return fmt.Errorf("missing dependency: %s", dep)
}
}
return nil
}
func isRunningAsRoot() bool {
return os.Geteuid() == 0
}
func checkFileExists(filepath string, forceMode bool) bool {
if _, err := os.Stat(filepath); err == nil {
if forceMode {
printWarning(fmt.Sprintf("File '%s' already exists, overwriting due to force mode", filepath))
return true
}
printWarning(fmt.Sprintf("File '%s' already exists", filepath))
return getYesNoInput("Do you want to overwrite it?", false, false)
}
return true
}
// Display instructions
func displayZionInstructions(hostname, publicKey, ipAddress string) {
printHeader("IMPORTANT: Update Zion Server Configuration")
fmt.Println()
printWarning("You MUST add this peer to Zion's configuration file:")
fmt.Println(" /etc/wireguard/wg0.conf")
fmt.Println()
fmt.Println("Add the following peer section to Zion's config:")
fmt.Println("----------------------------------------")
fmt.Println(generateZionPeerConfig(hostname, publicKey, ipAddress))
fmt.Println("----------------------------------------")
fmt.Println()
printWarning("After updating Zion's config:")
fmt.Println("1. Save the file")
fmt.Println("2. Restart Zion's WireGuard: sudo systemctl restart wg-quick@wg0")
fmt.Println("3. Then start this node's WireGuard: sudo wg-quick up " + hostname)
fmt.Println()
// Show Zion's current configuration structure
fmt.Println("Zion's current configuration structure:")
fmt.Println("----------------------------------------")
fmt.Println("[Interface]")
fmt.Println("Address = 10.8.0.1/24")
fmt.Println("ListenPort = 51820")
fmt.Println("PrivateKey = <zion_private_key>")
fmt.Println("PostUp = <iptables_rules>")
fmt.Println("PostDown = <iptables_rules>")
fmt.Println()
// Show existing peers
for name, pubKey := range map[string]string{
"Cth": "NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0=",
"Aza": "qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc=",
"Nyar": "2BA7L1oJP1tK6dIUNHMgcZmOmYmlyPRe2RaBqfUsEWo=",
"Galaxy": "QBNt00VSedxPlq3ZvsdYaqIcbudCAyxv9TG65aPVZzM=",
"nanocube": "/ZImoATDIS0e0N08CD7mqWbhtGlSnynpPuY04Ed4Zyc=",
"jupiter": "YIFQ43ULk/YoCgOv3SBU6+MOrbxd+mlvaw9rT8uoNmw=",
"HASS": "C+Poz/7DaXCxe4HZiL6D5cld4jMt5o1gBq3iPiBzrg0=",
"framebot": "loS3yZapqmt6lP53Q+s4EvUzw6FmwgZC8jzgLluJ1Es=",
} {
fmt.Printf("#%s\n", name)
fmt.Println("[Peer]")
fmt.Printf("PublicKey = %s\n", pubKey)
fmt.Printf("AllowedIPs = 10.8.0.x/32\n")
fmt.Println()
}
fmt.Println("# Add your peer here:")
fmt.Printf("# %s\n", hostname)
fmt.Println("# [Peer]")
fmt.Printf("# PublicKey = %s\n", publicKey)
fmt.Printf("# AllowedIPs = %s/32\n", ipAddress)
fmt.Println("----------------------------------------")
fmt.Println()
}
func displayFullTunnelInstructions() {
fmt.Println()
printWarning("FULL TUNNEL MODE DETECTED - Endpoint Changes Required:")
fmt.Println()
fmt.Println("Since this node will route ALL traffic through the VPN, you need to:")
fmt.Println()
fmt.Println("1. Update Zion's config (/etc/wireguard/wg0.conf) to allow this peer:")
fmt.Println(" - Add the peer section as shown above")
fmt.Println(" - Ensure Zion has proper iptables rules for NAT/masquerading")
fmt.Println()
fmt.Println("2. Check Zion's iptables rules (run on Zion server):")
fmt.Println(" sudo iptables -t nat -L POSTROUTING")
fmt.Println(" sudo iptables -L FORWARD")
fmt.Println()
fmt.Println("3. If Zion doesn't have proper NAT rules, add them:")
fmt.Println(" sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE")
fmt.Println(" sudo iptables -A FORWARD -i wg0 -j ACCEPT")
fmt.Println(" sudo iptables -A FORWARD -o wg0 -j ACCEPT")
fmt.Println()
fmt.Println("4. Enable IP forwarding on Zion (if not already enabled):")
fmt.Println(" echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf")
fmt.Println(" sudo sysctl -p")
fmt.Println()
}
func displayNextSteps(hostname, interfaceName, publicKey, ipAddress, routingMode string, runningAsRoot bool) {
step(4, "Next Steps")
fmt.Println()
if runningAsRoot {
printStatus("Ready to start WireGuard:")
fmt.Printf(" systemctl enable --now wg-quick@%s\n", interfaceName)
} else {
printWarning("To enable WireGuard (requires root):")
fmt.Printf(" sudo cp %s /etc/wireguard/\n", filepath.Join("wireguard_configs", fmt.Sprintf("%s.conf", interfaceName)))
fmt.Printf(" sudo chmod 600 /etc/wireguard/%s.conf\n", interfaceName)
fmt.Printf(" sudo systemctl enable --now wg-quick@%s\n", interfaceName)
}
fmt.Println()
printWarning("IMPORTANT: Update other nodes with this peer info:")
fmt.Printf(" PublicKey = %s\n", publicKey)
fmt.Printf(" AllowedIPs = %s/32\n", ipAddress)
fmt.Println()
if routingMode == "full_tunnel" {
displayFullTunnelInstructions()
}
// Zion update instructions
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Printf("%s !!! NOW UPDATE ZION SERVER !!! %s\n", Red, Reset)
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Println()
printWarning("You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):")
fmt.Println()
fmt.Printf("%s#%s%s\n", Yellow, hostname, Reset)
fmt.Printf("%s[Peer]%s\n", Yellow, Reset)
fmt.Printf("%sPublicKey = %s%s\n", Yellow, publicKey, Reset)
fmt.Printf("%sAllowedIPs = %s/32%s\n", Yellow, ipAddress, Reset)
fmt.Println()
printWarning("After updating Zion, restart its WireGuard:")
fmt.Println(" systemctl restart wg-quick@wg0")
fmt.Println()
if runningAsRoot {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" systemctl restart wg-quick@%s\n", interfaceName)
} else {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" sudo systemctl restart wg-quick@%s\n", interfaceName)
}
}
// Main function
func main() {
// Parse command line flags
var forceMode bool
flag.BoolVar(&forceMode, "force", false, "Skip confirmations and use defaults")
flag.Parse()
// Initialize application state
state := &AppState{
ForceMode: forceMode,
RunningAsRoot: isRunningAsRoot(),
StepCounter: 1,
}
printHeader("WireGuard Configuration Setup v" + ScriptVersion)
fmt.Println("This script will help you create WireGuard keys and configuration files.")
fmt.Println("Based on the CURRENT_WORKING configuration with Zion as central server.")
fmt.Println("Default peers (Zion & CTH) will be automatically included.")
fmt.Println()
if forceMode {
printWarning("Force mode enabled - skipping confirmations and using defaults")
fmt.Println()
}
// Determine WireGuard directory
if state.RunningAsRoot {
state.WGDirectory = "/etc/wireguard"
printStatus("Running as root - using system directories")
printStatus(fmt.Sprintf("WireGuard directory: %s", state.WGDirectory))
} else {
state.WGDirectory = "wireguard_configs"
printWarning("Not running as root - using current directory")
printStatus(fmt.Sprintf("WireGuard directory: %s", state.WGDirectory))
printWarning("You'll need to manually copy config files to /etc/wireguard/ later")
}
fmt.Println()
// Check dependencies
if err := checkDependencies(); err != nil {
printError(err.Error())
printStatus("Install with: apt install wireguard-tools")
os.Exit(1)
}
// Get directory for non-root users
if !state.RunningAsRoot {
step(state.StepCounter, "Directory Selection")
state.StepCounter++
printStatus("Choose where to save WireGuard files:")
fmt.Printf(" - Current directory: %s\n", state.WGDirectory)
fmt.Printf(" - Home directory: %s\n", os.Getenv("HOME"))
fmt.Println(" - Custom directory")
fmt.Println()
state.WGDirectory = getDirectoryInput("Enter directory path for WireGuard files", state.WGDirectory, forceMode)
fmt.Println()
}
// Create directory
if err := os.MkdirAll(state.WGDirectory, 0755); err != nil {
printError(fmt.Sprintf("Failed to create directory: %v", err))
os.Exit(1)
}
// Get node information
step(state.StepCounter, "Node Information")
state.StepCounter++
fmt.Println()
hostname := getUserInput("Enter hostname for this node: ", validateHostname, forceMode)
// Get IP address
fmt.Println()
fmt.Println("Available IP ranges:")
fmt.Println(" - 10.8.0.x (recommended for NextGen network)")
fmt.Println(" - 10.0.0.x (alternative range)")
fmt.Println()
ipAddress := getUserInput("Enter IP address for this node (e.g., 10.8.0.30): ", validateIP, forceMode)
// Get interface name
fmt.Println()
fmt.Println("Interface name options:")
fmt.Println(" - wg0 (default, most common)")
fmt.Println(" - wg1, wg2, etc. (if wg0 is already in use)")
fmt.Println(" - Custom name (e.g., nextgen, vpn, etc.)")
fmt.Println()
interfaceName := getUserInput("Enter interface name (default: wg0): ", validateInterface, forceMode)
if interfaceName == "" {
interfaceName = "wg0"
}
// Check if configuration file already exists
configFile := filepath.Join(state.WGDirectory, fmt.Sprintf("%s.conf", interfaceName))
if !checkFileExists(configFile, forceMode) {
printError("Operation cancelled. Please choose a different interface name or remove the existing file.")
os.Exit(1)
}
// Configuration options
fmt.Println()
step(state.StepCounter, "Configuration Options")
state.StepCounter++
fmt.Println()
fmt.Println("Choose an option:")
fmt.Println("1. Generate keys only (manual config creation)")
fmt.Println("2. Generate keys + complete config (recommended)")
fmt.Println()
var configChoice int
if forceMode {
configChoice = 2 // Default to complete config in force mode
printStatus("Force mode: Using complete config generation")
} else {
configChoice = getChoice("Enter your choice:", []string{"Generate keys only", "Generate keys + complete config"}, false)
}
// Traffic routing options
var routingMode string
if configChoice == 2 {
fmt.Println()
fmt.Println("Traffic routing options:")
fmt.Println("1. WireGuard traffic only (10.8.0.x network only)")
fmt.Println("2. All traffic through VPN (full tunnel)")
fmt.Println()
fmt.Println("Note: Full tunnel routes ALL internet traffic through the VPN.")
fmt.Println(" WireGuard-only keeps your regular internet traffic separate.")
fmt.Println()
var routingChoice int
if forceMode {
routingChoice = 1 // Default to WireGuard-only in force mode
printStatus("Force mode: Using WireGuard-only routing")
} else {
routingChoice = getChoice("Enter your choice:", []string{"WireGuard traffic only", "All traffic through VPN"}, false)
}
if routingChoice == 1 {
routingMode = "wg_only"
printStatus("Selected: WireGuard traffic only")
} else {
routingMode = "full_tunnel"
printStatus("Selected: All traffic through VPN")
}
}
printStatus(fmt.Sprintf("Starting setup for %s (%s)...", hostname, ipAddress))
fmt.Println()
// Generate keys
printStatus("Generating WireGuard keys...")
privateKey, publicKey, err := generateWireGuardKeys()
if err != nil {
printError(fmt.Sprintf("Failed to generate keys: %v", err))
os.Exit(1)
}
// Save keys
privateKeyFile := filepath.Join(state.WGDirectory, fmt.Sprintf("%s_private.key", hostname))
publicKeyFile := filepath.Join(state.WGDirectory, fmt.Sprintf("%s_public.key", hostname))
if err := os.WriteFile(privateKeyFile, []byte(privateKey), 0600); err != nil {
printError(fmt.Sprintf("Failed to save private key: %v", err))
os.Exit(1)
}
if err := os.WriteFile(publicKeyFile, []byte(publicKey), 0600); err != nil {
printError(fmt.Sprintf("Failed to save public key: %v", err))
os.Exit(1)
}
printStatus("Keys generated successfully!")
fmt.Printf(" Private key: %s\n", privateKeyFile)
fmt.Printf(" Public key: %s\n", publicKeyFile)
fmt.Println()
// Display node information
step(state.StepCounter, "Node Information")
state.StepCounter++
fmt.Println("==========================================")
fmt.Printf("HOSTNAME: %s\n", hostname)
fmt.Printf("IP ADDRESS: %s\n", ipAddress)
fmt.Printf("PRIVATE KEY: %s\n", privateKey)
fmt.Printf("PUBLIC KEY: %s\n", publicKey)
fmt.Printf("INTERFACE: %s\n", interfaceName)
fmt.Printf("ROUTING MODE: %s\n", routingMode)
fmt.Println("==========================================")
fmt.Println()
// Save structured info
infoFile := filepath.Join(state.WGDirectory, fmt.Sprintf("%s_wg_info.json", hostname))
if state.RunningAsRoot {
infoFile = filepath.Join("/tmp", fmt.Sprintf("%s_wg_info.json", hostname))
}
nodeConfig := NodeConfig{
Hostname: hostname,
IPAddress: ipAddress,
PrivateKey: privateKey,
PublicKey: publicKey,
RoutingMode: routingMode,
Interface: interfaceName,
Generated: time.Now().Format(time.RFC3339),
ScriptVer: ScriptVersion,
RunningRoot: state.RunningAsRoot,
}
infoData, err := json.MarshalIndent(nodeConfig, "", " ")
if err != nil {
printError(fmt.Sprintf("Failed to marshal config: %v", err))
os.Exit(1)
}
if err := os.WriteFile(infoFile, infoData, 0600); err != nil {
printError(fmt.Sprintf("Failed to save info file: %v", err))
os.Exit(1)
}
printStatus(fmt.Sprintf("Information saved to: %s", infoFile))
fmt.Println()
// Generate complete config if requested
if configChoice == 2 {
printStatus(fmt.Sprintf("Generating complete %s.conf...", interfaceName))
configContent := generateConfig(hostname, ipAddress, privateKey, routingMode, interfaceName)
if err := os.WriteFile(configFile, []byte(configContent), 0600); err != nil {
printError(fmt.Sprintf("Failed to write config file: %v", err))
os.Exit(1)
}
printStatus(fmt.Sprintf("Config written to: %s", configFile))
if state.RunningAsRoot {
printStatus("Permissions set to 600")
}
fmt.Println()
// Display Zion integration instructions
displayZionInstructions(hostname, publicKey, ipAddress)
// Display next steps
displayNextSteps(hostname, interfaceName, publicKey, ipAddress, routingMode, state.RunningAsRoot)
// Config preview
fmt.Printf("%sConfig Preview:%s\n", Blue, Reset)
fmt.Println("----------------------------------------")
lines := strings.Split(configContent, "\n")
for i, line := range lines {
if i >= 8 { // Show more lines to include default peers
break
}
fmt.Println(line)
}
fmt.Printf(" [... and %d total lines]\n", len(lines))
fmt.Println("----------------------------------------")
fmt.Println()
} else {
// Manual config generation path
step(state.StepCounter, "Next Steps")
state.StepCounter++
fmt.Println()
printStatus("Keys generated successfully!")
fmt.Printf(" Private key: %s\n", privateKeyFile)
fmt.Printf(" Public key: %s\n", publicKeyFile)
fmt.Println()
printWarning("Next steps:")
fmt.Printf(" - Create %s.conf manually using the keys above\n", interfaceName)
fmt.Printf(" - Copy config to %s\n", filepath.Join(state.WGDirectory, fmt.Sprintf("%s.conf", interfaceName)))
if state.RunningAsRoot {
fmt.Printf(" - Set permissions: chmod 600 %s\n", filepath.Join(state.WGDirectory, fmt.Sprintf("%s.conf", interfaceName)))
fmt.Printf(" - Enable/start: systemctl enable --now wg-quick@%s\n", interfaceName)
} else {
fmt.Printf(" - Copy to system: sudo cp %s /etc/wireguard/\n", filepath.Join(state.WGDirectory, fmt.Sprintf("%s.conf", interfaceName)))
fmt.Printf(" - Set permissions: sudo chmod 600 /etc/wireguard/%s.conf\n", interfaceName)
fmt.Printf(" - Enable/start: sudo systemctl enable --now wg-quick@%s\n", interfaceName)
}
fmt.Println()
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Printf("%s !!! NOW UPDATE ZION SERVER !!! %s\n", Red, Reset)
fmt.Printf("%s========================================%s\n", Red, Reset)
fmt.Println()
printWarning("You MUST add this peer to Zion's config (/etc/wireguard/wg0.conf):")
fmt.Println()
fmt.Printf("%s#%s%s\n", Yellow, hostname, Reset)
fmt.Printf("%s[Peer]%s\n", Yellow, Reset)
fmt.Printf("%sPublicKey = %s%s\n", Yellow, publicKey, Reset)
fmt.Printf("%sAllowedIPs = %s/32%s\n", Yellow, ipAddress, Reset)
fmt.Println()
printWarning("After updating Zion, restart its WireGuard:")
fmt.Println(" systemctl restart wg-quick@wg0")
fmt.Println()
if state.RunningAsRoot {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" systemctl restart wg-quick@%s\n", interfaceName)
} else {
printWarning("Then restart this node's WireGuard:")
fmt.Printf(" sudo systemctl restart wg-quick@%s\n", interfaceName)
}
}
fmt.Println()
printStatus(fmt.Sprintf("Setup complete for %s!", hostname))
fmt.Println()
}

518
wireguard_setup.sh Executable file
View File

@@ -0,0 +1,518 @@
#!/usr/bin/env bash
# WireGuard Setup Script
# This script guides users through creating WireGuard keys and configuration files
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration file for Zion settings
ZION_CONFIG_FILE="$(dirname "$0")/CURRENT_WORKING/zion.conf"
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${BLUE}================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}================================${NC}"
}
# Function to check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to validate IP address with subnet
validate_ip() {
local ip=$1
# Check if input contains subnet
if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
return 1
fi
# Extract IP and subnet parts
local ip_part="${ip%/*}"
local subnet="${ip#*/}"
# Validate subnet
if [[ ! $subnet =~ ^[0-9]+$ ]] || [ "$subnet" -lt 1 ] || [ "$subnet" -gt 32 ]; then
return 1
fi
# Validate each octet
IFS='.' read -ra OCTETS <<< "$ip_part"
if [ ${#OCTETS[@]} -ne 4 ]; then
return 1
fi
for octet in "${OCTETS[@]}"; do
if [[ ! $octet =~ ^[0-9]+$ ]] || [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
return 1
fi
done
return 0
}
# Function to validate port number
validate_port() {
local port=$1
if [[ ! $port =~ ^[0-9]+$ ]]; then
return 1
fi
if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
return 1
fi
# Check for common reserved ports
if [ "$port" -le 1024 ]; then
print_warning "Port $port is in privileged range (1-1024). You may need root access."
fi
return 0
}
# Function to validate WireGuard public key
validate_public_key() {
local key=$1
# WireGuard keys are base64 encoded and exactly 44 characters long
if [[ ! $key =~ ^[A-Za-z0-9+/]{43}=$ ]]; then
return 1
fi
return 0
}
# Function to detect network interface
detect_network_interface() {
# Try to detect the main network interface
local interface
# Check for common interface names
for name in eth0 ens33 ens160 enp0s3 eno1; do
if ip link show "$name" >/dev/null 2>&1; then
interface="$name"
break
fi
done
# Fallback to first non-loopback interface
if [ -z "$interface" ]; then
interface=$(ip route | grep default | awk '{print $5}' | head -1)
fi
echo "$interface"
}
# Function to load Zion configuration
load_zion_config() {
if [[ -f "$ZION_CONFIG_FILE" ]]; then
print_status "Found Zion configuration file: $ZION_CONFIG_FILE"
return 0
else
print_warning "Zion configuration file not found: $ZION_CONFIG_FILE"
print_warning "Using hardcoded Zion configuration"
return 1
fi
}
# Function to create safe filename
safe_filename() {
local name="$1"
# Replace invalid characters with underscores
echo "$name" | sed 's/[^a-zA-Z0-9._-]/_/g'
}
# Main setup function
main() {
print_header "WireGuard Configuration Setup"
echo "This script will help you create WireGuard keys and configuration files."
echo "Based on the CURRENT_WORKING configuration with Zion as central server."
echo ""
# Check if wg command exists
if ! command_exists wg; then
print_error "WireGuard tools not found. Please install WireGuard first:"
echo " Ubuntu/Debian: sudo apt install wireguard"
echo " CentOS/RHEL: sudo yum install wireguard-tools"
echo " Arch: sudo pacman -S wireguard-tools"
echo " Fedora: sudo dnf install wireguard-tools"
exit 1
fi
# Create output directory
OUTPUT_DIR="wireguard_configs"
mkdir -p "$OUTPUT_DIR"
print_status "Created output directory: $OUTPUT_DIR"
# Step 1: Generate keys
print_header "Step 1: Generate WireGuard Keys"
echo "Generating private and public keys..."
PRIVATE_KEY=$(wg genkey)
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | wg pubkey)
print_status "Keys generated successfully!"
echo "Private Key: $PRIVATE_KEY"
echo "Public Key: $PUBLIC_KEY"
echo ""
# Step 2: Get configuration details
print_header "Step 2: Configuration Details"
# Get node name
local node_name
while true; do
read -p "Enter node name (e.g., aza, cth, galaxy): " node_name
node_name=$(safe_filename "$node_name")
if [ -n "$node_name" ]; then
break
else
print_error "Node name cannot be empty"
fi
done
# Check if configuration file already exists
CONFIG_FILE="$OUTPUT_DIR/${node_name}.conf"
if [ -f "$CONFIG_FILE" ]; then
print_warning "Configuration file '$CONFIG_FILE' already exists."
read -p "Do you want to overwrite it? (y/n): " OVERWRITE_FILE
if [[ ! $OVERWRITE_FILE =~ ^[Yy]$ ]]; then
print_error "Operation cancelled. Please choose a different node name or remove the existing file."
exit 1
fi
print_status "Will overwrite existing configuration file."
fi
# Get IP address
local ip_address
while true; do
read -p "Enter IP address with subnet (e.g., 10.8.0.2/24): " ip_address
if validate_ip "$ip_address"; then
break
else
print_error "Invalid IP address format. Use format: x.x.x.x/y"
fi
done
# Ask if this is a server (listening) or client
read -p "Is this a server that will listen for connections? (y/n): " IS_SERVER
local listen_port
if [[ $IS_SERVER =~ ^[Yy]$ ]]; then
while true; do
read -p "Enter listen port (1-65535): " listen_port
if validate_port "$listen_port"; then
break
else
print_error "Invalid port number. Must be between 1-65535"
fi
done
fi
# Step 3: Create configuration file
print_header "Step 3: Create Configuration File"
# Create the configuration file
cat > "$CONFIG_FILE" << EOF
[Interface]
PrivateKey = $PRIVATE_KEY
Address = $ip_address
EOF
# Add listen port if it's a server
if [[ $IS_SERVER =~ ^[Yy]$ ]]; then
echo "ListenPort = $listen_port" >> "$CONFIG_FILE"
fi
# Add PostUp/PostDown rules for server (Zion-like configuration)
if [[ $IS_SERVER =~ ^[Yy]$ ]]; then
local network_interface
network_interface=$(detect_network_interface)
if [ -n "$network_interface" ]; then
print_status "Detected network interface: $network_interface"
else
print_warning "Could not detect network interface, using 'eth0'"
network_interface="eth0"
fi
echo "" >> "$CONFIG_FILE"
echo "# Server configuration - enable IP forwarding" >> "$CONFIG_FILE"
echo "PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o $network_interface -j MASQUERADE; ip route add 10.8.0.0/24 dev wg0 2>/dev/null || true" >> "$CONFIG_FILE"
echo "PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o $network_interface -j MASQUERADE; ip route del 10.8.0.0/24 dev wg0 2>/dev/null || true" >> "$CONFIG_FILE"
fi
print_status "Configuration file created: $CONFIG_FILE"
# Step 4: Add peers
print_header "Step 4: Add Peers"
# Zion default peer information (from CURRENT_WORKING/zion.conf)
local ZION_PUBLIC_KEY="2ztJbrN1x1NWanzPGLiKL19ZkdOhm5Y7WeKEWBT5cyg="
local ZION_ENDPOINT="ugh.im:51820"
local ZION_ALLOWED_IPS="10.8.0.0/24"
# Try to load Zion config, fall back to hardcoded if not available
if ! load_zion_config; then
print_warning "Using hardcoded Zion configuration"
fi
# Ask if user wants to add Zion as default peer (unless this IS Zion)
if [[ "$node_name" != "zion" ]]; then
echo "Zion is the central server for this network."
read -p "Do you want to add Zion as a peer? (y/n, default: y): " ADD_ZION
ADD_ZION=${ADD_ZION:-y}
if [[ $ADD_ZION =~ ^[Yy]$ ]]; then
echo ""
echo "Adding Zion as peer with default settings:"
echo " Public Key: $ZION_PUBLIC_KEY"
echo " Endpoint: $ZION_ENDPOINT"
echo " Allowed IPs: $ZION_ALLOWED_IPS"
echo " Persistent Keepalive: 25"
echo ""
# Add Zion peer to configuration
echo "" >> "$CONFIG_FILE"
echo "# Zion (central server)" >> "$CONFIG_FILE"
echo "[Peer]" >> "$CONFIG_FILE"
echo "PublicKey = $ZION_PUBLIC_KEY" >> "$CONFIG_FILE"
echo "AllowedIPs = $ZION_ALLOWED_IPS" >> "$CONFIG_FILE"
echo "Endpoint = $ZION_ENDPOINT" >> "$CONFIG_FILE"
echo "PersistentKeepalive = 25" >> "$CONFIG_FILE"
print_status "Added Zion as default peer"
fi
fi
# Add additional peers
while true; do
read -p "Do you want to add additional peers? (y/n): " ADD_PEER
if [[ ! $ADD_PEER =~ ^[Yy]$ ]]; then
break
fi
echo ""
local peer_name
read -p "Enter peer name (e.g., aza, cth, galaxy): " peer_name
peer_name=$(safe_filename "$peer_name")
local peer_public_key
while true; do
read -p "Enter peer public key: " peer_public_key
if validate_public_key "$peer_public_key"; then
break
else
print_error "Invalid WireGuard public key format"
fi
done
local peer_allowed_ips
while true; do
read -p "Enter allowed IPs for this peer (e.g., 10.8.0.1/32 or 10.8.0.0/24): " peer_allowed_ips
if validate_ip "$peer_allowed_ips"; then
break
else
print_error "Invalid IP address format. Use format: x.x.x.x/y"
fi
done
# Ask if this peer has an endpoint (for clients connecting to servers)
read -p "Does this peer have an endpoint? (y/n): " HAS_ENDPOINT
if [[ $HAS_ENDPOINT =~ ^[Yy]$ ]]; then
read -p "Enter endpoint (host:port): " PEER_ENDPOINT
local peer_keepalive
read -p "Enter persistent keepalive (seconds, default 25): " peer_keepalive
peer_keepalive=${peer_keepalive:-25}
fi
# Add peer to configuration
echo "" >> "$CONFIG_FILE"
echo "# $peer_name" >> "$CONFIG_FILE"
echo "[Peer]" >> "$CONFIG_FILE"
echo "PublicKey = $peer_public_key" >> "$CONFIG_FILE"
echo "AllowedIPs = $peer_allowed_ips" >> "$CONFIG_FILE"
if [[ $HAS_ENDPOINT =~ ^[Yy]$ ]]; then
echo "Endpoint = $PEER_ENDPOINT" >> "$CONFIG_FILE"
echo "PersistentKeepalive = $peer_keepalive" >> "$CONFIG_FILE"
fi
print_status "Added peer: $peer_name"
done
# Set proper file permissions
chmod 600 "$CONFIG_FILE"
print_status "Set file permissions to 600 (owner read/write only)"
# Step 5: Display results
print_header "Step 5: Setup Complete"
print_status "Configuration file created: $CONFIG_FILE"
print_status "Your public key: $PUBLIC_KEY"
echo ""
# Zion update instructions
print_header "IMPORTANT: Update Zion Server Configuration"
echo ""
print_warning "You MUST add this peer to Zion's configuration file:"
echo " /etc/wireguard/wg0.conf"
echo ""
echo "Add the following peer section to Zion's config:"
echo "----------------------------------------"
echo "# $node_name"
echo "[Peer]"
echo "PublicKey = $PUBLIC_KEY"
echo "AllowedIPs = ${ip_address%/*}/32"
echo "----------------------------------------"
echo ""
# Show Zion's current configuration structure
if ! load_zion_config; then
echo "Zion's current configuration structure:"
echo "----------------------------------------"
echo "[Interface]"
echo "Address = 10.8.0.1/24"
echo "ListenPort = 51820"
echo "PrivateKey = <zion_private_key>"
echo "PostUp = <iptables_rules>"
echo "PostDown = <iptables_rules>"
echo ""
echo "#Cth"
echo "[Peer]"
echo "PublicKey = NBktXKy1s0n2lIlIMODvOqKNwAtYdoZH5feKt5P43i0="
echo "AllowedIPs = 10.8.0.10/32"
echo ""
echo "#Aza"
echo "[Peer]"
echo "PublicKey = qmTKA257DLOrfhk5Zw8RyRmBSonmm6epbloT0P0ZWDc="
echo "AllowedIPs = 10.8.0.2/32"
echo ""
echo "# Add your peer section here:"
echo "# $node_name"
echo "# [Peer]"
echo "# PublicKey = $PUBLIC_KEY"
echo "# AllowedIPs = ${ip_address%/*}/32"
echo "----------------------------------------"
fi
echo ""
print_warning "After updating Zion's config:"
echo "1. Save the file"
echo "2. Restart Zion's WireGuard: sudo systemctl restart wg-quick@wg0"
echo "3. Then start this node's WireGuard: sudo wg-quick up ${node_name}"
echo ""
# Show the configuration file
echo "Your configuration file contents:"
echo "----------------------------------------"
cat "$CONFIG_FILE"
echo "----------------------------------------"
# Create a summary file
SUMMARY_FILE="$OUTPUT_DIR/${node_name}_summary.txt"
if [ -f "$SUMMARY_FILE" ]; then
print_warning "Summary file '$SUMMARY_FILE' already exists and will be overwritten."
fi
cat > "$SUMMARY_FILE" << EOF
WireGuard Configuration Summary for $node_name
===============================================
Node Name: $node_name
IP Address: $ip_address
$(if [[ $IS_SERVER =~ ^[Yy]$ ]]; then echo "Listen Port: $listen_port"; fi)
Your Public Key: $PUBLIC_KEY
Your Private Key: $PRIVATE_KEY
Configuration File: $CONFIG_FILE
Commands to use:
- Start: sudo wg-quick up ${node_name}
- Stop: sudo wg-quick down ${node_name}
- Status: sudo wg show
ZION SERVER UPDATE REQUIRED:
===========================
You MUST add this peer to Zion's configuration file (/etc/wireguard/wg0.conf):
# $node_name
[Peer]
PublicKey = $PUBLIC_KEY
AllowedIPs = ${ip_address%/*}/32
After updating Zion's config:
1. Save the file
2. Restart Zion's WireGuard: sudo systemctl restart wg-quick@wg0
3. Then start this node's WireGuard: sudo wg-quick up ${node_name}
Remember to:
1. Keep your private key secure
2. Update Zion's configuration with your public key
3. Set proper file permissions: chmod 600 $CONFIG_FILE
4. Restart both Zion and this node after configuration changes
EOF
print_status "Summary saved to: $SUMMARY_FILE"
}
# Function to show usage
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -d, --dir DIR Set output directory (default: wireguard_configs)"
echo " -c, --config Use custom Zion config file"
echo ""
echo "This script will interactively guide you through creating WireGuard"
echo "keys and configuration files."
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-d|--dir)
OUTPUT_DIR="$2"
shift 2
;;
-c|--config)
ZION_CONFIG_FILE="$2"
shift 2
;;
*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Run main function
main "$@"