Skip to content

Commit

Permalink
0.3.15 #patch (#348)
Browse files Browse the repository at this point in the history
## v0.3.15

Fixes
* Address APNS memory consumption bug 
* Ping API call was broken 
* Undo shadowports mitigation as it breaks sitevpn use cases

Improvements
* API performance refactoring with the event bus & notifications channel
* Improved packet_logs performance with cached interface name lookups
  • Loading branch information
lts-rad authored Aug 5, 2024
1 parent 0a1d86a commit 00189a0
Show file tree
Hide file tree
Showing 18 changed files with 445 additions and 121 deletions.
12 changes: 12 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
# Secure Programmable Router (SPR) Release Notes

## v0.3.15

Fixes
* Address APNS memory consumption bug
* Ping API call was broken
* Undo shadowports mitigation as it breaks sitevpn use cases

Improvements
* API performance refactoring with the event bus & notifications channel
* Improved packet_logs performance with cached interface name lookups

## v0.3.14
Fixes
* Fix deadlock in API with Devices mutex
Expand Down
2 changes: 1 addition & 1 deletion api/code/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ func AlertsRunEventListener() {
doStore := func(ch <-chan Alert) {
defer wg.Done()
for message := range ch {
sprbus.Publish(message.Topic, message.Info)
SprbusPublish(message.Topic, message.Info)
}
}

Expand Down
46 changes: 35 additions & 11 deletions api/code/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ var SuperdSocketPath = TEST_PREFIX + "/state/plugins/superd/socket"
// NOTE .Fire will dial, print to stdout/stderr if sprbus not started
var log = sprbus.NewLog("log:api")

var gSprbusClient *sprbus.Client

type InfluxConfig struct {
URL string
Org string
Expand Down Expand Up @@ -289,7 +291,7 @@ func getInfo(w http.ResponseWriter, r *http.Request) {

go func() {
defer stdin.Close()
io.WriteString(stdin, string(output))
io.WriteString(stdin, strings.Replace(string(output), "0 user,", "0 users,", 1))
}()

data, err = cmd.Output()
Expand Down Expand Up @@ -1162,7 +1164,7 @@ func saveDevicesJson(devices map[string]DeviceEntry) {
scrubbed_devices := convertDevicesPublic(devices)
savePublicDevicesJson(scrubbed_devices)

sprbus.Publish("devices:save", scrubbed_devices)
SprbusPublish("devices:save", scrubbed_devices)
}

func getDevicesJson() map[string]DeviceEntry {
Expand Down Expand Up @@ -1325,7 +1327,6 @@ func checkDeviceExpiries(devices map[string]DeviceEntry) {
lastTime, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", entry.DHCPLastTime)
if err != nil {
// Handle error
log.Printf("Error parsing time: %v", err)
return
}

Expand Down Expand Up @@ -1410,7 +1411,7 @@ func deleteDeviceLocked(devices map[string]DeviceEntry, identity string) {
}

//notify the bus
sprbus.Publish("device:delete", scrubDevice(val))
SprbusPublish("device:delete", scrubDevice(val))

}

Expand Down Expand Up @@ -1648,7 +1649,7 @@ func updateDevice(w http.ResponseWriter, r *http.Request, dev DeviceEntry, ident
val.PSKEntry.Psk = "**"
}

sprbus.Publish("device:update", scrubDevice(val))
SprbusPublish("device:update", scrubDevice(val))

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(val)
Expand Down Expand Up @@ -1706,7 +1707,7 @@ func updateDevice(w http.ResponseWriter, r *http.Request, dev DeviceEntry, ident
devices[identity] = dev
saveDevicesJson(devices)

sprbus.Publish("device:save", scrubDevice(dev))
SprbusPublish("device:save", scrubDevice(dev))

if pskModified {
//psks updated -- update hostapd
Expand Down Expand Up @@ -2152,7 +2153,7 @@ func reportPSKAuthFailure(w http.ResponseWriter, r *http.Request) {
return
}

sprbus.Publish("wifi:auth:fail", pskf)
SprbusPublish("wifi:auth:fail", pskf)

if pskf.MAC == "" || (pskf.Type != "sae" && pskf.Type != "wpa") || (pskf.Reason != "noentry" && pskf.Reason != "mismatch") {
http.Error(w, "malformed data", 400)
Expand All @@ -2177,7 +2178,7 @@ func reportPSKAuthSuccess(w http.ResponseWriter, r *http.Request) {
return
}

sprbus.Publish("wifi:auth:success", pska)
SprbusPublish("wifi:auth:success", pska)

if pska.Iface == "" || pska.Event != "AP-STA-CONNECTED" || pska.MAC == "" {
http.Error(w, "malformed data", 400)
Expand Down Expand Up @@ -2243,7 +2244,7 @@ func reportDisconnect(w http.ResponseWriter, r *http.Request) {
return
}

sprbus.Publish("wifi:station:disconnect", event)
SprbusPublish("wifi:station:disconnect", event)

if event.Iface == "" || event.Event != "AP-STA-DISCONNECTED" || event.MAC == "" {
http.Error(w, "malformed data", 400)
Expand Down Expand Up @@ -2708,14 +2709,26 @@ func logRequest(handler http.Handler) http.Handler {
logs["remoteaddr"] = r.RemoteAddr
logs["method"] = r.Method
logs["path"] = r.URL.Path
sprbus.Publish("log:www:access", logs)
SprbusPublish("log:www:access", logs)

handler.ServeHTTP(w, r)
})
}

var ServerEventSock = TEST_PREFIX + "/state/api/eventbus.sock"

// SprbusPublish() using default socket, make sure bytes are json
func SprbusPublish(topic string, bytes interface{}) error {
value, err := json.Marshal(bytes)

if err != nil {
return err
}

_, err = gSprbusClient.Publish(topic, string(value))
return err
}

func startEventBus() {
// make sure the client dont connect to the prev socket
os.Remove(ServerEventSock)
Expand All @@ -2730,6 +2743,16 @@ func startEventBus() {
// not reached
}

func registerEventClient() {
//tbd, sprbus needs an update to allow Publish from server
// as pub/service are private and theres no method.
client, err := sprbus.NewClient(ServerEventSock)
if err != nil {
log.Fatal(err)
}
gSprbusClient = client
}

func main() {

//update auth API
Expand All @@ -2743,6 +2766,7 @@ func main() {

// start eventbus
go startEventBus()
registerEventClient()

unix_dhcpd_router := mux.NewRouter().StrictSlash(true)
unix_wifid_router := mux.NewRouter().StrictSlash(true)
Expand Down Expand Up @@ -2772,7 +2796,7 @@ func main() {
external_router_setup.HandleFunc("/hostapd/restart", restartWifi).Methods("PUT")
external_router_setup.HandleFunc("/hostapd/restart_setup", restartSetupWifi).Methods("PUT")

external_router_setup.HandleFunc("/hostapd/calcChannel", hostapdChannelCalc).Methods("PUT")
external_router_setup.HandleFunc("/hostapd/calcChannel", hostapdChannelCalc).Methods("PUT")
external_router_setup.HandleFunc("/link/config", updateLinkConfig).Methods("PUT")

external_router_setup.HandleFunc("/iw/{command:.*}", iwCommand).Methods("GET")
Expand Down
9 changes: 4 additions & 5 deletions api/code/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/pquerna/otp/totp"
"github.com/spr-networks/sprbus"
)

var AuthUsersFile = TEST_PREFIX + "/configs/auth/auth_users.json"
Expand Down Expand Up @@ -257,7 +256,7 @@ func Authenticate(authenticatedNext *mux.Router, publicNext *mux.Router, setupMo
//NOTE: tokens dont check JWT OTP

if authorizedToken(r, token) {
sprbus.Publish("auth:success", map[string]string{"type": "token", "name": tokenName, "reason": "api"})
SprbusPublish("auth:success", map[string]string{"type": "token", "name": tokenName, "reason": "api"})
authenticatedNext.ServeHTTP(w, r)
return
} else {
Expand All @@ -280,7 +279,7 @@ func Authenticate(authenticatedNext *mux.Router, publicNext *mux.Router, setupMo
reason = "invalid or missing JWT OTP"
redirect_validate = true
} else {
sprbus.Publish("auth:success", map[string]string{"type": "user", "username": username, "reason": "api"})
SprbusPublish("auth:success", map[string]string{"type": "user", "username": username, "reason": "api"})

authenticatedNext.ServeHTTP(w, r)
return
Expand All @@ -301,7 +300,7 @@ func Authenticate(authenticatedNext *mux.Router, publicNext *mux.Router, setupMo
}

if authenticatedNext.Match(r, &matchInfo) || setupMode.Match(r, &matchInfo) {
sprbus.Publish("auth:failure", map[string]string{"reason": reason, "type": failType, "name": tokenName + username})
SprbusPublish("auth:failure", map[string]string{"reason": reason, "type": failType, "name": tokenName + username})

if redirect_validate {
http.Redirect(w, r, "/auth/validate", 302)
Expand All @@ -317,7 +316,7 @@ func Authenticate(authenticatedNext *mux.Router, publicNext *mux.Router, setupMo
}
}

sprbus.Publish("auth:failure", map[string]string{"reason": "unknown route, no credentials", "type": failType, "name": tokenName + username})
SprbusPublish("auth:failure", map[string]string{"reason": "unknown route, no credentials", "type": failType, "name": tokenName + username})
if redirect_validate {
http.Redirect(w, r, "/auth/validate", 302)
} else {
Expand Down
8 changes: 3 additions & 5 deletions api/code/dhcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import (
"strings"
"sync"
"time"

"github.com/spr-networks/sprbus"
)

var gDHCPConfigPath = TEST_PREFIX + "/configs/base/dhcp.json"
Expand Down Expand Up @@ -332,7 +330,7 @@ func dhcpRequest(w http.ResponseWriter, r *http.Request) {
return
}

sprbus.Publish("dhcp:request", dhcp)
SprbusPublish("dhcp:request", dhcp)

if dhcp.MAC == "" || dhcp.Iface == "" {
http.Error(w, "need MAC and Iface to dhcp", 400)
Expand Down Expand Up @@ -379,7 +377,7 @@ func dhcpRequest(w http.ResponseWriter, r *http.Request) {

response := DHCPResponse{dhcp.MAC, IP, Router, getLANIP(), LeaseTime}

sprbus.Publish("dhcp:response", response)
SprbusPublish("dhcp:response", response)

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
Expand Down Expand Up @@ -620,7 +618,7 @@ func wireguardUpdate(w http.ResponseWriter, r *http.Request) {
Devicesmtx.Lock()
defer Devicesmtx.Unlock()

sprbus.Publish("wg:update", wg)
SprbusPublish("wg:update", wg)

devices := getDevicesJson()
val, exists := lookupWGDevice(&devices, wg.PublicKey, wg.IP)
Expand Down
12 changes: 4 additions & 8 deletions api/code/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ import (
"github.com/gorilla/mux"
)

import (
"github.com/spr-networks/sprbus"
)

var FWmtx sync.Mutex

type BaseRule struct {
Expand Down Expand Up @@ -1130,7 +1126,7 @@ func refreshDeviceTags(dev DeviceEntry) {
applyEndpointRules(dev)
FWmtx.Unlock()
}()
sprbus.Publish("device:tags:update", scrubDevice(dev))
SprbusPublish("device:tags:update", scrubDevice(dev))
}

func refreshDeviceGroupsAndPolicy(dev DeviceEntry) {
Expand Down Expand Up @@ -1182,7 +1178,7 @@ func refreshDeviceGroupsAndPolicy(dev DeviceEntry) {
populateVmapEntries(ipv4, dev.MAC, ifname, "")
}

sprbus.Publish("device:groups:update", scrubDevice(dev))
SprbusPublish("device:groups:update", scrubDevice(dev))
}

func applyPingRules() {
Expand Down Expand Up @@ -2734,7 +2730,7 @@ func notifyVpnActivity(new_vpn_peers []string, endpoints []string) {
RemoteEndpoint: endpoints[i],
Status: "online",
}
sprbus.Publish("device:vpn:online", notification)
SprbusPublish("device:vpn:online", notification)
}
}

Expand All @@ -2747,7 +2743,7 @@ func notifyVpnActivity(new_vpn_peers []string, endpoints []string) {
RemoteEndpoint: gPreviousEndpoints[i],
Status: "offline",
}
sprbus.Publish("device:vpn:offline", notification)
SprbusPublish("device:vpn:offline", notification)
//recommended hardening against shadow port attacks
//https://petsymposium.org/popets/2024/popets-2024-0070.pdf
clearConntrackSrcIP(peer)
Expand Down
48 changes: 41 additions & 7 deletions api/code/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"crypto/rand"
"encoding/json"
"fmt"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"syscall"
"io/ioutil"
"math/big"
"net"
Expand Down Expand Up @@ -898,24 +901,55 @@ func pingTest(w http.ResponseWriter, r *http.Request) {
return
}

network := "ip4:icmp"
if ipAddr.IP.To4() == nil {
network = "ip6:ipv6-icmp"
}

result := []string{}

for i := 0; i < 4; i++ {
start := time.Now()

conn, err := net.ListenPacket(network, ief.Name)
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
if err != nil {
http.Error(w, "Failed to listen on interface", 400)
http.Error(w, "Error creating raw socket: %v\n", 400)
return
}
defer syscall.Close(fd)

if err := syscall.BindToDevice(fd, ief.Name); err != nil {
http.Error(w, "Error binding to interface", 400)
return
}

f := os.NewFile(uintptr(fd), "")
conn, err := net.FilePacketConn(f)
if err != nil {
http.Error(w, "Error creating ICMP connection", 400)
return
}
defer conn.Close()

_, err = conn.WriteTo([]byte{}, ipAddr)
err = syscall.BindToDevice(fd, ief.Name)
if err != nil {
http.Error(w, "Failed to listen on interface", 400)
return
}

msg := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: 1,
Seq: i,
Data: []byte("HELLO"),
},
}

msgBytes, err := msg.Marshal(nil)
if err != nil {
http.Error(w, "Failed to marshal ping", 400)
return
}

_, err = conn.WriteTo(msgBytes, ipAddr)
if err != nil {
continue
}
Expand Down
Loading

0 comments on commit 00189a0

Please sign in to comment.