config support
This commit is contained in:
parent
60e1f4c540
commit
4a0c0938e6
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
kvas2-go
|
||||
config.yaml
|
34
config.yaml.example
Normal file
34
config.yaml.example
Normal file
@ -0,0 +1,34 @@
|
||||
appConfig:
|
||||
additionalTTL: 216000
|
||||
chainPrefix: KVAS2_
|
||||
ipsetPrefix: kvas2_
|
||||
linkName: br0
|
||||
targetDNSServerAddress: 127.0.0.1
|
||||
targetDNSServerPort: 53
|
||||
listenDNSPort: 3553
|
||||
groups:
|
||||
- id: d663876a
|
||||
name: Example
|
||||
interface: nwg0
|
||||
fixProtect: false
|
||||
rules:
|
||||
- id: 6f34ee91
|
||||
name: Wildcard Example
|
||||
type: wildcard
|
||||
rule: '*wildcard.example.com'
|
||||
enable: true
|
||||
- id: 00ae5f7c
|
||||
name: RegEx Example
|
||||
type: regex
|
||||
rule: '^.*.regex.example.com$'
|
||||
enable: true
|
||||
- id: 6120dc8a
|
||||
name: Domain Example
|
||||
type: domain
|
||||
rule: 'domain.example.com'
|
||||
enable: true
|
||||
- id: b9751782
|
||||
name: Namespace Example
|
||||
type: namespace
|
||||
rule: 'namespace.example.com'
|
||||
enable: true
|
2
go.mod
2
go.mod
@ -5,10 +5,10 @@ go 1.21
|
||||
require (
|
||||
github.com/IGLOU-EU/go-wildcard/v2 v2.0.2
|
||||
github.com/coreos/go-iptables v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/miekg/dns v1.1.63
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
80
kvas2.go
80
kvas2.go
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@ -28,6 +27,7 @@ import (
|
||||
var (
|
||||
ErrAlreadyRunning = errors.New("already running")
|
||||
ErrGroupIDConflict = errors.New("group id conflict")
|
||||
ErrRuleIDConflict = errors.New("rule id conflict")
|
||||
)
|
||||
|
||||
func randomId() [4]byte {
|
||||
@ -39,7 +39,7 @@ func randomId() [4]byte {
|
||||
type Config struct {
|
||||
AdditionalTTL uint32
|
||||
ChainPrefix string
|
||||
IpSetPrefix string
|
||||
IPSetPrefix string
|
||||
LinkName string
|
||||
TargetDNSServerAddress string
|
||||
TargetDNSServerPort uint16
|
||||
@ -53,7 +53,7 @@ type App struct {
|
||||
NetfilterHelper4 *netfilterHelper.NetfilterHelper
|
||||
NetfilterHelper6 *netfilterHelper.NetfilterHelper
|
||||
Records *records.Records
|
||||
Groups map[[4]byte]*group.Group
|
||||
Groups []*group.Group
|
||||
|
||||
Link netlink.Link
|
||||
|
||||
@ -80,7 +80,7 @@ func (a *App) handleLink(event netlink.LinkUpdate) {
|
||||
|
||||
err := group.LinkUpdateHook(event)
|
||||
if err != nil {
|
||||
log.Error().Str("group", hex.EncodeToString(group.ID[:])).Err(err).Msg("error while handling interface up")
|
||||
log.Error().Str("group", group.ID.String()).Err(err).Msg("error while handling interface up")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,15 +287,27 @@ func (a *App) Start(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
func (a *App) AddGroup(groupModel *models.Group) error {
|
||||
if _, exists := a.Groups[groupModel.ID]; exists {
|
||||
return ErrGroupIDConflict
|
||||
for _, group := range a.Groups {
|
||||
if groupModel.ID == group.ID {
|
||||
return ErrGroupIDConflict
|
||||
}
|
||||
}
|
||||
dup := make(map[[4]byte]struct{})
|
||||
for _, rule := range groupModel.Rules {
|
||||
if _, exists := dup[rule.ID]; exists {
|
||||
return ErrRuleIDConflict
|
||||
}
|
||||
dup[rule.ID] = struct{}{}
|
||||
}
|
||||
|
||||
grp, err := group.NewGroup(groupModel, a.NetfilterHelper4, a.Config.ChainPrefix, a.Config.IpSetPrefix)
|
||||
grp, err := group.NewGroup(groupModel, a.NetfilterHelper4, a.Config.ChainPrefix, a.Config.IPSetPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create group: %w", err)
|
||||
}
|
||||
a.Groups[grp.ID] = grp
|
||||
a.Groups = append(a.Groups, grp)
|
||||
|
||||
log.Trace().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group")
|
||||
|
||||
return grp.Sync(a.Records)
|
||||
}
|
||||
|
||||
@ -423,12 +435,52 @@ func (a *App) handleMessage(msg dns.Msg) {
|
||||
}
|
||||
}
|
||||
|
||||
func New(config Config) (*App, error) {
|
||||
func (a *App) ImportConfig(cfg models.ConfigFile) error {
|
||||
a.Config = Config{
|
||||
AdditionalTTL: cfg.AppConfig.AdditionalTTL,
|
||||
ChainPrefix: cfg.AppConfig.ChainPrefix,
|
||||
IPSetPrefix: cfg.AppConfig.IPSetPrefix,
|
||||
LinkName: cfg.AppConfig.LinkName,
|
||||
TargetDNSServerAddress: cfg.AppConfig.TargetDNSServerAddress,
|
||||
TargetDNSServerPort: cfg.AppConfig.TargetDNSServerPort,
|
||||
ListenDNSPort: cfg.AppConfig.ListenDNSPort,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ExportConfig() models.ConfigFile {
|
||||
groups := make([]models.Group, len(a.Groups))
|
||||
for idx, group := range a.Groups {
|
||||
groups[idx] = *group.Group
|
||||
}
|
||||
return models.ConfigFile{
|
||||
AppConfig: models.AppConfig{
|
||||
AdditionalTTL: a.Config.AdditionalTTL,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func New(config models.ConfigFile) (*App, error) {
|
||||
var err error
|
||||
|
||||
app := &App{}
|
||||
|
||||
app.Config = config
|
||||
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
|
||||
@ -468,7 +520,6 @@ func New(config Config) (*App, error) {
|
||||
}
|
||||
|
||||
app.Records = records.New()
|
||||
app.Groups = make(map[[4]byte]*group.Group)
|
||||
|
||||
link, err := netlink.LinkByName(app.Config.LinkName)
|
||||
if err != nil {
|
||||
@ -496,7 +547,12 @@ func New(config Config) (*App, error) {
|
||||
return nil, fmt.Errorf("failed to clear iptables: %w", err)
|
||||
}
|
||||
|
||||
app.Groups = make(map[[4]byte]*group.Group)
|
||||
for _, group := range config.Groups {
|
||||
err = app.AddGroup(&group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return app, nil
|
||||
}
|
||||
|
25
main.go
25
main.go
@ -6,22 +6,29 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"kvas2-go/models"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||
|
||||
app, err := New(Config{
|
||||
AdditionalTTL: 216000, // 1 hour
|
||||
ChainPrefix: "KVAS2_",
|
||||
IpSetPrefix: "kvas2_",
|
||||
LinkName: "br0",
|
||||
TargetDNSServerAddress: "127.0.0.1",
|
||||
TargetDNSServerPort: 53,
|
||||
ListenDNSPort: 3553,
|
||||
})
|
||||
cfgFile, err := os.ReadFile("config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to read config.yaml")
|
||||
}
|
||||
|
||||
cfg := models.ConfigFile{}
|
||||
err = yaml.Unmarshal(cfgFile, &cfg)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to parse config.yaml")
|
||||
}
|
||||
|
||||
app, err := New(cfg)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to initialize application")
|
||||
}
|
||||
|
16
models/config.go
Normal file
16
models/config.go
Normal file
@ -0,0 +1,16 @@
|
||||
package models
|
||||
|
||||
type ConfigFile struct {
|
||||
AppConfig AppConfig `yaml:"appConfig"`
|
||||
Groups []Group `yaml:"groups"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
AdditionalTTL uint32 `yaml:"additionalTTL"`
|
||||
ChainPrefix string `yaml:"chainPrefix"`
|
||||
IPSetPrefix string `yaml:"ipsetPrefix"`
|
||||
LinkName string `yaml:"linkName"`
|
||||
TargetDNSServerAddress string `yaml:"targetDNSServerAddress"`
|
||||
TargetDNSServerPort uint16 `yaml:"targetDNSServerPort"`
|
||||
ListenDNSPort uint16 `yaml:"listenDNSPort"`
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package models
|
||||
|
||||
type Group struct {
|
||||
ID [4]byte
|
||||
Name string
|
||||
Interface string
|
||||
Rules []*Rule
|
||||
FixProtect bool
|
||||
ID ID `yaml:"id"`
|
||||
Name string `yaml:"name"`
|
||||
Interface string `yaml:"interface"`
|
||||
FixProtect bool `yaml:"fixProtect"`
|
||||
Rules []*Rule `yaml:"rules"`
|
||||
}
|
||||
|
20
models/id.go
Normal file
20
models/id.go
Normal file
@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
type ID [4]byte
|
||||
|
||||
func (id *ID) String() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func (id *ID) MarshalText() ([]byte, error) {
|
||||
return []byte(id.String()), nil
|
||||
}
|
||||
|
||||
func (id *ID) UnmarshalText(data []byte) error {
|
||||
_, err := hex.Decode(id[:], data)
|
||||
return err
|
||||
}
|
@ -2,16 +2,17 @@ package models
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/IGLOU-EU/go-wildcard/v2"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
ID [4]byte
|
||||
Name string
|
||||
Type string
|
||||
Rule string
|
||||
Enable bool
|
||||
ID ID `yaml:"id"`
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Rule string `yaml:"rule"`
|
||||
Enable bool `yaml:"enable"`
|
||||
}
|
||||
|
||||
func (d *Rule) IsEnabled() bool {
|
||||
@ -25,8 +26,13 @@ func (d *Rule) IsMatch(domainName string) bool {
|
||||
case "regex":
|
||||
ok, _ := regexp.MatchString(d.Rule, domainName)
|
||||
return ok
|
||||
case "plaintext":
|
||||
case "domain":
|
||||
return domainName == d.Rule
|
||||
case "namespace":
|
||||
if domainName == d.Rule {
|
||||
return true
|
||||
}
|
||||
return strings.HasSuffix(domainName, "."+d.Rule)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user