Skip to content

Commit e07e01d

Browse files
oktalzdkorunic
andcommitted
MAJOR: limit access to k8s secret token
Co-authored-by: Dinko Korunic <dkorunic@haproxy.com>
1 parent bd3d957 commit e07e01d

File tree

6 files changed

+163
-6
lines changed

6 files changed

+163
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ bin/golangci-lint
88
bin/check-commit
99
.local/*
1010
__debug_bin*
11+
pkg/protection/libblock_secrets.so

build/Dockerfile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
FROM golang:1.24-alpine AS builder
15+
FROM haproxytech/haproxy-alpine:3.2 AS builder-c
16+
RUN apk add --no-cache build-base gcc musl-dev
17+
WORKDIR /src
18+
19+
COPY pkg/protection/block_secrets.c .
20+
RUN gcc -O3 -Wall -flto -fPIC -shared -s -o libblock_secrets.so block_secrets.c -ldl
1621

22+
FROM golang:1.24-alpine AS builder
1723
RUN apk --no-cache add git openssh
1824

1925
COPY /go.mod /src/go.mod
@@ -46,7 +52,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
4652
rm -f /usr/local/bin/dataplaneapi-v2 /usr/bin/dataplaneapi-v2 && \
4753
chgrp -R haproxy /usr/local/etc/haproxy /run /var && \
4854
chmod -R ug+rwx /usr/local/etc/haproxy /run /var && \
49-
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy && \
5055
case "${TARGETPLATFORM}" in \
5156
"linux/arm64") S6_ARCH=aarch64 ;; \
5257
"linux/amd64") S6_ARCH=x86_64 ;; \
@@ -69,4 +74,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
6974

7075
COPY --from=builder /src/fs/haproxy-ingress-controller .
7176

77+
COPY --from=builder-c /src/libblock_secrets.so /usr/local/lib/libblock_secrets.so
78+
7279
ENTRYPOINT ["/start.sh"]

build/Dockerfile.dev

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
FROM haproxytech/haproxy-alpine:3.2 AS builder-c
15+
RUN apk add --no-cache build-base gcc musl-dev
16+
WORKDIR /src
17+
COPY pkg/protection/block_secrets.c .
18+
RUN gcc -O3 -Wall -flto -fPIC -shared -s -o libblock_secrets.so block_secrets.c -ldl
1419

1520
FROM haproxytech/haproxy-alpine:3.2
16-
1721
ARG TARGETPLATFORM
18-
1922
ARG S6_OVERLAY_VERSION=3.1.6.2
2023
ENV S6_OVERLAY_VERSION=$S6_OVERLAY_VERSION
2124
ENV S6_READ_ONLY_ROOT=1
@@ -28,7 +31,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
2831
rm -f /usr/local/bin/dataplaneapi /usr/bin/dataplaneapi /etc/haproxy/dataplaneapi.yml && \
2932
chgrp -R haproxy /usr/local/etc/haproxy /run /var && \
3033
chmod -R ug+rwx /usr/local/etc/haproxy /run /var && \
31-
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy && \
3234
case "${TARGETPLATFORM}" in \
3335
"linux/arm64") S6_ARCH=aarch64 ;; \
3436
"linux/amd64") S6_ARCH=x86_64 ;; \
@@ -51,4 +53,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
5153

5254
COPY kubernetes-ingress ./haproxy-ingress-controller
5355

56+
COPY --from=builder-c /src/libblock_secrets.so /usr/local/lib/libblock_secrets.so
57+
5458
ENTRYPOINT ["/start.sh"]

build/Dockerfile.pebble

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
FROM haproxytech/haproxy-alpine:3.2 AS builder-c
15+
RUN apk add --no-cache build-base gcc musl-dev
16+
WORKDIR /src
17+
18+
COPY pkg/protection/block_secrets.c .
19+
RUN gcc -O3 -Wall -flto -fPIC -shared -s -o libblock_secrets.so block_secrets.c -ldl
1420

1521
FROM golang:1.24-alpine AS builder
1622

@@ -41,7 +47,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
4147
rm -f /usr/local/bin/dataplaneapi /usr/bin/dataplaneapi && \
4248
chgrp -R haproxy /usr/local/etc/haproxy /run /var && \
4349
chmod -R ug+rwx /usr/local/etc/haproxy /run /var && \
44-
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy && \
4550
chown -R haproxy:haproxy /var/lib/pebble/default && \
4651
chmod ugo+rwx /var/lib/pebble/default/* && \
4752
rm -rf /etc/services.d/haproxy && \
@@ -52,4 +57,6 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
5257
COPY --from=builder /go/bin/pebble /usr/local/bin
5358
COPY --from=builder /src/fs/haproxy-ingress-controller .
5459

60+
COPY --from=builder-c /src/libblock_secrets.so /usr/local/lib/libblock_secrets.so
61+
5562
ENTRYPOINT ["/start-pebble.sh"]

fs/etc/s6-overlay/s6-rc.d/haproxy/run

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ if [ -r "${CG_LIMIT_FILE}" ]; then
2222
fi
2323

2424
echo "Memory limit for HAProxy: ${MEMLIMIT}MiB"
25+
export LD_PRELOAD=/usr/local/lib/libblock_secrets.so
2526

2627
# if master socket is changed, that needs to be aligned in pkg/haproxy/process/interface.go
2728
exec /usr/local/sbin/haproxy -W -db -m "${MEMLIMIT}" -S /var/run/haproxy-master.sock,level,admin -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/haproxy-aux.cfg

pkg/protection/block_secrets.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/* Copyright 2025 HAProxy Technologies LLC */
2+
/* */
3+
/* Licensed under the Apache License, Version 2.0 (the "License"); */
4+
/* you may not use this file except in compliance with the License. */
5+
/* You may obtain a copy of the License at */
6+
/* */
7+
/* http://www.apache.org/licenses/LICENSE-2.0 */
8+
/* */
9+
/* Unless required by applicable law or agreed to in writing, software */
10+
/* distributed under the License is distributed on an "AS IS" BASIS, */
11+
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
12+
/* See the License for the specific language governing permissions and */
13+
/* limitations under the License. */
14+
15+
#include <dlfcn.h>
16+
#include <errno.h>
17+
#include <fcntl.h>
18+
#include <limits.h>
19+
#include <stdarg.h>
20+
#include <stdio.h>
21+
#include <stdlib.h>
22+
#include <string.h>
23+
#include <sys/stat.h>
24+
#include <sys/types.h>
25+
#include <unistd.h>
26+
27+
#define PATH_MAX 4096
28+
#define BLOCKED_PATH "/var/run/secrets/kubernetes.io/"
29+
30+
static char canonical_blocked[PATH_MAX] = {0};
31+
static size_t canonical_blocked_len = 0;
32+
33+
__attribute__((constructor)) static void init_blocked_path() {
34+
if (!realpath(BLOCKED_PATH, canonical_blocked)) {
35+
strncpy(canonical_blocked, BLOCKED_PATH, PATH_MAX - 1);
36+
canonical_blocked[PATH_MAX - 1] = '\0';
37+
}
38+
canonical_blocked_len = strlen(canonical_blocked);
39+
}
40+
41+
__attribute__((always_inline)) inline static int
42+
is_blocked(const char *pathname) {
43+
char resolved[PATH_MAX];
44+
const char *target = pathname;
45+
46+
if (realpath(pathname, resolved)) {
47+
target = resolved;
48+
}
49+
50+
return strncmp(target, canonical_blocked, canonical_blocked_len) == 0;
51+
}
52+
53+
static int (*real_open)(const char *, int, ...) = NULL;
54+
static int (*real_open64)(const char *, int, ...) = NULL;
55+
static FILE *(*real_fopen)(const char *, const char *) = NULL;
56+
static FILE *(*real_fopen64)(const char *, const char *) = NULL;
57+
static FILE *(*real_freopen)(const char *, const char *, FILE *) = NULL;
58+
static FILE *(*real_freopen64)(const char *, const char *, FILE *) = NULL;
59+
60+
__attribute__((constructor)) static void init_hooks() {
61+
real_open = dlsym(RTLD_NEXT, "open");
62+
real_open64 = dlsym(RTLD_NEXT, "open64");
63+
real_fopen = dlsym(RTLD_NEXT, "fopen");
64+
real_fopen64 = dlsym(RTLD_NEXT, "fopen64");
65+
real_freopen = dlsym(RTLD_NEXT, "freopen");
66+
real_freopen64 = dlsym(RTLD_NEXT, "freopen64");
67+
}
68+
69+
int open(const char *pathname, int flags, ...) {
70+
if (is_blocked(pathname)) {
71+
errno = EACCES;
72+
return -1;
73+
}
74+
75+
va_list args;
76+
va_start(args, flags);
77+
int fd;
78+
if (flags & O_CREAT) {
79+
mode_t mode = (mode_t)va_arg(args, int);
80+
fd = real_open(pathname, flags, mode);
81+
} else {
82+
fd = real_open(pathname, flags);
83+
}
84+
va_end(args);
85+
return fd;
86+
}
87+
88+
int open64(const char *pathname, int flags, ...) {
89+
if (is_blocked(pathname)) {
90+
errno = EACCES;
91+
return -1;
92+
}
93+
94+
va_list args;
95+
va_start(args, flags);
96+
int fd;
97+
if (flags & O_CREAT) {
98+
mode_t mode = (mode_t)va_arg(args, int);
99+
fd = real_open64(pathname, flags, mode);
100+
} else {
101+
fd = real_open64(pathname, flags);
102+
}
103+
va_end(args);
104+
return fd;
105+
}
106+
107+
FILE *fopen(const char *pathname, const char *mode) {
108+
if (is_blocked(pathname)) {
109+
errno = EACCES;
110+
return NULL;
111+
}
112+
return real_fopen(pathname, mode);
113+
}
114+
115+
FILE *fopen64(const char *pathname, const char *mode) {
116+
if (is_blocked(pathname)) {
117+
errno = EACCES;
118+
return NULL;
119+
}
120+
return real_fopen64(pathname, mode);
121+
}
122+
123+
FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
124+
if (is_blocked(pathname)) {
125+
errno = EACCES;
126+
return NULL;
127+
}
128+
return real_freopen(pathname, mode, stream);
129+
}
130+
131+
FILE *freopen64(const char *pathname, const char *mode, FILE *stream) {
132+
if (is_blocked(pathname)) {
133+
errno = EACCES;
134+
return NULL;
135+
}
136+
return real_freopen64(pathname, mode, stream);
137+
}

0 commit comments

Comments
 (0)