+ GET /control/clients/find: add "disallowed" property

This commit is contained in:
Simon Zolin 2020-07-24 14:30:29 +03:00
parent 07db05dd80
commit dd3027afe7
9 changed files with 113 additions and 26 deletions

View File

@ -940,7 +940,7 @@ Error response (Client not found):
### API: Find clients by IP ### API: Find clients by IP
This method returns the list of clients (manual and auto-clients) matching the IP list. This method returns the list of clients (manual and auto-clients) matching the IP list.
For auto-clients only `name`, `ids` and `whois_info` fields are set. Other fields are empty. For auto-clients only `name`, `ids`, `whois_info`, `disallowed` fields are set. Other fields are empty.
Request: Request:
@ -966,11 +966,18 @@ Response:
key: "value" key: "value"
... ...
} }
"disallowed": "..."
} }
} }
... ...
] ]
`disallowed`:
* "": IP is allowed
* not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR)
* "not-in-allowed-list" - IP is disallowed by "allowed IP list"
## DNS general settings ## DNS general settings

View File

@ -80,43 +80,43 @@ func processIPCIDRArray(dst *map[string]bool, dstIPNet *[]net.IPNet, src []strin
} }
// IsBlockedIP - return TRUE if this client should be blocked // IsBlockedIP - return TRUE if this client should be blocked
func (a *accessCtx) IsBlockedIP(ip string) bool { func (a *accessCtx) IsBlockedIP(ip string) (bool, string) {
a.lock.Lock() a.lock.Lock()
defer a.lock.Unlock() defer a.lock.Unlock()
if len(a.allowedClients) != 0 || len(a.allowedClientsIPNet) != 0 { if len(a.allowedClients) != 0 || len(a.allowedClientsIPNet) != 0 {
_, ok := a.allowedClients[ip] _, ok := a.allowedClients[ip]
if ok { if ok {
return false return false, ""
} }
if len(a.allowedClientsIPNet) != 0 { if len(a.allowedClientsIPNet) != 0 {
ipAddr := net.ParseIP(ip) ipAddr := net.ParseIP(ip)
for _, ipnet := range a.allowedClientsIPNet { for _, ipnet := range a.allowedClientsIPNet {
if ipnet.Contains(ipAddr) { if ipnet.Contains(ipAddr) {
return false return false, ""
} }
} }
} }
return true return true, "not-in-allowed-list"
} }
_, ok := a.disallowedClients[ip] _, ok := a.disallowedClients[ip]
if ok { if ok {
return true return true, ip
} }
if len(a.disallowedClientsIPNet) != 0 { if len(a.disallowedClientsIPNet) != 0 {
ipAddr := net.ParseIP(ip) ipAddr := net.ParseIP(ip)
for _, ipnet := range a.disallowedClientsIPNet { for _, ipnet := range a.disallowedClientsIPNet {
if ipnet.Contains(ipAddr) { if ipnet.Contains(ipAddr) {
return true return true, ipnet.String()
} }
} }
} }
return false return false, ""
} }
// IsBlockedDomain - return TRUE if this domain should be blocked // IsBlockedDomain - return TRUE if this domain should be blocked

View File

@ -298,3 +298,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p.ServeHTTP(w, r) p.ServeHTTP(w, r)
} }
} }
// IsBlockedIP - return TRUE if this client should be blocked
func (s *Server) IsBlockedIP(ip string) (bool, string) {
return s.access.IsBlockedIP(ip)
}

View File

@ -878,20 +878,28 @@ func TestIsBlockedIPAllowed(t *testing.T) {
a := &accessCtx{} a := &accessCtx{}
assert.True(t, a.Init([]string{"1.1.1.1", "2.2.0.0/16"}, nil, nil) == nil) assert.True(t, a.Init([]string{"1.1.1.1", "2.2.0.0/16"}, nil, nil) == nil)
assert.True(t, !a.IsBlockedIP("1.1.1.1")) disallowed, _ := a.IsBlockedIP("1.1.1.1")
assert.True(t, a.IsBlockedIP("1.1.1.2")) assert.False(t, disallowed)
assert.True(t, !a.IsBlockedIP("2.2.1.1")) disallowed, _ = a.IsBlockedIP("1.1.1.2")
assert.True(t, a.IsBlockedIP("2.3.1.1")) assert.True(t, disallowed)
disallowed, _ = a.IsBlockedIP("2.2.1.1")
assert.False(t, disallowed)
disallowed, _ = a.IsBlockedIP("2.3.1.1")
assert.True(t, disallowed)
} }
func TestIsBlockedIPDisallowed(t *testing.T) { func TestIsBlockedIPDisallowed(t *testing.T) {
a := &accessCtx{} a := &accessCtx{}
assert.True(t, a.Init(nil, []string{"1.1.1.1", "2.2.0.0/16"}, nil) == nil) assert.True(t, a.Init(nil, []string{"1.1.1.1", "2.2.0.0/16"}, nil) == nil)
assert.True(t, a.IsBlockedIP("1.1.1.1")) disallowed, _ := a.IsBlockedIP("1.1.1.1")
assert.True(t, !a.IsBlockedIP("1.1.1.2")) assert.True(t, disallowed)
assert.True(t, a.IsBlockedIP("2.2.1.1")) disallowed, _ = a.IsBlockedIP("1.1.1.2")
assert.True(t, !a.IsBlockedIP("2.3.1.1")) assert.False(t, disallowed)
disallowed, _ = a.IsBlockedIP("2.2.1.1")
assert.True(t, disallowed)
disallowed, _ = a.IsBlockedIP("2.3.1.1")
assert.False(t, disallowed)
} }
func TestIsBlockedIPBlockedDomain(t *testing.T) { func TestIsBlockedIPBlockedDomain(t *testing.T) {

View File

@ -12,7 +12,8 @@ import (
func (s *Server) beforeRequestHandler(_ *proxy.Proxy, d *proxy.DNSContext) (bool, error) { func (s *Server) beforeRequestHandler(_ *proxy.Proxy, d *proxy.DNSContext) (bool, error) {
ip := ipFromAddr(d.Addr) ip := ipFromAddr(d.Addr)
if s.access.IsBlockedIP(ip) { disallowed, _ := s.access.IsBlockedIP(ip)
if disallowed {
log.Tracef("Client IP %s is blocked by settings", ip) log.Tracef("Client IP %s is blocked by settings", ip)
return false, nil return false, nil
} }

View File

@ -80,6 +80,8 @@ type clientsContainer struct {
// dhcpServer is used for looking up clients IP addresses by MAC addresses // dhcpServer is used for looking up clients IP addresses by MAC addresses
dhcpServer *dhcpd.Server dhcpServer *dhcpd.Server
DNSServer *dnsforward.Server
autoHosts *util.AutoHosts // get entries from system hosts-files autoHosts *util.AutoHosts // get entries from system hosts-files
testing bool // if TRUE, this object is used for internal tests testing bool // if TRUE, this object is used for internal tests

View File

@ -21,6 +21,13 @@ type clientJSON struct {
BlockedServices []string `json:"blocked_services"` BlockedServices []string `json:"blocked_services"`
Upstreams []string `json:"upstreams"` Upstreams []string `json:"upstreams"`
WhoisInfo map[string]interface{} `json:"whois_info"`
// * "": IP is allowed
// * not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR)
// * "not-in-allowed-list" - IP is disallowed by "allowed IP list"
Disallowed string `json:"disallowed"`
} }
type clientHostJSON struct { type clientHostJSON struct {
@ -123,15 +130,9 @@ func clientToJSON(c *Client) clientJSON {
return cj return cj
} }
type clientHostJSONWithID struct {
IDs []string `json:"ids"`
Name string `json:"name"`
WhoisInfo map[string]interface{} `json:"whois_info"`
}
// Convert ClientHost object to JSON // Convert ClientHost object to JSON
func clientHostToJSON(ip string, ch ClientHost) clientHostJSONWithID { func clientHostToJSON(ip string, ch ClientHost) clientJSON {
cj := clientHostJSONWithID{ cj := clientJSON{
Name: ch.Host, Name: ch.Host,
IDs: []string{ip}, IDs: []string{ip},
} }
@ -255,9 +256,21 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
continue // a client with this IP isn't found continue // a client with this IP isn't found
} }
cj := clientHostToJSON(ip, ch) cj := clientHostToJSON(ip, ch)
disallowed, disallowedRule := clients.DNSServer.IsBlockedIP(ip)
if disallowed {
cj.Disallowed = disallowedRule
}
el[ip] = cj el[ip] = cj
} else { } else {
cj := clientToJSON(&c) cj := clientToJSON(&c)
disallowed, disallowedRule := clients.DNSServer.IsBlockedIP(ip)
if disallowed {
cj.Disallowed = disallowedRule
}
el[ip] = cj el[ip] = cj
} }

View File

@ -70,6 +70,7 @@ func initDNSServer() error {
p.DHCPServer = Context.dhcpServer p.DHCPServer = Context.dhcpServer
} }
Context.dnsServer = dnsforward.NewServer(p) Context.dnsServer = dnsforward.NewServer(p)
Context.clients.DNSServer = Context.dnsServer
dnsConfig := generateServerConfig() dnsConfig := generateServerConfig()
err = Context.dnsServer.Prepare(&dnsConfig) err = Context.dnsServer.Prepare(&dnsConfig)
if err != nil { if err != nil {

View File

@ -1757,7 +1757,57 @@ components:
properties: properties:
1.2.3.4: 1.2.3.4:
items: items:
$ref: "#/components/schemas/Client" $ref: "#/components/schemas/ClientFindSubEntry"
ClientFindSubEntry:
type: object
properties:
name:
type: string
description: Name
example: localhost
ids:
type: array
description: IP, CIDR or MAC address
items:
type: string
use_global_settings:
type: boolean
filtering_enabled:
type: boolean
parental_enabled:
type: boolean
safebrowsing_enabled:
type: boolean
safesearch_enabled:
type: boolean
use_global_blocked_services:
type: boolean
blocked_services:
type: array
items:
type: string
upstreams:
type: array
items:
type: string
whois_info:
type: array
items:
$ref: "#/components/schemas/WhoisInfo"
disallowed:
description: >
* "": IP is allowed
* not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR)
* "not-in-allowed-list" - IP is disallowed by "allowed IP list"
type: string
WhoisInfo:
type: object
properties:
key:
type: string
Clients: Clients:
type: object type: object
properties: properties: