Compare commits
13
Commits
f9f8506963
..
1.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89afe87b47 | ||
|
|
e91bba759b | ||
|
|
b197fd5977 | ||
|
|
13926f2e9f | ||
|
|
ae16862a11 | ||
|
|
6ac20e25e3 | ||
|
|
620d00134c | ||
|
|
d13f79ed82 | ||
|
|
cf96acda92 | ||
|
|
d811faef45 | ||
|
|
85fb4b4e79 | ||
|
|
20ec627515 | ||
|
|
f8e6888d50 |
@@ -0,0 +1,22 @@
|
|||||||
|
# User/group id the process runs as, matches ownership of the /adguard mount
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
|
||||||
|
# Timezone of the container
|
||||||
|
TZ=Europe/Paris
|
||||||
|
|
||||||
|
# Country codes for CIDR lists, comma separated. Prefix with ! to exclude instead of include.
|
||||||
|
# Full lists here: https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4
|
||||||
|
BLOCK_COUNTRIES=cn,ru
|
||||||
|
|
||||||
|
# Scheduling: daily or weekly
|
||||||
|
BLOCKLIST_CRON_TYPE=daily
|
||||||
|
# If weekly, choose the day: mon, tue, wed, thu, fri, sat, sun
|
||||||
|
BLOCKLIST_CRON_DAY=mon
|
||||||
|
# Time of day to run the update, 24h HH:MM format
|
||||||
|
BLOCKLIST_CRON_TIME=06:00
|
||||||
|
|
||||||
|
# Docker API URL used to restart the AdGuard container (via socket-proxy)
|
||||||
|
DOCKER_API_URL=http://socket-proxy-adguard:2375
|
||||||
|
# Name of the AdGuard Home container to restart
|
||||||
|
ADGUARD_CONTAINER_NAME=adguardhome
|
||||||
+192
-9
@@ -16,6 +16,22 @@ jobs:
|
|||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Scan for secrets
|
||||||
|
run: |
|
||||||
|
# docker cp, not a build COPY: a repo's own .dockerignore (e.g. one that
|
||||||
|
# excludes .git for prod builds) would otherwise silently give an empty,
|
||||||
|
# falsely-clean scan.
|
||||||
|
CID=$(docker create zricethezav/gitleaks:v8.30.1 detect --source=/repo --no-banner -v)
|
||||||
|
docker cp . "$CID:/repo"
|
||||||
|
docker start -a "$CID"
|
||||||
|
STATUS=$?
|
||||||
|
docker rm "$CID" > /dev/null
|
||||||
|
exit $STATUS
|
||||||
|
|
||||||
|
- name: Lint Dockerfile with hadolint
|
||||||
|
run: docker run --rm -i hadolint/hadolint:v2.15.1-alpine hadolint --failure-threshold error - < Dockerfile
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: |
|
run: |
|
||||||
@@ -41,7 +57,10 @@ jobs:
|
|||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: |
|
run: |
|
||||||
docker build --target test -t adguard-cidre:test .
|
docker build --target test -t adguard-cidre:test .
|
||||||
docker run --rm adguard-cidre:test pytest -v
|
docker run --rm adguard-cidre:test pytest -v --cov=. --cov-report=term-missing --cov-fail-under=75
|
||||||
|
|
||||||
|
- name: Lint with ruff
|
||||||
|
run: docker build --target lint -t adguard-cidre:lint .
|
||||||
|
|
||||||
- name: Check deprecation warnings
|
- name: Check deprecation warnings
|
||||||
run: |
|
run: |
|
||||||
@@ -51,12 +70,53 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Scan with Trivy (critical - blocking)
|
- name: Scan with Trivy (critical - blocking)
|
||||||
|
id: trivy_critical
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-e DOCKER_HOST=tcp://dockerhost:2375 \
|
-e DOCKER_HOST=tcp://dockerhost:2375 \
|
||||||
--add-host=dockerhost:host-gateway \
|
--add-host=dockerhost:host-gateway \
|
||||||
aquasec/trivy:0.74.0 image --exit-code 1 --severity CRITICAL adguard-cidre:ci
|
aquasec/trivy:0.74.0 image --exit-code 1 --severity CRITICAL adguard-cidre:ci
|
||||||
|
|
||||||
|
- name: Handle CRITICAL findings
|
||||||
|
if: steps.trivy_critical.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" != "schedule" ]; then
|
||||||
|
echo "::error::CRITICAL vulnerabilities found, failing the build."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Scheduled scan found CRITICAL vulnerabilities — attempting an automatic apk upgrade + rescan."
|
||||||
|
|
||||||
|
sed -i '/^FROM .* AS base$/a RUN apk upgrade --no-cache' Dockerfile
|
||||||
|
docker build -t adguard-cidre:remediated .
|
||||||
|
|
||||||
|
if docker run --rm \
|
||||||
|
-e DOCKER_HOST=tcp://dockerhost:2375 \
|
||||||
|
--add-host=dockerhost:host-gateway \
|
||||||
|
aquasec/trivy:0.74.0 image --exit-code 1 --severity CRITICAL adguard-cidre:remediated; then
|
||||||
|
echo "apk upgrade clears the CRITICAL finding(s) — opening a PR for review."
|
||||||
|
|
||||||
|
BRANCH="auto/cve-fix-$(date +%Y%m%d)-$(echo "${{ github.sha }}" | cut -c1-7)"
|
||||||
|
git config user.name "adguard-cidre-ci"
|
||||||
|
git config user.email "[email protected]"
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add Dockerfile
|
||||||
|
git commit -m "Auto-remediate CRITICAL CVE via apk upgrade"
|
||||||
|
git config --unset-all http.https://git.djeex.fr/.extraheader || true
|
||||||
|
git push "https://Djeex:${{ secrets.CI_PUSH_TOKEN }}@git.djeex.fr/Djeex/adguard-cidre.git" "HEAD:$BRANCH"
|
||||||
|
|
||||||
|
PR_JSON=$(curl -s -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.CI_PUSH_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$(jq -n --arg head "$BRANCH" '{title: "🔒 Auto: remediate CRITICAL CVE via apk upgrade", head: $head, base: "main", body: "Opened automatically by the scheduled CVE scan. An `apk upgrade --no-cache` cleared the CRITICAL Trivy finding(s) in a rebuild — review the diff and merge to publish the fix."}')" \
|
||||||
|
"https://git.djeex.fr/api/v1/repos/Djeex/adguard-cidre/pulls")
|
||||||
|
echo "PR API response: $(echo "$PR_JSON" | jq -r '.html_url // .message // "unknown"')"
|
||||||
|
else
|
||||||
|
echo "::error::apk upgrade does not clear the CRITICAL finding(s) — no automatic fix available, needs manual review."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Scan with Trivy (high - informative)
|
- name: Scan with Trivy (high - informative)
|
||||||
run: |
|
run: |
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
@@ -69,10 +129,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
BEFORE="${{ github.event.before }}"
|
BEFORE="${{ github.event.before }}"
|
||||||
if [ -n "$BEFORE" ] && [ "$BEFORE" != "0000000000000000000000000000000000000000" ] && git cat-file -e "$BEFORE" 2>/dev/null; then
|
if [ -n "$BEFORE" ] && [ "$BEFORE" != "0000000000000000000000000000000000000000" ] && git cat-file -e "$BEFORE" 2>/dev/null; then
|
||||||
CHANGED=$(git diff --name-only "$BEFORE" "${{ github.sha }}")
|
BASE_REF="$BEFORE"
|
||||||
else
|
else
|
||||||
CHANGED=$(git diff --name-only HEAD~1 HEAD)
|
BASE_REF="HEAD~1"
|
||||||
fi
|
fi
|
||||||
|
CHANGED=$(git diff --name-only "$BASE_REF" "${{ github.sha }}")
|
||||||
echo "Changed files:"
|
echo "Changed files:"
|
||||||
echo "$CHANGED"
|
echo "$CHANGED"
|
||||||
|
|
||||||
@@ -81,18 +142,140 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED" | grep -qE '^VERSION$'; then
|
||||||
|
echo "VERSION was manually edited in this push, using it as-is."
|
||||||
|
else
|
||||||
|
echo "VERSION untouched but container files changed, auto-bumping the build number (Z)."
|
||||||
|
OLD_VERSION=$(tr -d '[:space:]' < VERSION)
|
||||||
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$OLD_VERSION"
|
||||||
|
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
|
||||||
|
echo "$NEW_VERSION" > VERSION
|
||||||
|
|
||||||
|
git config user.name "adguard-cidre-ci"
|
||||||
|
git config user.email "[email protected]"
|
||||||
|
git add VERSION
|
||||||
|
git commit -m "Bump build version to $NEW_VERSION [skip ci]"
|
||||||
|
|
||||||
|
# Belt and suspenders: actions/checkout can leave its own ephemeral
|
||||||
|
# credential injected as an extraheader, which would silently override
|
||||||
|
# the URL-embedded token below. persist-credentials:false on checkout
|
||||||
|
# should already prevent this, but strip it here too just in case.
|
||||||
|
git config --unset-all http.https://git.djeex.fr/.extraheader || true
|
||||||
|
|
||||||
|
git push "https://Djeex:${{ secrets.CI_PUSH_TOKEN }}@git.djeex.fr/Djeex/adguard-cidre.git" HEAD:main
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$(tr -d '[:space:]' < VERSION)
|
||||||
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
|
||||||
|
MINOR_TAG="${MAJOR}.${MINOR}"
|
||||||
|
|
||||||
IMAGE=git.djeex.fr/djeex/adguard-cidre
|
IMAGE=git.djeex.fr/djeex/adguard-cidre
|
||||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.djeex.fr -u Djeex --password-stdin
|
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.djeex.fr -u Djeex --password-stdin
|
||||||
|
|
||||||
# Retag the already-built, already-scanned image — never rebuild for publish,
|
# Retag the already-built, already-scanned image — never rebuild for publish,
|
||||||
# so what ships is byte-for-byte what Trivy just scanned.
|
# so what ships is byte-for-byte what Trivy just scanned.
|
||||||
docker tag adguard-cidre:ci "$IMAGE:latest"
|
docker tag adguard-cidre:ci "$IMAGE:latest"
|
||||||
docker push "$IMAGE:latest"
|
docker tag adguard-cidre:ci "$IMAGE:$MINOR_TAG"
|
||||||
|
|
||||||
if echo "$CHANGED" | grep -qE '^VERSION$'; then
|
|
||||||
VERSION=$(tr -d '[:space:]' < VERSION)
|
|
||||||
docker tag adguard-cidre:ci "$IMAGE:$VERSION"
|
docker tag adguard-cidre:ci "$IMAGE:$VERSION"
|
||||||
|
docker push "$IMAGE:latest"
|
||||||
|
docker push "$IMAGE:$MINOR_TAG"
|
||||||
docker push "$IMAGE:$VERSION"
|
docker push "$IMAGE:$VERSION"
|
||||||
else
|
|
||||||
echo "VERSION unchanged, skipping versioned tag to avoid overwriting an existing release."
|
GHCR_IMAGE=ghcr.io/djeex/adguard-cidre
|
||||||
|
echo "${{ secrets.GH_TOKEN }}" | docker login ghcr.io -u Djeex --password-stdin
|
||||||
|
|
||||||
|
docker tag adguard-cidre:ci "$GHCR_IMAGE:latest"
|
||||||
|
docker tag adguard-cidre:ci "$GHCR_IMAGE:$MINOR_TAG"
|
||||||
|
docker tag adguard-cidre:ci "$GHCR_IMAGE:$VERSION"
|
||||||
|
docker push "$GHCR_IMAGE:latest"
|
||||||
|
docker push "$GHCR_IMAGE:$MINOR_TAG"
|
||||||
|
docker push "$GHCR_IMAGE:$VERSION"
|
||||||
|
|
||||||
|
TRIGGER_MSG=$(git log -1 --format=%s "${{ github.sha }}")
|
||||||
|
PR_NUM=$(echo "$TRIGGER_MSG" | grep -oE '#[0-9]+' | head -1 | tr -d '#' || true)
|
||||||
|
|
||||||
|
CATEGORY="🔧 Maintenance"
|
||||||
|
CHANGE_TITLE="$TRIGGER_MSG"
|
||||||
|
|
||||||
|
if [ -n "$PR_NUM" ]; then
|
||||||
|
PR_JSON=$(curl -s -H "Authorization: token ${{ secrets.CI_PUSH_TOKEN }}" \
|
||||||
|
"https://git.djeex.fr/api/v1/repos/Djeex/adguard-cidre/pulls/$PR_NUM")
|
||||||
|
PR_TITLE=$(echo "$PR_JSON" | jq -r '.title // empty' 2>/dev/null || true)
|
||||||
|
LABELS=$(echo "$PR_JSON" | jq -r '.labels[]?.name' 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ -n "$PR_TITLE" ]; then
|
||||||
|
CHANGE_TITLE="$PR_TITLE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$LABELS" | grep -qx 'bug'; then
|
||||||
|
CATEGORY="⚠️ Hotfix"
|
||||||
|
elif echo "$LABELS" | grep -qx 'major'; then
|
||||||
|
CATEGORY="💥 Breaking change"
|
||||||
|
elif echo "$LABELS" | grep -qx 'minor'; then
|
||||||
|
CATEGORY="✨ Update"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_URL="https://git.djeex.fr/Djeex/adguard-cidre"
|
||||||
|
COMMIT_LIST=$(git log --no-merges --format="- %s ([%h](${REPO_URL}/commit/%H))" "$BASE_REF".."${{ github.sha }}")
|
||||||
|
|
||||||
|
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
|
||||||
|
SOURCE_LINE="[${SHORT_SHA}](${REPO_URL}/commit/${{ github.sha }})"
|
||||||
|
if [ -n "$PR_NUM" ]; then
|
||||||
|
SOURCE_LINE="[#${PR_NUM}](${REPO_URL}/pulls/${PR_NUM}) · ${SOURCE_LINE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BODY=$(cat <<EOF
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### ${CATEGORY}
|
||||||
|
${CHANGE_TITLE}
|
||||||
|
|
||||||
|
**Source:** ${SOURCE_LINE}
|
||||||
|
**Image:** \`${IMAGE}:${VERSION}\`
|
||||||
|
|
||||||
|
**Commits:**
|
||||||
|
${COMMIT_LIST}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
JSON_PAYLOAD=$(jq -n \
|
||||||
|
--arg tag "$VERSION" \
|
||||||
|
--arg name "$VERSION" \
|
||||||
|
--arg body "$BODY" \
|
||||||
|
'{tag_name: $tag, name: $name, target_commitish: "main", body: $body}')
|
||||||
|
|
||||||
|
curl -s -o /dev/null -w "Release API response: %{http_code}\n" -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.CI_PUSH_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$JSON_PAYLOAD" \
|
||||||
|
"https://git.djeex.fr/api/v1/repos/Djeex/adguard-cidre/releases"
|
||||||
|
|
||||||
|
MIRROR_NOTICE="_This github repo is a mirror of https://git.djeex.fr/Djeex/adguard-cidre. You'll find full package, PR, history and release note there._"
|
||||||
|
GH_BODY=$(printf '%s\n\n%s' "$MIRROR_NOTICE" "$BODY")
|
||||||
|
|
||||||
|
GH_JSON_PAYLOAD=$(jq -n \
|
||||||
|
--arg tag "$VERSION" \
|
||||||
|
--arg name "$VERSION" \
|
||||||
|
--arg body "$GH_BODY" \
|
||||||
|
--arg sha "${{ github.sha }}" \
|
||||||
|
'{tag_name: $tag, name: $name, target_commitish: $sha, body: $body}')
|
||||||
|
|
||||||
|
GH_STATUS=0
|
||||||
|
for i in 1 2 3 4 5; do
|
||||||
|
GH_STATUS=$(curl -s -o /tmp/gh_release.json -w "%{http_code}" -X POST \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.GH_TOKEN }}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$GH_JSON_PAYLOAD" \
|
||||||
|
"https://api.github.com/repos/Djeex/adguard-cidre/releases")
|
||||||
|
if [ "$GH_STATUS" = "201" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "GitHub Release attempt $i failed (HTTP $GH_STATUS) — mirror may not have synced this commit yet, retrying in 15s..."
|
||||||
|
sleep 15
|
||||||
|
done
|
||||||
|
echo "GitHub Release API response: $GH_STATUS"
|
||||||
|
if [ "$GH_STATUS" != "201" ]; then
|
||||||
|
cat /tmp/gh_release.json 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
+12
-5
@@ -1,8 +1,8 @@
|
|||||||
FROM python:3.14-alpine AS base
|
FROM python:3.14.7-alpine AS base
|
||||||
|
|
||||||
ENV TZ=Europe/Paris
|
ENV TZ=Europe/Paris
|
||||||
|
|
||||||
RUN apk add --no-cache tzdata curl \
|
RUN apk add --no-cache tzdata curl su-exec \
|
||||||
&& cp /usr/share/zoneinfo/$TZ /etc/localtime \
|
&& cp /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||||
&& echo $TZ > /etc/timezone
|
&& echo $TZ > /etc/timezone
|
||||||
|
|
||||||
@@ -11,12 +11,19 @@ WORKDIR /app
|
|||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
COPY blocklist_scheduler.py .
|
COPY blocklist_scheduler.py entrypoint.sh VERSION ./
|
||||||
|
RUN chmod +x entrypoint.sh
|
||||||
|
|
||||||
FROM base AS test
|
FROM base AS test
|
||||||
RUN pip install --no-cache-dir pytest==9.1.1
|
RUN pip install --no-cache-dir pytest==9.1.1 pytest-cov==7.1.0
|
||||||
COPY tests/ tests/
|
COPY tests/ tests/
|
||||||
COPY pytest.ini .
|
COPY pytest.ini .
|
||||||
|
|
||||||
|
FROM base AS lint
|
||||||
|
RUN pip install --no-cache-dir ruff==0.16.4
|
||||||
|
COPY ruff.toml .
|
||||||
|
COPY tests/ tests/
|
||||||
|
RUN ruff check . && ruff format --check .
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
ENTRYPOINT ["python3", "blocklist_scheduler.py"]
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
| Variable | Description | Example | Possible Values |
|
| Variable | Description | Example | Possible Values |
|
||||||
|--------------------------|--------------------------------------------------------------------------|-----------------------------|---------------------------------------------|
|
|--------------------------|--------------------------------------------------------------------------|-----------------------------|---------------------------------------------|
|
||||||
|
| `PUID` | User ID the process runs as (drops root at startup) | `1000` | Any valid numeric UID |
|
||||||
|
| `PGID` | Group ID the process runs as | `1000` | Any valid numeric GID |
|
||||||
| `TZ` | Timezone of the container to correctly schedule updates | `Europe/Paris` | Any valid timezone (e.g., `UTC`, `America/New_York`, etc.) |
|
| `TZ` | Timezone of the container to correctly schedule updates | `Europe/Paris` | Any valid timezone (e.g., `UTC`, `America/New_York`, etc.) |
|
||||||
| `BLOCK_COUNTRIES` | List of country codes for CIDR lists, separated by commas. You can also define an exclude list (all countries except the specified ones) by prefixing each country code with !. Mixing inclusion and exclusion codes is not supported. | including list : `cn,ru,ir`, excluding list : `!cn,!ru,!ir` | ISO 2-letter country codes |
|
| `BLOCK_COUNTRIES` | List of country codes for CIDR lists, separated by commas. You can also define an exclude list (all countries except the specified ones) by prefixing each country code with !. Mixing inclusion and exclusion codes is not supported. | including list : `cn,ru,ir`, excluding list : `!cn,!ru,!ir` | ISO 2-letter country codes |
|
||||||
| `BLOCKLIST_CRON_TYPE` | Scheduling type: `daily` or `weekly` | `daily` | `daily`, `weekly` |
|
| `BLOCKLIST_CRON_TYPE` | Scheduling type: `daily` or `weekly` | `daily` | `daily`, `weekly` |
|
||||||
@@ -64,6 +66,8 @@
|
|||||||
container_name: adguard-cidre
|
container_name: adguard-cidre
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
- PUID=1000 # user id the process runs as, matches ownership of the /adguard mount
|
||||||
|
- PGID=1000 # group id the process runs as
|
||||||
- TZ=Europe/Paris # change to your timezone
|
- TZ=Europe/Paris # change to your timezone
|
||||||
- BLOCK_COUNTRIES=cn,ru # choose countries listed IP to block. Full lists here https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4
|
- BLOCK_COUNTRIES=cn,ru # choose countries listed IP to block. Full lists here https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4
|
||||||
- BLOCKLIST_CRON_TYPE=daily # daily or weekly
|
- BLOCKLIST_CRON_TYPE=daily # daily or weekly
|
||||||
@@ -121,11 +125,12 @@
|
|||||||
git clone https://git.djeex.fr/Djeex/adguard-cidre
|
git clone https://git.djeex.fr/Djeex/adguard-cidre
|
||||||
cd adguard-cidre
|
cd adguard-cidre
|
||||||
```
|
```
|
||||||
2. **Modify docker-compose.yml**
|
2. **Edit the `.env` file**
|
||||||
|
|
||||||
- Set `BLOCK_COUNTRIES` environment variable with the countries you want to block.
|
- A `.env` file is included at the repo root with all environment variables (see [Environment Variables](#environment-variables)). Edit values there instead of `docker-compose.yml`.
|
||||||
- Adjust `BLOCKLIST_CRON` variables if you want a different update frequency.
|
- Set `BLOCK_COUNTRIES` with the countries you want to block.
|
||||||
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
|
- Adjust `BLOCKLIST_CRON_*` variables if you want a different update frequency.
|
||||||
|
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard` in `docker-compose.yml`.
|
||||||
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+51
-19
@@ -1,17 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import os
|
||||||
import yaml
|
|
||||||
import schedule
|
|
||||||
import time
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import schedule
|
||||||
|
import yaml
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format='[blocklist] %(levelname)s: %(message)s',
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||||
stream=sys.stdout,
|
stream=sys.stdout,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +20,9 @@ ADGUARD_YAML = Path("/adguard/AdGuardHome.yaml")
|
|||||||
TMP_YAML = ADGUARD_YAML.parent / (ADGUARD_YAML.name + ".tmp")
|
TMP_YAML = ADGUARD_YAML.parent / (ADGUARD_YAML.name + ".tmp")
|
||||||
MANUAL_IPS_FILE = Path("/adguard/manually_blocked_ips.conf")
|
MANUAL_IPS_FILE = Path("/adguard/manually_blocked_ips.conf")
|
||||||
CIDR_BASE_URL = "https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4"
|
CIDR_BASE_URL = "https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4"
|
||||||
COUNTRY_LIST_URL = "https://raw.githubusercontent.com/vulnebify/cidre/refs/heads/main/cidre/countries.py"
|
COUNTRY_LIST_URL = (
|
||||||
|
"https://raw.githubusercontent.com/vulnebify/cidre/refs/heads/main/cidre/countries.py"
|
||||||
|
)
|
||||||
|
|
||||||
FIRST_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.first-start.bak"
|
FIRST_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.first-start.bak"
|
||||||
LAST_UPDATE_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.last-update.bak"
|
LAST_UPDATE_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.last-update.bak"
|
||||||
@@ -32,6 +35,7 @@ BLOCKLIST_CRON_DAY = os.getenv("BLOCKLIST_CRON_DAY", "mon").lower()
|
|||||||
ADGUARD_CONTAINER_NAME = os.getenv("ADGUARD_CONTAINER_NAME", "adguardhome")
|
ADGUARD_CONTAINER_NAME = os.getenv("ADGUARD_CONTAINER_NAME", "adguardhome")
|
||||||
DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://socket-proxy-adguard:2375")
|
DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://socket-proxy-adguard:2375")
|
||||||
|
|
||||||
|
|
||||||
def backup_first_start():
|
def backup_first_start():
|
||||||
if not FIRST_BACKUP.exists():
|
if not FIRST_BACKUP.exists():
|
||||||
logging.info(f"Creating first start backup: {FIRST_BACKUP}")
|
logging.info(f"Creating first start backup: {FIRST_BACKUP}")
|
||||||
@@ -39,10 +43,12 @@ def backup_first_start():
|
|||||||
else:
|
else:
|
||||||
logging.info("First start backup already exists, skipping.")
|
logging.info("First start backup already exists, skipping.")
|
||||||
|
|
||||||
|
|
||||||
def backup_last_update():
|
def backup_last_update():
|
||||||
logging.info(f"Creating last update backup: {LAST_UPDATE_BACKUP}")
|
logging.info(f"Creating last update backup: {LAST_UPDATE_BACKUP}")
|
||||||
LAST_UPDATE_BACKUP.write_text(ADGUARD_YAML.read_text())
|
LAST_UPDATE_BACKUP.write_text(ADGUARD_YAML.read_text())
|
||||||
|
|
||||||
|
|
||||||
def fetch_all_country_codes():
|
def fetch_all_country_codes():
|
||||||
try:
|
try:
|
||||||
resp = requests.get(COUNTRY_LIST_URL, timeout=15)
|
resp = requests.get(COUNTRY_LIST_URL, timeout=15)
|
||||||
@@ -53,6 +59,7 @@ def fetch_all_country_codes():
|
|||||||
logging.error(f"Failed to fetch available country codes: {e}")
|
logging.error(f"Failed to fetch available country codes: {e}")
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
|
|
||||||
def get_selected_countries():
|
def get_selected_countries():
|
||||||
if not BLOCK_COUNTRIES:
|
if not BLOCK_COUNTRIES:
|
||||||
logging.error("BLOCK_COUNTRIES is not set. Skipping update.")
|
logging.error("BLOCK_COUNTRIES is not set. Skipping update.")
|
||||||
@@ -67,7 +74,9 @@ def get_selected_countries():
|
|||||||
is_inclusion = all(not c.startswith("!") for c in raw_codes)
|
is_inclusion = all(not c.startswith("!") for c in raw_codes)
|
||||||
|
|
||||||
if not (is_exclusion or is_inclusion):
|
if not (is_exclusion or is_inclusion):
|
||||||
logging.error("Mixed syntax in BLOCK_COUNTRIES. Use only inclusion (e.g. 'fr,de') or only exclusion (e.g. '!fr,!de').")
|
logging.error(
|
||||||
|
"Mixed syntax in BLOCK_COUNTRIES. Use only inclusion (e.g. 'fr,de') or only exclusion (e.g. '!fr,!de')."
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
available = fetch_all_country_codes()
|
available = fetch_all_country_codes()
|
||||||
@@ -81,6 +90,7 @@ def get_selected_countries():
|
|||||||
else:
|
else:
|
||||||
return sorted(selected & available)
|
return sorted(selected & available)
|
||||||
|
|
||||||
|
|
||||||
def download_cidr_lists(countries):
|
def download_cidr_lists(countries):
|
||||||
combined_ips = []
|
combined_ips = []
|
||||||
for code in countries:
|
for code in countries:
|
||||||
@@ -96,6 +106,7 @@ def download_cidr_lists(countries):
|
|||||||
logging.warning(f"Failed to download {code}: {e}")
|
logging.warning(f"Failed to download {code}: {e}")
|
||||||
return combined_ips
|
return combined_ips
|
||||||
|
|
||||||
|
|
||||||
def read_manual_ips():
|
def read_manual_ips():
|
||||||
if MANUAL_IPS_FILE.exists():
|
if MANUAL_IPS_FILE.exists():
|
||||||
logging.info(f"Reading manual IPs from {MANUAL_IPS_FILE}")
|
logging.info(f"Reading manual IPs from {MANUAL_IPS_FILE}")
|
||||||
@@ -103,7 +114,7 @@ def read_manual_ips():
|
|||||||
with MANUAL_IPS_FILE.open() as f:
|
with MANUAL_IPS_FILE.open() as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line and (line.count('.') == 3 or '/' in line):
|
if line and (line.count(".") == 3 or "/" in line):
|
||||||
valid_ips.append(line)
|
valid_ips.append(line)
|
||||||
logging.info(f"Added {len(valid_ips)} manual IP entries")
|
logging.info(f"Added {len(valid_ips)} manual IP entries")
|
||||||
return valid_ips
|
return valid_ips
|
||||||
@@ -111,6 +122,7 @@ def read_manual_ips():
|
|||||||
logging.info("Manual IPs file does not exist, skipping.")
|
logging.info("Manual IPs file does not exist, skipping.")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def update_yaml_with_ips(ips):
|
def update_yaml_with_ips(ips):
|
||||||
if not ADGUARD_YAML.exists():
|
if not ADGUARD_YAML.exists():
|
||||||
logging.error(f"{ADGUARD_YAML} does not exist. Cannot update.")
|
logging.error(f"{ADGUARD_YAML} does not exist. Cannot update.")
|
||||||
@@ -127,15 +139,16 @@ def update_yaml_with_ips(ips):
|
|||||||
logging.error("Invalid YAML format.")
|
logging.error("Invalid YAML format.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
data['dns']['disallowed_clients'] = ips
|
data["dns"]["disallowed_clients"] = ips
|
||||||
|
|
||||||
with TMP_YAML.open('w') as f:
|
with TMP_YAML.open("w") as f:
|
||||||
yaml.safe_dump(data, f)
|
yaml.safe_dump(data, f)
|
||||||
|
|
||||||
TMP_YAML.replace(ADGUARD_YAML)
|
TMP_YAML.replace(ADGUARD_YAML)
|
||||||
logging.info(f"Updated {ADGUARD_YAML} with new disallowed clients list.")
|
logging.info(f"Updated {ADGUARD_YAML} with new disallowed clients list.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def restart_adguard_container():
|
def restart_adguard_container():
|
||||||
restart_url = f"{DOCKER_API_URL}/containers/{ADGUARD_CONTAINER_NAME}/restart"
|
restart_url = f"{DOCKER_API_URL}/containers/{ADGUARD_CONTAINER_NAME}/restart"
|
||||||
logging.info(f"Restarting AdGuard container '{ADGUARD_CONTAINER_NAME}'...")
|
logging.info(f"Restarting AdGuard container '{ADGUARD_CONTAINER_NAME}'...")
|
||||||
@@ -148,6 +161,7 @@ def restart_adguard_container():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error restarting container: {e}")
|
logging.error(f"Error restarting container: {e}")
|
||||||
|
|
||||||
|
|
||||||
def update_blocklist():
|
def update_blocklist():
|
||||||
countries = get_selected_countries()
|
countries = get_selected_countries()
|
||||||
if not countries:
|
if not countries:
|
||||||
@@ -164,11 +178,14 @@ def update_blocklist():
|
|||||||
if success:
|
if success:
|
||||||
restart_adguard_container()
|
restart_adguard_container()
|
||||||
|
|
||||||
|
|
||||||
def schedule_job():
|
def schedule_job():
|
||||||
try:
|
try:
|
||||||
hour, minute = [int(x) for x in BLOCKLIST_CRON_TIME.split(":")]
|
hour, minute = [int(x) for x in BLOCKLIST_CRON_TIME.split(":")]
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.error(f"Invalid BLOCKLIST_CRON_TIME '{BLOCKLIST_CRON_TIME}', must be HH:MM. Defaulting to 06:00.")
|
logging.error(
|
||||||
|
f"Invalid BLOCKLIST_CRON_TIME '{BLOCKLIST_CRON_TIME}', must be HH:MM. Defaulting to 06:00."
|
||||||
|
)
|
||||||
hour, minute = 6, 0
|
hour, minute = 6, 0
|
||||||
|
|
||||||
if BLOCKLIST_CRON_TYPE == "daily":
|
if BLOCKLIST_CRON_TYPE == "daily":
|
||||||
@@ -176,20 +193,34 @@ def schedule_job():
|
|||||||
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
||||||
elif BLOCKLIST_CRON_TYPE == "weekly":
|
elif BLOCKLIST_CRON_TYPE == "weekly":
|
||||||
day_names = {
|
day_names = {
|
||||||
"mon": "monday", "tue": "tuesday", "wed": "wednesday", "thu": "thursday",
|
"mon": "monday",
|
||||||
"fri": "friday", "sat": "saturday", "sun": "sunday",
|
"tue": "tuesday",
|
||||||
|
"wed": "wednesday",
|
||||||
|
"thu": "thursday",
|
||||||
|
"fri": "friday",
|
||||||
|
"sat": "saturday",
|
||||||
|
"sun": "sunday",
|
||||||
}
|
}
|
||||||
day = BLOCKLIST_CRON_DAY[:3]
|
day = BLOCKLIST_CRON_DAY[:3]
|
||||||
if day not in day_names:
|
if day not in day_names:
|
||||||
logging.error(f"Invalid BLOCKLIST_CRON_DAY '{BLOCKLIST_CRON_DAY}', must be one of {list(day_names)}. Defaulting to Monday.")
|
logging.error(
|
||||||
|
f"Invalid BLOCKLIST_CRON_DAY '{BLOCKLIST_CRON_DAY}', must be one of {list(day_names)}. Defaulting to Monday."
|
||||||
|
)
|
||||||
day = "mon"
|
day = "mon"
|
||||||
getattr(schedule.every(), day_names[day]).at(f"{hour:02d}:{minute:02d}").do(update_blocklist)
|
getattr(schedule.every(), day_names[day]).at(f"{hour:02d}:{minute:02d}").do(
|
||||||
logging.info(f"Scheduled weekly update on {day_names[day].capitalize()} at {hour:02d}:{minute:02d}")
|
update_blocklist
|
||||||
|
)
|
||||||
|
logging.info(
|
||||||
|
f"Scheduled weekly update on {day_names[day].capitalize()} at {hour:02d}:{minute:02d}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.error(f"Invalid BLOCKLIST_CRON_TYPE '{BLOCKLIST_CRON_TYPE}', must be 'daily' or 'weekly'. Defaulting to daily.")
|
logging.error(
|
||||||
|
f"Invalid BLOCKLIST_CRON_TYPE '{BLOCKLIST_CRON_TYPE}', must be 'daily' or 'weekly'. Defaulting to daily."
|
||||||
|
)
|
||||||
schedule.every().day.at(f"{hour:02d}:{minute:02d}").do(update_blocklist)
|
schedule.every().day.at(f"{hour:02d}:{minute:02d}").do(update_blocklist)
|
||||||
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.info("Starting blocklist scheduler...")
|
logging.info("Starting blocklist scheduler...")
|
||||||
backup_first_start()
|
backup_first_start()
|
||||||
@@ -199,5 +230,6 @@ def main():
|
|||||||
schedule.run_pending()
|
schedule.run_pending()
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
+9
-7
@@ -5,14 +5,16 @@ services:
|
|||||||
container_name: adguard-cidre
|
container_name: adguard-cidre
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Paris # change to your timezone
|
- PUID=${PUID} # user id the process runs as, matches ownership of the /adguard mount
|
||||||
- BLOCK_COUNTRIES=cn,ru # choose countries listed IP to block. Full lists here https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4
|
- PGID=${PGID} # group id the process runs as
|
||||||
- BLOCKLIST_CRON_TYPE=daily # daily or weekly
|
- TZ=${TZ} # change to your timezone
|
||||||
|
- BLOCK_COUNTRIES=${BLOCK_COUNTRIES} # choose countries listed IP to block. Full lists here https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4
|
||||||
|
- BLOCKLIST_CRON_TYPE=${BLOCKLIST_CRON_TYPE} # daily or weekly
|
||||||
# if weekly, choose the day
|
# if weekly, choose the day
|
||||||
# - BLOCKLIST_CRON_DAY=mon
|
- BLOCKLIST_CRON_DAY=${BLOCKLIST_CRON_DAY}
|
||||||
- BLOCKLIST_CRON_TIME=06:00
|
- BLOCKLIST_CRON_TIME=${BLOCKLIST_CRON_TIME}
|
||||||
- DOCKER_API_URL=http://socket-proxy-adguard:2375 # docker socket proxy
|
- DOCKER_API_URL=${DOCKER_API_URL} # docker socket proxy
|
||||||
- ADGUARD_CONTAINER_NAME=adguardhome # adguard container name
|
- ADGUARD_CONTAINER_NAME=${ADGUARD_CONTAINER_NAME} # adguard container name
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/adguard/confdir:/adguard
|
- /path/to/adguard/confdir:/adguard
|
||||||
|
|
||||||
|
|||||||
Executable
+105
@@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CYAN="\033[1;36m"
|
||||||
|
NC="\033[0m"
|
||||||
|
|
||||||
|
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
||||||
|
fail() { echo "$(date '+%Y-%m-%d %H:%M:%S') [!] $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
print_banner() {
|
||||||
|
version=$(cat VERSION 2>/dev/null || echo "unknown")
|
||||||
|
title="AdGuard CIDRe - Version ${version}"
|
||||||
|
lines="Source: https://git.djeex.fr/Djeex/adguard-cidre
|
||||||
|
Mirror: https://github.com/Djeex/adguard-cidre"
|
||||||
|
|
||||||
|
width=${#title}
|
||||||
|
old_ifs=$IFS
|
||||||
|
IFS='
|
||||||
|
'
|
||||||
|
for l in $lines; do
|
||||||
|
[ ${#l} -gt "$width" ] && width=${#l}
|
||||||
|
done
|
||||||
|
IFS=$old_ifs
|
||||||
|
width=$((width + 2))
|
||||||
|
|
||||||
|
border=""
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt "$width" ]; do
|
||||||
|
border="${border}─"
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
printf "${CYAN}╭%s╮${NC}\n" "$border"
|
||||||
|
|
||||||
|
total_pad=$((width - ${#title}))
|
||||||
|
left=$((total_pad / 2))
|
||||||
|
right=$((total_pad - left))
|
||||||
|
printf "${CYAN}│${NC}%*s%s%*s${CYAN}│${NC}\n" "$left" "" "$title" "$right" ""
|
||||||
|
|
||||||
|
printf "${CYAN}├%s┤${NC}\n" "$border"
|
||||||
|
|
||||||
|
IFS='
|
||||||
|
'
|
||||||
|
for l in $lines; do
|
||||||
|
printf "${CYAN}│${NC} %-*s${CYAN}│${NC}\n" "$((width - 1))" "$l"
|
||||||
|
done
|
||||||
|
IFS=$old_ifs
|
||||||
|
|
||||||
|
printf "${CYAN}╰%s╯${NC}\n" "$border"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_banner
|
||||||
|
|
||||||
|
PUID=${PUID:-911}
|
||||||
|
PGID=${PGID:-911}
|
||||||
|
|
||||||
|
case "$PGID" in
|
||||||
|
''|*[!0-9]*) fail "PGID '$PGID' is not a valid numeric group id." ;;
|
||||||
|
esac
|
||||||
|
case "$PUID" in
|
||||||
|
''|*[!0-9]*) fail "PUID '$PUID' is not a valid numeric user id." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -d /adguard ] || fail "/adguard is not mounted — check the volume mapping in docker-compose.yml."
|
||||||
|
|
||||||
|
log "[i] Requested PUID=$PUID, PGID=$PGID"
|
||||||
|
|
||||||
|
log "[~] Checking group for GID $PGID..."
|
||||||
|
GROUP_NAME=$(getent group "$PGID" | cut -d: -f1 || true)
|
||||||
|
if [ -z "$GROUP_NAME" ]; then
|
||||||
|
log "[→] No existing group with GID $PGID, creating 'appgroup'."
|
||||||
|
addgroup -g "$PGID" appgroup || fail "Failed to create group with GID $PGID (addgroup exited $?)."
|
||||||
|
GROUP_NAME=appgroup
|
||||||
|
else
|
||||||
|
log "[i] Reusing existing group '$GROUP_NAME' (GID $PGID)."
|
||||||
|
fi
|
||||||
|
log "[✓] Group ready: $GROUP_NAME"
|
||||||
|
|
||||||
|
log "[~] Checking user for UID $PUID..."
|
||||||
|
USER_NAME=$(getent passwd "$PUID" | cut -d: -f1 || true)
|
||||||
|
if [ -z "$USER_NAME" ]; then
|
||||||
|
log "[→] No existing user with UID $PUID, creating 'appuser'."
|
||||||
|
adduser -D -u "$PUID" -G "$GROUP_NAME" appuser || fail "Failed to create user with UID $PUID (adduser exited $?)."
|
||||||
|
USER_NAME=appuser
|
||||||
|
else
|
||||||
|
log "[i] Reusing existing user '$USER_NAME' (UID $PUID)."
|
||||||
|
fi
|
||||||
|
log "[✓] User ready: $USER_NAME"
|
||||||
|
|
||||||
|
# Grant write access to the shared AdGuard config directory and to the files
|
||||||
|
# this script manages, without touching anything else AdGuardHome owns in
|
||||||
|
# there (its own db/certs/stats). AdGuardHome itself runs as root, so this is
|
||||||
|
# a one-way grant: it keeps full access regardless of what we chown here.
|
||||||
|
log "[~] Setting ownership of /adguard to $USER_NAME:$GROUP_NAME..."
|
||||||
|
chown "$USER_NAME:$GROUP_NAME" /adguard || fail "chown on /adguard failed — check that the host directory permissions allow it."
|
||||||
|
log "[✓] Ownership set on /adguard"
|
||||||
|
|
||||||
|
for f in AdGuardHome.yaml AdGuardHome.yaml.first-start.bak AdGuardHome.yaml.last-update.bak AdGuardHome.yaml.tmp; do
|
||||||
|
if [ -e "/adguard/$f" ]; then
|
||||||
|
chown "$USER_NAME:$GROUP_NAME" "/adguard/$f" || fail "chown on /adguard/$f failed."
|
||||||
|
log "[✓] chown OK: /adguard/$f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "[→] Dropping privileges to $USER_NAME:$GROUP_NAME and starting blocklist_scheduler.py"
|
||||||
|
exec su-exec "$USER_NAME:$GROUP_NAME" python3 blocklist_scheduler.py "$@"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[lint]
|
||||||
|
select = ["E", "F", "I", "UP", "B"]
|
||||||
|
ignore = ["E501"]
|
||||||
|
|
||||||
|
[lint.isort]
|
||||||
|
# See nvidia-stock-bot's ruff.toml for why this is pinned explicitly rather
|
||||||
|
# than left to auto-detection.
|
||||||
|
known-first-party = ["blocklist_scheduler"]
|
||||||
@@ -56,6 +56,7 @@ def test_backup_first_start_raises_if_adguard_yaml_missing(tmp_path, monkeypatch
|
|||||||
|
|
||||||
# --- update_yaml_with_ips (pyyaml) ---
|
# --- update_yaml_with_ips (pyyaml) ---
|
||||||
|
|
||||||
|
|
||||||
def test_update_yaml_with_ips_writes_disallowed_clients(tmp_path, monkeypatch):
|
def test_update_yaml_with_ips_writes_disallowed_clients(tmp_path, monkeypatch):
|
||||||
adguard_yaml = tmp_path / "AdGuardHome.yaml"
|
adguard_yaml = tmp_path / "AdGuardHome.yaml"
|
||||||
adguard_yaml.write_text("dns:\n bind_hosts:\n - 0.0.0.0\n")
|
adguard_yaml.write_text("dns:\n bind_hosts:\n - 0.0.0.0\n")
|
||||||
@@ -101,8 +102,11 @@ def test_update_yaml_with_ips_missing_dns_key_raises(tmp_path, monkeypatch):
|
|||||||
|
|
||||||
# --- fetch_all_country_codes / download_cidr_lists / restart_adguard_container (requests) ---
|
# --- fetch_all_country_codes / download_cidr_lists / restart_adguard_container (requests) ---
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_all_country_codes_parses_codes(monkeypatch):
|
def test_fetch_all_country_codes_parses_codes(monkeypatch):
|
||||||
monkeypatch.setattr(bs.requests, "get", lambda *a, **k: FakeResponse(text='COUNTRIES = ["FR", "DE", "US"]\n'))
|
monkeypatch.setattr(
|
||||||
|
bs.requests, "get", lambda *a, **k: FakeResponse(text='COUNTRIES = ["FR", "DE", "US"]\n')
|
||||||
|
)
|
||||||
|
|
||||||
assert bs.fetch_all_country_codes() == {"fr", "de", "us"}
|
assert bs.fetch_all_country_codes() == {"fr", "de", "us"}
|
||||||
|
|
||||||
@@ -136,7 +140,9 @@ def test_restart_adguard_container_success_does_not_raise(monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
def test_restart_adguard_container_error_status_does_not_raise(monkeypatch):
|
def test_restart_adguard_container_error_status_does_not_raise(monkeypatch):
|
||||||
monkeypatch.setattr(bs.requests, "post", lambda *a, **k: FakeResponse(status_code=500, text="err"))
|
monkeypatch.setattr(
|
||||||
|
bs.requests, "post", lambda *a, **k: FakeResponse(status_code=500, text="err")
|
||||||
|
)
|
||||||
|
|
||||||
bs.restart_adguard_container()
|
bs.restart_adguard_container()
|
||||||
|
|
||||||
@@ -152,6 +158,7 @@ def test_restart_adguard_container_network_error_does_not_raise(monkeypatch):
|
|||||||
|
|
||||||
# --- schedule_job (schedule) ---
|
# --- schedule_job (schedule) ---
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def clear_schedule():
|
def clear_schedule():
|
||||||
yield
|
yield
|
||||||
|
|||||||
Reference in New Issue
Block a user