-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathDockerfile
More file actions
213 lines (174 loc) · 8.91 KB
/
Dockerfile
File metadata and controls
213 lines (174 loc) · 8.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
ARG OPA_BUILD=permit
# RUST BUILD STAGE -----------------------------------
# Build the Rust PDP binary for all targets
# ----------------------------------------------------
# BIG thanks to
# - https://medium.com/@vladkens/fast-multi-arch-docker-build-for-rust-projects-a7db42f3adde
# - https://stackoverflow.com/questions/70561544/rust-openssl-could-not-find-directory-of-openssl-installation
# couldn't get this to work without the help of those two sources
# (1) this stage will be run always on current arch
# zigbuild & Cargo targets added
FROM --platform=$BUILDPLATFORM rust:1.94-alpine AS rust_chef
WORKDIR /app
ENV PKGCONFIG_SYSROOTDIR=/
RUN apk add --no-cache musl-dev openssl-dev zig pkgconf perl make
# Cache cargo installations
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
cargo install --locked cargo-zigbuild cargo-chef
RUN rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl
# (2) nothing changed
FROM rust_chef AS rust_planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# (3) building project deps: need to specify all targets; zigbuild used
FROM rust_chef AS rust_builder
COPY --from=rust_planner /app/recipe.json recipe.json
ENV OPENSSL_DIR=/usr
# Enable incremental compilation and use cache mounts
ENV CARGO_INCREMENTAL=1
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/app/target \
cargo chef cook --recipe-path recipe.json --release --zigbuild \
--target x86_64-unknown-linux-musl --target aarch64-unknown-linux-musl
# (4) actual project build for all targets
# binary renamed to easier copy in runtime stage
COPY . .
# Use cache mounts for incremental builds - this is the key optimization!
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/app/target \
cargo zigbuild -r --target x86_64-unknown-linux-musl --target aarch64-unknown-linux-musl && \
mkdir -p /app/linux/arm64/ && \
mkdir -p /app/linux/amd64/ && \
cp target/aarch64-unknown-linux-musl/release/pdp-server /app/linux/arm64/pdp && \
cp target/x86_64-unknown-linux-musl/release/pdp-server /app/linux/amd64/pdp
# OPA BUILD STAGE -----------------------------------
# Build OPA from source or download precompiled binary
# ---------------------------------------------------
FROM golang:1.25-bookworm AS opa_build
COPY custom* /custom
# Build OPA binary if custom_opa.tar.gz is provided
# Fix for ARM64 compatibility issue (#289): Build fully static binary to avoid dynamic linking issues
# Problem: Dynamic linking creates dependencies on system libc (glibc), but Alpine Linux uses musl libc
# Result: Binary fails with "/lib/ld-musl-aarch64.so.1: /app/bin/opa: Not a valid dynamic program"
# Solution: Build a truly static binary with no external libc dependencies
# - CGO_ENABLED=0: Disables CGO to ensure pure Go compilation (eliminates glibc dependency)
# - -a: Forces rebuilding of all packages to ensure clean static build
# - -tags netgo: Uses pure Go network stack instead of C-based libc resolver
# - -s -w: Strips debug info and symbol table to reduce binary size
# - -extldflags=-static: Ensures static linking if CGO were enabled (defense in depth)
# Use BuildKit cache mounts for Go modules and build cache for MUCH faster incremental builds
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
if [ -f /custom/custom_opa.tar.gz ]; \
then \
cd /custom && \
tar xzf custom_opa.tar.gz && \
CGO_ENABLED=0 go build -a -ldflags="-s -w -extldflags=-static" -tags netgo -installsuffix netgo -o /opa && \
rm -rf /custom; \
else \
case $(uname -m) in \
x86_64) curl -L -o /opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static ;; \
aarch64) curl -L -o /opa https://openpolicyagent.org/downloads/latest/opa_linux_arm64_static ;; \
*) echo "Unknown architecture." && exit 1 ;; \
esac; \
fi
# MAIN IMAGE ----------------------------------------
# Main image setup (optimized)
# ---------------------------------------------------
FROM python:3.10-alpine3.22 AS main
WORKDIR /app
# Create necessary user and group in a single step
RUN addgroup -S permit -g 1001 && \
adduser -S -s /bin/bash -u 1000 -G permit -h /home/permit permit
# Create backup directory with permissions
RUN mkdir -p /app/backup && chmod -R 777 /app/backup
# Install runtime libraries and delete SQLite
# Build deps (build-base, *-dev) are installed and removed in the pip install
# layer to avoid persisting binutils CVEs (CVE-2025-69649, CVE-2025-69650)
RUN --mount=type=cache,target=/var/cache/apk \
ln -s /var/cache/apk /etc/apk/cache && \
apk update && \
apk upgrade && \
apk add bash libffi libressl gcompat && \
apk del sqlite
# Copy OPA binary from the build stage
COPY --from=opa_build --chmod=755 /opa /app/bin/opa
# Copy the Rust PDP binary from the builder stage
ARG TARGETPLATFORM
COPY --from=rust_builder --chmod=755 /app/${TARGETPLATFORM}/pdp /app/pdp
# Environment variables for OPA
ENV OPAL_INLINE_OPA_EXEC_PATH="/app/bin/opa"
# Set permissions and ownership for the application
RUN mkdir -p /config && chown -R permit:permit /config
# Ensure the `permit` user has the correct permissions for home directory and binaries
RUN chown -R permit:permit /home/permit /app /usr/local/bin
# Switch to permit user
USER permit
# Copy Kong routes and Gunicorn config
COPY kong_routes.json /config/kong_routes.json
USER root
# Install python dependencies in one command to optimize layer size
# Use cache mount for pip to speed up incremental builds
COPY ./requirements.txt ./requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
apk add --no-cache --virtual .build-deps build-base libffi-dev libressl-dev musl-dev zlib-dev && \
pip install --upgrade pip setuptools && \
pip install -r requirements.txt && \
python -m pip uninstall -y pip setuptools wheel && \
rm -r /usr/local/lib/python3.10/ensurepip && \
apk del .build-deps
USER permit
# Copy the application code
COPY ./horizon /app/horizon
USER permit
# Version file for the application
COPY ./permit_pdp_version /app/permit_pdp_version
# Set the PATH to ensure the local binary paths are used
ENV PATH="/app/bin:/home/permit/.local/bin:$PATH"
# opal configuration --------------------------------
ENV OPAL_SERVER_URL="https://opal.permit.io"
ENV OPAL_LOG_DIAGNOSE="false"
ENV OPAL_LOG_TRACEBACK="false"
ENV OPAL_LOG_MODULE_EXCLUDE_LIST="[]"
ENV OPAL_INLINE_OPA_ENABLED="true"
ENV OPAL_INLINE_OPA_LOG_FORMAT="http"
# horizon configuration -----------------------------
# by default, the backend is at port 8000 on the docker host
# in prod, you must pass the correct url
ENV PDP_CONTROL_PLANE="https://api.permit.io"
ENV PDP_API_KEY="MUST BE DEFINED"
ENV PDP_REMOTE_CONFIG_ENDPOINT="/v2/pdps/me/config"
ENV PDP_REMOTE_STATE_ENDPOINT="/v2/pdps/me/state"
ENV PDP_VERSION_FILE_PATH="/app/permit_pdp_version"
# This is a default PUBLIC (not secret) key,
# and it is here as a safety measure on purpose.
ENV OPAL_AUTH_PUBLIC_KEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe2iQ+/E01P2W5/EZwD5NpRiSQ8/r/k18pFnym+vWCSNMWpd9UVpgOUWfA9CAX4oEo5G6RfVVId/epPH/qVSL87uh5PakkLZ3E+PWVnYtbzuFPs/lHZ9HhSqNtOQ3WcPDTcY/ST2jyib2z0sURYDMInSc1jnYKqPQ6YuREdoaNdPHwaTFN1tEKhQ1GyyhL5EDK97qU1ejvcYjpGm+EeE2sjauHYn2iVXa2UA9fC+FAKUwKqNcwRTf3VBLQTE6EHGWbxVzXv1Feo8lPZgL7Yu/UPgp7ivCZhZCROGDdagAfK9sveYjkKiWCLNUSpado/E5Vb+/1EVdAYj6fCzk45AdQzA9vwZefP0sVg7EuZ8VQvlz7cU9m+XYIeWqduN4Qodu87rtBYtSEAsru/8YDCXBDWlLJfuZb0p/klbte3TayKnQNSWD+tNYSJHrtA/3ZewP+tGDmtgLeB38NLy1xEsgd31v6ISOSCTHNS8ku9yWQXttv0/xRnuITr8a3TCLuqtUrNOhCx+nKLmYF2cyjYeQjOWWpn/Z6VkZvOa35jhG1ETI8IwE+t5zXqrf2s505mh18LwA1DhC8L/wHk8ZG7bnUe56QwxEo32myUBN8nHdu7XmPCVP8MWQNLh406QRAysishWhXVs/+0PbgfBJ/FxKP8BXW9zqzeIG+7b/yk8tRHQ=="
# We ignore this callback because we are sunsetting this feature in favor of the new inline OPA data updater
ENV PDP_IGNORE_DEFAULT_DATA_UPDATE_CALLBACKS_URLS='["http://localhost:8181/v1/data/permit/rebac/cache_rebuild"]'
# We need to set v0_compatible to true to make sure the PDP works with the OPA v0
# syntax.
ENV OPAL_INLINE_OPA_CONFIG='{"v0_compatible": true}'
# if we are using the custom OPA binary, we need to load the permit plugin,
# if we don't then we MUST not add a non existing plugin
FROM main AS main-vanilla
# if we are using the vanilla OPA binary, we don't need to load the permit plugin
ENV PDP_OPA_PLUGINS='{}'
FROM main AS main-permit
# if we are using the custom OPA binary, we need to load the permit plugin,
ENV PDP_OPA_PLUGINS='{"permit_graph":{}}'
FROM main-${OPA_BUILD} AS application
# Environment variables with defaults
ENV PDP_HORIZON_HOST=0.0.0.0
ENV PDP_HORIZON_PORT=7001
ENV PDP_PORT=7000
ENV PDP_PYTHON_PATH=python3
ENV NO_PROXY=localhost,127.0.0.1,::1
# 7000 pdp port
# 7001 horizon port
# 8181 opa port
EXPOSE 7000 7001 8181
# Run the application using the startup script
CMD ["/app/pdp"]