package infra import ( "bytes" "fmt" "os/exec" "strings" ) // WGKeys holds a WireGuard Curve25519 key pair and an optional preshared key, // all encoded as base64 strings. type WGKeys struct { PrivateKey string // base64 Curve25519 private key PublicKey string // base64 Curve25519 public key PresharedKey string // base64 preshared key, empty if not requested } // WGKeygen generates a WireGuard key pair using `wg genkey` / `wg pubkey`. // If withPSK is true it also runs `wg genpsk` to produce a preshared key. // Requires the `wg` binary in PATH (install wireguard-tools). // NEVER log PrivateKey or PresharedKey in plain text. func WGKeygen(withPSK bool) (WGKeys, error) { // Generate private key privOut, err := runWG(nil, "genkey") if err != nil { return WGKeys{}, fmt.Errorf("wg genkey: %w", err) } privateKey := strings.TrimSpace(privOut) // Derive public key from private key pubOut, err := runWG(strings.NewReader(privateKey), "pubkey") if err != nil { return WGKeys{}, fmt.Errorf("wg pubkey: %w", err) } publicKey := strings.TrimSpace(pubOut) keys := WGKeys{ PrivateKey: privateKey, PublicKey: publicKey, } if withPSK { pskOut, err := runWG(nil, "genpsk") if err != nil { return WGKeys{}, fmt.Errorf("wg genpsk: %w", err) } keys.PresharedKey = strings.TrimSpace(pskOut) } return keys, nil } // runWG executes `wg `, optionally piping stdin, and returns stdout. // stderr is captured and included in the error when exit code != 0. func runWG(stdin interface{ Read([]byte) (int, error) }, subcommand string) (string, error) { cmd := exec.Command("wg", subcommand) if stdin != nil { cmd.Stdin = stdin } var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return "", fmt.Errorf("%w: %s", err, strings.TrimSpace(stderr.String())) } return stdout.String(), nil }