Skip to content

Commit cba5c6a

Browse files
committed
adding sudo escalation workflow for podman including reset of cache to improve security posture
1 parent 30e0134 commit cba5c6a

2 files changed

Lines changed: 95 additions & 15 deletions

File tree

cmd/neoctl/instance.go

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"github.com/spf13/cobra"
1010
"netapp/neoctl/internal/config"
1111
"netapp/neoctl/internal/runner"
12+
"netapp/neoctl/internal/runtime"
1213
"netapp/neoctl/internal/utils"
14+
"os/exec"
1315
"path/filepath"
1416
"text/tabwriter"
1517
"time"
@@ -207,9 +209,18 @@ func runInstanceAction(name string, action string) {
207209
fmt.Printf("Creating instance \"%s\" ...\n", name)
208210

209211
// Verify prerequisites first
210-
runStep("Verifying prerequisites", func() error {
212+
if err := runStep("Verifying prerequisites", func() error {
211213
return runVerifyChecks(false)
212-
})
214+
}); err != nil {
215+
resetPrivileges()
216+
os.Exit(1)
217+
}
218+
219+
// Ensure privileges if needed (Podman)
220+
if err := ensurePrivileges(); err != nil {
221+
fmt.Printf("Error ensuring privileges: %v\n", err)
222+
os.Exit(1)
223+
}
213224

214225
// Load Bundle Config for images
215226
bundleCfg, err := config.LoadConfig()
@@ -227,9 +238,12 @@ func runInstanceAction(name string, action string) {
227238
// Configuration missing - print X and fetch
228239
fmt.Printf("\r \u001b[31m✗\u001b[0m %s \n", checkDesc)
229240

230-
runStep("Fetching bundle configuration", func() error {
241+
if err := runStep("Fetching bundle configuration", func() error {
231242
return fetchLatestBundles(false)
232-
})
243+
}); err != nil {
244+
resetPrivileges()
245+
os.Exit(1)
246+
}
233247

234248
// Reload config after fetching
235249
bundleCfg, err = config.LoadConfig()
@@ -244,15 +258,21 @@ func runInstanceAction(name string, action string) {
244258

245259

246260

247-
runStep("Writing configuration", func() error {
261+
if err := runStep("Writing configuration", func() error {
248262
return runner.GenerateComposeFile(inst, bundleCfg, configDir)
249-
})
263+
}); err != nil {
264+
resetPrivileges()
265+
os.Exit(1)
266+
}
250267

251-
runStep(fmt.Sprintf("Ensuring images (%s, %s)", inst.Name+"-neo", inst.Name+"-neoui"), func() error {
268+
if err := runStep(fmt.Sprintf("Ensuring images (%s, %s)", inst.Name+"-neo", inst.Name+"-neoui"), func() error {
252269
return runner.RunComposePull(configDir, name)
253-
})
270+
}); err != nil {
271+
resetPrivileges()
272+
os.Exit(1)
273+
}
254274

255-
runStep("Starting instance", func() error {
275+
if err := runStep("Starting instance", func() error {
256276
if err := runner.RunComposeUp(configDir, name); err != nil {
257277
return err
258278
}
@@ -271,7 +291,12 @@ func runInstanceAction(name string, action string) {
271291
fmt.Printf("\n\u001b[33mWarning: Instance is running but degraded (%d/%d containers up)\u001b[0m\n", runningCount, totalCount)
272292
}
273293
return nil
274-
})
294+
}); err != nil {
295+
resetPrivileges()
296+
os.Exit(1)
297+
}
298+
299+
resetPrivileges()
275300

276301
fmt.Printf("Instance \"%s\" started! 🚀\n", name)
277302

@@ -293,15 +318,33 @@ func runInstanceAction(name string, action string) {
293318
case "stop":
294319
fmt.Printf("Stopping instance \"%s\" ...\n", name)
295320

296-
runStep("Stopping all containers", func() error {
321+
// Ensure privileges if needed (Podman)
322+
if err := ensurePrivileges(); err != nil {
323+
fmt.Printf("Error ensuring privileges: %v\n", err)
324+
os.Exit(1)
325+
}
326+
327+
if err := runStep("Stopping all containers", func() error {
297328
return runner.RunComposeDown(configDir, name)
298-
})
329+
}); err != nil {
330+
resetPrivileges()
331+
os.Exit(1)
332+
}
333+
334+
resetPrivileges()
299335

300336
fmt.Printf("Instance \"%s\" stopped! 🛑\n", name)
301337

302338
case "delete":
303339
// Stop first
304340
fmt.Printf("Stopping instance '%s' before deletion...\n", name)
341+
342+
// Ensure privileges if needed (Podman)
343+
if err := ensurePrivileges(); err != nil {
344+
fmt.Printf("Error ensuring privileges: %v\n", err)
345+
os.Exit(1)
346+
}
347+
305348
if err := runner.RunComposeDown(configDir, name); err != nil {
306349
fmt.Printf("Warning: Failed to stop instance (might be already stopped or files missing): %v\n", err)
307350
}
@@ -463,12 +506,46 @@ func init() {
463506
instanceLogsCmd.Flags().BoolVar(&instAll, "all", false, "Fetch logs for all instances")
464507
}
465508

466-
func runStep(description string, action func() error) {
509+
func runStep(description string, action func() error) error {
467510
fmt.Printf(" \u001b[36m•\u001b[0m %s ...", description)
468511
if err := action(); err != nil {
469512
fmt.Printf("\r \u001b[31m✗\u001b[0m %s \n", description)
470513
fmt.Printf("Error: %v\n", err)
471-
os.Exit(1)
514+
return err
472515
}
473516
fmt.Printf("\r \u001b[32m✓\u001b[0m %s \n", description)
517+
return nil
518+
}
519+
520+
func ensurePrivileges() error {
521+
rt, err := runtime.CheckRuntime()
522+
if err != nil {
523+
return err
524+
}
525+
526+
if rt == runtime.Podman {
527+
fmt.Printf(" ⚠️ Podman runtime requires privilege escalation ...\n")
528+
// sudo -v refreshes the timestamp. -p prompts if needed.
529+
// We use the same prompt as in the runner to be consistent, though theoretically
530+
// if this succeeds, the runner one won't prompt.
531+
cmd := exec.Command("sudo", "-v", "-p", " [sudo] password for %u: ")
532+
cmd.Stdin = os.Stdin
533+
cmd.Stdout = os.Stdout
534+
cmd.Stderr = os.Stderr
535+
536+
if err := cmd.Run(); err != nil {
537+
return fmt.Errorf("sudo authentication failed: %w", err)
538+
}
539+
}
540+
return nil
541+
}
542+
543+
func resetPrivileges() {
544+
rt, err := runtime.CheckRuntime()
545+
if err == nil && rt == runtime.Podman {
546+
cmd := exec.Command("sudo", "-k")
547+
if err := cmd.Run(); err == nil {
548+
fmt.Println(" \u001b[32m✓\u001b[0m Reset privilege escalation session for security purposes")
549+
}
550+
}
474551
}

internal/runner/compose.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,10 @@ func runComposeCommand(dir string, args ...string) ([]byte, error) {
311311
var cmd *exec.Cmd
312312
if rt == runtime.Podman {
313313
// Podman requires root for SMB mounts in containers basically
314-
newArgs := append([]string{bin}, args...)
314+
// sudo -p prompt (retained for safety if pre-auth expires, but main auth happens earlier)
315+
prompt := " [sudo] password for %u: "
316+
317+
newArgs := append([]string{"-p", prompt, bin}, args...)
315318
cmd = exec.Command("sudo", newArgs...)
316319
} else {
317320
cmd = exec.Command(bin, args...)

0 commit comments

Comments
 (0)