Test CI
This commit is contained in:
70
.gitea/workflows/ci.yaml
Normal file
70
.gitea/workflows/ci.yaml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# .gitea/workflows/ci.yml
|
||||||
|
name: CI/CD Workflow
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main # Trigger auf den Main-Branch (kannst du anpassen)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest # Du kannst auch einen anderen selbstgehosteten Runner verwenden
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3.11 python3-pip
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
echo "Running tests..."
|
||||||
|
echo "Checking for python3..."
|
||||||
|
if command -v python3 &> /dev/null; then
|
||||||
|
echo "Python found"
|
||||||
|
python3 -c "print('Python is working')"
|
||||||
|
else
|
||||||
|
echo "Python not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy
|
||||||
|
runs-on: ubuntu-latest # Du kannst auch einen selbstgehosteten Runner verwenden
|
||||||
|
needs: test # Wartet auf das Test-Job
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3.11 python3-pip
|
||||||
|
|
||||||
|
- name: Install required Python packages
|
||||||
|
run: |
|
||||||
|
echo "Installing required Python packages..."
|
||||||
|
pip install requests pyyaml python-dotenv
|
||||||
|
|
||||||
|
- name: Export config vars as environment
|
||||||
|
env:
|
||||||
|
ADGUARD_URL: ${{ vars.ADGUARD_URL }}
|
||||||
|
ADGUARD_USER: ${{ vars.ADGUARD_USER }}
|
||||||
|
ADGUARD_PASSWORD: ${{ secrets.ADGUARD_PASSWORD }}
|
||||||
|
YAML_FILE: ${{ vars.YAML_FILE }}
|
||||||
|
run: |
|
||||||
|
echo "Exporting config vars..."
|
||||||
|
echo "URL: $ADGUARD_URL"
|
||||||
|
echo "User: $ADGUARD_USER"
|
||||||
|
echo "YAML_FILE: $YAML_FILE"
|
||||||
|
|
||||||
|
# - name: Run the Python script
|
||||||
|
# run: |
|
||||||
|
# echo "Deploying application..."
|
||||||
|
# python3 main.py # Dein Python-Skript ausführen
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: production
|
||||||
|
if: branch == 'main' # Läuft nur für den `main`-Branch
|
||||||
54
README.md
54
README.md
@@ -1,2 +1,54 @@
|
|||||||
# adguard-dns-tools
|
# AdGuard DNS Rewrite Sync Tool
|
||||||
|
|
||||||
|
This script synchronizes DNS rewrite rules between a local YAML configuration and an AdGuard Home instance using its REST API.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Logs into AdGuard Home via its control API
|
||||||
|
- Loads desired DNS rewrite entries from a YAML file
|
||||||
|
- Retrieves current DNS rewrite rules from AdGuard
|
||||||
|
- Compares current vs. desired state and:
|
||||||
|
- Adds new entries
|
||||||
|
- Updates existing ones with changed IPs
|
||||||
|
- Deletes entries no longer needed
|
||||||
|
- Merges local and remote rules and writes back to the YAML file
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- [`requests`](https://pypi.org/project/requests/)
|
||||||
|
- [`pyyaml`](https://pypi.org/project/PyYAML/)
|
||||||
|
- [`python-dotenv`](https://pypi.org/project/python-dotenv/)
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Create a .env file in the same directory with the following variables:
|
||||||
|
|
||||||
|
```env
|
||||||
|
ADGUARD_URL=http://<adguard-host>:3000
|
||||||
|
ADGUARD_USER=admin
|
||||||
|
PASSWORD=yourpassword
|
||||||
|
YAML_FILE=rewrites.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
# YAML File Format
|
||||||
|
The YAML file should contain rewrite entries under the rewrites key:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rewrites:
|
||||||
|
- domain: "example.org"
|
||||||
|
answer: "127.0.0.1"
|
||||||
|
- domain: "another.example.org"
|
||||||
|
answer: "192.168.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
Run the script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
131
main.py
Normal file
131
main.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
|
||||||
|
# from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# load_dotenv()
|
||||||
|
|
||||||
|
url = os.getenv("ADGUARD_URL")
|
||||||
|
username = os.getenv("ADGUARD_USER")
|
||||||
|
password = os.getenv("ADGUARD_PASSWORD")
|
||||||
|
yaml_file = os.getenv("YAML_FILE")
|
||||||
|
|
||||||
|
print(f"AdGuard URL: {url}")
|
||||||
|
print(f"Username: {username}")
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
|
||||||
|
class IndentDumper(yaml.Dumper):
|
||||||
|
def increase_indent(self, flow=False, indentless=False):
|
||||||
|
return super().increase_indent(flow, False)
|
||||||
|
|
||||||
|
def login():
|
||||||
|
r = session.post(f"{url}/control/login", json={"name": username, "password": password})
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise Exception(f"Login fehlgeschlagen: {r.status_code} {r.text}")
|
||||||
|
print("Login erfolgreich.")
|
||||||
|
|
||||||
|
def load_yaml():
|
||||||
|
with open(yaml_file, "r") as f:
|
||||||
|
return yaml.safe_load(f)["rewrites"]
|
||||||
|
|
||||||
|
def save_yaml(data):
|
||||||
|
with open(yaml_file, "w") as f:
|
||||||
|
yaml.dump(
|
||||||
|
{"rewrites": data},
|
||||||
|
f,
|
||||||
|
Dumper=IndentDumper,
|
||||||
|
default_flow_style=False,
|
||||||
|
sort_keys=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_rewrites():
|
||||||
|
r = session.get(f"{url}/control/rewrite/list")
|
||||||
|
r.raise_for_status()
|
||||||
|
print(r.json())
|
||||||
|
return r.json() # AdGuard gibt direkt eine Liste zurück
|
||||||
|
|
||||||
|
def add_rewrite(entry):
|
||||||
|
r = session.post(f"{url}/control/rewrite/add", json=entry)
|
||||||
|
r.raise_for_status()
|
||||||
|
print(f"Hinzugefügt: {entry}")
|
||||||
|
|
||||||
|
def delete_rewrite(entry):
|
||||||
|
r = session.post(f"{url}/control/rewrite/delete", json=entry)
|
||||||
|
r.raise_for_status()
|
||||||
|
print(f"Gelöscht: {entry}")
|
||||||
|
|
||||||
|
def update_rewrite(old_entry, new_entry):
|
||||||
|
r = session.put(f"{url}/control/rewrite/update", json={"target": old_entry, "update": new_entry})
|
||||||
|
r.raise_for_status()
|
||||||
|
print(f"Aktualisiert: {old_entry} -> {new_entry}")
|
||||||
|
|
||||||
|
def merge_rewrites(existing, desired):
|
||||||
|
# Mapping: domain -> IP (bestehend)
|
||||||
|
existing_map = {r["domain"]: r["answer"] for r in existing}
|
||||||
|
desired_map = {entry["domain"]: entry["answer"] for entry in desired}
|
||||||
|
|
||||||
|
updated = existing.copy()
|
||||||
|
|
||||||
|
# Hinzufügen und Ändern
|
||||||
|
for domain, ip in desired_map.items():
|
||||||
|
if domain in existing_map:
|
||||||
|
# Ändern, wenn die IP-Adresse unterschiedlich ist
|
||||||
|
if existing_map[domain] != ip:
|
||||||
|
print(f"Ändere {domain}: {existing_map[domain]} -> {ip}")
|
||||||
|
for r in updated:
|
||||||
|
if r["domain"] == domain:
|
||||||
|
r["answer"] = ip
|
||||||
|
else:
|
||||||
|
# Hinzufügen, wenn der Eintrag noch nicht existiert
|
||||||
|
print(f"Füge hinzu: {domain} -> {ip}")
|
||||||
|
updated.append({"domain": domain, "answer": ip})
|
||||||
|
|
||||||
|
# Entfernen von Einträgen, die nicht mehr gewünscht sind
|
||||||
|
for domain in existing_map.keys():
|
||||||
|
if domain not in desired_map:
|
||||||
|
updated = [r for r in updated if r["domain"] != domain]
|
||||||
|
print(f"Lösche: {domain} : {ip}")
|
||||||
|
|
||||||
|
return updated
|
||||||
|
|
||||||
|
def apply_rewrites(desired):
|
||||||
|
existing = get_rewrites()
|
||||||
|
|
||||||
|
existing_map = {r["domain"]: r["answer"] for r in existing}
|
||||||
|
desired_map = {r["domain"]: r["answer"] for r in desired}
|
||||||
|
|
||||||
|
# Änderungen oder Neueinträge
|
||||||
|
for domain, ip in desired_map.items():
|
||||||
|
if domain in existing_map:
|
||||||
|
if existing_map[domain] != ip:
|
||||||
|
update_rewrite(
|
||||||
|
{"domain": domain, "answer": existing_map[domain]},
|
||||||
|
{"domain": domain, "answer": ip}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
add_rewrite({"domain": domain, "answer": ip})
|
||||||
|
|
||||||
|
# Entferne veraltete
|
||||||
|
for domain, ip in existing_map.items():
|
||||||
|
if domain not in desired_map:
|
||||||
|
delete_rewrite({"domain": domain, "answer": ip})
|
||||||
|
|
||||||
|
def main():
|
||||||
|
login()
|
||||||
|
current = get_rewrites()
|
||||||
|
desired = load_yaml()
|
||||||
|
|
||||||
|
# Merge existing and desired rewrites
|
||||||
|
merged = merge_rewrites(current, desired)
|
||||||
|
|
||||||
|
# Save merged rewrites back to YAML
|
||||||
|
save_yaml(merged)
|
||||||
|
|
||||||
|
# Apply the merged rewrites to AdGuard
|
||||||
|
apply_rewrites(merged)
|
||||||
|
print("Alle Änderungen wurden erfolgreich angewendet.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
30
rewrites.yaml
Normal file
30
rewrites.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
rewrites:
|
||||||
|
- domain: pve01.home
|
||||||
|
answer: 192.168.178.141
|
||||||
|
- domain: share-box.home
|
||||||
|
answer: 192.168.178.123
|
||||||
|
- domain: pve02.home
|
||||||
|
answer: 192.168.178.20
|
||||||
|
- domain: pve03.home
|
||||||
|
answer: 192.168.178.189
|
||||||
|
- domain: vaultwarden.home
|
||||||
|
answer: 192.168.178.113
|
||||||
|
- domain: tdarr.home
|
||||||
|
answer: 192.168.178.106
|
||||||
|
- domain: gitlab.home
|
||||||
|
answer: 192.168.178.111
|
||||||
|
- domain: jellyfin.home
|
||||||
|
answer: 192.168.178.192
|
||||||
|
- domain: adguard01.home
|
||||||
|
answer: 192.168.178.117
|
||||||
|
- domain: ipmi.home
|
||||||
|
answer: 192.168.178.126
|
||||||
|
- domain: docker-server.home
|
||||||
|
answer: 192.168.178.21
|
||||||
|
- domain: arr.home
|
||||||
|
answer: 192.168.178.121
|
||||||
|
- domain: minecraft.home
|
||||||
|
answer: 192.168.178.119
|
||||||
|
- domain: gitea.home
|
||||||
|
answer: 192.168.178.116
|
||||||
Reference in New Issue
Block a user