mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-03-31 06:24:09 -04:00
Add ability for device/network scan when rootless (#1700)
* Add ability for device/network scan when rootless Signed-off-by: invario <67800603+invario@users.noreply.github.com> * adjust err msg --------- Signed-off-by: invario <67800603+invario@users.noreply.github.com> Co-authored-by: Maxi Quoß <maxi@quoss.org>
This commit is contained in:
@@ -132,6 +132,10 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.77 h1:iQtQTjFUOcTT19fI8sTCzYXsjeVs56et3D8AbKS2Uks=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.77/go.mod h1:oV+IO8kGh0B7TxErbydDe2+BRmi9g/W0CkpVV+QBTJU=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 h1:Z06sMOzc0GNCwp6efaVrIrz4ywGJ1v+DP0pjVkOfDuA=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.77/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
|
||||
@@ -2,14 +2,9 @@ package pb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
@@ -216,97 +211,6 @@ type Nmaprun struct {
|
||||
} `xml:"host"`
|
||||
}
|
||||
|
||||
func HandlerScan(e *core.RequestEvent) error {
|
||||
// check if nmap installed
|
||||
nmap, err := exec.LookPath("nmap")
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// check if scan range is valid
|
||||
allPrivateSettings, err := e.App.FindAllRecords("settings_private")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settingsPrivate := allPrivateSettings[0]
|
||||
scanRange := settingsPrivate.GetString("scan_range")
|
||||
_, ipNet, err := net.ParseCIDR(scanRange)
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// run nmap
|
||||
timeout := os.Getenv("UPSNAP_SCAN_TIMEOUT")
|
||||
if timeout == "" {
|
||||
timeout = "500ms"
|
||||
}
|
||||
cmd := exec.Command(nmap, "-sn", "-oX", "-", scanRange, "--host-timeout", timeout)
|
||||
cmdOutput, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshal xml
|
||||
nmapOutput := Nmaprun{}
|
||||
if err := xml.Unmarshal(cmdOutput, &nmapOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
MAC string `json:"mac"`
|
||||
MACVendor string `json:"mac_vendor"`
|
||||
}
|
||||
|
||||
// extract info from struct into data
|
||||
type Response struct {
|
||||
Netmask string `json:"netmask"`
|
||||
Devices []Device `json:"devices"`
|
||||
}
|
||||
res := Response{}
|
||||
var nm []string
|
||||
for _, octet := range ipNet.Mask {
|
||||
nm = append(nm, strconv.Itoa(int(octet)))
|
||||
}
|
||||
res.Netmask = strings.Join(nm, ".")
|
||||
|
||||
for _, host := range nmapOutput.Host {
|
||||
dev := Device{}
|
||||
for _, addr := range host.Address {
|
||||
if addr.Addrtype == "ipv4" {
|
||||
dev.IP = addr.Addr
|
||||
} else if addr.Addrtype == "mac" {
|
||||
dev.MAC = addr.Addr
|
||||
}
|
||||
if addr.Vendor != "" {
|
||||
dev.MACVendor = addr.Vendor
|
||||
} else {
|
||||
dev.MACVendor = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
if dev.IP == "" || dev.MAC == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
names, err := net.LookupAddr(dev.IP)
|
||||
if err != nil || len(names) == 0 {
|
||||
dev.Name = dev.MACVendor
|
||||
} else {
|
||||
dev.Name = strings.TrimSuffix(names[0], ".")
|
||||
}
|
||||
|
||||
if dev.Name == "" && dev.MACVendor == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Devices = append(res.Devices, dev)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func HandlerInitSuperuser(e *core.RequestEvent) error {
|
||||
superusersCollection, err := e.App.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
|
||||
137
backend/pb/handlerscan_linux.go
Normal file
137
backend/pb/handlerscan_linux.go
Normal file
@@ -0,0 +1,137 @@
|
||||
//go:build linux
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"kernel.org/pub/linux/libs/security/libcap/cap"
|
||||
)
|
||||
|
||||
func HandlerScan(e *core.RequestEvent) error {
|
||||
// check if nmap installed
|
||||
nmap, err := exec.LookPath("nmap")
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// check if scan range is valid
|
||||
allPrivateSettings, err := e.App.FindAllRecords("settings_private")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settingsPrivate := allPrivateSettings[0]
|
||||
scanRange := settingsPrivate.GetString("scan_range")
|
||||
_, ipNet, err := net.ParseCIDR(scanRange)
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// run nmap
|
||||
timeout := os.Getenv("UPSNAP_SCAN_TIMEOUT")
|
||||
if timeout == "" {
|
||||
timeout = "500ms"
|
||||
}
|
||||
orig := cap.GetProc()
|
||||
defer orig.SetProc() // restore original caps on exit.
|
||||
|
||||
c, err := orig.Dup()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to dup existing capabilities: %v", err)
|
||||
}
|
||||
|
||||
if on, _ := c.GetFlag(cap.Permitted, cap.NET_RAW); !on {
|
||||
return fmt.Errorf("unable to get NET_RAW permissions")
|
||||
}
|
||||
|
||||
if err := c.SetFlag(cap.Effective, true, cap.NET_RAW); err != nil {
|
||||
return fmt.Errorf("unable to set NET_RAW capability effective")
|
||||
}
|
||||
|
||||
if err := c.SetFlag(cap.Inheritable, true, cap.NET_RAW); err != nil {
|
||||
return fmt.Errorf("unable to set NET_RAW capability inheritable")
|
||||
}
|
||||
|
||||
if err := c.SetProc(); err != nil {
|
||||
return fmt.Errorf("unable to raise NET_RAW capability")
|
||||
}
|
||||
|
||||
if err := cap.SetAmbient(true, cap.NET_RAW); err != nil {
|
||||
return fmt.Errorf("unable to set NET_RAW capability ambient")
|
||||
}
|
||||
|
||||
cmd := exec.Command(nmap, "-sn", "-oX", "-", scanRange, "--host-timeout", timeout, "--privileged")
|
||||
cmdOutput, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshal xml
|
||||
nmapOutput := Nmaprun{}
|
||||
if err := xml.Unmarshal(cmdOutput, &nmapOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
MAC string `json:"mac"`
|
||||
MACVendor string `json:"mac_vendor"`
|
||||
}
|
||||
|
||||
// extract info from struct into data
|
||||
type Response struct {
|
||||
Netmask string `json:"netmask"`
|
||||
Devices []Device `json:"devices"`
|
||||
}
|
||||
res := Response{}
|
||||
var nm []string
|
||||
for _, octet := range ipNet.Mask {
|
||||
nm = append(nm, strconv.Itoa(int(octet)))
|
||||
}
|
||||
res.Netmask = strings.Join(nm, ".")
|
||||
|
||||
for _, host := range nmapOutput.Host {
|
||||
dev := Device{}
|
||||
for _, addr := range host.Address {
|
||||
if addr.Addrtype == "ipv4" {
|
||||
dev.IP = addr.Addr
|
||||
} else if addr.Addrtype == "mac" {
|
||||
dev.MAC = addr.Addr
|
||||
}
|
||||
if addr.Vendor != "" {
|
||||
dev.MACVendor = addr.Vendor
|
||||
} else {
|
||||
dev.MACVendor = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
if dev.IP == "" || dev.MAC == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
names, err := net.LookupAddr(dev.IP)
|
||||
if err != nil || len(names) == 0 {
|
||||
dev.Name = dev.MACVendor
|
||||
} else {
|
||||
dev.Name = strings.TrimSuffix(names[0], ".")
|
||||
}
|
||||
|
||||
if dev.Name == "" && dev.MACVendor == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Devices = append(res.Devices, dev)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, res)
|
||||
}
|
||||
107
backend/pb/handlerscan_other.go
Normal file
107
backend/pb/handlerscan_other.go
Normal file
@@ -0,0 +1,107 @@
|
||||
//go:build !linux
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
func HandlerScan(e *core.RequestEvent) error {
|
||||
// check if nmap installed
|
||||
nmap, err := exec.LookPath("nmap")
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// check if scan range is valid
|
||||
allPrivateSettings, err := e.App.FindAllRecords("settings_private")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settingsPrivate := allPrivateSettings[0]
|
||||
scanRange := settingsPrivate.GetString("scan_range")
|
||||
_, ipNet, err := net.ParseCIDR(scanRange)
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError(err.Error(), nil)
|
||||
}
|
||||
|
||||
// run nmap
|
||||
timeout := os.Getenv("UPSNAP_SCAN_TIMEOUT")
|
||||
if timeout == "" {
|
||||
timeout = "500ms"
|
||||
}
|
||||
cmd := exec.Command(nmap, "-sn", "-oX", "-", scanRange, "--host-timeout", timeout, "--privileged")
|
||||
cmdOutput, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshal xml
|
||||
nmapOutput := Nmaprun{}
|
||||
if err := xml.Unmarshal(cmdOutput, &nmapOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
MAC string `json:"mac"`
|
||||
MACVendor string `json:"mac_vendor"`
|
||||
}
|
||||
|
||||
// extract info from struct into data
|
||||
type Response struct {
|
||||
Netmask string `json:"netmask"`
|
||||
Devices []Device `json:"devices"`
|
||||
}
|
||||
res := Response{}
|
||||
var nm []string
|
||||
for _, octet := range ipNet.Mask {
|
||||
nm = append(nm, strconv.Itoa(int(octet)))
|
||||
}
|
||||
res.Netmask = strings.Join(nm, ".")
|
||||
|
||||
for _, host := range nmapOutput.Host {
|
||||
dev := Device{}
|
||||
for _, addr := range host.Address {
|
||||
if addr.Addrtype == "ipv4" {
|
||||
dev.IP = addr.Addr
|
||||
} else if addr.Addrtype == "mac" {
|
||||
dev.MAC = addr.Addr
|
||||
}
|
||||
if addr.Vendor != "" {
|
||||
dev.MACVendor = addr.Vendor
|
||||
} else {
|
||||
dev.MACVendor = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
if dev.IP == "" || dev.MAC == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
names, err := net.LookupAddr(dev.IP)
|
||||
if err != nil || len(names) == 0 {
|
||||
dev.Name = dev.MACVendor
|
||||
} else {
|
||||
dev.Name = strings.TrimSuffix(names[0], ".")
|
||||
}
|
||||
|
||||
if dev.Name == "" && dev.MACVendor == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Devices = append(res.Devices, dev)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, res)
|
||||
}
|
||||
Reference in New Issue
Block a user