Skip to content

Commit 7155d04

Browse files
birdayzrockwotj
andauthored
Allow chroot directory to pre-exist for volume mount support (#3967)
The chroot setup previously required the chroot directory to not exist, which prevented mounting ConfigMaps or other volumes directly inside the chroot path. This is a problem because the chroot-passthrough mechanism copies files (losing symlinks), which breaks fsnotify-based hot reload of authorization policy files mounted from ConfigMaps. Allow the chroot directory to pre-exist so that Kubernetes volume mounts can place files directly inside it. Also handle EROFS errors in the makeReadOnly step, since read-only volume mounts (like ConfigMaps) cannot be chmod'd. Pre-create /tmp/chroot in the cloud Dockerfile owned by the connect user so the process can populate the rest of the chroot structure at runtime. * chore: update docs --------- Co-authored-by: Tyler Rockwood <rockwood@redpanda.com>
1 parent 0980e4d commit 7155d04

File tree

4 files changed

+24
-6
lines changed

4 files changed

+24
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Changelog
33

44
All notable changes to this project will be documented in this file.
55

6+
## 4.80.1 - 2026-02-05
7+
8+
### Changed
9+
10+
- chroot: existing directories are now allowed. (@birdayz)
11+
612
## 4.80.0 - 2026-02-04
713

814
### Added

internal/cli/chroot_linux.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package cli
1212

1313
import (
14+
"errors"
1415
"fmt"
1516
"io"
1617
"io/fs"
@@ -45,10 +46,9 @@ func chroot(path string, passthroughFiles []string) error {
4546
}
4647

4748
func setupChrootDir(chrootDir string, passthroughFiles []string) error {
48-
// Make sure chroot directory does not exist
49-
if _, err := os.Stat(chrootDir); err == nil {
50-
return fmt.Errorf("chroot directory %s must not exist", chrootDir)
51-
} else if !os.IsNotExist(err) {
49+
// Allow the chroot directory to pre-exist (e.g. created by volume
50+
// mounts). Only fail on unexpected stat errors.
51+
if _, err := os.Stat(chrootDir); err != nil && !os.IsNotExist(err) {
5252
return fmt.Errorf("check directory: %w", err)
5353
}
5454

@@ -234,6 +234,13 @@ func makeReadOnly(root string) error {
234234
if err != nil {
235235
return err
236236
}
237-
return os.Chmod(filePath, info.Mode() & ^os.FileMode(0o222))
237+
if err := os.Chmod(filePath, info.Mode() & ^os.FileMode(0o222)); err != nil {
238+
// Ignore read-only filesystem errors from volume mounts.
239+
if errors.Is(err, syscall.EROFS) {
240+
return nil
241+
}
242+
return err
243+
}
244+
return nil
238245
})
239246
}

internal/cli/flags_redpanda.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ func applyLicenseFlag(c *cli.Context, conf *license.Config) {
8383
var chrootFlag = &cli.StringFlag{
8484
Name: "chroot",
8585
Usage: "Chroot into the provided directory after parsing configuration. " +
86-
"The directory must not exist and will be created. " +
8786
"Common /etc/ files are copied to the chroot directory, and the directory is made read-only. " +
8887
"This flag is only supported on Linux.",
8988
}

resources/docker/cloud.Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ COPY --from=build /etc/passwd /etc/passwd
2727
COPY --from=build /tmp/redpanda-connect /redpanda-connect
2828
COPY config/docker.yaml /connect.yaml
2929

30+
# Pre-create the chroot directory so that volume mounts placed inside it
31+
# (e.g. ConfigMaps at /tmp/chroot/...) don't cause kubelet to create it
32+
# as root-owned, which would prevent the connect user from populating the
33+
# rest of the chroot structure at runtime.
34+
RUN mkdir -p /tmp/chroot && chown 10001:10001 /tmp/chroot
35+
3036
USER connect
3137

3238
EXPOSE 4195

0 commit comments

Comments
 (0)