7 Commits
Author SHA1 Message Date
Djeex 6ac20e25e3 Merge pull request 'Run container as non-root via PUID/PGID / Move environment configuration to a .env file' (#14) from dev-v2 into main
CI / build-and-scan (push) Successful in 32s
Reviewed-on: #14
2026-08-23 15:17:00 +02:00
Djeex 620d00134c Move environment configuration to a .env file
CI / build-and-scan (pull_request) Successful in 47s
2026-08-23 15:11:22 +02:00
Djeex d13f79ed82 Bump version to 1.5.0 for non-root support 2026-08-23 14:15:20 +02:00
Djeex cf96acda92 Run container as non-root via PUID/PGID with a startup entrypoint banner and logs 2026-08-23 14:14:54 +02:00
adguard-cidre-ci d811faef45 Bump build version to 1.4.1 [skip ci] 2026-08-22 14:04:02 +00:00
Djeex 85fb4b4e79 Pin Python to a patch version and auto-generate versioned releases with changelog
CI / build-and-scan (push) Successful in 36s
2026-08-22 16:02:26 +02:00
Djeex 20ec627515 Always update the versioned image tag alongside :latest on container changes
CI / build-and-scan (push) Successful in 37s
2026-08-22 14:46:56 +02:00
8 changed files with 243 additions and 24 deletions
+22
View File
@@ -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
+90 -6
View File
@@ -16,6 +16,7 @@ jobs:
uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
- name: Build Docker image
run: |
@@ -81,18 +82,101 @@ jobs:
exit 0
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
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.djeex.fr -u Djeex --password-stdin
# Retag the already-built, already-scanned image — never rebuild for publish,
# so what ships is byte-for-byte what Trivy just scanned.
docker tag adguard-cidre:ci "$IMAGE:latest"
docker push "$IMAGE:latest"
if echo "$CHANGED" | grep -qE '^VERSION$'; then
VERSION=$(tr -d '[:space:]' < VERSION)
docker tag adguard-cidre:ci "$IMAGE:$MINOR_TAG"
docker tag adguard-cidre:ci "$IMAGE:$VERSION"
docker push "$IMAGE:latest"
docker push "$IMAGE:$MINOR_TAG"
docker push "$IMAGE:$VERSION"
else
echo "VERSION unchanged, skipping versioned tag to avoid overwriting an existing release."
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
CHANGED_LIST=$(echo "$CHANGED" | sed 's/^/- /')
REPO_URL="https://git.djeex.fr/Djeex/adguard-cidre"
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}\`
**Changed files:**
${CHANGED_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"
+5 -4
View File
@@ -1,8 +1,8 @@
FROM python:3.14-alpine AS base
FROM python:3.14.7-alpine AS base
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 \
&& echo $TZ > /etc/timezone
@@ -11,7 +11,8 @@ WORKDIR /app
COPY 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
RUN pip install --no-cache-dir pytest==9.1.1
@@ -19,4 +20,4 @@ COPY tests/ tests/
COPY pytest.ini .
FROM base
ENTRYPOINT ["python3", "blocklist_scheduler.py"]
ENTRYPOINT ["./entrypoint.sh"]
+9 -4
View File
@@ -31,6 +31,8 @@
| 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.) |
| `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` |
@@ -64,6 +66,8 @@
container_name: adguard-cidre
restart: unless-stopped
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
- 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
@@ -121,11 +125,12 @@
git clone https://git.djeex.fr/Djeex/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.
- Adjust `BLOCKLIST_CRON` variables if you want a different update frequency.
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
- 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`.
- Set `BLOCK_COUNTRIES` with the countries you want to block.
- 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 :
```bash
+1 -1
View File
@@ -1 +1 @@
1.4.0
1.5.0
+1 -1
View File
@@ -11,7 +11,7 @@ from pathlib import Path
logging.basicConfig(
level=logging.INFO,
format='[blocklist] %(levelname)s: %(message)s',
format="%(asctime)s [%(levelname)s] %(message)s",
stream=sys.stdout,
)
+9 -7
View File
@@ -5,14 +5,16 @@ services:
container_name: adguard-cidre
restart: unless-stopped
environment:
- 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
- BLOCKLIST_CRON_TYPE=daily # daily or weekly
- PUID=${PUID} # user id the process runs as, matches ownership of the /adguard mount
- PGID=${PGID} # group id the process runs as
- 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
# - BLOCKLIST_CRON_DAY=mon
- BLOCKLIST_CRON_TIME=06:00
- DOCKER_API_URL=http://socket-proxy-adguard:2375 # docker socket proxy
- ADGUARD_CONTAINER_NAME=adguardhome # adguard container name
- BLOCKLIST_CRON_DAY=${BLOCKLIST_CRON_DAY}
- BLOCKLIST_CRON_TIME=${BLOCKLIST_CRON_TIME}
- DOCKER_API_URL=${DOCKER_API_URL} # docker socket proxy
- ADGUARD_CONTAINER_NAME=${ADGUARD_CONTAINER_NAME} # adguard container name
volumes:
- /path/to/adguard/confdir:/adguard
Executable
+105
View File
@@ -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 "$@"