Add WireGuard TUI implementation
- Add Go TUI with bubbletea for WireGuard management - Implement client CRUD operations with QR code generation - Add configuration and validation modules - Install/update scripts for client setup - Update Makefile to build binaries to bin/ directory - Add .gitignore for Go projects
This commit is contained in:
106
internal/wireguard/config.go
Normal file
106
internal/wireguard/config.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenerateServerConfig generates a server-side WireGuard [Peer] configuration
|
||||
// and writes it to /etc/wireguard/conf.d/client-<name>.conf
|
||||
func GenerateServerConfig(name, publicKey string, hasPSK bool, ipv4, ipv6, psk string) (string, error) {
|
||||
var builder strings.Builder
|
||||
|
||||
builder.WriteString(fmt.Sprintf("[Peer]\n"))
|
||||
builder.WriteString(fmt.Sprintf("# %s\n", name))
|
||||
builder.WriteString(fmt.Sprintf("PublicKey = %s\n", publicKey))
|
||||
|
||||
if hasPSK {
|
||||
builder.WriteString(fmt.Sprintf("PresharedKey = %s\n", psk))
|
||||
}
|
||||
|
||||
builder.WriteString(fmt.Sprintf("AllowedIPs = %s/32", ipv4))
|
||||
if ipv6 != "" {
|
||||
builder.WriteString(fmt.Sprintf(", %s/128", ipv6))
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
|
||||
configContent := builder.String()
|
||||
|
||||
configDir := "/etc/wireguard/conf.d"
|
||||
configPath := filepath.Join(configDir, fmt.Sprintf("client-%s.conf", name))
|
||||
|
||||
if err := atomicWrite(configPath, configContent); err != nil {
|
||||
return "", fmt.Errorf("failed to write server config: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Generated server config: %s", configPath)
|
||||
return configPath, nil
|
||||
}
|
||||
|
||||
// GenerateClientConfig generates a client-side WireGuard configuration
|
||||
// with [Interface] and [Peer] sections and writes it to /etc/wireguard/clients/<name>.conf
|
||||
func GenerateClientConfig(name, privateKey, ipv4, ipv6, dns, serverPublicKey, endpoint string, port int, hasPSK bool, psk string) (string, error) {
|
||||
var builder strings.Builder
|
||||
|
||||
// [Interface] section
|
||||
builder.WriteString("[Interface]\n")
|
||||
builder.WriteString(fmt.Sprintf("PrivateKey = %s\n", privateKey))
|
||||
builder.WriteString(fmt.Sprintf("Address = %s/24", ipv4))
|
||||
if ipv6 != "" {
|
||||
builder.WriteString(fmt.Sprintf(", %s/64", ipv6))
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
builder.WriteString(fmt.Sprintf("DNS = %s\n", dns))
|
||||
|
||||
// [Peer] section
|
||||
builder.WriteString("\n[Peer]\n")
|
||||
builder.WriteString(fmt.Sprintf("PublicKey = %s\n", serverPublicKey))
|
||||
|
||||
if hasPSK {
|
||||
builder.WriteString(fmt.Sprintf("PresharedKey = %s\n", psk))
|
||||
}
|
||||
|
||||
builder.WriteString(fmt.Sprintf("Endpoint = %s:%d\n", endpoint, port))
|
||||
builder.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
|
||||
builder.WriteString("PersistentKeepalive = 25\n")
|
||||
|
||||
configContent := builder.String()
|
||||
|
||||
clientsDir := "/etc/wireguard/clients"
|
||||
configPath := filepath.Join(clientsDir, fmt.Sprintf("%s.conf", name))
|
||||
|
||||
if err := atomicWrite(configPath, configContent); err != nil {
|
||||
return "", fmt.Errorf("failed to write client config: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Generated client config: %s", configPath)
|
||||
return configPath, nil
|
||||
}
|
||||
|
||||
// atomicWrite writes content to a file atomically
|
||||
// Uses temp file + rename pattern for atomicity and sets permissions to 0600
|
||||
func atomicWrite(path, content string) error {
|
||||
// Ensure parent directory exists
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", dir, err)
|
||||
}
|
||||
|
||||
// Write to temp file first
|
||||
tempPath := path + ".tmp"
|
||||
if err := os.WriteFile(tempPath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write temp file: %w", err)
|
||||
}
|
||||
|
||||
// Atomic rename
|
||||
if err := os.Rename(tempPath, path); err != nil {
|
||||
// Clean up temp file if rename fails
|
||||
_ = os.Remove(tempPath)
|
||||
return fmt.Errorf("failed to rename temp file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user