diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 139cd64..091788f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,12 +35,12 @@ jobs: proxy_mode: audit test_dockerfile: test/Dockerfile.audit assert_script: ./test/assert-audit-mode.sh - endpoint_port: 1234 + builder_name: buildcage-audit - mode: restrict proxy_mode: restrict test_dockerfile: test/Dockerfile.restrict assert_script: ./test/assert-restrict-mode.sh - endpoint_port: 1235 + builder_name: buildcage-restrict name: test (${{ matrix.mode }}) @@ -59,14 +59,14 @@ jobs: - name: Start containers env: PROXY_MODE: ${{ matrix.proxy_mode }} - PORT: ${{ matrix.endpoint_port }} + BUILDER_NAME: ${{ matrix.builder_name }} run: docker compose up -d --wait - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 with: driver: remote - endpoint: tcp://localhost:${{ matrix.endpoint_port }} + endpoint: docker-container://${{ matrix.builder_name }} - name: Build test image uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 @@ -84,6 +84,9 @@ jobs: - name: Show logs if: always() + env: + BUILDER_NAME: ${{ matrix.builder_name }} + INPUT_BUILDER_NAME: ${{ matrix.builder_name }} run: node report/main.mjs ./compose.yml || true - name: Run assertions @@ -91,4 +94,6 @@ jobs: - name: Cleanup if: always() + env: + BUILDER_NAME: ${{ matrix.builder_name }} run: docker compose down -v --rmi all 2>/dev/null || true diff --git a/Makefile b/Makefile index 53a3288..e57af4d 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ run_audit_mode: ## Start in audit mode @echo "Creating buildx builder..." @docker buildx create --bootstrap \ --name buildcage \ - --driver remote tcp://localhost:1234 + --driver remote docker-container://buildcage .PHONY: run_restrict_mode run_restrict_mode: ## Start in restrict mode @@ -36,7 +36,7 @@ run_restrict_mode: ## Start in restrict mode @echo "Creating buildx builder..." @docker buildx create --bootstrap \ --name buildcage \ - --driver remote tcp://localhost:1234 + --driver remote docker-container://buildcage .PHONY: test_restrict_mode test_restrict_mode: ## Run restrict mode tests diff --git a/README.md b/README.md index 7866c7f..c3bd536 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ jobs: uses: docker/setup-buildx-action@v3 with: driver: remote - endpoint: tcp://localhost:${{ steps.buildcage.outputs.port }} + endpoint: docker-container://buildcage - name: Build and discover dependencies uses: docker/build-push-action@v6 @@ -150,7 +150,7 @@ jobs: uses: docker/setup-buildx-action@v3 with: driver: remote - endpoint: tcp://localhost:${{ steps.buildcage.outputs.port }} + endpoint: docker-container://buildcage - name: Build with protection uses: docker/build-push-action@v6 @@ -189,11 +189,11 @@ Starts the Buildcage builder container. |-----------|----------|---------|-------------| | `buildcage_image` | No | `ghcr.io//` | Docker image name | | `buildcage_version` | No | `1` | Image tag | +| `builder_name` | No | `buildcage` | Name of the builder container | | `proxy_mode` | No | `restrict` | Operation mode (`audit` / `restrict`) | | `allowed_https_rules` | No | empty | HTTPS allow rules (wildcard or regex, port required) | | `allowed_http_rules` | No | empty | HTTP allow rules (wildcard or regex, port required) | | `allowed_ip_rules` | No | empty | IP address allow rules (wildcard or regex, port required) | -| `port` | No | `1234` | BuildKit endpoint port on localhost | **Rule syntax** @@ -210,20 +210,16 @@ IP address rules (e.g., `192.168.1.1:443`) use the same syntax but go in `allowe For detailed syntax, see [Rule Syntax](./docs/rules.md). -#### Outputs +#### Connecting Buildx -| Name | Description | -|------|-------------| -| `port` | BuildKit endpoint port | - -Pass this port to [`docker/setup-buildx-action`](https://github.com/docker/setup-buildx-action) to use Buildcage as a remote builder: +Pass the container name to [`docker/setup-buildx-action`](https://github.com/docker/setup-buildx-action) to use Buildcage as a remote builder. The `endpoint` must match the `builder_name` parameter (default: `buildcage`): ```yaml - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: driver: remote - endpoint: tcp://localhost:${{ steps.buildcage.outputs.port }} + endpoint: docker-container://buildcage ``` #### Operation Modes @@ -286,6 +282,7 @@ In restrict mode, the report step fails if blocked connections are detected, cau | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| +| `builder_name` | No | `buildcage` | Name of the builder container | | `fail_on_blocked` | No | `true` | Fail the step if blocked connections are detected (restrict mode only; ignored in audit mode) | --- diff --git a/compose.yml b/compose.yml index 9f1cbaf..031c01a 100644 --- a/compose.yml +++ b/compose.yml @@ -1,5 +1,6 @@ services: builder: + container_name: ${BUILDER_NAME:-buildcage} build: context: docker dockerfile: Dockerfile @@ -25,8 +26,6 @@ services: cgroup: host volumes: - /sys/fs/cgroup:/sys/fs/cgroup:rw - ports: - - "${PORT:-1234}:1234" environment: - PROXY_MODE=${PROXY_MODE:-restrict} - ALLOWED_HTTPS_RULES=${ALLOWED_HTTPS_RULES:-} @@ -35,7 +34,7 @@ services: - EXTERNAL_RESOLVER=${EXTERNAL_RESOLVER:-1.1.1.1,8.8.8.8} restart: unless-stopped healthcheck: - test: ["CMD", "sh", "-c", "curl -sf --unix-socket /var/run/haproxy-health.sock http://localhost/health && nc -z 127.0.0.1 1234"] + test: ["CMD", "sh", "-c", "curl -sf --unix-socket /var/run/haproxy-health.sock http://localhost/health"] interval: 30s timeout: 5s retries: 10 diff --git a/docker/Dockerfile b/docker/Dockerfile index a2c4ded..9e7ff69 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -74,4 +74,4 @@ RUN chmod +x /etc/s6-overlay/scripts/* && \ chmod +x /etc/s6-overlay/s6-rc.d/haproxy-log/run ENTRYPOINT ["/init"] -CMD ["buildkitd", "--oci-worker-net=cni", "--addr", "tcp://0.0.0.0:1234"] +CMD ["buildkitd", "--oci-worker-net=cni"] diff --git a/docker/files/s6-scripts/init-iptables b/docker/files/s6-scripts/init-iptables index 7595cf6..e6c9efd 100644 --- a/docker/files/s6-scripts/init-iptables +++ b/docker/files/s6-scripts/init-iptables @@ -9,7 +9,3 @@ iptables -t nat -A PREROUTING -i buildkit0 -p tcp \ iptables -A FORWARD -i buildkit0 -j DROP ip6tables -A FORWARD -i buildkit0 -j DROP echo "init-iptables: REDIRECT configured, FORWARD from buildkit0 blocked (IPv4/IPv6)" - -iptables -A INPUT -i buildkit0 -p tcp --dport 1234 -j DROP -ip6tables -A INPUT -i buildkit0 -p tcp --dport 1234 -j DROP -echo "init-iptables: INPUT to buildkitd API from buildkit0 blocked (IPv4/IPv6)" diff --git a/report/action.yml b/report/action.yml index 063701f..5323919 100644 --- a/report/action.yml +++ b/report/action.yml @@ -2,6 +2,10 @@ name: Report description: Show proxy communication logs and fail if blocked connections detected inputs: + builder_name: + description: "Name of the builder container" + required: false + default: 'buildcage' fail_on_blocked: description: "Fail the step if blocked connections are detected" required: false diff --git a/report/main.mjs b/report/main.mjs index 0948151..67ea8b7 100644 --- a/report/main.mjs +++ b/report/main.mjs @@ -8,13 +8,17 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); // 1. Get structured report from container via QuickJS const composeFile = process.argv[2] || join(__dirname, "..", "setup", "compose.yml"); +const composeEnv = { + ...process.env, + BUILDER_NAME: process.env.INPUT_BUILDER_NAME || "buildcage", +}; let jsonOutput; try { jsonOutput = execFileSync( "docker", ["compose", "-f", composeFile, "exec", "builder", "qjs", "/opt/buildcage/tools/report.mjs"], - { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] } + { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: composeEnv } ); } catch (e) { console.log("Failed to get report from container:", e.message); @@ -29,7 +33,7 @@ try { const rawLog = execFileSync( "docker", ["compose", "-f", composeFile, "exec", "builder", "cat", "/var/log/haproxy/current"], - { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] } + { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: composeEnv } ); process.stdout.write(rawLog); } catch { diff --git a/setup/action.yml b/setup/action.yml index dd9f4f0..b4de7d0 100644 --- a/setup/action.yml +++ b/setup/action.yml @@ -8,6 +8,10 @@ inputs: buildcage_version: description: "Image tag" required: false + builder_name: + description: "Name of the builder container" + required: false + default: 'buildcage' proxy_mode: description: "audit or restrict" required: false @@ -41,14 +45,6 @@ inputs: description: "Deprecated: specify port per rule in allowed_https_rules (e.g., 'example.com:8443')" required: false default: '443' - port: - description: "BuildKit endpoint port on localhost" - required: false - default: '1234' - -outputs: - port: - description: "BuildKit endpoint port" runs: using: node24 diff --git a/setup/compose.yml b/setup/compose.yml index 25339dd..e7c24bb 100644 --- a/setup/compose.yml +++ b/setup/compose.yml @@ -1,5 +1,6 @@ services: builder: + container_name: ${BUILDER_NAME:-buildcage} image: ${BUILDCAGE_IMAGE}:${BUILDCAGE_VERSION:-1} # Instead of privileged: true, grant only the minimum privileges required # to run BuildKit and iptables. This avoids granting full device access @@ -23,8 +24,6 @@ services: cgroup: host volumes: - /sys/fs/cgroup:/sys/fs/cgroup:rw - ports: - - "${PORT:-1234}:1234" environment: - PROXY_MODE=${PROXY_MODE:-restrict} - ALLOWED_HTTPS_RULES=${ALLOWED_HTTPS_RULES:-} @@ -33,7 +32,7 @@ services: - EXTERNAL_RESOLVER=${EXTERNAL_RESOLVER:-1.1.1.1,8.8.8.8} restart: unless-stopped healthcheck: - test: ["CMD", "sh", "-c", "curl -sf --unix-socket /var/run/haproxy-health.sock http://localhost/health && nc -z 127.0.0.1 1234"] + test: ["CMD", "sh", "-c", "curl -sf --unix-socket /var/run/haproxy-health.sock http://localhost/health"] interval: 30s timeout: 5s retries: 10 diff --git a/setup/main.mjs b/setup/main.mjs index db55884..aade3f5 100644 --- a/setup/main.mjs +++ b/setup/main.mjs @@ -1,5 +1,4 @@ import { execFileSync } from "node:child_process"; -import { appendFileSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { buildLegacyRules } from "./lib/legacy-rules.mjs"; @@ -43,13 +42,13 @@ function main() { const composeEnv = { ...env, + BUILDER_NAME: env.INPUT_BUILDER_NAME || "buildcage", PROXY_MODE: env.INPUT_PROXY_MODE || "restrict", ALLOWED_HTTPS_RULES: rules.httpsRules.join('\n'), ALLOWED_HTTP_RULES: rules.httpRules.join('\n'), ALLOWED_IP_RULES: rules.ipRules.join('\n'), BUILDCAGE_IMAGE: image.repository, BUILDCAGE_VERSION: image.tag, - PORT: env.INPUT_PORT || "1234", }; execFileSync( @@ -68,9 +67,6 @@ function main() { { stdio: "inherit", env: composeEnv } ); - // Set action output - const port = env.INPUT_PORT || "1234"; - appendFileSync(env.GITHUB_OUTPUT, `port=${port}\n`); } /** diff --git a/setup/post.mjs b/setup/post.mjs index c54fcee..61de2f2 100644 --- a/setup/post.mjs +++ b/setup/post.mjs @@ -7,5 +7,11 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); execFileSync( "docker", ["compose", "-f", join(__dirname, "compose.yml"), "down"], - { stdio: "inherit" } + { + stdio: "inherit", + env: { + ...process.env, + BUILDER_NAME: env.INPUT_BUILDER_NAME || "buildcage", + }, + } );