Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
57cc17f8ac | |||
468bc28482 | |||
78f964e9c4 | |||
552b175a48 | |||
4de4f1745f | |||
87ee5f5f9b | |||
e8d53872b0 | |||
c7dac46d67 | |||
cc8d3ede3c | |||
867788dc4c | |||
1ab11fa899 | |||
9926cfa0fb | |||
7833bc3db4 | |||
965b4e6718 | |||
c6831f98e0 | |||
891f6ee7c2 | |||
bfe6a00589 | |||
4c49d2ff10 | |||
1967d4e0dc | |||
6a2c5e6c11 | |||
61905052d9 | |||
b16f7fc876 | |||
baab8eca40 |
90
.github/workflows/build-for-release.yml
vendored
Normal file
90
.github/workflows/build-for-release.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
name: Build and Package OPKG
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.github/**'
|
||||||
|
- '!.github/workflows/build-for-release.yml'
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build for "${{ matrix.target }}"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- target: mipsel-3.4
|
||||||
|
goos: linux
|
||||||
|
goarch: mipsle
|
||||||
|
gomips: softfloat
|
||||||
|
- target: mips-3.4
|
||||||
|
goos: linux
|
||||||
|
goarch: mips
|
||||||
|
gomips: softfloat
|
||||||
|
- target: aarch64-3.10
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
- target: armv7-3.2
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- target: armv7-2.6
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- target: armv5-3.2
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 5
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.21'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y fakeroot
|
||||||
|
|
||||||
|
- name: Build and Package
|
||||||
|
run: |
|
||||||
|
TARGET=${{ matrix.target }} GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} make
|
||||||
|
|
||||||
|
- name: Fetching file list for artifact
|
||||||
|
if: ${{ github.event_name != 'release' }}
|
||||||
|
id: create_file_list
|
||||||
|
run: |
|
||||||
|
echo 'file_list<<EOF' >> $GITHUB_OUTPUT
|
||||||
|
find . -name "magitrickle_*_${{ matrix.target }}.ipk" -print >> $GITHUB_OUTPUT
|
||||||
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
if: ${{ github.event_name != 'release' }}
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target }}
|
||||||
|
path: ${{ steps.create_file_list.outputs.file_list }}
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload asset to release
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: https://gitea.com/actions/release-action@main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
files: |-
|
||||||
|
./.build/magitrickle_*_${{ matrix.target }}.ipk
|
||||||
|
api_key: '${{secrets.RELEASE_TOKEN}}'
|
@ -1,13 +1,18 @@
|
|||||||
name: Build and Package OPKG
|
name: Checking
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
|
- develop
|
||||||
- main
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.github/**'
|
||||||
|
- '!.github/workflows/check.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
check:
|
||||||
name: Build for ${{ matrix.arch }}
|
name: Finding suspicious constructs for ${{ matrix.arch }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -43,24 +48,10 @@ jobs:
|
|||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.21'
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Finding suspicious constructs
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} go vet ./...
|
||||||
sudo apt-get install -y fakeroot
|
|
||||||
|
|
||||||
- name: Build and Package
|
|
||||||
run: |
|
|
||||||
ARCH=${{ matrix.arch }} GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} make
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: kvas2_${{ matrix.arch }}.ipk.zip
|
|
||||||
path: .build/kvas2_${{ matrix.arch }}.ipk
|
|
||||||
if-no-files-found: error
|
|
||||||
compression-level: 0
|
|
@ -1,5 +1,5 @@
|
|||||||
# Contributors
|
# Коллабораторы
|
||||||
|
|
||||||
## Consultants
|
## Консультация
|
||||||
|
|
||||||
- **nesteroff561** - [GitHub](https://github.com/nesteroff561) - "Help with understanding `iptables`"
|
- **nesteroff561** ([GitHub](https://github.com/nesteroff561)) - Помощь с `iptables`
|
40
Makefile
40
Makefile
@ -1,41 +1,55 @@
|
|||||||
APP_NAME = kvas2
|
APP_NAME = magitrickle
|
||||||
APP_DESCRIPTION = DNS-based routing application
|
APP_DESCRIPTION = DNS-based routing application
|
||||||
APP_MAINTAINER = Vladimir Avtsenov <vladimir.lsk.cool@gmail.com>
|
APP_MAINTAINER = Vladimir Avtsenov <vladimir.lsk.cool@gmail.com>
|
||||||
|
|
||||||
TAG = $(shell git describe --tags --abbrev=0 2> /dev/null || git rev-parse --short HEAD)
|
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
COMMITS_SINCE_TAG = $(shell git rev-list ${TAG}..HEAD --count || echo "0")
|
UPSTREAM_VERSION = $(shell git describe --tags --abbrev=0 2> /dev/null || echo "0.0.0")
|
||||||
VERSION ?= $(TAG)
|
PKG_REVISION ?= 1
|
||||||
|
|
||||||
ARCH ?= mipsel
|
TAG = $(shell git describe --tags --abbrev=0 2> /dev/null)
|
||||||
|
COMMITS_SINCE_TAG = $(shell git rev-list ${TAG}..HEAD --count 2>/dev/null)
|
||||||
|
PRERELEASE_POSTFIX =
|
||||||
|
PRERELEASE_DATE = $(shell date +%Y%m%d)
|
||||||
|
ifneq ($(TAG),)
|
||||||
|
ifneq ($(COMMITS_SINCE_TAG), 0)
|
||||||
|
PRERELEASE_POSTFIX = ~git$(PRERELEASE_DATE).$(COMMIT)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
PRERELEASE_POSTFIX = ~git$(PRERELEASE_DATE).$(COMMIT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET ?= mipsel-3.4
|
||||||
GOOS ?= linux
|
GOOS ?= linux
|
||||||
GOARCH ?= mipsle
|
GOARCH ?= mipsle
|
||||||
GOMIPS ?= softfloat
|
GOMIPS ?= softfloat
|
||||||
GOARM ?=
|
GOARM ?=
|
||||||
|
|
||||||
BUILD_DIR = ./.build
|
BUILD_DIR = ./.build
|
||||||
PKG_DIR = $(BUILD_DIR)/$(ARCH)
|
PKG_DIR = $(BUILD_DIR)/$(TARGET)
|
||||||
BIN_DIR = $(PKG_DIR)/data/opt/bin
|
BIN_DIR = $(PKG_DIR)/data/opt/bin
|
||||||
PARAMS = -v -a -trimpath -ldflags="-X 'kvas2/constant.Version=$(VERSION)' -X 'kvas2/constant.Commit=$(COMMIT)' -w -s"
|
PARAMS = -v -a -trimpath -ldflags="-X 'magitrickle/constant.Version=$(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)' -X 'magitrickle/constant.Commit=$(COMMIT)' -w -s"
|
||||||
|
|
||||||
all: build_daemon package
|
all: clear build_daemon package
|
||||||
|
|
||||||
|
clear:
|
||||||
|
echo $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
rm -rf $(PKG_DIR)
|
||||||
|
|
||||||
build_daemon:
|
build_daemon:
|
||||||
GOOS=$(GOOS) GOARCH=$(GOARCH) GOMIPS=$(GOMIPS) GOARM=$(GOARM) go build $(PARAMS) -o $(BIN_DIR)/kvas2d ./cmd/kvas2d
|
GOOS=$(GOOS) GOARCH=$(GOARCH) GOMIPS=$(GOMIPS) GOARM=$(GOARM) go build $(PARAMS) -o $(BIN_DIR)/magitrickled ./cmd/magitrickled
|
||||||
|
|
||||||
package:
|
package:
|
||||||
@mkdir -p $(PKG_DIR)/control
|
@mkdir -p $(PKG_DIR)/control
|
||||||
@echo '2.0' > $(PKG_DIR)/debian-binary
|
@echo '2.0' > $(PKG_DIR)/debian-binary
|
||||||
@echo 'Package: $(APP_NAME)' > $(PKG_DIR)/control/control
|
@echo 'Package: $(APP_NAME)' > $(PKG_DIR)/control/control
|
||||||
@echo 'Version: $(VERSION)-$(COMMITS_SINCE_TAG)' >> $(PKG_DIR)/control/control
|
@echo 'Version: $(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)-$(PKG_REVISION)' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Architecture: $(ARCH)' >> $(PKG_DIR)/control/control
|
@echo 'Architecture: $(TARGET)' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Maintainer: $(APP_MAINTAINER)' >> $(PKG_DIR)/control/control
|
@echo 'Maintainer: $(APP_MAINTAINER)' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Description: $(APP_DESCRIPTION)' >> $(PKG_DIR)/control/control
|
@echo 'Description: $(APP_DESCRIPTION)' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Section: net' >> $(PKG_DIR)/control/control
|
@echo 'Section: net' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Priority: optional' >> $(PKG_DIR)/control/control
|
@echo 'Priority: optional' >> $(PKG_DIR)/control/control
|
||||||
@echo 'Depends: libc, iptables, socat' >> $(PKG_DIR)/control/control
|
@echo 'Depends: libc, iptables, socat' >> $(PKG_DIR)/control/control
|
||||||
@mkdir -p $(PKG_DIR)/data/opt/usr/bin
|
|
||||||
@cp -r ./opt $(PKG_DIR)/data/
|
@cp -r ./opt $(PKG_DIR)/data/
|
||||||
@fakeroot sh -c "tar -C $(PKG_DIR)/control -czvf $(PKG_DIR)/control.tar.gz ."
|
@fakeroot sh -c "tar -C $(PKG_DIR)/control -czvf $(PKG_DIR)/control.tar.gz ."
|
||||||
@fakeroot sh -c "tar -C $(PKG_DIR)/data -czvf $(PKG_DIR)/data.tar.gz ."
|
@fakeroot sh -c "tar -C $(PKG_DIR)/data -czvf $(PKG_DIR)/data.tar.gz ."
|
||||||
@tar -C $(PKG_DIR) -czvf $(BUILD_DIR)/$(APP_NAME)_$(ARCH).ipk ./debian-binary ./control.tar.gz ./data.tar.gz
|
@tar -C $(PKG_DIR) -czvf $(BUILD_DIR)/$(APP_NAME)_$(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)-$(PKG_REVISION)_$(TARGET).ipk ./debian-binary ./control.tar.gz ./data.tar.gz
|
||||||
|
131
README.md
131
README.md
@ -1,3 +1,130 @@
|
|||||||
# kvas2
|
# MagiTrickle
|
||||||
|
|
||||||
Better implementation of [KVAS](https://github.com/qzeleza/kvas)
|
MagiTrickle - Маршрутизация трафика на основе DNS запросов для роутеров Keenetic (под управлением [Entware](https://github.com/The-BB/Entware-Keenetic)).
|
||||||
|
|
||||||
|
*(Продукт в данный момент находится в состоянии разработки)*
|
||||||
|
|
||||||
|
Данное программное обеспечение реализует маршрутизацию трафика на основе проксирования через себя DNS запросов. Можно указать список доменных имён, которые нужно маршрутизировать на тот, или иной интерфейс, вместо бесконечного накопления IP адресов.
|
||||||
|
|
||||||
|
### Особенности, в сравнении с другим ПО:
|
||||||
|
1. Не требует отключения встроенного в Keenetic DNS сервера - всё работает методом перенаправления портов.
|
||||||
|
2. Работает с любыми туннелями, которые умеют поднимать UNIX интерфейс.
|
||||||
|
3. Несколько типов правил - domain, namespace, wildcard и regex.
|
||||||
|
4. Не тянет за собой огромное количество сторонних пакетов пакетов. Вся конфигурация находится в одном месте (в одном файле).
|
||||||
|
5. Возможность создавать несколько групп на разные сети.
|
||||||
|
6. Моментальное бесшовное включение/выключение сервиса.
|
||||||
|
|
||||||
|
### Roadmap:
|
||||||
|
1. CLI интерфейс для добавления/удаления записей в режиме реального времени. (Уже заложен функционал обработки записей в реальном времени, необходимо заняться CLI интерфейсом)
|
||||||
|
2. Дружелюбный к пользователю Web-GUI для конфигурации записей.
|
||||||
|
3. Поддержка подсетей и диапазона IP адресов.
|
||||||
|
4. Поддержка автообновляемых "подпискок" на список доменных имён (готовые списки подключаемые несколькими кликами мышки).
|
||||||
|
|
||||||
|
### Установка:
|
||||||
|
Т.к. в данный момент нету никакого дружелюбного к пользователю интерфейсов - данное руководство рассчитано на тех, кому просто нужна маршрутизация на требуемые для него домены без отключения встроенного в Keenetic DNS сервера.
|
||||||
|
|
||||||
|
Программа не была досканально протестирована, возможны очень редкие "вылеты". Максимально возможный риск заключается в том, что придётся перезапускать роутер, но шанс этого маловероятен.
|
||||||
|
|
||||||
|
1. Устанавливаем пакет:
|
||||||
|
```bash
|
||||||
|
opkg install magitrickle_<version>_<arch>.ipk
|
||||||
|
```
|
||||||
|
2. Копируем конфиг:
|
||||||
|
```bash
|
||||||
|
cp /opt/var/lib/magitrickle/config.yaml.example /opt/var/lib/magitrickle/config.yaml
|
||||||
|
```
|
||||||
|
3. Настраиваем конфиг (если не понимаете что делаете - не трогайте группу "app"!):
|
||||||
|
```yaml
|
||||||
|
configVersion: 0.1.0
|
||||||
|
app: # Настройки программы - не трогайте, если не знаете что к чему
|
||||||
|
dnsProxy:
|
||||||
|
host:
|
||||||
|
address: '[::]' # Адрес, который будет слушать программа для приёма DNS запросов
|
||||||
|
port: 3553 # Порт
|
||||||
|
upstream:
|
||||||
|
address: 127.0.0.1 # Адрес, используемый для отправки DNS запросов
|
||||||
|
port: 53 # Порт
|
||||||
|
disableRemap53: false # Флаг отключения перепривязки 53 порта
|
||||||
|
disableFakePTR: false # Флаг отключения подделки PTR записи (без неё есть проблемы, может быть будет исправлено в будущем)
|
||||||
|
disableDropAAAA: false # Флаг отключения откидывания AAAA записей
|
||||||
|
netfilter:
|
||||||
|
iptables:
|
||||||
|
chainPrefix: MT_ # Префикс для названий цепочек IPTables
|
||||||
|
ipset:
|
||||||
|
tablePrefix: mt_ # Префикс для названий таблиц IPSet
|
||||||
|
additionalTTL: 3600 # Дополнительный TTL (если от DNS пришел TTL 300, то к этому числу прибавится указанный TTL)
|
||||||
|
link: # Список адресов где будет подменяться DNS
|
||||||
|
- br0
|
||||||
|
- br1
|
||||||
|
logLevel: info # Уровень логов (trace, debug, info, warn, error)
|
||||||
|
groups: # Список групп
|
||||||
|
- id: d663876a # Уникальный ID группы (8 символов в диапозоне "0123456789abcdef")
|
||||||
|
name: Routing 1 # Человеко-читаемое имя (для будущего CLI и Web-GUI)
|
||||||
|
interface: nwg0 # Интерфейс, на который будет выполняться маршрутизация
|
||||||
|
fixProtect: false # Подключение интерфейса в список для выхода в интернет (для неподдерживаемых Keenetic туннелей)
|
||||||
|
rules: # Список правил
|
||||||
|
- id: 6f34ee91 # Уникальный ID правила (8 символов в диапозоне "0123456789abcdef")
|
||||||
|
name: Wildcard Example # Человеко-читаемое имя (для будущего CLI и Web-GUI)
|
||||||
|
type: wildcard # Тип правила
|
||||||
|
rule: '*.example.com' # Правило
|
||||||
|
enable: true # Флаг активации
|
||||||
|
- id: 00ae5f7c
|
||||||
|
name: RegEx Example
|
||||||
|
type: regex
|
||||||
|
rule: '^.*.regex.example.com$'
|
||||||
|
enable: true
|
||||||
|
- id: d663876b
|
||||||
|
name: Routing 2
|
||||||
|
interface: nwg1
|
||||||
|
fixProtect: false
|
||||||
|
rules:
|
||||||
|
- id: 6120dc8a
|
||||||
|
name: Domain Example
|
||||||
|
type: domain
|
||||||
|
rule: 'domain.example.com'
|
||||||
|
enable: true
|
||||||
|
```
|
||||||
|
Примеры правил:
|
||||||
|
* Domain (один домен без поддоменов)
|
||||||
|
```yaml
|
||||||
|
- id: 6120dc8a
|
||||||
|
name: Domain Example
|
||||||
|
type: domain
|
||||||
|
rule: 'example.com'
|
||||||
|
enable: true
|
||||||
|
```
|
||||||
|
* Namespace (домен и все его поддомены)
|
||||||
|
```yaml
|
||||||
|
- id: b9751782
|
||||||
|
name: Namespace Example
|
||||||
|
type: namespace
|
||||||
|
rule: 'example.com'
|
||||||
|
enable: true
|
||||||
|
```
|
||||||
|
* Wildcard
|
||||||
|
```yaml
|
||||||
|
- id: 6f34ee91
|
||||||
|
name: Wildcard Example
|
||||||
|
type: wildcard
|
||||||
|
rule: '*.example.com'
|
||||||
|
enable: true
|
||||||
|
```
|
||||||
|
* RegEx
|
||||||
|
```yaml
|
||||||
|
- id: 00ae5f7c
|
||||||
|
name: RegEx Example
|
||||||
|
type: regex
|
||||||
|
rule: '^.*.regex.example.com$'
|
||||||
|
enable: true
|
||||||
|
```
|
||||||
|
4. Запускаем сервис:
|
||||||
|
```bash
|
||||||
|
/opt/etc/init.d/S99magitrickle start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отладка
|
||||||
|
Если вам нужна отладка, то останавливаем сервис и запускаем "демона" руками:
|
||||||
|
```bash
|
||||||
|
/opt/etc/init.d/S99magitrickle stop
|
||||||
|
magitrickled
|
||||||
|
```
|
||||||
|
7
cmd/magitrickle/main.go
Normal file
7
cmd/magitrickle/main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package magitrickle
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("dummy file")
|
||||||
|
}
|
@ -10,18 +10,18 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"kvas2"
|
"magitrickle"
|
||||||
"kvas2/constant"
|
"magitrickle/constant"
|
||||||
"kvas2/models"
|
"magitrickle/models"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const cfgFolderLocation = "/opt/var/lib/kvas2"
|
const cfgFolderLocation = "/opt/var/lib/magitrickle"
|
||||||
const cfgFileLocation = cfgFolderLocation + "/config.yaml"
|
const cfgFileLocation = cfgFolderLocation + "/config.yaml"
|
||||||
const pidFileLocation = "/opt/var/run/kvas2.pid"
|
const pidFileLocation = "/opt/var/run/magitrickle.pid"
|
||||||
|
|
||||||
func checkPIDFile() error {
|
func checkPIDFile() error {
|
||||||
data, err := os.ReadFile(pidFileLocation)
|
data, err := os.ReadFile(pidFileLocation)
|
||||||
@ -59,10 +59,10 @@ func main() {
|
|||||||
log.Info().
|
log.Info().
|
||||||
Str("version", constant.Version).
|
Str("version", constant.Version).
|
||||||
Str("commit", constant.Commit).
|
Str("commit", constant.Commit).
|
||||||
Msg("starting kvas2 daemon")
|
Msg("starting MagiTrickle daemon")
|
||||||
|
|
||||||
if err := checkPIDFile(); err != nil {
|
if err := checkPIDFile(); err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to start kvas2 daemon")
|
log.Fatal().Err(err).Msg("failed to start MagiTrickle daemon")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := createPIDFile(); err != nil {
|
if err := createPIDFile(); err != nil {
|
||||||
@ -78,7 +78,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
cfg = models.Config{
|
cfg = models.Config{
|
||||||
ConfigVersion: "0.1.0",
|
ConfigVersion: "0.1.0",
|
||||||
App: kvas2.DefaultAppConfig,
|
App: magitrickle.DefaultAppConfig,
|
||||||
}
|
}
|
||||||
out, err := yaml.Marshal(cfg)
|
out, err := yaml.Marshal(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -122,7 +122,7 @@ func main() {
|
|||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
app := kvas2.New()
|
app := magitrickle.New()
|
||||||
err = app.ImportConfig(cfg)
|
err = app.ImportConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to import config")
|
log.Fatal().Err(err).Msg("failed to import config")
|
@ -5,9 +5,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"kvas2/models"
|
"magitrickle/models"
|
||||||
"kvas2/netfilter-helper"
|
"magitrickle/netfilter-helper"
|
||||||
"kvas2/records"
|
"magitrickle/records"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package kvas2
|
package magitrickle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -9,11 +9,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"kvas2/dns-mitm-proxy"
|
"magitrickle/dns-mitm-proxy"
|
||||||
"kvas2/group"
|
"magitrickle/group"
|
||||||
"kvas2/models"
|
"magitrickle/models"
|
||||||
"kvas2/netfilter-helper"
|
"magitrickle/netfilter-helper"
|
||||||
"kvas2/records"
|
"magitrickle/records"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -38,10 +38,10 @@ var DefaultAppConfig = models.App{
|
|||||||
},
|
},
|
||||||
Netfilter: models.Netfilter{
|
Netfilter: models.Netfilter{
|
||||||
IPTables: models.IPTables{
|
IPTables: models.IPTables{
|
||||||
ChainPrefix: "KVAS2_",
|
ChainPrefix: "MT_",
|
||||||
},
|
},
|
||||||
IPSet: models.IPSet{
|
IPSet: models.IPSet{
|
||||||
TablePrefix: "kvas2_",
|
TablePrefix: "mt_",
|
||||||
AdditionalTTL: 3600,
|
AdditionalTTL: 3600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -254,7 +254,7 @@ func (a *App) start(ctx context.Context) (err error) {
|
|||||||
/*
|
/*
|
||||||
Socket (for netfilter.d events)
|
Socket (for netfilter.d events)
|
||||||
*/
|
*/
|
||||||
socketPath := "/opt/var/run/kvas2.sock"
|
socketPath := "/opt/var/run/magitrickle.sock"
|
||||||
err = os.Remove(socketPath)
|
err = os.Remove(socketPath)
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
return fmt.Errorf("failed to remove existed UNIX socket: %w", err)
|
return fmt.Errorf("failed to remove existed UNIX socket: %w", err)
|
@ -17,7 +17,7 @@ type DNSProxy struct {
|
|||||||
Host DNSProxyServer `yaml:"host"`
|
Host DNSProxyServer `yaml:"host"`
|
||||||
Upstream DNSProxyServer `yaml:"upstream"`
|
Upstream DNSProxyServer `yaml:"upstream"`
|
||||||
DisableRemap53 bool `yaml:"disableRemap53"`
|
DisableRemap53 bool `yaml:"disableRemap53"`
|
||||||
DisableFakePTR bool `yaml:"disableDropPTR"`
|
DisableFakePTR bool `yaml:"disableFakePTR"`
|
||||||
DisableDropAAAA bool `yaml:"disableDropAAAA"`
|
DisableDropAAAA bool `yaml:"disableDropAAAA"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,16 @@ package models
|
|||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestDomain_IsMatch_Plaintext(t *testing.T) {
|
func TestDomain_IsMatch_Domain(t *testing.T) {
|
||||||
rule := &Rule{
|
rule := &Rule{
|
||||||
Type: "plaintext",
|
Type: "domain",
|
||||||
Rule: "example.com",
|
Rule: "example.com",
|
||||||
}
|
}
|
||||||
if !rule.IsMatch("example.com") {
|
if !rule.IsMatch("example.com") {
|
||||||
t.Fatal("&Rule{Type: \"plaintext\", Rule: \"example.com\"}.IsMatch(\"example.com\") returns false")
|
t.Fatal("&Rule{Type: \"domain\", Rule: \"example.com\"}.IsMatch(\"example.com\") returns false")
|
||||||
}
|
}
|
||||||
if rule.IsMatch("noexample.com") {
|
if rule.IsMatch("noexample.com") {
|
||||||
t.Fatal("&Rule{Type: \"plaintext\", Rule: \"example.com\"}.IsMatch(\"noexample.com\") returns true")
|
t.Fatal("&Rule{Type: \"domain\", Rule: \"example.com\"}.IsMatch(\"noexample.com\") returns true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ func (r *IPSetToLink) insertIPTablesRules(table string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, iptablesArgs := range [][]string{
|
for _, iptablesArgs := range [][]string{
|
||||||
|
{"-j", "CONNMARK", "--restore-mark"},
|
||||||
{"-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))},
|
{"-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))},
|
||||||
{"-j", "CONNMARK", "--save-mark"},
|
{"-j", "CONNMARK", "--save-mark"},
|
||||||
} {
|
} {
|
||||||
@ -112,6 +113,8 @@ func (r *IPSetToLink) insertIPRule() error {
|
|||||||
}
|
}
|
||||||
r.ipRule = rule
|
r.ipRule = rule
|
||||||
|
|
||||||
|
log.Trace().Int("table", r.table).Int("mark", int(r.mark)).Msg("using ip table and mark")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ type PortRemap struct {
|
|||||||
|
|
||||||
func (r *PortRemap) insertIPTablesRules(table string) error {
|
func (r *PortRemap) insertIPTablesRules(table string) error {
|
||||||
if table == "" || table == "nat" {
|
if table == "" || table == "nat" {
|
||||||
err := r.IPTables.NewChain("nat", r.ChainName)
|
preroutingChain := r.ChainName + "_PRR"
|
||||||
|
err := r.IPTables.NewChain("nat", preroutingChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If not "AlreadyExists"
|
// If not "AlreadyExists"
|
||||||
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
|
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
|
||||||
@ -34,18 +35,62 @@ func (r *PortRemap) insertIPTablesRules(table string) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.IPTables.Proto() != iptables.ProtocolIPv6 {
|
||||||
|
for _, iptablesArgs := range [][]string{
|
||||||
|
{"-p", "tcp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
|
||||||
|
{"-p", "udp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
|
||||||
|
} {
|
||||||
|
err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to append rule: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for _, iptablesArgs := range [][]string{
|
for _, iptablesArgs := range [][]string{
|
||||||
{"-p", "tcp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
|
{"-p", "tcp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
|
||||||
{"-p", "udp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
|
{"-p", "udp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
|
||||||
} {
|
} {
|
||||||
err = r.IPTables.AppendUnique("nat", r.ChainName, iptablesArgs...)
|
err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to append rule: %w", err)
|
return fmt.Errorf("failed to append rule: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = r.IPTables.InsertUnique("nat", "PREROUTING", 1, "-j", r.ChainName)
|
err = r.IPTables.InsertUnique("nat", "PREROUTING", 1, "-j", preroutingChain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to linking chain: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
postroutingChain := r.ChainName + "_POR"
|
||||||
|
err = r.IPTables.NewChain("nat", postroutingChain)
|
||||||
|
if err != nil {
|
||||||
|
// If not "AlreadyExists"
|
||||||
|
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
|
||||||
|
return fmt.Errorf("failed to create chain: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range r.Addresses {
|
||||||
|
if !((r.IPTables.Proto() == iptables.ProtocolIPv4 && len(addr.IP) == net.IPv4len) || (r.IPTables.Proto() == iptables.ProtocolIPv6 && len(addr.IP) == net.IPv6len)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IPTables.Proto() == iptables.ProtocolIPv4 {
|
||||||
|
for _, iptablesArgs := range [][]string{
|
||||||
|
{"-p", "tcp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
|
||||||
|
{"-p", "udp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
|
||||||
|
} {
|
||||||
|
err = r.IPTables.AppendUnique("nat", postroutingChain, iptablesArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to append rule: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.IPTables.InsertUnique("nat", "POSTROUTING", 1, "-j", postroutingChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to linking chain: %w", err)
|
return fmt.Errorf("failed to linking chain: %w", err)
|
||||||
}
|
}
|
||||||
@ -57,7 +102,14 @@ func (r *PortRemap) insertIPTablesRules(table string) error {
|
|||||||
func (r *PortRemap) deleteIPTablesRules() []error {
|
func (r *PortRemap) deleteIPTablesRules() []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
err := r.IPTables.DeleteIfExists("nat", "PREROUTING", "-j", r.ChainName)
|
preroutingChain := r.ChainName + "_PRR"
|
||||||
|
err := r.IPTables.DeleteIfExists("nat", "PREROUTING", "-j", preroutingChain)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
postroutingChain := r.ChainName + "_POR"
|
||||||
|
err = r.IPTables.DeleteIfExists("nat", "POSTROUTING", "-j", postroutingChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
|
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
ENABLED=yes
|
ENABLED=yes
|
||||||
PROCS=kvas2d
|
PROCS=magitrickled
|
||||||
ARGS=""
|
ARGS=""
|
||||||
PREARGS=""
|
PREARGS=""
|
||||||
DESC=$PROCS
|
DESC=$PROCS
|
@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
SOCKET_PATH="/opt/var/run/kvas2.sock"
|
SOCKET_PATH="/opt/var/run/magitrickle.sock"
|
||||||
if [ ! -S "$SOCKET_PATH" ]; then
|
if [ ! -S "$SOCKET_PATH" ]; then
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
@ -8,13 +8,13 @@ app:
|
|||||||
address: 127.0.0.1
|
address: 127.0.0.1
|
||||||
port: 53
|
port: 53
|
||||||
disableRemap53: false
|
disableRemap53: false
|
||||||
disableDropPTR: false
|
disableFakePTR: false
|
||||||
disableDropAAAA: false
|
disableDropAAAA: false
|
||||||
netfilter:
|
netfilter:
|
||||||
iptables:
|
iptables:
|
||||||
chainPrefix: KVAS2_
|
chainPrefix: MT_
|
||||||
ipset:
|
ipset:
|
||||||
tablePrefix: kvas2_
|
tablePrefix: mt_
|
||||||
additionalTTL: 3600
|
additionalTTL: 3600
|
||||||
link:
|
link:
|
||||||
- br0
|
- br0
|
@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
func TestLoop(t *testing.T) {
|
func TestLoop(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddCNameRecord("1", "2", time.Minute)
|
r.AddCNameRecord("1", "2", 60)
|
||||||
r.AddCNameRecord("2", "1", time.Minute)
|
r.AddCNameRecord("2", "1", 60)
|
||||||
if r.GetARecords("1") != nil {
|
if r.GetARecords("1") != nil {
|
||||||
t.Fatal("loop detected")
|
t.Fatal("loop detected")
|
||||||
}
|
}
|
||||||
@ -21,8 +21,8 @@ func TestLoop(t *testing.T) {
|
|||||||
|
|
||||||
func TestCName(t *testing.T) {
|
func TestCName(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddARecord("example.com", []byte{1, 2, 3, 4}, time.Minute)
|
r.AddARecord("example.com", []byte{1, 2, 3, 4}, 60)
|
||||||
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute)
|
r.AddCNameRecord("gateway.example.com", "example.com", 60)
|
||||||
records := r.GetARecords("gateway.example.com")
|
records := r.GetARecords("gateway.example.com")
|
||||||
if records == nil {
|
if records == nil {
|
||||||
t.Fatal("no records")
|
t.Fatal("no records")
|
||||||
@ -34,7 +34,7 @@ func TestCName(t *testing.T) {
|
|||||||
|
|
||||||
func TestA(t *testing.T) {
|
func TestA(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddARecord("example.com", []byte{1, 2, 3, 4}, time.Minute)
|
r.AddARecord("example.com", []byte{1, 2, 3, 4}, 60)
|
||||||
records := r.GetARecords("example.com")
|
records := r.GetARecords("example.com")
|
||||||
if records == nil {
|
if records == nil {
|
||||||
t.Fatal("no records")
|
t.Fatal("no records")
|
||||||
@ -46,7 +46,8 @@ func TestA(t *testing.T) {
|
|||||||
|
|
||||||
func TestDeprecated(t *testing.T) {
|
func TestDeprecated(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddARecord("example.com", []byte{1, 2, 3, 4}, -time.Minute)
|
r.AddARecord("example.com", []byte{1, 2, 3, 4}, 0)
|
||||||
|
time.Sleep(time.Second)
|
||||||
records := r.GetARecords("example.com")
|
records := r.GetARecords("example.com")
|
||||||
if records != nil {
|
if records != nil {
|
||||||
t.Fatal("deprecated records")
|
t.Fatal("deprecated records")
|
||||||
@ -63,7 +64,7 @@ func TestNotExistedA(t *testing.T) {
|
|||||||
|
|
||||||
func TestNotExistedCNameAlias(t *testing.T) {
|
func TestNotExistedCNameAlias(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute)
|
r.AddCNameRecord("gateway.example.com", "example.com", 60)
|
||||||
records := r.GetARecords("gateway.example.com")
|
records := r.GetARecords("gateway.example.com")
|
||||||
if records != nil {
|
if records != nil {
|
||||||
t.Fatal("not existed records")
|
t.Fatal("not existed records")
|
||||||
@ -72,8 +73,8 @@ func TestNotExistedCNameAlias(t *testing.T) {
|
|||||||
|
|
||||||
func TestReplacing(t *testing.T) {
|
func TestReplacing(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute)
|
r.AddCNameRecord("gateway.example.com", "example.com", 60)
|
||||||
r.AddARecord("gateway.example.com", []byte{1, 2, 3, 4}, time.Minute)
|
r.AddARecord("gateway.example.com", []byte{1, 2, 3, 4}, 60)
|
||||||
records := r.GetARecords("gateway.example.com")
|
records := r.GetARecords("gateway.example.com")
|
||||||
if bytes.Compare(records[0].Address, []byte{1, 2, 3, 4}) != 0 {
|
if bytes.Compare(records[0].Address, []byte{1, 2, 3, 4}) != 0 {
|
||||||
t.Fatal("mismatch")
|
t.Fatal("mismatch")
|
||||||
@ -82,11 +83,11 @@ func TestReplacing(t *testing.T) {
|
|||||||
|
|
||||||
func TestAliases(t *testing.T) {
|
func TestAliases(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.AddARecord("1", []byte{1, 2, 3, 4}, time.Minute)
|
r.AddARecord("1", []byte{1, 2, 3, 4}, 60)
|
||||||
r.AddCNameRecord("2", "1", time.Minute)
|
r.AddCNameRecord("2", "1", 60)
|
||||||
r.AddCNameRecord("3", "2", time.Minute)
|
r.AddCNameRecord("3", "2", 60)
|
||||||
r.AddCNameRecord("4", "2", time.Minute)
|
r.AddCNameRecord("4", "2", 60)
|
||||||
r.AddCNameRecord("5", "1", time.Minute)
|
r.AddCNameRecord("5", "1", 60)
|
||||||
aliases := r.GetAliases("1")
|
aliases := r.GetAliases("1")
|
||||||
if aliases == nil {
|
if aliases == nil {
|
||||||
t.Fatal("no aliases")
|
t.Fatal("no aliases")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user