696 lines
21 KiB
Go
696 lines
21 KiB
Go
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()
|
|
}
|