mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Merge pull request #66 from safing/fix/api-auth
Increase resilience of api auth by waiting long and trying more often
This commit is contained in:
commit
b4da62e654
1 changed files with 69 additions and 24 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/safing/portbase/api"
|
"github.com/safing/portbase/api"
|
||||||
"github.com/safing/portbase/dataroot"
|
"github.com/safing/portbase/dataroot"
|
||||||
|
@ -40,48 +41,71 @@ func startAPIAuth() {
|
||||||
log.Tracef("filter: api port set to %d", apiPort)
|
log.Tracef("filter: api port set to %d", apiPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiAuthenticator(s *http.Server, r *http.Request) (grantAccess bool, err error) {
|
func apiAuthenticator(s *http.Server, r *http.Request) (err error) {
|
||||||
if devMode() {
|
if devMode() {
|
||||||
return true, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// get local IP/Port
|
// get local IP/Port
|
||||||
localIP, localPort, err := parseHostPort(s.Addr)
|
localIP, localPort, err := parseHostPort(s.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get local IP/Port: %s", err)
|
return fmt.Errorf("failed to get local IP/Port: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get remote IP/Port
|
// get remote IP/Port
|
||||||
remoteIP, remotePort, err := parseHostPort(r.RemoteAddr)
|
remoteIP, remotePort, err := parseHostPort(r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get remote IP/Port: %s", err)
|
return fmt.Errorf("failed to get remote IP/Port: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It is very important that this works, retry extensively (every 250ms for 5s)
|
||||||
|
for tries := 0; tries < 20; tries++ {
|
||||||
|
err = authenticateAPIRequest(
|
||||||
|
r.Context(),
|
||||||
|
&packet.Info{
|
||||||
|
Inbound: false, // outbound as we are looking for the process of the source address
|
||||||
|
Version: packet.IPv4,
|
||||||
|
Protocol: packet.TCP,
|
||||||
|
Src: remoteIP, // source as in the process we are looking for
|
||||||
|
SrcPort: remotePort, // source as in the process we are looking for
|
||||||
|
Dst: localIP,
|
||||||
|
DstPort: localPort,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait a little
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) error {
|
||||||
var procsChecked []string
|
var procsChecked []string
|
||||||
|
|
||||||
// get process
|
// get process
|
||||||
proc, _, err := process.GetProcessByConnection(
|
proc, _, err := process.GetProcessByConnection(ctx, pktInfo)
|
||||||
r.Context(),
|
|
||||||
&packet.Info{
|
|
||||||
Inbound: false, // outbound as we are looking for the process of the source address
|
|
||||||
Version: packet.IPv4,
|
|
||||||
Protocol: packet.TCP,
|
|
||||||
Src: remoteIP, // source as in the process we are looking for
|
|
||||||
SrcPort: remotePort, // source as in the process we are looking for
|
|
||||||
Dst: localIP,
|
|
||||||
DstPort: localPort,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get process: %s", err)
|
return fmt.Errorf("failed to get process: %s", err)
|
||||||
}
|
}
|
||||||
|
originalPid := proc.Pid
|
||||||
|
|
||||||
// go up up to two levels, if we don't match
|
// go up up to two levels, if we don't match
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
// check if the requesting process is in database root / updates dir
|
// check for eligible PID
|
||||||
if strings.HasPrefix(proc.Path, dataRoot.Path) {
|
switch proc.Pid {
|
||||||
return true, nil
|
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 nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add checked process to list
|
// add checked process to list
|
||||||
procsChecked = append(procsChecked, proc.Path)
|
procsChecked = append(procsChecked, proc.Path)
|
||||||
|
|
||||||
|
@ -89,13 +113,34 @@ func apiAuthenticator(s *http.Server, r *http.Request) (grantAccess bool, err er
|
||||||
// get parent process
|
// get parent process
|
||||||
proc, err = process.GetOrFindProcess(context.Background(), proc.ParentPid)
|
proc, err = process.GetOrFindProcess(context.Background(), proc.ParentPid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get process: %s", err)
|
return fmt.Errorf("failed to get process: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("filter: denying api access to %s - also checked %s (trusted root is %s)", procsChecked[0], strings.Join(procsChecked[1:], " "), dataRoot.Path)
|
switch originalPid {
|
||||||
return false, nil
|
case process.UnidentifiedProcessID:
|
||||||
|
log.Warningf("filter: denying api access: failed to identify process")
|
||||||
|
return fmt.Errorf("%wFailed to identify the requesting process. You can enable the Development Mode to disable API authentication for development purposes.", api.ErrAPIAccessDeniedMessage)
|
||||||
|
|
||||||
|
case process.SystemProcessID:
|
||||||
|
log.Warningf("filter: denying api access: request by system")
|
||||||
|
return fmt.Errorf("%wSystem access to the Portmaster API is not permitted. You can enable the Development Mode to disable API authentication for development purposes.", api.ErrAPIAccessDeniedMessage)
|
||||||
|
|
||||||
|
default: //
|
||||||
|
log.Warningf("filter: denying api access to %s - also checked %s (trusted root is %s)", procsChecked[0], strings.Join(procsChecked[1:], " "), dataRoot.Path)
|
||||||
|
return fmt.Errorf(
|
||||||
|
`%wThe requesting process is not authorized to access the Portmaster API.
|
||||||
|
Checked process paths:
|
||||||
|
%s
|
||||||
|
|
||||||
|
The authorithed root path is %s.
|
||||||
|
You can enable the Development Mode to disable API authentication for development purposes.`,
|
||||||
|
api.ErrAPIAccessDeniedMessage,
|
||||||
|
dataRoot.Path,
|
||||||
|
strings.Join(procsChecked, "\n"),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHostPort(address string) (net.IP, uint16, error) {
|
func parseHostPort(address string) (net.IP, uint16, error) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue