mirror of
https://github.com/r3bo0tbx1/tor-guard-relay.git
synced 2026-04-06 00:32:04 +02:00
v1.1.1 Security Hardening & Config Enhancement 🛡️✨
🚀 Major architectural release with a near full rewrite of the entrypoint, validation system, diagnostics, and templates. The image is now ~20MB, fully busybox based, more secure, and more flexible for relay and bridge operators. 🔧 Critical Fixes - Busybox compatible rewrite of OBFS4V_* parsing for values with spaces. - Rewritten TOR_CONTACT_INFO validation to prevent crash loops. - Restored bootstrap logs with Log notice stdout. - Fixed ENV healthchecks and validation order. - Resolved busybox regex and quoting issues across the script. ✨ Features and Enhancements - Added PT_PORT with complete obfs4 bridge compatibility. - Support for OR_PORT, PT_PORT, EMAIL, and NICKNAME. - Rewritten bandwidth logic with correct Rate and Burst translation. - Unified guard, exit, and bridge via TOR_RELAY_MODE. - Integrated obfs4 with rewritten diagnostics for status, health, fingerprint, and bridge-line. - Reliable ENV only mode without torrc files. 📦 Build Improvements - Image reduced ~45MB to ~20MB with busybox only tools. - Rewritten healthcheck for ENV and mounted configs. - Four diagnostic tools rewritten to pure busybox sh. - Weekly rebuilds with latest Alpine and Tor. 📚 Templates and Documentation - All templates rewritten and updated with bandwidth options and naming alternatives. - Updated Cosmos and Docker Compose templates for bridge, guard, exit. - New templates README with full deployment, migration, and config comparisons. - Revised Claude file with clearer differences and bandwidth notes. 🔒 Security Hardening - 32 vulnerabilities fixed across critical, high, medium, low categories. - Non root runtime with UID 100. - Strict OBFS4V_* whitelist and rewritten validation. - No exposed diagnostics ports, docker exec only. - Smaller attack surface with removed binaries. 🚀 Migration Notes - From v1.1.0: direct upgrade, no config changes, fingerprint preserved. - From official obfs4 bridge: one time UID fix required, full ENV compatibility afterward. - Templates include both TOR_ and official naming for smooth migration. 🧩 Compatibility - Alpine 3.22.2 base, latest Tor from edge. - AMD64 and ARM64 supported. - Works with Docker, Compose, Cosmos Cloud, Portainer.
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -93,7 +93,7 @@ body:
|
||||
id: diagnostic
|
||||
attributes:
|
||||
label: Diagnostic Output
|
||||
description: Output from `docker exec guard-relay relay-status`
|
||||
description: Output from `docker exec guard-relay status` or `docker exec guard-relay health`
|
||||
render: shell
|
||||
placeholder: Paste diagnostic output here
|
||||
|
||||
|
||||
31
.github/dependabot.yml
vendored
31
.github/dependabot.yml
vendored
@@ -12,12 +12,13 @@ updates:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "03:00"
|
||||
timezone: "Asia/Tokyo"
|
||||
timezone: "UTC"
|
||||
open-pull-requests-limit: 5
|
||||
rebase-strategy: "auto"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github-actions"
|
||||
- "security"
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
include: "scope"
|
||||
@@ -25,6 +26,10 @@ updates:
|
||||
- "r3bo0tbx1"
|
||||
assignees:
|
||||
- "r3bo0tbx1"
|
||||
# Security: Allow both patch and minor updates for GitHub Actions
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
# ────────────────────────────────
|
||||
# 🐳 Docker (Base Image Updates)
|
||||
@@ -34,12 +39,13 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "03:00"
|
||||
timezone: "Asia/Tokyo"
|
||||
timezone: "UTC"
|
||||
open-pull-requests-limit: 3
|
||||
rebase-strategy: "auto"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
- "security"
|
||||
commit-message:
|
||||
prefix: "build"
|
||||
include: "scope"
|
||||
@@ -47,6 +53,10 @@ updates:
|
||||
- "r3bo0tbx1"
|
||||
assignees:
|
||||
- "r3bo0tbx1"
|
||||
# Security: Allow patch updates for Alpine base image
|
||||
ignore:
|
||||
- dependency-name: "alpine"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
# ────────────────────────────────
|
||||
# Strategy Notes
|
||||
@@ -55,12 +65,19 @@ updates:
|
||||
# ✅ Daily Docker checks for timely base image security patches
|
||||
# ✅ Automatic PR rebasing to prevent stale dependency branches
|
||||
# ✅ Manual merge approval required (no auto-merge)
|
||||
# ✅ Security-focused: Block major version updates, allow patch/minor
|
||||
#
|
||||
# Monitored ecosystems:
|
||||
# 1. GitHub Actions (workflow dependencies)
|
||||
# 2. Docker (Alpine base image)
|
||||
# 2. Docker (Alpine base image - tracks 3.22.x → 3.23.x updates)
|
||||
#
|
||||
# Manual updates:
|
||||
# - Shell scripts and Tor binary (require validation)
|
||||
# - Alpine system packages pinned for consistency
|
||||
# - Other dependencies managed outside Dependabot
|
||||
# NOT monitored (by design):
|
||||
# - Alpine packages (tor, tini, lyrebird) - No pinned versions
|
||||
# → Updated automatically via weekly rebuilds (Sundays 18:30 UTC)
|
||||
# → Strategy: apk add --no-cache always pulls latest available
|
||||
#
|
||||
# Security strategy:
|
||||
# - Base image updates: Dependabot tracks Alpine version changes
|
||||
# - Package updates: Weekly rebuilds pull latest tor/tini/lyrebird from Alpine repos
|
||||
# - CI/CD updates: Dependabot tracks GitHub Actions version changes
|
||||
# - Manual review required for all PRs (no auto-merge)
|
||||
|
||||
55
.github/renovate.json
vendored
55
.github/renovate.json
vendored
@@ -13,46 +13,41 @@
|
||||
"assignees": ["r3bo0tbx1"],
|
||||
"reviewers": ["r3bo0tbx1"],
|
||||
|
||||
"enabledManagers": ["dockerfile", "regex"],
|
||||
|
||||
"regexManagers": [
|
||||
{
|
||||
"fileMatch": ["Dockerfile"],
|
||||
"matchStrings": [
|
||||
"tor=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"bash=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"tini=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"curl=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"jq=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"grep=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"coreutils=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"bind-tools=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"netcat-openbsd=(?<currentValue>[0-9a-zA-Z\\.-]+)",
|
||||
"dos2unix=(?<currentValue>[0-9a-zA-Z\\.-]+)"
|
||||
],
|
||||
"datasourceTemplate": "alpine",
|
||||
"depNameTemplate": "{{{matchString}}}"
|
||||
}
|
||||
],
|
||||
"enabledManagers": ["dockerfile"],
|
||||
|
||||
"packageRules": [
|
||||
{
|
||||
"matchDatasources": ["alpine"],
|
||||
"groupName": "🧅 Alpine Packages",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["alpine"],
|
||||
"groupName": "🐳 Alpine Base Image",
|
||||
"schedule": ["before 3am on monday"],
|
||||
"prPriority": 1,
|
||||
"allowedVersions": "<3.23.0"
|
||||
"automerge": false,
|
||||
"allowedVersions": "/^3\\.(2[2-9]|[3-9][0-9])\\./",
|
||||
"description": "Track Alpine 3.22+ versions for security updates"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["dockerfile"],
|
||||
"groupName": "🐳 Base Images",
|
||||
"excludePackageNames": ["alpine"],
|
||||
"groupName": "🐳 Other Docker Images",
|
||||
"schedule": ["before 3am on monday"],
|
||||
"prPriority": 2
|
||||
"prPriority": 2,
|
||||
"automerge": false
|
||||
}
|
||||
],
|
||||
|
||||
"timezone": "UTC",
|
||||
"automerge": false,
|
||||
"prConcurrentLimit": 5
|
||||
}
|
||||
"vulnerabilityAlerts": {
|
||||
"enabled": true,
|
||||
"labels": ["security", "dependencies"],
|
||||
"assignees": ["r3bo0tbx1"],
|
||||
"schedule": ["at any time"]
|
||||
},
|
||||
|
||||
"osvVulnerabilityAlerts": true,
|
||||
"timezone": "UTC",
|
||||
"prConcurrentLimit": 5,
|
||||
|
||||
"customManagers": [],
|
||||
|
||||
"description": "Renovate tracks only Docker base images. Alpine packages (tor, tini, lyrebird) are NOT pinned and update automatically via weekly rebuilds."
|
||||
}
|
||||
|
||||
63
.github/workflows/release.yml
vendored
63
.github/workflows/release.yml
vendored
@@ -18,9 +18,10 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
# SECURITY: Global permissions set to read-only (principle of least privilege)
|
||||
# Individual jobs grant additional permissions as needed
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
GHCR_REGISTRY: ghcr.io
|
||||
@@ -31,6 +32,8 @@ jobs:
|
||||
determine-version:
|
||||
name: 🏷️ Determine Version and Build Type
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read # Only needs to read repo for version detection
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
build_type: ${{ steps.version.outputs.build_type }}
|
||||
@@ -61,11 +64,13 @@ jobs:
|
||||
IS_RELEASE="false"
|
||||
echo "👤 Manual build version: ${VERSION}"
|
||||
else
|
||||
# Weekly rebuild: Use the last release version (no suffix)
|
||||
# This rebuilds the image with updated Alpine packages
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v1.0.0")
|
||||
VERSION="${LATEST_TAG#v}"
|
||||
BUILD_TYPE="weekly"
|
||||
IS_RELEASE="false"
|
||||
echo "📅 Weekly build version: ${VERSION}"
|
||||
echo "📅 Weekly rebuild of last release: ${VERSION} (with updated packages)"
|
||||
fi
|
||||
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
SHORT_SHA=$(git rev-parse --short HEAD)
|
||||
@@ -88,6 +93,9 @@ jobs:
|
||||
name: 🏗️ Multi-Arch Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine-version
|
||||
permissions:
|
||||
contents: read # Read repository code
|
||||
packages: write # Push to GHCR
|
||||
if: |
|
||||
github.event_name != 'workflow_run' ||
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
@@ -96,10 +104,10 @@ jobs:
|
||||
- name: 📥 Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: 🎯 Verify Shell Script Extensions
|
||||
- name: 🎯 Verify Tools Directory
|
||||
run: |
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Pre-Build: Verifying .sh Extensions"
|
||||
echo "📝 Pre-Build: Verifying Tools"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
@@ -108,31 +116,24 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MISSING_EXT=0
|
||||
SH_COUNT=0
|
||||
TOOL_COUNT=0
|
||||
|
||||
for file in tools/*; do
|
||||
[ -f "$file" ] || continue
|
||||
filename=$(basename "$file")
|
||||
extension="${filename##*.}"
|
||||
|
||||
if head -1 "$file" 2>/dev/null | grep -q "^#!/"; then
|
||||
if [ "$extension" != "sh" ]; then
|
||||
echo "❌ Shell script missing .sh extension: $filename"
|
||||
MISSING_EXT=1
|
||||
else
|
||||
echo "✅ $filename"
|
||||
SH_COUNT=$((SH_COUNT + 1))
|
||||
fi
|
||||
if head -1 "$file" 2>/dev/null | grep -q "^#!/bin/sh"; then
|
||||
echo "✅ $filename (busybox sh)"
|
||||
TOOL_COUNT=$((TOOL_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
if [ $MISSING_EXT -eq 1 ]; then
|
||||
echo "❌ Build blocked: Some shell scripts missing .sh extension"
|
||||
if [ $TOOL_COUNT -lt 1 ]; then
|
||||
echo "❌ Build blocked: No tools found in tools/"
|
||||
exit 1
|
||||
else
|
||||
echo "🎉 All $SH_COUNT shell scripts have proper .sh extension"
|
||||
echo "🎉 Found $TOOL_COUNT diagnostic tools (busybox-only, no .sh extension)"
|
||||
fi
|
||||
|
||||
- name: 🔧 Normalize scripts before build
|
||||
@@ -145,7 +146,7 @@ jobs:
|
||||
sudo apt-get update -qq && sudo apt-get install -y dos2unix
|
||||
|
||||
echo "📄 Processing main scripts..."
|
||||
for script in docker-entrypoint.sh integration-check.sh Dockerfile; do
|
||||
for script in docker-entrypoint.sh healthcheck.sh Dockerfile; do
|
||||
if [ -f "$script" ]; then
|
||||
dos2unix "$script" 2>/dev/null || true
|
||||
echo " ✅ Normalized: $script"
|
||||
@@ -153,17 +154,17 @@ jobs:
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📁 Processing tools/*.sh..."
|
||||
echo "📁 Processing tools/*..."
|
||||
if [ -d "tools" ]; then
|
||||
find tools -type f -name "*.sh" -exec dos2unix {} \; 2>/dev/null || true
|
||||
TOOL_COUNT=$(find tools -type f -name "*.sh" | wc -l)
|
||||
find tools -type f -exec dos2unix {} \; 2>/dev/null || true
|
||||
TOOL_COUNT=$(find tools -type f | wc -l)
|
||||
echo " ✅ Normalized: $TOOL_COUNT tool scripts"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔐 Setting execute permissions..."
|
||||
chmod +x docker-entrypoint.sh integration-check.sh 2>/dev/null || true
|
||||
[ -d "tools" ] && chmod +x tools/*.sh 2>/dev/null || true
|
||||
chmod +x docker-entrypoint.sh healthcheck.sh 2>/dev/null || true
|
||||
[ -d "tools" ] && chmod +x tools/* 2>/dev/null || true
|
||||
|
||||
echo " ✅ Permissions verified"
|
||||
echo ""
|
||||
@@ -200,17 +201,22 @@ jobs:
|
||||
TAGS=()
|
||||
|
||||
if [ "$BUILD_TYPE" = "release" ]; then
|
||||
# New release: Update both :latest and version tags
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest")
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:latest")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
|
||||
elif [ "$BUILD_TYPE" = "weekly" ]; then
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}-weekly")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}-weekly")
|
||||
# Weekly rebuild: Update version tag with fresh packages, keep :latest pointing to last release
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:latest")
|
||||
elif [ "$BUILD_TYPE" = "validated" ]; then
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}-validated")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}-validated")
|
||||
else
|
||||
# Manual builds: version tag only
|
||||
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}")
|
||||
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
|
||||
fi
|
||||
@@ -243,6 +249,8 @@ jobs:
|
||||
name: 📝 Generate Release Notes
|
||||
runs-on: ubuntu-latest
|
||||
needs: [determine-version, build-and-push]
|
||||
permissions:
|
||||
contents: write # Create GitHub releases
|
||||
if: needs.determine-version.outputs.is_release == 'true'
|
||||
|
||||
steps:
|
||||
@@ -304,4 +312,3 @@ jobs:
|
||||
body_path: release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
180
.github/workflows/validate.yml
vendored
180
.github/workflows/validate.yml
vendored
@@ -14,10 +14,9 @@ on:
|
||||
# 🐳 Core build and runtime logic
|
||||
- 'Dockerfile'
|
||||
- 'docker-entrypoint.sh'
|
||||
- 'integration-check.sh'
|
||||
|
||||
# 🧩 Tools and validation scripts
|
||||
- 'tools/*.sh'
|
||||
# 🧩 Tools and validation scripts (no .sh extension)
|
||||
- 'tools/*'
|
||||
|
||||
# ⚙️ CI/CD workflows
|
||||
- '.github/workflows/release.yml'
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
- name: 🔍 Validate Dockerfile Syntax
|
||||
run: |
|
||||
echo "🐳 Validating Dockerfile build context..."
|
||||
docker build --no-cache --target builder -t tor-relay-test . 2>&1 | \
|
||||
docker build --no-cache -t tor-relay-test . 2>&1 | \
|
||||
tee /tmp/docker-build.log || true
|
||||
if grep -i "error" /tmp/docker-build.log; then
|
||||
echo "❌ Dockerfile validation failed"
|
||||
@@ -85,31 +84,23 @@ jobs:
|
||||
bash -n docker-entrypoint.sh || exit 1
|
||||
echo " ✅ docker-entrypoint.sh syntax valid"
|
||||
fi
|
||||
|
||||
# Check integration test script
|
||||
if [ -f integration-check.sh ]; then
|
||||
echo "📄 Checking integration-check.sh..."
|
||||
bash -n integration-check.sh || exit 1
|
||||
echo " ✅ integration-check.sh syntax valid"
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo "📁 Checking tools directory (.sh files)..."
|
||||
|
||||
# Check if tools directory exists and has .sh files
|
||||
echo "📁 Checking tools directory (no .sh extension)..."
|
||||
|
||||
# Check if tools directory exists
|
||||
if [ ! -d "tools" ]; then
|
||||
echo " ⚠️ tools/ directory not found"
|
||||
elif [ -z "$(find tools -maxdepth 1 -type f -name '*.sh' 2>/dev/null)" ]; then
|
||||
echo " ⚠️ No .sh files found in tools/"
|
||||
else
|
||||
# Check all .sh files in tools/
|
||||
# Check all files in tools/ (no .sh extension)
|
||||
TOOL_COUNT=0
|
||||
for script in tools/*.sh; do
|
||||
[ -f "$script" ] || continue
|
||||
echo "📄 Checking $(basename "$script")..."
|
||||
bash -n "$script" || exit 1
|
||||
echo " ✅ $script syntax valid"
|
||||
TOOL_COUNT=$((TOOL_COUNT + 1))
|
||||
for script in tools/status tools/health tools/fingerprint tools/bridge-line; do
|
||||
if [ -f "$script" ]; then
|
||||
echo "📄 Checking $(basename "$script")..."
|
||||
sh -n "$script" || exit 1
|
||||
echo " ✅ $script syntax valid"
|
||||
TOOL_COUNT=$((TOOL_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo " ✅ All $TOOL_COUNT tool scripts validated"
|
||||
@@ -135,17 +126,17 @@ jobs:
|
||||
echo "🔍 ShellCheck: docker-entrypoint.sh"
|
||||
shellcheck -S warning docker-entrypoint.sh || true
|
||||
fi
|
||||
|
||||
if [ -f integration-check.sh ]; then
|
||||
echo "🔍 ShellCheck: integration-check.sh"
|
||||
shellcheck -S warning integration-check.sh || true
|
||||
fi
|
||||
|
||||
# ShellCheck all .sh files in tools/
|
||||
|
||||
# ShellCheck all tools (no .sh extension)
|
||||
if [ -d "tools" ]; then
|
||||
echo ""
|
||||
echo "🔍 ShellCheck: tools/*.sh"
|
||||
find tools -maxdepth 1 -type f -name "*.sh" -exec shellcheck -S warning {} \; || true
|
||||
echo "🔍 ShellCheck: tools/*"
|
||||
for tool in tools/status tools/health tools/fingerprint tools/bridge-line; do
|
||||
if [ -f "$tool" ]; then
|
||||
echo " Checking $(basename $tool)..."
|
||||
shellcheck -S warning "$tool" || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -154,51 +145,50 @@ jobs:
|
||||
- name: 🎯 Verify Tool Extensions
|
||||
run: |
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Verifying Tool File Extensions"
|
||||
echo "📝 Verifying Tool File Extensions (NO .sh)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
|
||||
if [ ! -d "tools" ]; then
|
||||
echo "⚠️ tools/ directory not found - skipping check"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MISSING_EXT=0
|
||||
NON_SH_COUNT=0
|
||||
SH_COUNT=0
|
||||
|
||||
|
||||
HAS_SH_EXT=0
|
||||
NO_EXT_COUNT=0
|
||||
|
||||
# Check all files in tools/
|
||||
for file in tools/*; do
|
||||
[ -f "$file" ] || continue
|
||||
|
||||
|
||||
filename=$(basename "$file")
|
||||
extension="${filename##*.}"
|
||||
|
||||
|
||||
# Check if it's a shell script (has shebang)
|
||||
if head -1 "$file" 2>/dev/null | grep -q "^#!/"; then
|
||||
if [ "$extension" != "sh" ]; then
|
||||
echo "❌ Shell script missing .sh extension: $filename"
|
||||
MISSING_EXT=1
|
||||
# Tools should NOT have .sh extension
|
||||
if echo "$filename" | grep -q '\.sh$'; then
|
||||
echo "❌ Tool should NOT have .sh extension: $filename"
|
||||
HAS_SH_EXT=1
|
||||
else
|
||||
SH_COUNT=$((SH_COUNT + 1))
|
||||
NO_EXT_COUNT=$((NO_EXT_COUNT + 1))
|
||||
echo "✅ Tool has correct format (no .sh): $filename"
|
||||
fi
|
||||
else
|
||||
NON_SH_COUNT=$((NON_SH_COUNT + 1))
|
||||
echo "ℹ️ Non-script file: $filename"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
echo ""
|
||||
echo "📊 Summary:"
|
||||
echo " • Shell scripts with .sh: $SH_COUNT"
|
||||
[ $NON_SH_COUNT -gt 0 ] && echo " • Other files: $NON_SH_COUNT"
|
||||
echo " • Tools without .sh extension: $NO_EXT_COUNT"
|
||||
echo ""
|
||||
|
||||
if [ $MISSING_EXT -eq 1 ]; then
|
||||
echo "❌ Some shell scripts are missing .sh extension"
|
||||
|
||||
if [ $HAS_SH_EXT -eq 1 ]; then
|
||||
echo "❌ Some tools have .sh extension (should not have it)"
|
||||
exit 1
|
||||
elif [ $NO_EXT_COUNT -lt 4 ]; then
|
||||
echo "❌ Expected 4 tools (status, health, fingerprint, bridge-line)"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All shell scripts have proper .sh extension"
|
||||
echo "✅ All tools have correct format (no .sh extension)"
|
||||
fi
|
||||
|
||||
- name: 📋 Lint YAML Files
|
||||
@@ -343,9 +333,8 @@ jobs:
|
||||
- name: 🚀 Run Container Smoke Test
|
||||
run: |
|
||||
echo "🚀 Starting container for smoke test..."
|
||||
docker run -d --name tor-test -e ENABLE_METRICS=true \
|
||||
-e ENABLE_HEALTH_CHECK=true tor-relay:test \
|
||||
/bin/sh -c "echo 'Test mode active'; sleep 60" || true
|
||||
docker run -d --name tor-test --entrypoint /bin/sh tor-relay:test \
|
||||
-c "echo '✅ Container started successfully'; sleep 60" || true
|
||||
sleep 5
|
||||
|
||||
- name: 🔍 Check Container Status
|
||||
@@ -359,16 +348,15 @@ jobs:
|
||||
- name: 🔧 Verify Tools Installation
|
||||
run: |
|
||||
echo "🔍 Checking installed tools..."
|
||||
docker run --rm tor-relay:test ls -la /usr/local/bin/ || exit 1
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "ls -la /usr/local/bin/" || exit 1
|
||||
echo ""
|
||||
echo "🛠️ Available diagnostic tools:"
|
||||
docker run --rm tor-relay:test sh -c "ls -1 /usr/local/bin/*.sh 2>/dev/null | \
|
||||
xargs -n1 basename | sort"
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "ls -1 /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line"
|
||||
|
||||
- name: 🧅 Verify Tor Installation
|
||||
run: |
|
||||
echo "🔍 Checking Tor version..."
|
||||
TOR_VERSION=$(docker run --rm tor-relay:test tor --version | head -1)
|
||||
TOR_VERSION=$(docker run --rm --entrypoint tor tor-relay:test --version | head -1)
|
||||
if [ -z "$TOR_VERSION" ]; then
|
||||
echo "❌ Tor installation failed"
|
||||
exit 1
|
||||
@@ -377,7 +365,7 @@ jobs:
|
||||
|
||||
- name: 📁 Verify File Structure
|
||||
run: |
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
echo '📂 === Directory Structure ===' && \
|
||||
ls -la / | grep -E 'var|etc|usr' && \
|
||||
echo '' && \
|
||||
@@ -394,16 +382,27 @@ jobs:
|
||||
- name: 📋 Check Build Metadata
|
||||
run: |
|
||||
echo "🔍 Checking build metadata..."
|
||||
docker run --rm tor-relay:test cat /build-info.txt || exit 1
|
||||
docker run --rm --entrypoint cat tor-relay:test /build-info.txt || exit 1
|
||||
|
||||
- name: 🔐 Verify Permissions
|
||||
run: |
|
||||
echo "🔐 Verifying directory permissions..."
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
ls -ld /var/lib/tor | grep -q '^d.*tor tor' && \
|
||||
echo '✅ /var/lib/tor permissions correct' || exit 1
|
||||
ls -ld /var/log/tor | grep -q '^d.*tor tor' && \
|
||||
echo '✅ /var/log/tor permissions correct' || exit 1
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
DATA_OWNER=\$(stat -c '%u %g' /var/lib/tor)
|
||||
if [ \"\$DATA_OWNER\" = '100 101' ]; then
|
||||
echo '✅ /var/lib/tor ownership correct (100:101)'
|
||||
else
|
||||
echo '❌ /var/lib/tor ownership incorrect: \$DATA_OWNER'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOG_OWNER=\$(stat -c '%u %g' /var/log/tor)
|
||||
if [ \"\$LOG_OWNER\" = '100 101' ]; then
|
||||
echo '✅ /var/log/tor ownership correct (100:101)'
|
||||
else
|
||||
echo '❌ /var/log/tor ownership incorrect: \$LOG_OWNER'
|
||||
exit 1
|
||||
fi
|
||||
"
|
||||
|
||||
- name: ✅ Integration Tests Summary
|
||||
@@ -490,32 +489,29 @@ jobs:
|
||||
- name: "🔍 Test: Help Flags"
|
||||
if: matrix.test-case == 'help-flags'
|
||||
run: |
|
||||
echo "🧪 Testing tool help flags..."
|
||||
for tool in health.sh metrics.sh status.sh fingerprint.sh view-logs.sh \
|
||||
net-check.sh setup.sh dashboard.sh; do
|
||||
echo "🧪 Testing tool existence..."
|
||||
for tool in status health fingerprint bridge-line; do
|
||||
TOOL_PATH="/usr/local/bin/$tool"
|
||||
if docker run --rm tor-relay:test sh -c "test -f $TOOL_PATH"; then
|
||||
echo "🔍 Testing $tool --help..."
|
||||
docker run --rm tor-relay:test $tool --help > /dev/null && \
|
||||
echo "✅ $tool --help works" || \
|
||||
echo "⚠️ $tool --help failed (non-critical)"
|
||||
if docker run --rm --entrypoint /bin/sh tor-relay:test -c "test -f $TOOL_PATH"; then
|
||||
echo "✅ $tool exists at $TOOL_PATH"
|
||||
else
|
||||
echo "⚠️ $tool not found at $TOOL_PATH"
|
||||
echo "❌ $tool not found at $TOOL_PATH"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: "🔐 Test: File Permissions"
|
||||
if: matrix.test-case == 'file-permissions'
|
||||
run: |
|
||||
echo "🔐 Verifying file permissions for .sh tools..."
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
for tool in /usr/local/bin/*.sh; do
|
||||
echo "🔐 Verifying file permissions for tools..."
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
for tool in /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line; do
|
||||
if [ -f \"\$tool\" ]; then
|
||||
test -x \"\$tool\" && echo \"✅ \$(basename \$tool) is executable\" || exit 1
|
||||
fi
|
||||
done
|
||||
"
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
test -x /usr/local/bin/docker-entrypoint.sh && \
|
||||
echo '✅ docker-entrypoint.sh is executable' || exit 1
|
||||
"
|
||||
@@ -524,7 +520,7 @@ jobs:
|
||||
if: matrix.test-case == 'alpine-compatibility'
|
||||
run: |
|
||||
echo "🏔️ Verifying Alpine base image..."
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
cat /etc/os-release | grep -i alpine && \
|
||||
echo '✅ Alpine base confirmed' || exit 1
|
||||
test -x /bin/sh && echo '✅ /bin/sh available' || exit 1
|
||||
@@ -536,13 +532,13 @@ jobs:
|
||||
if: matrix.test-case == 'tool-executability'
|
||||
run: |
|
||||
echo "🔧 Testing tool execution..."
|
||||
docker run --rm tor-relay:test sh -c "
|
||||
for tool in /usr/local/bin/*.sh; do
|
||||
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
|
||||
for tool in /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line; do
|
||||
if [ -f \"\$tool\" ]; then
|
||||
BASENAME=\$(basename \"\$tool\")
|
||||
echo \"🔍 Testing \$BASENAME...\"
|
||||
\"\$tool\" --help > /dev/null 2>&1 || \
|
||||
echo \" ⚠️ \$BASENAME --help failed (may require Tor process)\"
|
||||
# Tools require Tor to be running, just verify they're executable
|
||||
test -x \"\$tool\" && echo \" ✅ \$BASENAME is executable\" || exit 1
|
||||
fi
|
||||
done
|
||||
"
|
||||
@@ -574,8 +570,8 @@ jobs:
|
||||
|
||||
## ✅ Checks Performed
|
||||
- 🐳 Dockerfile validation with Hadolint
|
||||
- 📝 Shell script syntax checking (all .sh files)
|
||||
- 🎯 Shell script extension verification
|
||||
- 📝 Shell script syntax checking (POSIX sh)
|
||||
- 🎯 Tool extension verification (no .sh)
|
||||
- 🔎 ShellCheck static analysis
|
||||
- 📋 YAML validation (lenient for templates)
|
||||
- 🔖 JSON validation
|
||||
@@ -583,7 +579,7 @@ jobs:
|
||||
- 🏗️ Docker image build (multi-arch ready)
|
||||
- 🚀 Container smoke test
|
||||
- 🧅 Tor installation verification
|
||||
- 🔧 Tool availability check (.sh files)
|
||||
- 🔧 Tool availability check (status, health, fingerprint, bridge-line)
|
||||
- 🔐 Permission verification
|
||||
- 🛡️ Security scanning with Trivy
|
||||
- 🧪 Test matrix execution
|
||||
|
||||
Reference in New Issue
Block a user