chore: initial commit of wgtool
This commit is contained in:
695
scripting/wgez-setup.go
Normal file
695
scripting/wgez-setup.go
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user