поправил, добавил возможность выбора определённой секции
This commit is contained in:
parent
ac21080bff
commit
d8b70cf993
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
.idea
|
||||
.venv
|
||||
.venv
|
||||
config.ini
|
||||
21
README.md
21
README.md
|
|
@ -29,16 +29,18 @@ cp config.ini.example config.ini
|
|||
|
||||
```ini
|
||||
[redirector]
|
||||
threads = # Количество потоков для dns-резолва (50)
|
||||
exclude_cloudflare = # Исключить адреса CloudFlare (yes, no)
|
||||
filename = # Имя файла в котором списки ip, cidr и доменов (input.txt)
|
||||
script = # Имя готового скрипта (apply_routing.sh)
|
||||
rollback_script = # Имя скрипта для отката изменений (rollback_routing.sh)
|
||||
exclude_cloudflare = # Исключить адреса CloudFlare (yes, no)
|
||||
threads = # Количество потоков для dns-резолва (50)
|
||||
table = # Номер таблицы маршрутизации (1010)
|
||||
priority = # Приоритет таблицы (ниже цифра - выше приоритет)
|
||||
gateway = # IP шлюза на который направлять адреса (10.10.0.1)
|
||||
interface = # Интерфейс на который направлять адреса (awg0)
|
||||
table = # Номер таблицы маршрутизации (1010)
|
||||
run = # Shell команда которая выполнится после отработки скрипта (echo "lol kek")
|
||||
```
|
||||
- Можно добавить несколько секций для нескольких конфигураций
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -60,19 +62,20 @@ example.org
|
|||
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
venv/bin/python redirector.py
|
||||
```
|
||||
или
|
||||
- Обработать все секции в конфиге:
|
||||
```bash
|
||||
python redirector.py
|
||||
```
|
||||
- Или определённую секцию:
|
||||
```bash
|
||||
python redirector.py --env custom
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Результат
|
||||
|
||||
### ON скрипт
|
||||
### Создать и наполнить таблицу маршрутизацию
|
||||
|
||||
```
|
||||
apply_routing.sh
|
||||
|
|
@ -80,7 +83,7 @@ apply_routing.sh
|
|||
|
||||
Добавляет таблицу (если её нет) и маршруты в таблицу.
|
||||
|
||||
### OFF скрипт (rollback)
|
||||
### Очистить и удалить таблицу маршрутизации
|
||||
|
||||
```
|
||||
rollback_routing.sh
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ script =
|
|||
rollback_script =
|
||||
threads =
|
||||
table =
|
||||
priority =
|
||||
gateway =
|
||||
interface =
|
||||
run =
|
||||
|
|
|
|||
189
redirector.py
189
redirector.py
|
|
@ -24,7 +24,7 @@ def setup_logging():
|
|||
|
||||
handler = RotatingFileHandler(
|
||||
log_file,
|
||||
maxBytes=2 * 1024 * 1024, # 2MB
|
||||
maxBytes=2 * 1024 * 1024,
|
||||
backupCount=5
|
||||
)
|
||||
|
||||
|
|
@ -40,28 +40,37 @@ def read_config(cfg_file, section=None):
|
|||
config = configparser.ConfigParser()
|
||||
config.read(cfg_file)
|
||||
|
||||
if section:
|
||||
section = section.capitalize()
|
||||
configs = []
|
||||
|
||||
if section and section in config:
|
||||
cfg = config[section]
|
||||
if section:
|
||||
if section not in config:
|
||||
logging.error(f"Секция {section} не найдена")
|
||||
exit(1)
|
||||
|
||||
sections = [section]
|
||||
logging.info(f"Используется конфиг: {section}")
|
||||
else:
|
||||
default_section = config.sections()[0]
|
||||
cfg = config[default_section]
|
||||
logging.info(f"Используется дефолтный конфиг: {default_section}")
|
||||
sections = config.sections()
|
||||
logging.info(f"Используются ВСЕ конфиги: {', '.join(sections)}")
|
||||
|
||||
return {
|
||||
"threads": int(cfg.get("threads", "50")),
|
||||
"filename": os.path.join(BASE_DIR, cfg.get("filename", "forced_list.txt")),
|
||||
"script": os.path.join(BASE_DIR, cfg.get("script", "forced_vpn_ON.sh")),
|
||||
"rollback_script": os.path.join(BASE_DIR, cfg.get("rollback_script", "forced_vpn_OFF.sh")),
|
||||
"exclude_cloudflare": cfg.get("exclude_cloudflare", "no").lower() in ["yes", "y"],
|
||||
"gateway": cfg.get("gateway", ""),
|
||||
"interface": cfg.get("interface", ""),
|
||||
"table": cfg.get("table", "1010"),
|
||||
"run": cfg.get("run", "")
|
||||
}
|
||||
for sec in sections:
|
||||
cfg = config[sec]
|
||||
|
||||
configs.append({
|
||||
"name": sec,
|
||||
"threads": int(cfg.get("threads", "50")),
|
||||
"filename": os.path.join(BASE_DIR, cfg.get("filename", f"{sec}_forced_list.txt")),
|
||||
"script": os.path.join(BASE_DIR, cfg.get("script", f"{sec}_ON.sh")),
|
||||
"rollback_script": os.path.join(BASE_DIR, cfg.get("rollback_script", f"{sec}_OFF.sh")),
|
||||
"exclude_cloudflare": cfg.get("exclude_cloudflare", "no").lower() in ["yes", "y"],
|
||||
"gateway": cfg.get("gateway", ""),
|
||||
"interface": cfg.get("interface", ""),
|
||||
"table": cfg.get("table", "1010"),
|
||||
"priority": cfg.get("priority", "101"),
|
||||
"run": cfg.get("run", "")
|
||||
})
|
||||
|
||||
return configs
|
||||
|
||||
|
||||
def load_forced_list(cfg):
|
||||
|
|
@ -162,105 +171,99 @@ async def main():
|
|||
setup_logging()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c", "--config", default=os.path.join(BASE_DIR, "config.ini"))
|
||||
parser.add_argument("--env", help="Config section: test/prod")
|
||||
parser.add_argument("--env", help="Config section")
|
||||
args = parser.parse_args()
|
||||
|
||||
cfg = read_config(args.config, args.env)
|
||||
config_path = os.path.join(BASE_DIR, "config.ini")
|
||||
configs = read_config(config_path, args.env)
|
||||
|
||||
semaphore = Semaphore(cfg["threads"])
|
||||
for cfg in configs:
|
||||
logging.info(f"=== Обработка: {cfg['name']} ===")
|
||||
|
||||
domains, ready_ips = load_forced_list(cfg)
|
||||
resolvers = get_resolvers()
|
||||
semaphore = Semaphore(cfg["threads"])
|
||||
|
||||
logging.info(f"Домены: {len(domains)} | IP/CIDR: {len(ready_ips)}")
|
||||
domains, ready_ips = load_forced_list(cfg)
|
||||
resolvers = get_resolvers()
|
||||
|
||||
tasks = []
|
||||
logging.info(f"Домены: {len(domains)} | IP/CIDR: {len(ready_ips)}")
|
||||
|
||||
for name, servers in resolvers:
|
||||
resolver = dns.asyncresolver.Resolver()
|
||||
resolver.nameservers = servers
|
||||
tasks = []
|
||||
|
||||
for domain in domains:
|
||||
tasks.append(resolve_domain(domain, resolver, semaphore))
|
||||
for _, servers in resolvers:
|
||||
resolver = dns.asyncresolver.Resolver()
|
||||
resolver.nameservers = servers
|
||||
|
||||
results = await asyncio.gather(*tasks)
|
||||
for domain in domains:
|
||||
tasks.append(resolve_domain(domain, resolver, semaphore))
|
||||
|
||||
all_ips = set(ready_ips)
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
for res in results:
|
||||
all_ips.update(res)
|
||||
all_ips = set(ready_ips)
|
||||
|
||||
logging.info(f"Всего IP до фильтра: {len(all_ips)}")
|
||||
for res in results:
|
||||
all_ips.update(res)
|
||||
|
||||
if cfg["exclude_cloudflare"]:
|
||||
cf_ips = await get_cloudflare_ips()
|
||||
before = len(all_ips)
|
||||
logging.info(f"Всего IP до фильтра: {len(all_ips)}")
|
||||
|
||||
all_ips = {ip for ip in all_ips if ip not in cf_ips}
|
||||
logging.info(f"Удалено Cloudflare IP: {before - len(all_ips)}")
|
||||
if cfg["exclude_cloudflare"]:
|
||||
cf_ips = await get_cloudflare_ips()
|
||||
before = len(all_ips)
|
||||
|
||||
before_private = len(all_ips)
|
||||
all_ips = {ip for ip in all_ips if is_public_ip(ip)}
|
||||
logging.info(f"Удалено приватных IP: {before_private - len(all_ips)}")
|
||||
logging.info(f"Финальных IP: {len(all_ips)}")
|
||||
all_ips = {ip for ip in all_ips if ip not in cf_ips}
|
||||
logging.info(f"Удалено Cloudflare IP: {before - len(all_ips)}")
|
||||
|
||||
result_file = os.path.join(BASE_DIR, "result_ips.txt")
|
||||
with open(result_file, "w") as f:
|
||||
for ip in sorted(all_ips):
|
||||
f.write(ip + "\n")
|
||||
before_private = len(all_ips)
|
||||
all_ips = {ip for ip in all_ips if is_public_ip(ip)}
|
||||
logging.info(f"Удалено приватных IP: {before_private - len(all_ips)}")
|
||||
logging.info(f"Финальных IP: {len(all_ips)}")
|
||||
|
||||
gateway = ""
|
||||
interface = ""
|
||||
if cfg["gateway"]:
|
||||
gateway = cfg["gateway"]
|
||||
result_file = os.path.join(BASE_DIR, f"{cfg['name']}_result_ips.txt")
|
||||
with open(result_file, "w") as f:
|
||||
for ip in sorted(all_ips):
|
||||
f.write(ip + "\n")
|
||||
|
||||
if cfg["interface"]:
|
||||
interface = cfg["interface"]
|
||||
if not cfg["gateway"] and not cfg["interface"]:
|
||||
logging.error("Не указан Gateway и Interface, выход.")
|
||||
exit(1)
|
||||
|
||||
if not gateway and not interface:
|
||||
logging.error("Не указан Gateway и Interface, выход.")
|
||||
exit(1)
|
||||
with open(cfg["script"], "w") as f:
|
||||
f.write(
|
||||
f'#!/bin/bash\n\n'
|
||||
f'if ! ip rule list | grep -q "lookup {cfg["table"]}.*priority {cfg["priority"]}"; then\n'
|
||||
f' ip rule add table {cfg["table"]} priority {cfg["priority"]}\n'
|
||||
f'fi\n\n'
|
||||
f'ip route flush table {cfg["table"]}\n\n'
|
||||
)
|
||||
|
||||
with open(cfg["script"], "w") as f:
|
||||
f.write(
|
||||
f'#!/bin/bash\n\n'
|
||||
f'if ! ip rule list | grep -q "lookup {cfg["table"]}"; then\n'
|
||||
f' ip rule add table {cfg["table"]} priority 101\n'
|
||||
f'fi\n\n'
|
||||
f'ip route flush table {cfg["table"]}\n\n'
|
||||
)
|
||||
for ip in sorted(all_ips):
|
||||
if "/" in ip:
|
||||
if cfg["gateway"]:
|
||||
f.write(f'ip route replace {ip} via {cfg["gateway"]} table {cfg["table"]}\n')
|
||||
else:
|
||||
f.write(f'ip route replace {ip} dev {cfg["interface"]} table {cfg["table"]}\n')
|
||||
else:
|
||||
if cfg["gateway"]:
|
||||
f.write(f'ip route replace {ip}/32 via {cfg["gateway"]} table {cfg["table"]}\n')
|
||||
else:
|
||||
f.write(f'ip route replace {ip}/32 dev {cfg["interface"]} table {cfg["table"]}\n')
|
||||
|
||||
for ip in sorted(all_ips):
|
||||
if "/" in ip:
|
||||
if cfg["gateway"]:
|
||||
f.write(f'ip route replace {ip} via {cfg["gateway"]} table {cfg["table"]}\n')
|
||||
elif cfg["interface"]:
|
||||
f.write(f'ip route replace {ip} dev {cfg["interface"]} table {cfg["table"]}\n')
|
||||
os.chmod(cfg["script"], 0o755)
|
||||
|
||||
else:
|
||||
if cfg["gateway"]:
|
||||
f.write(f'ip route replace {ip}/32 via {cfg["gateway"]} table {cfg["table"]}\n')
|
||||
elif cfg["interface"]:
|
||||
f.write(f'ip route replace {ip}/32 dev {cfg["interface"]} table {cfg["table"]}\n')
|
||||
with open(cfg["rollback_script"], "w") as f:
|
||||
f.write(
|
||||
f'#!/bin/bash\n\n'
|
||||
f'ip route flush table {cfg["table"]}\n'
|
||||
f'ip rule del table {cfg["table"]} priority {cfg["priority"]} 2>/dev/null\n'
|
||||
)
|
||||
|
||||
os.chmod(cfg["script"], 0o755)
|
||||
os.chmod(cfg["rollback_script"], 0o755)
|
||||
|
||||
with open(cfg["rollback_script"], "w") as f:
|
||||
f.write(
|
||||
f'#!/bin/bash\n\n'
|
||||
f'ip route flush table {cfg["table"]}\n'
|
||||
f'ip rule del table {cfg["table"]} priority 101 2>/dev/null\n'
|
||||
)
|
||||
logging.info(f"ON: {cfg['script']}")
|
||||
logging.info(f"OFF: {cfg['rollback_script']}")
|
||||
|
||||
os.chmod(cfg["rollback_script"], 0o755)
|
||||
|
||||
logging.info(f"ON: {cfg['script']}")
|
||||
logging.info(f"OFF: {cfg['rollback_script']}")
|
||||
|
||||
if cfg["run"]:
|
||||
logging.info("Запуск post-команды")
|
||||
os.system(cfg["run"])
|
||||
if cfg["run"]:
|
||||
logging.info("Запуск post-команды")
|
||||
os.system(cfg["run"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user