Skip to content

Commit 531c866

Browse files
committed
BUILD/MINOR: docker: clean docker image, add security component
1 parent e6cc4ac commit 531c866

File tree

10 files changed

+223
-22
lines changed

10 files changed

+223
-22
lines changed

build/Dockerfile

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ RUN mkdir -p /var/run/vars && \
2828
-tags="$BUILD_TAGS" \
2929
-o fs/kubernetes-controller ./cmd/controller
3030

31+
FROM haproxytech/haproxy-alpine:3.2 AS builder-c
32+
RUN apk add --no-cache build-base gcc musl-dev
33+
WORKDIR /src
34+
35+
COPY hug/protection/block_secrets.c .
36+
COPY hug/protection/haproxy_wrapper.c .
37+
RUN gcc -O3 -Wall -flto -fPIC -shared -s -o libblock_secrets.so block_secrets.c -ldl
38+
RUN gcc -O3 -Wall -g -s -o haproxy_wrapper haproxy_wrapper.c
39+
3140
FROM haproxytech/haproxy-alpine:3.2
3241

3342
ARG TARGETPLATFORM
@@ -39,19 +48,20 @@ ENV S6_USER=haproxy
3948
ENV S6_GROUP=haproxy
4049

4150
COPY /fs /
42-
43-
RUN mkdir -p /usr/local/hug/aux && \
44-
chgrp -R haproxy /usr/local/hug && \
45-
chmod -R ug+rwx /usr/local/hug
51+
COPY --from=builder-c /src/libblock_secrets.so /usr/local/lib/libblock_secrets.so
52+
COPY --from=builder-c /src/haproxy_wrapper /usr/local/sbin/haproxy_wrapper
4653

4754
RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
4855
rm -f /usr/local/bin/dataplaneapi /usr/bin/dataplaneapi /etc/haproxy/dataplaneapi.yml && \
4956
rm -f /usr/local/bin/dataplaneapi-v2 /usr/bin/dataplaneapi-v2 && \
5057
rm -f /etc/haproxy/haproxy.cfg && \
51-
mkdir -p /usr/local/hug && \
52-
chgrp -R haproxy /usr/local/hug /run /var && \
53-
chmod -R ug+rwx /usr/local/hug /run /var && \
54-
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy && \
58+
mkdir -p /usr/local/hug/aux && \
59+
chgrp -R haproxy /usr/local/hug && \
60+
chmod -R ug+rwx /usr/local/hug && \
61+
chown -R "${S6_USER}:${S6_GROUP}" /usr/local/etc/haproxy /run /var && \
62+
chmod -R ug+rwx /usr/local/etc/haproxy /run /var && \
63+
chmod u+rx /usr/local/sbin/haproxy_wrapper && \
64+
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy_wrapper && \
5565
case "${TARGETPLATFORM}" in \
5666
"linux/arm64") S6_ARCH=aarch64 ;; \
5767
"linux/amd64") S6_ARCH=x86_64 ;; \

build/Dockerfile.dev

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

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 hug/protection/block_secrets.c .
20+
COPY hug/protection/haproxy_wrapper.c .
21+
RUN gcc -O3 -Wall -flto -fPIC -shared -s -o libblock_secrets.so block_secrets.c -ldl
22+
RUN gcc -O3 -Wall -g -s -o haproxy_wrapper haproxy_wrapper.c
23+
1524
FROM haproxytech/haproxy-alpine:3.2
1625

1726
ARG TARGETPLATFORM
@@ -23,6 +32,8 @@ ENV S6_USER=haproxy
2332
ENV S6_GROUP=haproxy
2433

2534
COPY /fs /
35+
COPY --from=builder-c /src/libblock_secrets.so /usr/local/lib/libblock_secrets.so
36+
COPY --from=builder-c /src/haproxy_wrapper /usr/local/sbin/haproxy_wrapper
2637

2738
RUN mkdir -p /usr/local/hug/aux && \
2839
chgrp -R haproxy /usr/local/hug && \
@@ -32,10 +43,13 @@ RUN apk --no-cache add socat openssl util-linux htop tzdata curl libcap && \
3243
rm -f /usr/local/bin/dataplaneapi /usr/bin/dataplaneapi /etc/haproxy/dataplaneapi.yml && \
3344
rm -f /usr/local/bin/dataplaneapi-v2 /usr/bin/dataplaneapi-v2 && \
3445
rm -f /etc/haproxy/haproxy.cfg && \
35-
mkdir -p /usr/local/hug && \
36-
chgrp -R haproxy /usr/local/hug /run /var && \
37-
chmod -R ug+rwx /usr/local/hug /run /var && \
38-
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy && \
46+
mkdir -p /usr/local/hug/aux && \
47+
chgrp -R haproxy /usr/local/hug && \
48+
chmod -R ug+rwx /usr/local/hug && \
49+
chown -R "${S6_USER}:${S6_GROUP}" /usr/local/etc/haproxy /run /var && \
50+
chmod -R ug+rwx /usr/local/etc/haproxy /run /var && \
51+
chmod u+rx /usr/local/sbin/haproxy_wrapper && \
52+
setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy_wrapper && \
3953
case "${TARGETPLATFORM}" in \
4054
"linux/arm64") S6_ARCH=aarch64 ;; \
4155
"linux/amd64") S6_ARCH=x86_64 ;; \

fs/etc/s6-overlay/s6-rc.d/aux-cfg/type

Lines changed: 0 additions & 1 deletion
This file was deleted.

fs/etc/s6-overlay/s6-rc.d/aux-cfg/up

Lines changed: 0 additions & 1 deletion
This file was deleted.

fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/aux-cfg

Whitespace-only changes.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ fi
2424
echo "Memory limit for HAProxy: ${MEMLIMIT}MiB"
2525

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

fs/etc/s6-overlay/s6-rc.d/kubernetes-controller/dependencies.d/aux-cfg

Whitespace-only changes.

fs/etc/s6-overlay/scripts/01-aux-cfg

Lines changed: 0 additions & 7 deletions
This file was deleted.

hug/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+
}

hug/protection/haproxy_wrapper.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
#define _GNU_SOURCE
16+
#include <stdio.h>
17+
#include <stdlib.h>
18+
#include <unistd.h>
19+
20+
#define LIB_PATH "LD_PRELOAD=/usr/local/lib/libblock_secrets.so"
21+
#define TARGET_PATH "/usr/local/sbin/haproxy"
22+
23+
int main(int argc, char *argv[], char *envp[]) {
24+
char **newargv = malloc((argc + 1) * sizeof(char *));
25+
if (!newargv) {
26+
perror("malloc");
27+
return 1;
28+
}
29+
newargv[0] = (char *)TARGET_PATH;
30+
for (int i = 1; i < argc; i++) {
31+
newargv[i] = argv[i];
32+
}
33+
newargv[argc] = NULL;
34+
35+
int envc = 0;
36+
while (environ[envc]) envc++;
37+
38+
char **new_envp = malloc((envc + 2) * sizeof(char *));
39+
for (int i = 0; i < envc; i++) {
40+
new_envp[i] = environ[i];
41+
}
42+
new_envp[envc] = LIB_PATH;
43+
new_envp[envc + 1] = NULL;
44+
45+
execve(TARGET_PATH, newargv, new_envp);
46+
47+
perror("execve");
48+
return 1;
49+
}

0 commit comments

Comments
 (0)