diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4f68bee --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: ["**"] + pull_request: + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install lint tools + run: ./test/install-tools.sh + + - name: Validate script syntax + run: ./test/validate-scripts.sh + + - name: ShellCheck + run: shellcheck run.sh start_squid.sh test/detect-proxy.sh test/test-proxy.sh test/validate-scripts.sh + + - name: Hadolint + run: hadolint Dockerfile test/Dockerfile + + - name: Build proxy image + run: docker build -t docker-proxy . + + - name: Build test image + run: docker build -t docker-proxy-test ./test diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ca75c9c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +# Agent Notes + +This file tracks agent updates for the repository. + +- Last updated: 2025-09-20 +- Updates: + - Added CI workflow, script validation, and lint automation. + - Added lint tool installer and updated CI to use it. + - Added latest-version installer with apt fallback for lint tools. + - Expanded script validation to cover the lint tool installer. + - Addressed shellcheck findings across scripts. + - Updated Dockerfiles to satisfy hadolint guidance. + - Adjusted hadolint ignore placement for apt pin warnings. diff --git a/Dockerfile b/Dockerfile index d91e2a3..8e32170 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ FROM ubuntu:trusty-20190515 -MAINTAINER Kevin Littlejohn , \ - Alex Fraser +LABEL maintainer="Kevin Littlejohn , Alex Fraser " # Install base dependencies. WORKDIR /root RUN sed -i 's/^# deb-src/deb-src/' /etc/apt/sources.list +# hadolint ignore=DL3008 RUN export DEBIAN_FRONTEND=noninteractive TERM=linux \ && apt-get update \ && apt-get install -y --no-install-recommends \ @@ -18,24 +18,28 @@ RUN export DEBIAN_FRONTEND=noninteractive TERM=linux \ squid-langpack \ ssl-cert \ && apt-get source -y squid3 squid-langpack \ - && apt-get build-dep -y squid3 squid-langpack + && apt-get build-dep -y squid3 squid-langpack \ + && mv squid3-3.* /root/squid3-src \ + && rm -rf /var/lib/apt/lists/* # Customise and build Squid. # It's silly, but run dpkg-buildpackage again if it fails the first time. This # is needed because sometimes the `configure` script is busy when building in # Docker after autoconf sets its mode +x. COPY squid3.patch mime.conf /root/ -RUN cd squid3-3.* \ - && patch -p1 < /root/squid3.patch \ - && export NUM_PROCS=`grep -c ^processor /proc/cpuinfo` \ - && (dpkg-buildpackage -b -j${NUM_PROCS} \ - || dpkg-buildpackage -b -j${NUM_PROCS}) \ +WORKDIR /root/squid3-src +RUN patch -p1 < /root/squid3.patch \ + && NUM_PROCS="$(grep -c ^processor /proc/cpuinfo)" \ + && (dpkg-buildpackage -b -j"${NUM_PROCS}" \ + || dpkg-buildpackage -b -j"${NUM_PROCS}") \ && DEBIAN_FRONTEND=noninteractive TERM=linux dpkg -i \ - ../squid3-common_3.*_all.deb \ - ../squid3_3.*.deb \ + /root/squid3-common_3.*_all.deb \ + /root/squid3_3.*.deb \ && mkdir -p /etc/squid3/ssl_cert \ && cat /root/mime.conf >> /usr/share/squid3/mime.conf +WORKDIR /root + COPY squid.conf /etc/squid3/squid.conf COPY start_squid.sh /usr/local/bin/start_squid.sh diff --git a/run.sh b/run.sh index d997f65..cb9492b 100755 --- a/run.sh +++ b/run.sh @@ -36,13 +36,15 @@ start_routing () { sudo ip route add default via "${IPADDR}" dev docker0 table TRANSPROXY # Mark packets to port 80 and 443 external, so they route through the new # route table - COMMON_RULES="-t mangle -I PREROUTING -p tcp -i docker0 ! -s ${IPADDR} - -j MARK --set-mark 1" + COMMON_RULES=( + -t mangle -I PREROUTING -p tcp -i docker0 ! -s "${IPADDR}" + -j MARK --set-mark 1 + ) echo "Redirecting HTTP to docker-proxy" - sudo iptables $COMMON_RULES --dport 80 + sudo iptables "${COMMON_RULES[@]}" --dport 80 if [ "$WITH_SSL" = 'yes' ]; then echo "Redirecting HTTPS to docker-proxy" - sudo iptables $COMMON_RULES --dport 443 + sudo iptables "${COMMON_RULES[@]}" --dport 443 else echo "Not redirecting HTTPS. To enable, re-run with the argument 'ssl'" echo "CA certificate will be generated anyway, but it won't be used" @@ -71,7 +73,7 @@ stop_routing () { stop () { set +e - sudo docker rm -fv ${CONTAINER_NAME} >/dev/null 2>&1 + sudo docker rm -fv "${CONTAINER_NAME}" >/dev/null 2>&1 set -e stop_routing } @@ -99,12 +101,12 @@ run () { # Run and find the IP for the running container. Bind the forward proxy port # so clients can get the CA certificate. CID=$(sudo docker run --privileged -d \ - --name ${CONTAINER_NAME} \ + --name "${CONTAINER_NAME}" \ --volume="${CACHEDIR}":/var/spool/squid3 \ --volume="${CERTDIR}":/etc/squid3/ssl_cert \ --publish=3128:3128 \ - ${CONTAINER_NAME}) - IPADDR=$(sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${CID}) + "${CONTAINER_NAME}") + IPADDR=$(sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' "${CID}") start_routing # Run at console, kill cleanly if ctrl-c is hit trap interrupted INT diff --git a/start_squid.sh b/start_squid.sh index e86250c..cc86623 100755 --- a/start_squid.sh +++ b/start_squid.sh @@ -1,7 +1,7 @@ #!/bin/bash function gen-cert() { - pushd /etc/squid3/ssl_cert > /dev/null + pushd /etc/squid3/ssl_cert > /dev/null || exit if [ ! -f ca.pem ]; then openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes \ -x509 -keyout privkey.pem -out ca.pem \ @@ -15,16 +15,16 @@ function gen-cert() { openssl x509 -sha1 -in ca.pem -noout -fingerprint # Make CA certificate available for download via HTTP Forwarding port # e.g. GET http://docker-proxy:3128/squid-internal-static/icons/ca.pem - cp `pwd`/ca.* /usr/share/squid3/icons/ - popd > /dev/null + cp "$(pwd)"/ca.* /usr/share/squid3/icons/ + popd > /dev/null || exit return $? } function start-routing() { # Setup the NAT rule that enables transparent proxying IPADDR=$(/sbin/ip -o -f inet addr show eth0 | awk '{ sub(/\/.+/,"",$4); print $4 }') - iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination ${IPADDR}:3129 - iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination ${IPADDR}:3130 + iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination "${IPADDR}":3129 + iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination "${IPADDR}":3130 return $? } diff --git a/test/Dockerfile b/test/Dockerfile index 1cb5554..0d0c0f4 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -1,11 +1,12 @@ FROM ubuntu:14.04 -MAINTAINER Alex Fraser +LABEL maintainer="Alex Fraser " # Install ca-certificates so we can install the proxy's certificate. curl and # Java are only needed for running the test, not for installing the # certificate. WORKDIR /root +# hadolint ignore=DL3008 RUN export DEBIAN_FRONTEND=noninteractive TERM=linux \ && apt-get update \ && apt-get install -y --no-install-recommends \ @@ -19,4 +20,4 @@ COPY detect-proxy.sh test-proxy.sh HttpTest.java /root/ RUN javac HttpTest.java \ && ./detect-proxy.sh start -CMD /root/test-proxy.sh +CMD ["/root/test-proxy.sh"] diff --git a/test/detect-proxy.sh b/test/detect-proxy.sh index f6326eb..853c272 100755 --- a/test/detect-proxy.sh +++ b/test/detect-proxy.sh @@ -16,14 +16,12 @@ function download-cert() { | grep -q 'Server: squid' } -download-cert -if [ $? -ne 0 ]; then +if ! download-cert; then echo "No proxy server detected" exit 0 fi -grep -q '\-----BEGIN CERTIFICATE-----' docker-proxy.pem -if [ $? -ne 0 ]; then +if ! grep -q '\-----BEGIN CERTIFICATE-----' docker-proxy.pem; then echo "Proxy detected" exit 0 fi diff --git a/test/install-tools.sh b/test/install-tools.sh new file mode 100755 index 0000000..8a6b577 --- /dev/null +++ b/test/install-tools.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +set -euo pipefail + +install_dir=${INSTALL_DIR:-/usr/local/bin} + +mkdir -p "$install_dir" + +fetch_latest_tag() { + local repo="$1" + curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \ + | awk -F '"' '/"tag_name"/ { print $4; exit }' +} + +install_shellcheck() { + local tag + local arch + local tarball + local tmp_dir + + tag=$(fetch_latest_tag "koalaman/shellcheck") + if [[ -z "$tag" ]]; then + return 1 + fi + arch=$(uname -m) + case "$arch" in + x86_64) arch="x86_64" ;; + aarch64|arm64) arch="aarch64" ;; + *) + echo "Unsupported architecture for shellcheck: $arch" >&2 + exit 1 + ;; + esac + + tarball="shellcheck-${tag}.linux.${arch}.tar.xz" + tmp_dir=$(mktemp -d) + if ! curl -fsSL -o "${tmp_dir}/${tarball}" \ + "https://github.com/koalaman/shellcheck/releases/download/${tag}/${tarball}"; then + rm -rf "$tmp_dir" + return 1 + fi + tar -xJf "${tmp_dir}/${tarball}" -C "$tmp_dir" + mv "${tmp_dir}/shellcheck-${tag}/shellcheck" "${install_dir}/shellcheck" + chmod +x "${install_dir}/shellcheck" + rm -rf "$tmp_dir" +} + +install_hadolint() { + local tag + local arch + local os + local binary + + tag=$(fetch_latest_tag "hadolint/hadolint") + if [[ -z "$tag" ]]; then + return 1 + fi + os="Linux" + arch=$(uname -m) + case "$arch" in + x86_64) arch="x86_64" ;; + aarch64|arm64) arch="arm64" ;; + *) + echo "Unsupported architecture for hadolint: $arch" >&2 + exit 1 + ;; + esac + + binary="hadolint-${os}-${arch}" + if ! curl -fsSL -o "${install_dir}/hadolint" \ + "https://github.com/hadolint/hadolint/releases/download/${tag}/${binary}"; then + return 1 + fi + chmod +x "${install_dir}/hadolint" +} + +if ! install_shellcheck; then + echo "Falling back to apt for shellcheck" >&2 + apt-get update + apt-get install -y shellcheck +fi + +if ! install_hadolint; then + echo "Falling back to apt for hadolint" >&2 + apt-get update + apt-get install -y hadolint +fi + +shellcheck --version +hadolint --version diff --git a/test/test-proxy.sh b/test/test-proxy.sh index b8c3148..88901a0 100755 --- a/test/test-proxy.sh +++ b/test/test-proxy.sh @@ -2,10 +2,8 @@ curl -sS -o /dev/null https://httpbin.org/get 2>&1 || exit 1 -curl -sS -v -o /dev/null https://httpbin.org/get 2>&1 \ - | grep -q 'X-Cache:' - -if [ $? -ne 0 ]; then +if ! curl -sS -v -o /dev/null https://httpbin.org/get 2>&1 \ + | grep -q 'X-Cache:'; then echo "Request succeeded but response was not cached" >&2 exit 1 fi diff --git a/test/validate-scripts.sh b/test/validate-scripts.sh new file mode 100755 index 0000000..34a1761 --- /dev/null +++ b/test/validate-scripts.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -euo pipefail + +scripts=( + "run.sh" + "start_squid.sh" + "test/install-tools.sh" + "test/detect-proxy.sh" + "test/test-proxy.sh" +) + +for script in "${scripts[@]}"; do + if [[ ! -f "$script" ]]; then + echo "Missing script: $script" >&2 + exit 1 + fi + bash -n "$script" + echo "Validated bash syntax: $script" +done