feat: Add uc wg show command to inspect a machine's uncloud wireguard network (#161)

* feat: Add `uc wg show` command to inspect a machine's uncloud wireguard network (draft / work in progress)

* Compile proto files with `make proto-mise`

* Fix lint errors

* Output peers in tabwriter table

* Lookup machine name for wireguard peer public key

* Add --machine flag to `uc wg show` to proxy call to specific machine

* Small refactor to move wg show logic out into a separate function
This commit is contained in:
Justin Bradford
2026-01-10 16:49:35 +11:00
committed by GitHub
parent 06c3fba96e
commit a945291371
6 changed files with 620 additions and 165 deletions
+2
View File
@@ -14,6 +14,7 @@ import (
"github.com/psviderski/uncloud/cmd/uncloud/machine"
"github.com/psviderski/uncloud/cmd/uncloud/service"
"github.com/psviderski/uncloud/cmd/uncloud/volume"
"github.com/psviderski/uncloud/cmd/uncloud/wg"
"github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/config"
"github.com/psviderski/uncloud/internal/fs"
@@ -134,6 +135,7 @@ func main() {
service.NewStartCommand("service"),
service.NewStopCommand("service"),
volume.NewRootCommand(),
wg.NewRootCommand(),
)
cobra.CheckErr(cmd.Execute())
}
+122
View File
@@ -0,0 +1,122 @@
package wg
import (
"context"
"encoding/base64"
"fmt"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/docker/go-units"
"github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/spf13/cobra"
)
func NewRootCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "wg",
Short: "Inspect WireGuard network",
}
cmd.AddCommand(newShowCommand())
return cmd
}
type showOptions struct {
machine string
}
func newShowCommand() *cobra.Command {
opts := showOptions{}
cmd := &cobra.Command{
Use: "show",
Short: "Show WireGuard configuration for the current machine",
Long: "Shows the WireGuard configuration for the machine currently connected to (or specified by the global --connect flag).",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return runShow(cmd.Context(), uncli, opts)
},
}
cmd.Flags().StringVarP(&opts.machine, "machine", "m", "", "Name or ID of the machine to show configuration for")
return cmd
}
func runShow(ctx context.Context, uncli *cli.CLI, opts showOptions) error {
client, err := uncli.ConnectCluster(ctx)
if err != nil {
return fmt.Errorf("connection failed: %w", err)
}
defer client.Close()
if opts.machine != "" {
// Proxy requests to the specified machine.
ctx, _, err = client.ProxyMachinesContext(ctx, []string{opts.machine})
if err != nil {
return err
}
}
// Explicitly using the interface method to avoid ambiguity if any
var _ pb.MachineClient = client.MachineClient
resp, err := client.MachineClient.GetWireGuardDevice(ctx, nil)
if err != nil {
return err
}
machines, err := client.ListMachines(ctx, nil)
if err != nil {
return fmt.Errorf("list machines: %w", err)
}
machinesNamesByPublicKey := make(map[string]string)
for _, m := range machines {
publicKey := base64.StdEncoding.EncodeToString(m.Machine.Network.PublicKey)
machinesNamesByPublicKey[publicKey] = m.Machine.Name
}
// Fetch the machine's name for more descriptive output
inspectResp, err := client.Inspect(ctx, nil)
if err == nil {
fmt.Printf("Machine Name: %s\n", inspectResp.Name)
}
fmt.Printf("WireGuard interface: %s\n", resp.Name)
fmt.Printf("WireGuard public key: %s\n", resp.PublicKey)
fmt.Printf("WireGuard port: %d\n", resp.ListenPort)
fmt.Println()
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if _, err = fmt.Fprintln(tw, "MACHINE\tPUBLIC KEY\tENDPOINT\tHANDSHAKE\tRECEIVED\tSENT\tALLOWED IPS"); err != nil {
return fmt.Errorf("write header: %w", err)
}
for _, peer := range resp.Peers {
machineName, ok := machinesNamesByPublicKey[peer.PublicKey]
if !ok {
machineName = "(unknown)"
}
lastHandshake := ""
if peer.LastHandshakeTime != nil {
lastHandshake = time.Since(peer.LastHandshakeTime.AsTime()).Round(time.Second).String() + " ago"
}
_, err = fmt.Fprintf(
tw,
"%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
machineName,
peer.PublicKey,
peer.Endpoint,
lastHandshake,
units.HumanSize(float64(peer.ReceiveBytes)),
units.HumanSize(float64(peer.TransmitBytes)),
strings.Join(peer.AllowedIps, ", "),
)
if err != nil {
return fmt.Errorf("write row: %w", err)
}
}
return tw.Flush()
}