From c783af99f9f3da740f553e7c9cbc768fd2a89724 Mon Sep 17 00:00:00 2001 From: mo khan Date: Thu, 3 Jul 2025 15:53:00 -0600 Subject: chore: include envoy in docker image --- .gitignore | 1 + Dockerfile | 33 ++++++++++++++---- Makefile | 43 +++++++++++++++++------ Procfile | 2 ++ bin/.keep | 0 bin/envoy | 3 ++ etc/envoy/envoy.yaml | 97 +++++++++++++++++++++++++++++++++++++++------------- mise.toml | 1 + 8 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 Procfile create mode 100644 bin/.keep create mode 100755 bin/envoy diff --git a/.gitignore b/.gitignore index 4ea11ebf..2d67c87d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +bin target # Explicitly include vendor directory diff --git a/Dockerfile b/Dockerfile index 02079de5..cbb0692f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,35 @@ # syntax=docker/dockerfile:1 -FROM rust:alpine AS builder +# Build stage for authzd +FROM rust:alpine AS authzd-builder RUN apk add --no-cache musl-dev WORKDIR /app COPY . ./ RUN cargo build --release --target x86_64-unknown-linux-musl --offline RUN strip /app/target/x86_64-unknown-linux-musl/release/authzd -FROM gcr.io/distroless/static-debian12:nonroot +# Build stage for getting Envoy binary +FROM envoyproxy/envoy:v1.34-latest AS envoy-binary + +# Build stage for goreman (Procfile supervisor) +FROM golang:1.23-alpine AS goreman-builder +RUN go install github.com/mattn/goreman@latest + +# Final stage +FROM gcr.io/distroless/base-debian12:nonroot EXPOSE 9901 10000 50051 -WORKDIR /var/www -COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/authzd /bin/authzd -COPY --from=builder /app/etc/authzd /etc/authzd -ENTRYPOINT ["/bin/authzd"] +WORKDIR / + +# Copy binaries +COPY --from=authzd-builder /app/target/x86_64-unknown-linux-musl/release/authzd /bin/authzd +COPY --from=envoy-binary /usr/local/bin/envoy /bin/envoy +COPY --from=goreman-builder /go/bin/goreman /bin/goreman + +# Copy configurations +COPY --from=authzd-builder /app/etc/authzd /etc/authzd +COPY --from=authzd-builder /app/etc/envoy /etc/envoy + +# Copy Procfile and goreman config +COPY --from=authzd-builder /app/Procfile /Procfile + +ENTRYPOINT ["/bin/goreman"] +CMD ["start"] diff --git a/Makefile b/Makefile index ca1a93db..2ab437eb 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,17 @@ -PROJECT_NAME := $(shell basename $(shell pwd)) +PROJECT_NAME := $(shell basename $(shell pwd))# {{{}}} GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD | sed 's/\//_/g') IMAGE_TAG := $(PROJECT_NAME):$(GIT_BRANCH) .PHONY: build check test run clean fmt lint doc vendor -.PHONY: build-image run-image health-check list-services test-grpc +.PHONY: build-image run-image health-check list-services test-image setup: - @rustup component add clippy rustfmt + mise install + mise exec go -- go install github.com/mattn/goreman@latest + mise exec rustup -- rustup component add clippy rustfmt # Cargo targets -build: vendor +build: @cargo build --offline check: @@ -18,8 +20,9 @@ check: test: @cargo test -run: - @cargo run --offline +run: build + @cp target/debug/authzd bin/authzd + @goreman -set-ports=false -rpc-server=false -f ./Procfile -exit-on-error=true start clean: @cargo clean @@ -37,18 +40,36 @@ vendor: @cargo vendor # Docker targets -build-image: vendor +build-image: @docker build --tag $(IMAGE_TAG) . build-image-clean: - @docker build --no-cache --tag $(IMAGE_TAG) . + @docker build --tag $(IMAGE_TAG) . run-image: build-image - @docker run --rm -p 50051:50051 --init -it $(IMAGE_TAG) + @docker run --rm -p 10000:10000 -p 9901:9901 --init -it $(IMAGE_TAG) -# gRPC testing targets +# HTTP and gRPC testing targets health-check: - @grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check + @curl -s http://localhost:10000/health || echo "Service not running" + +envoy-admin: + @curl -s http://localhost:9901/stats/prometheus | head -20 list-services: @grpcurl -plaintext localhost:50051 list + +test-image: build-image + @echo "Starting container..." + @docker run -d --name authzd-test -p 10000:10000 -p 9901:9901 $(IMAGE_TAG) + @echo "Waiting for services to start..." + @sleep 5 + @echo "Testing Envoy admin endpoint..." + @curl -s http://localhost:9901/stats/prometheus | grep -q "envoy_" && echo "✓ Envoy admin is accessible" || echo "✗ Envoy admin failed" + @echo "Testing health endpoint..." + @curl -s -o /dev/null -w "%{http_code}" http://localhost:10000/health | grep -q "200" && echo "✓ Health check passed" || echo "✗ Health check failed" + @echo "Testing authorization flow..." + @curl -s -H "Authorization: Bearer valid-token" http://localhost:10000/ -w "\n%{http_code}" | grep -q "200" && echo "✓ Auth with valid token passed" || echo "✗ Auth with valid token failed" + @curl -s http://localhost:10000/ -w "\n%{http_code}" | grep -q "401" && echo "✓ Auth without token correctly rejected" || echo "✗ Auth without token failed" + @echo "Cleaning up..." + @docker stop authzd-test && docker rm authzd-test diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..6646bcf9 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +authzd: ./bin/authzd +envoy: ./bin/envoy -c ./etc/envoy/envoy.yaml --service-cluster authzd --service-node authzd diff --git a/bin/.keep b/bin/.keep new file mode 100644 index 00000000..e69de29b diff --git a/bin/envoy b/bin/envoy new file mode 100755 index 00000000..ede6290c --- /dev/null +++ b/bin/envoy @@ -0,0 +1,3 @@ +#!/bin/sh + +exec envoy $@ diff --git a/etc/envoy/envoy.yaml b/etc/envoy/envoy.yaml index e050a49e..31b29c80 100644 --- a/etc/envoy/envoy.yaml +++ b/etc/envoy/envoy.yaml @@ -6,21 +6,23 @@ admin: application_log_config: log_format: json_format: - Timestamp: "%Y-%m-%dT%T.%F" - ThreadId: "%t" - SourceLine: "%s:%#" - Level: "%l" - Message: "%j" + timestamp: "%Y-%m-%dT%T.%FZ" + thread_id: "%t" + level: "%l" + logger: "%n" + message: "%j" overload_manager: resource_monitors: - name: "envoy.resource_monitors.global_downstream_max_connections" typed_config: "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig - max_active_downstream_connections: 1024 + max_active_downstream_connections: 10240 static_resources: clusters: - name: authzd connect_timeout: 5s + type: STATIC + lb_policy: ROUND_ROBIN load_assignment: cluster_name: authzd endpoints: @@ -35,8 +37,21 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} + health_checks: + - timeout: 3s + interval: 5s + unhealthy_threshold: 2 + healthy_threshold: 2 + grpc_health_check: {} + circuit_breakers: + thresholds: + - priority: DEFAULT + max_connections: 1024 + max_pending_requests: 1024 + max_requests: 1024 + max_retries: 3 listeners: - - name: listener_0 + - name: main_listener address: socket_address: protocol: TCP @@ -49,25 +64,30 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager access_log: - name: envoy.access_loggers.stdout + filter: + not_health_check_filter: {} typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog log_format: json_format: - app: "envoy" - authority: "%REQ(:AUTHORITY)%" - bytes_received: "%BYTES_RECEIVED%" - bytes_sent: "%BYTES_SENT%" - client_ip: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" - duration: "%DURATION%" - forwarded_for: "%REQ(X-FORWARDED-FOR)%" + timestamp: "%START_TIME(%FT%T.%3fZ)%" method: "%REQ(:METHOD)%" path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" protocol: "%PROTOCOL%" - request_id: "%REQ(X-REQUEST-ID)%" response_code: "%RESPONSE_CODE%" - timestamp: "%START_TIME%" + response_flags: "%RESPONSE_FLAGS%" + bytes_received: "%BYTES_RECEIVED%" + bytes_sent: "%BYTES_SENT%" + duration_ms: "%DURATION%" + upstream_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%" + x_forwarded_for: "%REQ(X-FORWARDED-FOR)%" user_agent: "%REQ(USER-AGENT)%" + request_id: "%REQ(X-REQUEST-ID)%" + authority: "%REQ(:AUTHORITY)%" + upstream_host: "%UPSTREAM_HOST%" codec_type: AUTO + request_timeout: 30s + stream_idle_timeout: 300s http_filters: - name: envoy.filters.http.health_check typed_config: @@ -80,30 +100,61 @@ static_resources: - name: envoy.filters.http.ext_authz typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + transport_api_version: V3 grpc_service: envoy_grpc: cluster_name: authzd - timeout: 30s + timeout: 5s failure_mode_allow: false + include_peer_certificate: true + clear_route_cache: true + status_on_error: + code: 503 - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router suppress_envoy_headers: true route_config: + name: local_route request_headers_to_remove: - authorization - cookie - - user-agent + response_headers_to_add: + - header: + key: "x-content-type-options" + value: "nosniff" + - header: + key: "x-frame-options" + value: "DENY" + - header: + key: "x-xss-protection" + value: "1; mode=block" virtual_hosts: - - name: local + - name: backend domains: ["*"] routes: - match: prefix: "/" route: cluster: authzd - timeout: 5s - retry_policy: - retry_on: "5xx" - num_retries: 3 + timeout: 30s + retry_policy: + retry_on: "5xx,reset,connect-failure,retriable-status-codes" + num_retries: 3 + per_try_timeout: 10s + retriable_status_codes: [503] + request_headers_to_add: + - header: + key: "x-real-ip" + value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" + - header: + key: "x-forwarded-proto" + value: "%REQ(X-FORWARDED-PROTO)%" stat_prefix: ingress_http + common_http_protocol_options: + idle_timeout: 300s + headers_with_underscores_action: REJECT_REQUEST + http2_protocol_options: + max_concurrent_streams: 100 + initial_stream_window_size: 65536 + server_header_transformation: PASS_THROUGH diff --git a/mise.toml b/mise.toml index 276d2e72..21f7cb80 100644 --- a/mise.toml +++ b/mise.toml @@ -1,4 +1,5 @@ [tools] +go = "latest" grpcurl = "latest" make = "latest" rust = "latest" -- cgit v1.2.3