Skip to content

Commit 7da90d8

Browse files
krockpotJeremy Talis
andauthored
client: allow daemon to run as non-root with init-created dirs (#121)
- chmodIfNeeded: only chmod when current perms differ from desired, avoiding 'operation not permitted' when init container already set correct permissions - ensureDirExists: succeed when path already exists (e.g. created by init) so MkdirAll permission failure does not crash the sidecar - Add table-driven tests for both helpers Made-with: Cursor Co-authored-by: Jeremy Talis <jtalis@pinterest.com>
1 parent df6b676 commit 7da90d8

File tree

2 files changed

+121
-18
lines changed

2 files changed

+121
-18
lines changed

client/daemon.go

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -122,40 +122,60 @@ func (d *daemon) loop(refresh time.Duration) {
122122
}
123123
}
124124

125-
func (d *daemon) initialize() error {
126-
err := os.MkdirAll(d.dir, defaultDirPermission)
125+
// chmodIfNeeded sets mode on path only if it differs from the current mode.
126+
// This avoids "operation not permitted" when running as non-root when permissions are already correct.
127+
func chmodIfNeeded(path string, want os.FileMode) error {
128+
info, err := os.Stat(path)
127129
if err != nil {
130+
return err
131+
}
132+
if info.Mode().Perm() == want.Perm() {
133+
return nil
134+
}
135+
return os.Chmod(path, want)
136+
}
137+
138+
// ensureDirExists creates path as a directory with perm, or returns nil if it already exists.
139+
// When running as non-root (e.g. in Kubernetes), the directory may have been created by an
140+
// init container; if MkdirAll fails with permission denied, we still succeed when the path exists.
141+
func ensureDirExists(path string, perm os.FileMode) error {
142+
err := os.MkdirAll(path, perm)
143+
if err == nil {
144+
return nil
145+
}
146+
info, statErr := os.Stat(path)
147+
if statErr == nil && info.IsDir() {
148+
return nil
149+
}
150+
return err
151+
}
152+
153+
func (d *daemon) initialize() error {
154+
if err := ensureDirExists(d.dir, defaultDirPermission); err != nil {
128155
return fmt.Errorf("failed to initialize /var/lib/knox (run 'sudo mkdir /var/lib/knox'?): %w", err)
129156
}
130157

131-
// Need to chmod due to a umask set on masterless puppet machines
132-
err = os.Chmod(d.dir, defaultDirPermission)
133-
if err != nil {
158+
// Need to chmod due to a umask set on masterless puppet machines (skip if already correct for non-root)
159+
if err := chmodIfNeeded(d.dir, defaultDirPermission); err != nil {
134160
return fmt.Errorf("failed to open up directory permissions: %w", err)
135161
}
136-
err = os.MkdirAll(d.keyDir(), defaultDirPermission)
137-
if err != nil {
162+
if err := ensureDirExists(d.keyDir(), defaultDirPermission); err != nil {
138163
return fmt.Errorf("failed to make key folders: %w", err)
139164
}
140165

141-
// Need to chmod due to a umask set on masterless puppet machines
142-
err = os.Chmod(d.keyDir(), defaultDirPermission)
143-
if err != nil {
166+
if err := chmodIfNeeded(d.keyDir(), defaultDirPermission); err != nil {
144167
return fmt.Errorf("failed to open up directory permissions: %w", err)
145168
}
146-
_, err = os.Stat(d.registerFilename())
169+
_, err := os.Stat(d.registerFilename())
147170
if os.IsNotExist(err) {
148-
err := os.WriteFile(d.registerFilename(), []byte{}, defaultFilePermission)
149-
if err != nil {
171+
if err := os.WriteFile(d.registerFilename(), []byte{}, defaultFilePermission); err != nil {
150172
return fmt.Errorf("failed to initialize registered key file: %w", err)
151173
}
152174
} else if err != nil {
153175
return err
154176
}
155177

156-
// Need to chmod due to a umask set on masterless puppet machines
157-
err = os.Chmod(d.registerFilename(), defaultFilePermission)
158-
if err != nil {
178+
if err := chmodIfNeeded(d.registerFilename(), defaultFilePermission); err != nil {
159179
return fmt.Errorf("failed to open up register file permissions: %w", err)
160180
}
161181
d.registerKeyFile = NewKeysFile(d.registerFilename())
@@ -319,8 +339,7 @@ func (d daemon) processKey(keyID string) error {
319339
return fmt.Errorf("error renaming key %s temporary file: %w", keyID, err)
320340
}
321341

322-
err = os.Chmod(d.keyFilename(keyID), defaultFilePermission)
323-
if err != nil {
342+
if err := chmodIfNeeded(d.keyFilename(keyID), defaultFilePermission); err != nil {
324343
return fmt.Errorf("failed to open up key file permissions: %w", err)
325344
}
326345
return nil

client/daemon_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,90 @@ func TearDownTest(dir string) {
105105
os.RemoveAll(dir)
106106
}
107107

108+
func TestChmodIfNeeded(t *testing.T) {
109+
dir, err := os.MkdirTemp("", "knox-chmod-test")
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
defer os.RemoveAll(dir)
114+
115+
tests := []struct {
116+
name string
117+
setup func() string // returns path to chmod
118+
wantPerm os.FileMode
119+
}{
120+
{"skips when dir perms match", func() string {
121+
os.Chmod(dir, 0777)
122+
return dir
123+
}, 0777},
124+
{"skips when file perms match", func() string {
125+
f := path.Join(dir, "f")
126+
os.WriteFile(f, []byte{}, 0666)
127+
return f
128+
}, 0666},
129+
{"chmods when perms differ", func() string {
130+
f := path.Join(dir, "g")
131+
os.WriteFile(f, []byte{}, 0600)
132+
return f
133+
}, 0666},
134+
}
135+
for _, tt := range tests {
136+
t.Run(tt.name, func(t *testing.T) {
137+
path := tt.setup()
138+
want := tt.wantPerm
139+
if want == 0666 {
140+
err = chmodIfNeeded(path, defaultFilePermission)
141+
} else {
142+
err = chmodIfNeeded(path, defaultDirPermission)
143+
}
144+
if err != nil {
145+
t.Fatalf("chmodIfNeeded: %v", err)
146+
}
147+
info, _ := os.Stat(path)
148+
if got := info.Mode().Perm(); got != want {
149+
t.Errorf("perms: got %o, want %o", got, want)
150+
}
151+
})
152+
}
153+
}
154+
155+
func TestEnsureDirExists(t *testing.T) {
156+
dir, err := os.MkdirTemp("", "knox-ensure-test")
157+
if err != nil {
158+
t.Fatal(err)
159+
}
160+
defer os.RemoveAll(dir)
161+
162+
tests := []struct {
163+
name string
164+
setup func() string
165+
wantErr bool
166+
}{
167+
{"creates when missing", func() string { return path.Join(dir, "a", "b") }, false},
168+
{"succeeds when already exists", func() string {
169+
target := path.Join(dir, "existing")
170+
os.MkdirAll(target, 0700)
171+
return target
172+
}, false},
173+
}
174+
for _, tt := range tests {
175+
t.Run(tt.name, func(t *testing.T) {
176+
target := tt.setup()
177+
err := ensureDirExists(target, 0755)
178+
if (err != nil) != tt.wantErr {
179+
t.Fatalf("ensureDirExists: err=%v, wantErr=%v", err, tt.wantErr)
180+
}
181+
info, err := os.Stat(target)
182+
if err != nil {
183+
t.Fatalf("stat: %v", err)
184+
}
185+
if !info.IsDir() {
186+
t.Error("path is not a directory")
187+
}
188+
})
189+
}
190+
}
191+
108192
func TestProcessKey(t *testing.T) {
109193
params, dir, d := setUpTest(t)
110194
defer TearDownTest(dir)

0 commit comments

Comments
 (0)