config refactoring
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 45s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 40s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 41s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 40s
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 45s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 40s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 41s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 40s
This commit is contained in:
parent
5e909ac18d
commit
07c07b6aba
@ -70,30 +70,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer removePIDFile()
|
defer removePIDFile()
|
||||||
|
|
||||||
cfg := models.ConfigFile{}
|
cfg := models.Config{}
|
||||||
|
|
||||||
cfgFile, err := os.ReadFile(cfgFileLocation)
|
cfgFile, err := os.ReadFile(cfgFileLocation)
|
||||||
if err == nil {
|
|
||||||
err = yaml.Unmarshal(cfgFile, &cfg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to parse config.yaml")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
log.Fatal().Err(err).Msg("failed to read config.yaml")
|
log.Fatal().Err(err).Msg("failed to read config.yaml")
|
||||||
}
|
}
|
||||||
cfg = models.ConfigFile{
|
cfg = models.Config{
|
||||||
AppConfig: models.AppConfig{
|
ConfigVersion: "0.1.0",
|
||||||
LogLevel: "info",
|
App: kvas2.DefaultAppConfig,
|
||||||
AdditionalTTL: 216000,
|
|
||||||
ChainPrefix: "KVAS2_",
|
|
||||||
IPSetPrefix: "kvas2_",
|
|
||||||
LinkName: "br0",
|
|
||||||
TargetDNSServerAddress: "127.0.0.1",
|
|
||||||
TargetDNSServerPort: 53,
|
|
||||||
ListenDNSPort: 3553,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
out, err := yaml.Marshal(cfg)
|
out, err := yaml.Marshal(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -107,9 +92,14 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to save config.yaml")
|
log.Fatal().Err(err).Msg("failed to save config.yaml")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
err = yaml.Unmarshal(cfgFile, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("failed to parse config.yaml")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch cfg.AppConfig.LogLevel {
|
switch cfg.App.LogLevel {
|
||||||
case "trace":
|
case "trace":
|
||||||
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
||||||
case "debug":
|
case "debug":
|
||||||
@ -132,18 +122,18 @@ func main() {
|
|||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err := kvas2.New(cfg)
|
app := kvas2.New()
|
||||||
|
err = app.ImportConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to initialize application")
|
log.Fatal().Err(err).Msg("failed to import config")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
log.Info().Msg("starting service")
|
log.Info().Msg("starting service")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Starting app with graceful shutdown
|
Starting app with graceful shutdown
|
||||||
*/
|
*/
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
appResult := make(chan error)
|
appResult := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
appResult <- app.Start(ctx)
|
appResult <- app.Start(ctx)
|
||||||
|
@ -12,33 +12,33 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DNSMITMProxy struct {
|
type DNSMITMProxy struct {
|
||||||
TargetDNSServerAddress string
|
UpstreamDNSAddress string
|
||||||
TargetDNSServerPort uint16
|
UpstreamDNSPort uint16
|
||||||
|
|
||||||
RequestHook func(net.Addr, dns.Msg, string) (*dns.Msg, *dns.Msg, error)
|
RequestHook func(net.Addr, dns.Msg, string) (*dns.Msg, *dns.Msg, error)
|
||||||
ResponseHook func(net.Addr, dns.Msg, dns.Msg, string) (*dns.Msg, error)
|
ResponseHook func(net.Addr, dns.Msg, dns.Msg, string) (*dns.Msg, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
|
func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
|
||||||
serverConn, err := net.Dial(network, fmt.Sprintf("%s:%d", p.TargetDNSServerAddress, p.TargetDNSServerPort))
|
upstreamConn, err := net.Dial(network, fmt.Sprintf("%s:%d", p.UpstreamDNSAddress, p.UpstreamDNSPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to dial DNS server: %w", err)
|
return nil, fmt.Errorf("failed to dial DNS upstream: %w", err)
|
||||||
}
|
}
|
||||||
defer func() { _ = serverConn.Close() }()
|
defer func() { _ = upstreamConn.Close() }()
|
||||||
|
|
||||||
err = serverConn.SetDeadline(time.Now().Add(time.Second * 5))
|
err = upstreamConn.SetDeadline(time.Now().Add(time.Second * 5))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to set deadline: %w", err)
|
return nil, fmt.Errorf("failed to set deadline: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if network == "tcp" {
|
if network == "tcp" {
|
||||||
err = binary.Write(serverConn, binary.BigEndian, uint16(len(req)))
|
err = binary.Write(upstreamConn, binary.BigEndian, uint16(len(req)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to write length: %w", err)
|
return nil, fmt.Errorf("failed to write length: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := serverConn.Write(req)
|
n, err := upstreamConn.Write(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to write request: %w", err)
|
return nil, fmt.Errorf("failed to write request: %w", err)
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
|
|||||||
var resp []byte
|
var resp []byte
|
||||||
if network == "tcp" {
|
if network == "tcp" {
|
||||||
var respLen uint16
|
var respLen uint16
|
||||||
err = binary.Read(serverConn, binary.BigEndian, &respLen)
|
err = binary.Read(upstreamConn, binary.BigEndian, &respLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read length: %w", err)
|
return nil, fmt.Errorf("failed to read length: %w", err)
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
|
|||||||
resp = make([]byte, 512)
|
resp = make([]byte, 512)
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err = serverConn.Read(resp)
|
n, err = upstreamConn.Read(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||||
}
|
}
|
||||||
@ -213,9 +213,3 @@ func (p DNSMITMProxy) ListenUDP(ctx context.Context, addr *net.UDPAddr) error {
|
|||||||
}(conn, clientAddr)
|
}(conn, clientAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *DNSMITMProxy {
|
|
||||||
return &DNSMITMProxy{
|
|
||||||
TargetDNSServerPort: 53,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
*models.Group
|
models.Group
|
||||||
|
|
||||||
enabled bool
|
enabled bool
|
||||||
iptables *iptables.IPTables
|
iptables *iptables.IPTables
|
||||||
@ -126,9 +126,14 @@ func (g *Group) Sync(records *records.Records) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for addr, ttl := range addresses {
|
for addr, ttl := range addresses {
|
||||||
// TODO: Check TTL
|
|
||||||
if _, exists := currentAddresses[addr]; exists {
|
if _, exists := currentAddresses[addr]; exists {
|
||||||
|
if currentAddresses[addr] == nil {
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
if ttl < *currentAddresses[addr] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ip := net.IP(addr)
|
ip := net.IP(addr)
|
||||||
err = g.AddIP(ip, ttl)
|
err = g.AddIP(ip, ttl)
|
||||||
@ -182,7 +187,7 @@ func (g *Group) LinkUpdateHook(event netlink.LinkUpdate) error {
|
|||||||
return g.ipsetToLink.LinkUpdateHook(event)
|
return g.ipsetToLink.LinkUpdateHook(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGroup(group *models.Group, nh4 *netfilterHelper.NetfilterHelper, chainPrefix, ipsetNamePrefix string) (*Group, error) {
|
func NewGroup(group models.Group, nh4 *netfilterHelper.NetfilterHelper, chainPrefix, ipsetNamePrefix string) (*Group, error) {
|
||||||
ipsetName := fmt.Sprintf("%s%8x", ipsetNamePrefix, group.ID)
|
ipsetName := fmt.Sprintf("%s%8x", ipsetNamePrefix, group.ID)
|
||||||
ipset, err := nh4.IPSet(ipsetName)
|
ipset, err := nh4.IPSet(ipsetName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
337
kvas2.go
337
kvas2.go
@ -2,13 +2,10 @@ package kvas2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -28,34 +25,39 @@ var (
|
|||||||
ErrAlreadyRunning = errors.New("already running")
|
ErrAlreadyRunning = errors.New("already running")
|
||||||
ErrGroupIDConflict = errors.New("group id conflict")
|
ErrGroupIDConflict = errors.New("group id conflict")
|
||||||
ErrRuleIDConflict = errors.New("rule id conflict")
|
ErrRuleIDConflict = errors.New("rule id conflict")
|
||||||
|
ErrConfigUnsupportedVersion = errors.New("config unsupported version")
|
||||||
)
|
)
|
||||||
|
|
||||||
func randomId() [4]byte {
|
var DefaultAppConfig = models.App{
|
||||||
id := make([]byte, 4)
|
DNSProxy: models.DNSProxy{
|
||||||
binary.BigEndian.PutUint32(id, rand.Uint32())
|
Host: models.DNSProxyServer{Address: "[::]", Port: 3553},
|
||||||
return [4]byte(id)
|
Upstream: models.DNSProxyServer{Address: "127.0.0.1", Port: 53},
|
||||||
}
|
DisableRemap53: false,
|
||||||
|
DisableFakePTR: false,
|
||||||
type Config struct {
|
DisableDropAAAA: false,
|
||||||
AdditionalTTL uint32
|
},
|
||||||
ChainPrefix string
|
Netfilter: models.Netfilter{
|
||||||
IPSetPrefix string
|
IPTables: models.IPTables{
|
||||||
LinkName string
|
ChainPrefix: "KVAS2_",
|
||||||
TargetDNSServerAddress string
|
},
|
||||||
TargetDNSServerPort uint16
|
IPSet: models.IPSet{
|
||||||
ListenDNSPort uint16
|
TablePrefix: "kvas2_",
|
||||||
|
AdditionalTTL: 3600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Link: []string{"br0"},
|
||||||
|
LogLevel: "info",
|
||||||
}
|
}
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
Config Config
|
config models.App
|
||||||
|
unprocessedGroups []models.Group
|
||||||
|
|
||||||
DNSMITM *dnsMitmProxy.DNSMITMProxy
|
dnsMITM *dnsMitmProxy.DNSMITMProxy
|
||||||
NetfilterHelper4 *netfilterHelper.NetfilterHelper
|
nfHelper4 *netfilterHelper.NetfilterHelper
|
||||||
NetfilterHelper6 *netfilterHelper.NetfilterHelper
|
nfHelper6 *netfilterHelper.NetfilterHelper
|
||||||
Records *records.Records
|
records *records.Records
|
||||||
Groups []*group.Group
|
groups []*group.Group
|
||||||
|
|
||||||
Link netlink.Link
|
|
||||||
|
|
||||||
isRunning bool
|
isRunning bool
|
||||||
dnsOverrider4 *netfilterHelper.PortRemap
|
dnsOverrider4 *netfilterHelper.PortRemap
|
||||||
@ -63,7 +65,6 @@ type App struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) handleLink(event netlink.LinkUpdate) {
|
func (a *App) handleLink(event netlink.LinkUpdate) {
|
||||||
|
|
||||||
switch event.Change {
|
switch event.Change {
|
||||||
case 0x00000001:
|
case 0x00000001:
|
||||||
log.Trace().
|
log.Trace().
|
||||||
@ -71,7 +72,7 @@ func (a *App) handleLink(event netlink.LinkUpdate) {
|
|||||||
Int("change", int(event.Change)).
|
Int("change", int(event.Change)).
|
||||||
Msg("interface event")
|
Msg("interface event")
|
||||||
ifaceName := event.Link.Attrs().Name
|
ifaceName := event.Link.Attrs().Name
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
if group.Interface != ifaceName {
|
if group.Interface != ifaceName {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -98,6 +99,72 @@ func (a *App) handleLink(event netlink.LinkUpdate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) start(ctx context.Context) (err error) {
|
func (a *App) start(ctx context.Context) (err error) {
|
||||||
|
a.dnsMITM = &dnsMitmProxy.DNSMITMProxy{
|
||||||
|
UpstreamDNSAddress: a.config.DNSProxy.Upstream.Address,
|
||||||
|
UpstreamDNSPort: a.config.DNSProxy.Upstream.Port,
|
||||||
|
RequestHook: func(clientAddr net.Addr, reqMsg dns.Msg, network string) (*dns.Msg, *dns.Msg, error) {
|
||||||
|
if a.config.DNSProxy.DisableFakePTR {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Проверить на интерфейс
|
||||||
|
if len(reqMsg.Question) == 1 && reqMsg.Question[0].Qtype == dns.TypePTR {
|
||||||
|
respMsg := &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: reqMsg.Id,
|
||||||
|
Response: true,
|
||||||
|
RecursionAvailable: true,
|
||||||
|
Rcode: dns.RcodeNameError,
|
||||||
|
},
|
||||||
|
Question: reqMsg.Question,
|
||||||
|
}
|
||||||
|
return nil, respMsg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, nil
|
||||||
|
},
|
||||||
|
ResponseHook: func(clientAddr net.Addr, reqMsg dns.Msg, respMsg dns.Msg, network string) (*dns.Msg, error) {
|
||||||
|
defer a.handleMessage(respMsg, clientAddr, &network)
|
||||||
|
|
||||||
|
if a.config.DNSProxy.DisableDropAAAA {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx int
|
||||||
|
for _, answer := range respMsg.Answer {
|
||||||
|
if answer.Header().Rrtype == dns.TypeAAAA {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
respMsg.Answer[idx] = answer
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
respMsg.Answer = respMsg.Answer[:idx]
|
||||||
|
|
||||||
|
return &respMsg, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.records = records.New()
|
||||||
|
|
||||||
|
nh4, err := netfilterHelper.New(false)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("netfilter helper init fail: %w", err)
|
||||||
|
}
|
||||||
|
err = nh4.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to clear iptables: %w", err)
|
||||||
|
}
|
||||||
|
a.nfHelper4 = nh4
|
||||||
|
|
||||||
|
nh6, err := netfilterHelper.New(true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("netfilter helper init fail: %w", err)
|
||||||
|
}
|
||||||
|
err = nh6.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to clear iptables: %w", err)
|
||||||
|
}
|
||||||
|
a.nfHelper6 = nh6
|
||||||
|
|
||||||
newCtx, cancel := context.WithCancel(ctx)
|
newCtx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@ -108,12 +175,12 @@ func (a *App) start(ctx context.Context) (err error) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
addr, err := net.ResolveUDPAddr("udp", "[::]:"+strconv.Itoa(int(a.Config.ListenDNSPort)))
|
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- fmt.Errorf("failed to resolve udp address: %v", err)
|
errChan <- fmt.Errorf("failed to resolve udp address: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = a.DNSMITM.ListenUDP(newCtx, addr)
|
err = a.dnsMITM.ListenUDP(newCtx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- fmt.Errorf("failed to serve DNS UDP proxy: %v", err)
|
errChan <- fmt.Errorf("failed to serve DNS UDP proxy: %v", err)
|
||||||
return
|
return
|
||||||
@ -121,49 +188,65 @@ func (a *App) start(ctx context.Context) (err error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
addr, err := net.ResolveTCPAddr("tcp", "[::]:"+strconv.Itoa(int(a.Config.ListenDNSPort)))
|
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- fmt.Errorf("failed to resolve tcp address: %v", err)
|
errChan <- fmt.Errorf("failed to resolve tcp address: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = a.DNSMITM.ListenTCP(newCtx, addr)
|
err = a.dnsMITM.ListenTCP(newCtx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- fmt.Errorf("failed to serve DNS TCP proxy: %v", err)
|
errChan <- fmt.Errorf("failed to serve DNS TCP proxy: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
addrList, err := netlink.AddrList(a.Link, nl.FAMILY_ALL)
|
var addrList []netlink.Addr
|
||||||
|
for _, linkName := range a.config.Link {
|
||||||
|
link, err := netlink.LinkByName(linkName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find link %s: %w", linkName, err)
|
||||||
|
}
|
||||||
|
linkAddrList, err := netlink.AddrList(link, nl.FAMILY_ALL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list address of interface: %w", err)
|
return fmt.Errorf("failed to list address of interface: %w", err)
|
||||||
}
|
}
|
||||||
|
addrList = append(addrList, linkAddrList...)
|
||||||
|
}
|
||||||
|
|
||||||
a.dnsOverrider4 = a.NetfilterHelper4.PortRemap(fmt.Sprintf("%sDNSOR", a.Config.ChainPrefix), 53, a.Config.ListenDNSPort, addrList)
|
if !a.config.DNSProxy.DisableRemap53 {
|
||||||
|
a.dnsOverrider4 = a.nfHelper4.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
|
||||||
err = a.dnsOverrider4.Enable()
|
err = a.dnsOverrider4.Enable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to override DNS (IPv4): %v", err)
|
return fmt.Errorf("failed to override DNS (IPv4): %v", err)
|
||||||
}
|
}
|
||||||
defer func() { _ = a.dnsOverrider4.Disable() }()
|
defer func() { _ = a.dnsOverrider4.Disable() }()
|
||||||
|
|
||||||
a.dnsOverrider6 = a.NetfilterHelper6.PortRemap(fmt.Sprintf("%sDNSOR", a.Config.ChainPrefix), 53, a.Config.ListenDNSPort, addrList)
|
a.dnsOverrider6 = a.nfHelper6.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
|
||||||
err = a.dnsOverrider6.Enable()
|
err = a.dnsOverrider6.Enable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to override DNS (IPv6): %v", err)
|
return fmt.Errorf("failed to override DNS (IPv6): %v", err)
|
||||||
}
|
}
|
||||||
defer func() { _ = a.dnsOverrider6.Disable() }()
|
defer func() { _ = a.dnsOverrider6.Disable() }()
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Groups
|
Groups
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.unprocessedGroups {
|
||||||
|
err := a.AddGroup(group)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, group := range a.groups {
|
||||||
err = group.Enable()
|
err = group.Enable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to enable group: %w", err)
|
return fmt.Errorf("failed to enable group: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
_ = group.Destroy()
|
_ = group.Destroy()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -219,7 +302,7 @@ func (a *App) start(ctx context.Context) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
||||||
}
|
}
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
err := group.NetfilterDHook(args[2])
|
err := group.NetfilterDHook(args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
||||||
@ -281,8 +364,8 @@ func (a *App) Start(ctx context.Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AddGroup(groupModel *models.Group) error {
|
func (a *App) AddGroup(groupModel models.Group) error {
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
if groupModel.ID == group.ID {
|
if groupModel.ID == group.ID {
|
||||||
return ErrGroupIDConflict
|
return ErrGroupIDConflict
|
||||||
}
|
}
|
||||||
@ -295,15 +378,18 @@ func (a *App) AddGroup(groupModel *models.Group) error {
|
|||||||
dup[rule.ID] = struct{}{}
|
dup[rule.ID] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
grp, err := group.NewGroup(groupModel, a.NetfilterHelper4, a.Config.ChainPrefix, a.Config.IPSetPrefix)
|
grp, err := group.NewGroup(groupModel, a.nfHelper4, a.config.Netfilter.IPTables.ChainPrefix, a.config.Netfilter.IPSet.TablePrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create group: %w", err)
|
return fmt.Errorf("failed to create group: %w", err)
|
||||||
}
|
}
|
||||||
a.Groups = append(a.Groups, grp)
|
a.groups = append(a.groups, grp)
|
||||||
|
|
||||||
log.Debug().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group")
|
log.Debug().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group")
|
||||||
|
|
||||||
return grp.Sync(a.Records)
|
if a.isRunning {
|
||||||
|
return grp.Sync(a.records)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ListInterfaces() ([]net.Interface, error) {
|
func (a *App) ListInterfaces() ([]net.Interface, error) {
|
||||||
@ -341,12 +427,12 @@ func (a *App) processARecord(aRecord dns.A, clientAddr net.Addr, network *string
|
|||||||
Str("network", networkStr).
|
Str("network", networkStr).
|
||||||
Msg("processing a record")
|
Msg("processing a record")
|
||||||
|
|
||||||
ttlDuration := aRecord.Hdr.Ttl + a.Config.AdditionalTTL
|
ttlDuration := aRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
|
||||||
|
|
||||||
a.Records.AddARecord(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1], aRecord.A, ttlDuration)
|
a.records.AddARecord(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1], aRecord.A, ttlDuration)
|
||||||
|
|
||||||
names := a.Records.GetAliases(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1])
|
names := a.records.GetAliases(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1])
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
Rule:
|
Rule:
|
||||||
for _, domain := range group.Rules {
|
for _, domain := range group.Rules {
|
||||||
if !domain.IsEnabled() {
|
if !domain.IsEnabled() {
|
||||||
@ -392,15 +478,15 @@ func (a *App) processCNameRecord(cNameRecord dns.CNAME, clientAddr net.Addr, net
|
|||||||
Str("network", networkStr).
|
Str("network", networkStr).
|
||||||
Msg("processing cname record")
|
Msg("processing cname record")
|
||||||
|
|
||||||
ttlDuration := cNameRecord.Hdr.Ttl + a.Config.AdditionalTTL
|
ttlDuration := cNameRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
|
||||||
|
|
||||||
a.Records.AddCNameRecord(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1], cNameRecord.Target[:len(cNameRecord.Target)-1], ttlDuration)
|
a.records.AddCNameRecord(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1], cNameRecord.Target[:len(cNameRecord.Target)-1], ttlDuration)
|
||||||
|
|
||||||
// TODO: Optimization
|
// TODO: Optimization
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
aRecords := a.Records.GetARecords(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
aRecords := a.records.GetARecords(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
||||||
names := a.Records.GetAliases(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
names := a.records.GetAliases(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
||||||
for _, group := range a.Groups {
|
for _, group := range a.groups {
|
||||||
Rule:
|
Rule:
|
||||||
for _, domain := range group.Rules {
|
for _, domain := range group.Rules {
|
||||||
if !domain.IsEnabled() {
|
if !domain.IsEnabled() {
|
||||||
@ -446,124 +532,51 @@ func (a *App) handleMessage(msg dns.Msg, clientAddr net.Addr, network *string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ImportConfig(cfg models.ConfigFile) error {
|
func (a *App) ImportConfig(cfg models.Config) error {
|
||||||
a.Config = Config{
|
if !strings.HasPrefix(cfg.ConfigVersion, "0.1.") {
|
||||||
AdditionalTTL: cfg.AppConfig.AdditionalTTL,
|
return ErrConfigUnsupportedVersion
|
||||||
ChainPrefix: cfg.AppConfig.ChainPrefix,
|
|
||||||
IPSetPrefix: cfg.AppConfig.IPSetPrefix,
|
|
||||||
LinkName: cfg.AppConfig.LinkName,
|
|
||||||
TargetDNSServerAddress: cfg.AppConfig.TargetDNSServerAddress,
|
|
||||||
TargetDNSServerPort: cfg.AppConfig.TargetDNSServerPort,
|
|
||||||
ListenDNSPort: cfg.AppConfig.ListenDNSPort,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.App.DNSProxy.Upstream.Address != "" {
|
||||||
|
a.config.DNSProxy.Upstream.Address = cfg.App.DNSProxy.Upstream.Address
|
||||||
|
}
|
||||||
|
if cfg.App.DNSProxy.Upstream.Port != 0 {
|
||||||
|
a.config.DNSProxy.Upstream.Port = cfg.App.DNSProxy.Upstream.Port
|
||||||
|
}
|
||||||
|
if cfg.App.DNSProxy.Host.Address != "" {
|
||||||
|
a.config.DNSProxy.Host.Address = cfg.App.DNSProxy.Host.Address
|
||||||
|
}
|
||||||
|
if cfg.App.DNSProxy.Host.Port != 0 {
|
||||||
|
a.config.DNSProxy.Host.Port = cfg.App.DNSProxy.Host.Port
|
||||||
|
}
|
||||||
|
a.config.DNSProxy.DisableRemap53 = cfg.App.DNSProxy.DisableRemap53
|
||||||
|
a.config.DNSProxy.DisableFakePTR = cfg.App.DNSProxy.DisableFakePTR
|
||||||
|
a.config.DNSProxy.DisableDropAAAA = cfg.App.DNSProxy.DisableDropAAAA
|
||||||
|
if cfg.App.Netfilter.IPTables.ChainPrefix != "" {
|
||||||
|
a.config.Netfilter.IPTables.ChainPrefix = cfg.App.Netfilter.IPTables.ChainPrefix
|
||||||
|
}
|
||||||
|
if cfg.App.Netfilter.IPSet.TablePrefix != "" {
|
||||||
|
a.config.Netfilter.IPSet.TablePrefix = cfg.App.Netfilter.IPSet.TablePrefix
|
||||||
|
}
|
||||||
|
a.config.Netfilter.IPSet.AdditionalTTL = cfg.App.Netfilter.IPSet.AdditionalTTL
|
||||||
|
|
||||||
|
a.unprocessedGroups = cfg.Groups
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ExportConfig() models.ConfigFile {
|
func (a *App) ExportConfig() models.Config {
|
||||||
groups := make([]models.Group, len(a.Groups))
|
groups := make([]models.Group, len(a.groups))
|
||||||
for idx, group := range a.Groups {
|
for idx, group := range a.groups {
|
||||||
groups[idx] = *group.Group
|
groups[idx] = group.Group
|
||||||
}
|
}
|
||||||
return models.ConfigFile{
|
return models.Config{
|
||||||
AppConfig: models.AppConfig{
|
ConfigVersion: "0.1.0",
|
||||||
AdditionalTTL: a.Config.AdditionalTTL,
|
App: a.config,
|
||||||
ChainPrefix: a.Config.ChainPrefix,
|
|
||||||
IPSetPrefix: a.Config.IPSetPrefix,
|
|
||||||
LinkName: a.Config.LinkName,
|
|
||||||
TargetDNSServerAddress: a.Config.TargetDNSServerAddress,
|
|
||||||
TargetDNSServerPort: a.Config.TargetDNSServerPort,
|
|
||||||
ListenDNSPort: a.Config.ListenDNSPort,
|
|
||||||
},
|
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config models.ConfigFile) (*App, error) {
|
func New() *App {
|
||||||
var err error
|
return &App{config: DefaultAppConfig}
|
||||||
|
|
||||||
app := &App{}
|
|
||||||
|
|
||||||
app.Config = Config{
|
|
||||||
AdditionalTTL: config.AppConfig.AdditionalTTL,
|
|
||||||
ChainPrefix: config.AppConfig.ChainPrefix,
|
|
||||||
IPSetPrefix: config.AppConfig.IPSetPrefix,
|
|
||||||
LinkName: config.AppConfig.LinkName,
|
|
||||||
TargetDNSServerAddress: config.AppConfig.TargetDNSServerAddress,
|
|
||||||
TargetDNSServerPort: config.AppConfig.TargetDNSServerPort,
|
|
||||||
ListenDNSPort: config.AppConfig.ListenDNSPort,
|
|
||||||
}
|
|
||||||
|
|
||||||
app.DNSMITM = dnsMitmProxy.New()
|
|
||||||
app.DNSMITM.TargetDNSServerAddress = app.Config.TargetDNSServerAddress
|
|
||||||
app.DNSMITM.TargetDNSServerPort = app.Config.TargetDNSServerPort
|
|
||||||
app.DNSMITM.RequestHook = func(clientAddr net.Addr, reqMsg dns.Msg, network string) (*dns.Msg, *dns.Msg, error) {
|
|
||||||
// TODO: Need to understand why it not works in proxy mode
|
|
||||||
if len(reqMsg.Question) == 1 && reqMsg.Question[0].Qtype == dns.TypePTR {
|
|
||||||
respMsg := &dns.Msg{
|
|
||||||
MsgHdr: dns.MsgHdr{
|
|
||||||
Id: reqMsg.Id,
|
|
||||||
Response: true,
|
|
||||||
RecursionAvailable: true,
|
|
||||||
Rcode: dns.RcodeNameError,
|
|
||||||
},
|
|
||||||
Question: reqMsg.Question,
|
|
||||||
}
|
|
||||||
return nil, respMsg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
app.DNSMITM.ResponseHook = func(clientAddr net.Addr, reqMsg dns.Msg, respMsg dns.Msg, network string) (*dns.Msg, error) {
|
|
||||||
// TODO: Make it optional
|
|
||||||
var idx int
|
|
||||||
for _, a := range respMsg.Answer {
|
|
||||||
if a.Header().Rrtype == dns.TypeAAAA {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
respMsg.Answer[idx] = a
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
respMsg.Answer = respMsg.Answer[:idx]
|
|
||||||
|
|
||||||
app.handleMessage(respMsg, clientAddr, &network)
|
|
||||||
|
|
||||||
return &respMsg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Records = records.New()
|
|
||||||
|
|
||||||
link, err := netlink.LinkByName(app.Config.LinkName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to find link %s: %w", app.Config.LinkName, err)
|
|
||||||
}
|
|
||||||
app.Link = link
|
|
||||||
|
|
||||||
nh4, err := netfilterHelper.New(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("netfilter helper init fail: %w", err)
|
|
||||||
}
|
|
||||||
app.NetfilterHelper4 = nh4
|
|
||||||
err = app.NetfilterHelper4.CleanIPTables(app.Config.ChainPrefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to clear iptables: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nh6, err := netfilterHelper.New(true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("netfilter helper init fail: %w", err)
|
|
||||||
}
|
|
||||||
app.NetfilterHelper6 = nh6
|
|
||||||
err = app.NetfilterHelper6.CleanIPTables(app.Config.ChainPrefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to clear iptables: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, group := range config.Groups {
|
|
||||||
err = app.AddGroup(&group)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return app, nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,41 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type ConfigFile struct {
|
type Config struct {
|
||||||
AppConfig AppConfig `yaml:"appConfig"`
|
ConfigVersion string `yaml:"configVersion"`
|
||||||
|
App App `yaml:"app"`
|
||||||
Groups []Group `yaml:"groups"`
|
Groups []Group `yaml:"groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppConfig struct {
|
type App struct {
|
||||||
|
DNSProxy DNSProxy `yaml:"dnsProxy"`
|
||||||
|
Netfilter Netfilter `yaml:"netfilter"`
|
||||||
|
Link []string `yaml:"link"`
|
||||||
LogLevel string `yaml:"logLevel"`
|
LogLevel string `yaml:"logLevel"`
|
||||||
AdditionalTTL uint32 `yaml:"additionalTTL"`
|
}
|
||||||
ChainPrefix string `yaml:"chainPrefix"`
|
|
||||||
IPSetPrefix string `yaml:"ipsetPrefix"`
|
type DNSProxy struct {
|
||||||
LinkName string `yaml:"linkName"`
|
Host DNSProxyServer `yaml:"host"`
|
||||||
TargetDNSServerAddress string `yaml:"targetDNSServerAddress"`
|
Upstream DNSProxyServer `yaml:"upstream"`
|
||||||
TargetDNSServerPort uint16 `yaml:"targetDNSServerPort"`
|
DisableRemap53 bool `yaml:"disableRemap53"`
|
||||||
ListenDNSPort uint16 `yaml:"listenDNSPort"`
|
DisableFakePTR bool `yaml:"disableDropPTR"`
|
||||||
|
DisableDropAAAA bool `yaml:"disableDropAAAA"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSProxyServer struct {
|
||||||
|
Address string `yaml:"address"`
|
||||||
|
Port uint16 `yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Netfilter struct {
|
||||||
|
IPTables IPTables `yaml:"iptables"`
|
||||||
|
IPSet IPSet `yaml:"ipset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPTables struct {
|
||||||
|
ChainPrefix string `yaml:"chainPrefix"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPSet struct {
|
||||||
|
TablePrefix string `yaml:"tablePrefix"`
|
||||||
|
AdditionalTTL uint32 `yaml:"additionalTTL"`
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
)
|
)
|
||||||
@ -133,7 +134,7 @@ func (r *IPSetToLink) insertIPRoute() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Нормально отлавливать ошибку
|
// TODO: Нормально отлавливать ошибку
|
||||||
if err.Error() == "Link not found" {
|
if err.Error() == "Link not found" {
|
||||||
// TODO: Логи
|
log.Debug().Str("iface", r.IfaceName).Msg("interface not found (waiting for it to exist)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("error while getting interface: %w", err)
|
return fmt.Errorf("error while getting interface: %w", err)
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
appConfig:
|
configVersion: 0.1.0
|
||||||
additionalTTL: 216000
|
app:
|
||||||
|
dnsProxy:
|
||||||
|
host:
|
||||||
|
address: '[::]'
|
||||||
|
port: 3553
|
||||||
|
upstream:
|
||||||
|
address: 127.0.0.1
|
||||||
|
port: 53
|
||||||
|
disableRemap53: false
|
||||||
|
disableDropPTR: false
|
||||||
|
disableDropAAAA: false
|
||||||
|
netfilter:
|
||||||
|
iptables:
|
||||||
chainPrefix: KVAS2_
|
chainPrefix: KVAS2_
|
||||||
ipsetPrefix: kvas2_
|
ipset:
|
||||||
linkName: br0
|
tablePrefix: kvas2_
|
||||||
targetDNSServerAddress: 127.0.0.1
|
additionalTTL: 3600
|
||||||
targetDNSServerPort: 53
|
link:
|
||||||
listenDNSPort: 3553
|
- br0
|
||||||
|
logLevel: info
|
||||||
groups:
|
groups:
|
||||||
- id: d663876a
|
- id: d663876a
|
||||||
name: Example
|
name: Example
|
||||||
|
Loading…
x
Reference in New Issue
Block a user