mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-03-04 02:14:40 +01:00
allowed ips plugin (#1510)
This commit is contained in:
parent
6b6c6753aa
commit
90a9a9d992
6 changed files with 205 additions and 0 deletions
|
@ -67,6 +67,7 @@ type Config struct {
|
||||||
AllowedName AllowedNameConfig `toml:"allowed_names"`
|
AllowedName AllowedNameConfig `toml:"allowed_names"`
|
||||||
BlockIP BlockIPConfig `toml:"blocked_ips"`
|
BlockIP BlockIPConfig `toml:"blocked_ips"`
|
||||||
BlockIPLegacy BlockIPConfigLegacy `toml:"ip_blacklist"`
|
BlockIPLegacy BlockIPConfigLegacy `toml:"ip_blacklist"`
|
||||||
|
AllowIP AllowIPConfig `toml:"allowed_ips"`
|
||||||
ForwardFile string `toml:"forwarding_rules"`
|
ForwardFile string `toml:"forwarding_rules"`
|
||||||
CloakFile string `toml:"cloaking_rules"`
|
CloakFile string `toml:"cloaking_rules"`
|
||||||
CaptivePortalFile string `toml:"captive_portal_handler"`
|
CaptivePortalFile string `toml:"captive_portal_handler"`
|
||||||
|
@ -213,6 +214,12 @@ type BlockIPConfigLegacy struct {
|
||||||
Format string `toml:"log_format"`
|
Format string `toml:"log_format"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AllowIPConfig struct {
|
||||||
|
File string `toml:"allowed_ips_file"`
|
||||||
|
LogFile string `toml:"log_file"`
|
||||||
|
Format string `toml:"log_format"`
|
||||||
|
}
|
||||||
|
|
||||||
type AnonymizedDNSRouteConfig struct {
|
type AnonymizedDNSRouteConfig struct {
|
||||||
ServerName string `toml:"server_name"`
|
ServerName string `toml:"server_name"`
|
||||||
RelayNames []string `toml:"via"`
|
RelayNames []string `toml:"via"`
|
||||||
|
@ -558,6 +565,18 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
|
||||||
proxy.blockIPFormat = config.BlockIP.Format
|
proxy.blockIPFormat = config.BlockIP.Format
|
||||||
proxy.blockIPLogFile = config.BlockIP.LogFile
|
proxy.blockIPLogFile = config.BlockIP.LogFile
|
||||||
|
|
||||||
|
if len(config.AllowIP.Format) == 0 {
|
||||||
|
config.AllowIP.Format = "tsv"
|
||||||
|
} else {
|
||||||
|
config.AllowIP.Format = strings.ToLower(config.AllowIP.Format)
|
||||||
|
}
|
||||||
|
if config.AllowIP.Format != "tsv" && config.AllowIP.Format != "ltsv" {
|
||||||
|
return errors.New("Unsupported allowed_ips log format")
|
||||||
|
}
|
||||||
|
proxy.allowedIPFile = config.AllowIP.File
|
||||||
|
proxy.allowedIPFormat = config.AllowIP.Format
|
||||||
|
proxy.allowedIPLogFile = config.AllowIP.LogFile
|
||||||
|
|
||||||
proxy.forwardFile = config.ForwardFile
|
proxy.forwardFile = config.ForwardFile
|
||||||
proxy.cloakFile = config.CloakFile
|
proxy.cloakFile = config.CloakFile
|
||||||
proxy.captivePortalFile = config.CaptivePortalFile
|
proxy.captivePortalFile = config.CaptivePortalFile
|
||||||
|
|
7
dnscrypt-proxy/example-allowed-ips.txt
Normal file
7
dnscrypt-proxy/example-allowed-ips.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
##############################
|
||||||
|
# Allowed IPs List #
|
||||||
|
##############################
|
||||||
|
|
||||||
|
#192.168.0.*
|
||||||
|
#fe80:53:* # IPv6 prefix example
|
||||||
|
#81.169.145.105
|
|
@ -545,6 +545,33 @@ cache_neg_max_ttl = 600
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
# Pattern-based allowed IPs lists (blocklists bypass) #
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
## Allowed IP lists support the same patterns as IP blocklists
|
||||||
|
## If an IP response matches an allow ip entry, the corresponding session
|
||||||
|
## will bypass IP filters.
|
||||||
|
##
|
||||||
|
## Time-based rules are also supported to make some websites only accessible at specific times of the day.
|
||||||
|
|
||||||
|
[allowed_ips]
|
||||||
|
|
||||||
|
## Path to the file of allowed ip rules (absolute, or relative to the same directory as the config file)
|
||||||
|
|
||||||
|
# allowed_ips_file = 'allowed-ips.txt'
|
||||||
|
|
||||||
|
|
||||||
|
## Optional path to a file logging allowed queries
|
||||||
|
|
||||||
|
# log_file = 'allowed-ips.log'
|
||||||
|
|
||||||
|
## Optional log format: tsv or ltsv (default: tsv)
|
||||||
|
|
||||||
|
# log_format = 'tsv'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# Time access restrictions #
|
# Time access restrictions #
|
||||||
##########################################
|
##########################################
|
||||||
|
|
146
dnscrypt-proxy/plugin_allow_ip.go
Normal file
146
dnscrypt-proxy/plugin_allow_ip.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
iradix "github.com/hashicorp/go-immutable-radix"
|
||||||
|
"github.com/jedisct1/dlog"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginAllowedIP struct {
|
||||||
|
allowedPrefixes *iradix.Tree
|
||||||
|
allowedIPs map[string]interface{}
|
||||||
|
logger io.Writer
|
||||||
|
format string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Name() string {
|
||||||
|
return "allow_ip"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Description() string {
|
||||||
|
return "Allows DNS queries containing specific IP addresses"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Init(proxy *Proxy) error {
|
||||||
|
dlog.Noticef("Loading the set of allowed IP rules from [%s]", proxy.allowedIPFile)
|
||||||
|
bin, err := ReadTextFile(proxy.allowedIPFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
plugin.allowedPrefixes = iradix.New()
|
||||||
|
plugin.allowedIPs = make(map[string]interface{})
|
||||||
|
for lineNo, line := range strings.Split(string(bin), "\n") {
|
||||||
|
line = TrimAndStripInlineComments(line)
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(line)
|
||||||
|
trailingStar := strings.HasSuffix(line, "*")
|
||||||
|
if len(line) < 2 || (ip != nil && trailingStar) {
|
||||||
|
dlog.Errorf("Suspicious allowed IP rule [%s] at line %d", line, lineNo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if trailingStar {
|
||||||
|
line = line[:len(line)-1]
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(line, ":") || strings.HasSuffix(line, ".") {
|
||||||
|
line = line[:len(line)-1]
|
||||||
|
}
|
||||||
|
if len(line) == 0 {
|
||||||
|
dlog.Errorf("Empty allowed IP rule at line %d", lineNo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(line, "*") {
|
||||||
|
dlog.Errorf("Invalid rule: [%s] - wildcards can only be used as a suffix at line %d", line, lineNo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = strings.ToLower(line)
|
||||||
|
if trailingStar {
|
||||||
|
plugin.allowedPrefixes, _, _ = plugin.allowedPrefixes.Insert([]byte(line), 0)
|
||||||
|
} else {
|
||||||
|
plugin.allowedIPs[line] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(proxy.allowedIPLogFile) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.allowedIPLogFile)
|
||||||
|
plugin.format = proxy.allowedIPFormat
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Drop() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Reload() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *PluginAllowedIP) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
|
||||||
|
answers := msg.Answer
|
||||||
|
if len(answers) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
allowed, reason, ipStr := false, "", ""
|
||||||
|
for _, answer := range answers {
|
||||||
|
header := answer.Header()
|
||||||
|
Rrtype := header.Rrtype
|
||||||
|
if header.Class != dns.ClassINET || (Rrtype != dns.TypeA && Rrtype != dns.TypeAAAA) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if Rrtype == dns.TypeA {
|
||||||
|
ipStr = answer.(*dns.A).A.String()
|
||||||
|
} else if Rrtype == dns.TypeAAAA {
|
||||||
|
ipStr = answer.(*dns.AAAA).AAAA.String() // IPv4-mapped IPv6 addresses are converted to IPv4
|
||||||
|
}
|
||||||
|
if _, found := plugin.allowedIPs[ipStr]; found {
|
||||||
|
allowed, reason = true, ipStr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
match, _, found := plugin.allowedPrefixes.Root().LongestPrefix([]byte(ipStr))
|
||||||
|
if found {
|
||||||
|
if len(match) == len(ipStr) || (ipStr[len(match)] == '.' || ipStr[len(match)] == ':') {
|
||||||
|
allowed, reason = true, string(match)+"*"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allowed {
|
||||||
|
pluginsState.sessionData["whitelisted"] = true
|
||||||
|
if plugin.logger != nil {
|
||||||
|
qName := pluginsState.qName
|
||||||
|
var clientIPStr string
|
||||||
|
if pluginsState.clientProto == "udp" {
|
||||||
|
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
|
||||||
|
} else {
|
||||||
|
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
|
||||||
|
}
|
||||||
|
var line string
|
||||||
|
if plugin.format == "tsv" {
|
||||||
|
now := time.Now()
|
||||||
|
year, month, day := now.Date()
|
||||||
|
hour, minute, second := now.Clock()
|
||||||
|
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
|
||||||
|
line = fmt.Sprintf("%s\t%s\t%s\t%s\t%s\n", tsStr, clientIPStr, StringQuote(qName), StringQuote(ipStr), StringQuote(reason))
|
||||||
|
} else if plugin.format == "ltsv" {
|
||||||
|
line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tip:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(ipStr), StringQuote(reason))
|
||||||
|
} else {
|
||||||
|
dlog.Fatalf("Unexpected log format: [%s]", plugin.format)
|
||||||
|
}
|
||||||
|
if plugin.logger == nil {
|
||||||
|
return errors.New("Log file not initialized")
|
||||||
|
}
|
||||||
|
_, _ = plugin.logger.Write([]byte(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -132,6 +132,9 @@ func (proxy *Proxy) InitPluginsGlobals() error {
|
||||||
if len(proxy.nxLogFile) != 0 {
|
if len(proxy.nxLogFile) != 0 {
|
||||||
*responsePlugins = append(*responsePlugins, Plugin(new(PluginNxLog)))
|
*responsePlugins = append(*responsePlugins, Plugin(new(PluginNxLog)))
|
||||||
}
|
}
|
||||||
|
if len(proxy.allowedIPFile) != 0 {
|
||||||
|
*responsePlugins = append(*responsePlugins, Plugin(new(PluginAllowedIP)))
|
||||||
|
}
|
||||||
if len(proxy.blockNameFile) != 0 {
|
if len(proxy.blockNameFile) != 0 {
|
||||||
*responsePlugins = append(*responsePlugins, Plugin(new(PluginBlockNameResponse)))
|
*responsePlugins = append(*responsePlugins, Plugin(new(PluginBlockNameResponse)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,9 @@ type Proxy struct {
|
||||||
forwardFile string
|
forwardFile string
|
||||||
blockIPFormat string
|
blockIPFormat string
|
||||||
blockIPLogFile string
|
blockIPLogFile string
|
||||||
|
allowedIPFile string
|
||||||
|
allowedIPFormat string
|
||||||
|
allowedIPLogFile string
|
||||||
queryLogFormat string
|
queryLogFormat string
|
||||||
blockIPFile string
|
blockIPFile string
|
||||||
whitelistNameFormat string
|
whitelistNameFormat string
|
||||||
|
|
Loading…
Add table
Reference in a new issue