Add support for network service
This commit is contained in:
104
firewall/api.go
104
firewall/api.go
@@ -6,10 +6,15 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/netenv"
|
||||
|
||||
"github.com/safing/portmaster/updates"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/dataroot"
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -33,7 +38,10 @@ Checked process paths:
|
||||
%s
|
||||
|
||||
The authorized root path is %s.
|
||||
You can enable the Development Mode to disable API authentication for development purposes.`
|
||||
You can enable the Development Mode to disable API authentication for development purposes.
|
||||
For production use please create an API key in the settings.`
|
||||
|
||||
deniedMsgMisconfigured = `%wThe authentication system is misconfigured.`
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -80,11 +88,18 @@ func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, er
|
||||
return nil, fmt.Errorf("failed to get remote IP/Port: %s", err)
|
||||
}
|
||||
|
||||
// Check if the request is even local.
|
||||
myIP, err := netenv.IsMyIP(remoteIP)
|
||||
if err == nil && !myIP {
|
||||
// Return to caller that the request was not handled.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Tracer(r.Context()).Tracef("filter: authenticating API request from %s", r.RemoteAddr)
|
||||
|
||||
// It is very important that this works, retry extensively (every 250ms for 5s)
|
||||
var retry bool
|
||||
for tries := 0; tries < 20; tries++ {
|
||||
for tries := 0; tries < 5; tries++ {
|
||||
retry, err = authenticateAPIRequest(
|
||||
r.Context(),
|
||||
&packet.Info{
|
||||
@@ -102,7 +117,7 @@ func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, er
|
||||
}
|
||||
|
||||
// wait a little
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -116,45 +131,58 @@ func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, er
|
||||
|
||||
func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bool, err error) {
|
||||
var procsChecked []string
|
||||
var originalPid int
|
||||
|
||||
// get process
|
||||
// Get authenticated path.
|
||||
authenticatedPath := updates.RootPath()
|
||||
if authenticatedPath == "" {
|
||||
return false, fmt.Errorf(deniedMsgMisconfigured, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
|
||||
}
|
||||
authenticatedPath += string(filepath.Separator)
|
||||
|
||||
// Get process of request.
|
||||
proc, _, err := process.GetProcessByConnection(ctx, pktInfo)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to get process: %s", err)
|
||||
}
|
||||
originalPid := proc.Pid
|
||||
var previousPid int
|
||||
log.Tracer(ctx).Debugf("filter: failed to get process of api request: %s", err)
|
||||
originalPid = process.UnidentifiedProcessID
|
||||
} else {
|
||||
originalPid = proc.Pid
|
||||
var previousPid int
|
||||
|
||||
// go up up to two levels, if we don't match
|
||||
for i := 0; i < 5; i++ {
|
||||
// check for eligible PID
|
||||
switch proc.Pid {
|
||||
case process.UnidentifiedProcessID, process.SystemProcessID:
|
||||
break
|
||||
default: // normal process
|
||||
// check if the requesting process is in database root / updates dir
|
||||
if strings.HasPrefix(proc.Path, dataRoot.Path) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// add checked process to list
|
||||
procsChecked = append(procsChecked, proc.Path)
|
||||
|
||||
if i < 4 {
|
||||
// save previous PID
|
||||
previousPid = proc.Pid
|
||||
|
||||
// get parent process
|
||||
proc, err = process.GetOrFindProcess(ctx, proc.ParentPid)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to get process: %s", err)
|
||||
}
|
||||
|
||||
// abort if we are looping
|
||||
if proc.Pid == previousPid {
|
||||
// this also catches -1 pid loops
|
||||
// Go up up to two levels, if we don't match the path.
|
||||
checkLevels := 2
|
||||
for i := 0; i < checkLevels+1; i++ {
|
||||
// Check for eligible path.
|
||||
switch proc.Pid {
|
||||
case process.UnidentifiedProcessID, process.SystemProcessID:
|
||||
break
|
||||
default: // normal process
|
||||
// Check if the requesting process is in database root / updates dir.
|
||||
if strings.HasPrefix(proc.Path, authenticatedPath) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add checked path to list.
|
||||
procsChecked = append(procsChecked, proc.Path)
|
||||
|
||||
// Get the parent process.
|
||||
if i < checkLevels {
|
||||
// save previous PID
|
||||
previousPid = proc.Pid
|
||||
|
||||
// get parent process
|
||||
proc, err = process.GetOrFindProcess(ctx, proc.ParentPid)
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Debugf("filter: failed to get parent process of api request: %s", err)
|
||||
break
|
||||
}
|
||||
|
||||
// abort if we are looping
|
||||
if proc.Pid == previousPid {
|
||||
// this also catches -1 pid loops
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,7 +202,7 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo
|
||||
deniedMsgUnauthorized,
|
||||
api.ErrAPIAccessDeniedMessage,
|
||||
strings.Join(procsChecked, "\n"),
|
||||
dataRoot.Path,
|
||||
authenticatedPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package firewall
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -88,7 +87,7 @@ func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *res
|
||||
p := conn.Process().Profile()
|
||||
|
||||
// do not modify own queries
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
if conn.Process().Pid == ownPID {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/netenv"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -38,6 +40,8 @@ var (
|
||||
|
||||
blockedIPv4 = net.IPv4(0, 0, 0, 17)
|
||||
blockedIPv6 = net.ParseIP("::17")
|
||||
|
||||
ownPID = os.Getpid()
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -177,6 +181,16 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Only fast-track local requests.
|
||||
isMe, err := netenv.IsMyIP(meta.Src)
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||
return false
|
||||
case !isMe:
|
||||
return false
|
||||
}
|
||||
|
||||
// Log and permit.
|
||||
log.Debugf("filter: fast-track accepting api connection: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
@@ -196,6 +210,16 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Only fast-track local requests.
|
||||
isMe, err := netenv.IsMyIP(meta.Src)
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||
return false
|
||||
case !isMe:
|
||||
return false
|
||||
}
|
||||
|
||||
// Log and permit.
|
||||
log.Debugf("filter: fast-track accepting local dns: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
@@ -224,7 +248,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// Redirect rogue dns requests to the Portmaster.
|
||||
if pkt.IsOutbound() &&
|
||||
pkt.Info().DstPort == 53 &&
|
||||
conn.Process().Pid != os.Getpid() &&
|
||||
conn.Process().Pid != ownPID &&
|
||||
nameserverIPMatcherReady.IsSet() &&
|
||||
!nameserverIPMatcher(pkt.Info().Dst) {
|
||||
conn.Verdict = network.VerdictRerouteToNameserver
|
||||
|
||||
@@ -3,7 +3,6 @@ package firewall
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -118,8 +117,9 @@ func runDeciders(ctx context.Context, conn *network.Connection, pkt packet.Packe
|
||||
// checkPortmasterConnection allows all connection that originate from
|
||||
// portmaster itself.
|
||||
func checkPortmasterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) bool {
|
||||
// grant self
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
// Grant own outgoing connections.
|
||||
if conn.Process().Pid == ownPID &&
|
||||
(pkt == nil || pkt.IsOutbound()) {
|
||||
log.Tracer(ctx).Infof("filter: granting own connection %s", conn)
|
||||
conn.Accept("connection by Portmaster", noReasonOptionKey)
|
||||
conn.Internal = true
|
||||
|
||||
Reference in New Issue
Block a user