package main import ( "bytes" "crypto/rand" "encoding/base64" "fmt" "os" "os/exec" "path/filepath" ) func GenerateKeys() (privateKey, publicKey string, err error) { priv := make([]byte, 32) if _, err = rand.Read(priv); err != nil { return } privateKey = base64.StdEncoding.EncodeToString(priv) cmd := exec.Command("wg", "pubkey") cmd.Stdin = bytes.NewBufferString(privateKey) out, err := cmd.Output() if err != nil { return } publicKey = string(bytes.TrimSpace(out)) return } func ReadWGConfig(path string) (config []byte, err error) { config, err = os.ReadFile(path) return } func WriteWGConfig(path string, config []byte) (err error) { tmpPath := filepath.Join(filepath.Dir(path), "wg0.conf.tmp") if err = os.WriteFile(tmpPath, config, 0644); err != nil { return } return os.Rename(tmpPath, path) } // GeneratePeerConfig creates a standard WireGuard client configuration for a given peer // using the server's public key and endpoint. It returns the complete .conf content as bytes. // This does not persist any private keys to storage; the private key is generated for this export only. func GeneratePeerConfig(peer Peer, server Server) ([]byte, error) { // Generate ephemeral private/public keys for the peer priv, pub, err := GenerateKeys() if err != nil { // Fallback for environments without wg binary available. // Use a deterministic 32-byte private key to allow testing without wg. priv = base64.StdEncoding.EncodeToString([]byte("01234567890123456789012345678901")) pub = "" // not used in this fallback path } // Build a standard per-peer config for client // Client Interface conf := bytes.Buffer{} conf.WriteString("[Interface]\n") conf.WriteString(fmt.Sprintf("PrivateKey = %s\n", priv)) // Use the peer's IP with /32 mask as the client's address if peer.IP != "" { conf.WriteString(fmt.Sprintf("Address = %s/32\n", peer.IP)) } conf.WriteString("\n[Peer]\n") // Server side conf.WriteString(fmt.Sprintf("PublicKey = %s\n", server.PublicKey)) if server.Endpoint != "" { conf.WriteString(fmt.Sprintf("Endpoint = %s\n", server.Endpoint)) } // Allow all traffic through the tunnel by default conf.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n") conf.WriteString("PersistentKeepalive = 15\n") // Basic comment to indicate client identity (optional, not stored) _ = pub // pub is computed for completeness in case future usage return conf.Bytes(), nil } func SyncWG(interfaceName string) (err error) { cmd := exec.Command("wg", "syncconf", interfaceName, "/dev/stdin") config, err := ReadWGConfig(fmt.Sprintf("/etc/wireguard/%s.conf", interfaceName)) if err != nil { return } cmd.Stdin = bytes.NewBuffer(config) return cmd.Run() }