Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server Network Interface Enumeration #69

Merged
merged 3 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ Once the client configs have been imported and Wireguard is started, you can vie
╰─────────────────────╯
```

> [!TIP]
> Add the `--network-info` flag to this command to get a list of each Server host's network interfaces and associated CIDR addresses.

## Add Server (Optional)

<div align="center">
Expand Down
21 changes: 21 additions & 0 deletions src/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type request struct {
Body []byte
}

// Re-export the server api struct so files importing this package can access it
type HostInterface serverapi.HostInterface

// MakeRequest attempts to send an API query to the Wiretap server.
func makeRequest(req request) ([]byte, error) {
client := &http.Client{Timeout: 3 * time.Second}
Expand Down Expand Up @@ -89,6 +92,24 @@ func ServerInfo(apiAddr netip.AddrPort) (peer.Config, peer.Config, error) {
return *configs.RelayConfig, *configs.E2EEConfig, nil
}

func ServerInterfaces(apiAddr netip.AddrPort) ([]HostInterface, error) {
body, err := makeRequest(request{
URL: makeUrl(apiAddr, "serverinterfaces", []string{}),
Method: "GET",
})
if err != nil {
return nil, err
}

var interfaces []HostInterface
err = json.Unmarshal(body, &interfaces)
if err != nil {
return nil, err
}

return interfaces, nil
}

func AllocateNode(apiAddr netip.AddrPort, peerType peer.PeerType) (serverapi.NetworkState, error) {
body, err := makeRequest(request{
URL: makeUrl(apiAddr, "allocate", []string{fmt.Sprintf("type=%d", peerType)}),
Expand Down
85 changes: 66 additions & 19 deletions src/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import (
)

type statusCmdConfig struct {
networkInfo bool
configFileRelay string
configFileE2EE string
}

// Defaults for status command.
// See root command for shared defaults.
var statusCmd = statusCmdConfig{
networkInfo: false,
configFileRelay: ConfigRelay,
configFileE2EE: ConfigE2EE,
}
Expand All @@ -39,29 +41,31 @@ func init() {

rootCmd.AddCommand(cmd)

cmd.Flags().BoolVarP(&statusCmd.networkInfo, "network-info", "n", statusCmd.networkInfo, "Display network info for each online server node")
cmd.Flags().StringVarP(&statusCmd.configFileRelay, "relay", "1", statusCmd.configFileRelay, "wireguard relay config input filename")
cmd.Flags().StringVarP(&statusCmd.configFileE2EE, "e2ee", "2", statusCmd.configFileE2EE, "wireguard E2EE config input filename")

cmd.Flags().SortFlags = false
}

// Run attempts to parse config files into a network diagram.
func (c statusCmdConfig) Run() {
func (cc statusCmdConfig) Run() {
// Start building tree.
type Node struct {
peerConfig peer.PeerConfig
relayConfig peer.Config
e2eeConfig peer.Config
children []*Node
interfaces []api.HostInterface
error string
}

var err error

// Parse the relay and e2ee config files
clientConfigRelay, err := peer.ParseConfig(c.configFileRelay)
clientConfigRelay, err := peer.ParseConfig(cc.configFileRelay)
check("failed to parse relay config file", err)
clientConfigE2EE, err := peer.ParseConfig(c.configFileE2EE)
clientConfigE2EE, err := peer.ParseConfig(cc.configFileE2EE)
check("failed to parse e2ee config file", err)

client := Node{
Expand All @@ -81,15 +85,26 @@ func (c statusCmdConfig) Run() {
relayConfig, e2eeConfig, err := api.ServerInfo(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
if err != nil {
errorNodes = append(errorNodes, Node{
peerConfig: ep,
error: err.Error(),
peerConfig: ep,
error: err.Error(),
})

} else {
var interfaces []api.HostInterface
if cc.networkInfo {
interfaces, err = api.ServerInterfaces(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
if err != nil {
interfaces = append(interfaces, api.HostInterface{
Name: "ERROR: " + err.Error(),
})
}
}

nodes[relayConfig.GetPublicKey()] = Node{
peerConfig: ep,
relayConfig: relayConfig,
e2eeConfig: e2eeConfig,
interfaces: interfaces,
}
}
}
Expand Down Expand Up @@ -131,19 +146,43 @@ func (c statusCmdConfig) Run() {
ips := []string{}
var api string
for j, a := range c.peerConfig.GetAllowedIPs() {
if j == len(c.peerConfig.GetAllowedIPs()) - 1 {
if j == len(c.peerConfig.GetAllowedIPs())-1 {
api = a.IP.String()
} else {
ips = append(ips, a.String())
}
}
t.AddChild(tree.NodeString(fmt.Sprintf(`server

nodeString := fmt.Sprintf(
`server
nickname: %v
relay: %v...
e2ee: %v...

api: %v
routes: %v `, c.peerConfig.GetNickname(), c.relayConfig.GetPublicKey()[:8], c.e2eeConfig.GetPublicKey()[:8], api, strings.Join(ips, ","))))
routes: %v `,
c.peerConfig.GetNickname(),
c.relayConfig.GetPublicKey()[:8],
c.e2eeConfig.GetPublicKey()[:8],
api,
strings.Join(ips, ","),
)

if cc.networkInfo {
nodeString += `

Network Interfaces:
-------------------
`
for _, ifx := range c.interfaces {
nodeString += fmt.Sprintf("%v:\n", ifx.Name)
for _, a := range ifx.Addrs {
nodeString += strings.Repeat(" ", 2) + a.String() + "\n"
}
}
}

t.AddChild(tree.NodeString(nodeString))
child, err := t.Child(0)
check("could not build tree", err)
treeTraversal(node.children[i], child)
Expand All @@ -156,31 +195,39 @@ func (c statusCmdConfig) Run() {
fmt.Println()
fmt.Fprintln(color.Output, WhiteBold(t))
fmt.Println()

if len(errorNodes) > 0 {
// Display known peers that we had issues connecting to
fmt.Fprintln(color.Output, WhiteBold("Peers with Errors:"))
fmt.Println()

for _, node := range errorNodes {
ips := []string{}
var api string
for j, a := range node.peerConfig.GetAllowedIPs() {
if j == len(node.peerConfig.GetAllowedIPs()) - 1 {
if j == len(node.peerConfig.GetAllowedIPs())-1 {
api = a.IP.String()
} else {
ips = append(ips, a.String())
}
}

t = tree.NewTree(tree.NodeString(fmt.Sprintf(`server

nickname: %v
e2ee: %v...
api: %v
routes: %v
nodeString := fmt.Sprintf(
`server

error: %v`, node.peerConfig.GetNickname(), node.peerConfig.GetPublicKey().String()[:8], api, strings.Join(ips, ","), errorWrap(node.error, 80))))
nickname: %v
e2ee: %v...
api: %v
routes: %v

error: %v`,
node.peerConfig.GetNickname(),
node.peerConfig.GetPublicKey().String()[:8],
api, strings.Join(ips, ","),
errorWrap(node.error, 80),
)

t = tree.NewTree(tree.NodeString(nodeString))
fmt.Fprintln(color.Output, WhiteBold(t))
}
}
Expand Down
55 changes: 54 additions & 1 deletion src/transport/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ type NetworkState struct {
ServerRelaySubnet6 netip.Addr
}

type HostInterface struct {
Name string
Addrs []net.IPNet
}

type AddAllowedIPsRequest struct {
PublicKey wgtypes.Key
AllowedIPs []net.IPNet
Expand Down Expand Up @@ -129,6 +134,7 @@ func Handle(tnet *netstack.Net, devRelay *device.Device, devE2EE *device.Device,

http.HandleFunc("/ping", wrapApi(handlePing()))
http.HandleFunc("/serverinfo", wrapApi(handleServerInfo(configs)))
http.HandleFunc("/serverinterfaces", wrapApi(handleServerInterfaces()))
http.HandleFunc("/addpeer", wrapApi(handleAddPeer(devRelay, devE2EE, configs)))
http.HandleFunc("/allocate", wrapApi(handleAllocate(ns)))
http.HandleFunc("/addallowedips", wrapApi(handleAddAllowedIPs(devRelay, configs)))
Expand Down Expand Up @@ -189,6 +195,53 @@ func handleServerInfo(configs ServerConfigs) http.HandlerFunc {
}
}

// handleServerInterfaces responds with network interface information for this server.
func handleServerInterfaces() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

var interfaces []HostInterface
ifs, err := net.Interfaces()
if err != nil {
log.Printf("API Error: %v", err)
}

for _, ifx := range ifs {
newIF := HostInterface{}
newIF.Name = ifx.Name

addrs, err := ifx.Addrs()
if err != nil {
log.Printf("API Error: %v", err)
}

for _, a := range addrs {
_, cidr, err := net.ParseCIDR(a.String())
if err != nil {
log.Printf("API Error: %v", err)
}
newIF.Addrs = append(newIF.Addrs, *cidr)
}

interfaces = append(interfaces, newIF)
}

body, err := json.Marshal(interfaces)
if err != nil {
writeErr(w, err)
return
}

_, err = w.Write(body)
if err != nil {
log.Printf("API Error: %v", err)
}
}
}

// handleAddPeer adds a peer to either this server's relay or e2ee device.
func handleAddPeer(devRelay *device.Device, devE2EE *device.Device, config ServerConfigs) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -321,7 +374,7 @@ func handleAllocate(ns *NetworkState) http.HandlerFunc {
}
}

// handleAddAllowedIPs adds new route to a specfied peer.
// handleAddAllowedIPs adds new route to a specified peer.
func handleAddAllowedIPs(devRelay *device.Device, config ServerConfigs) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
Expand Down
Loading