diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-12-15 12:43:58 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-12-15 12:57:06 +0900 |
| commit | 54610aaddcc01e335c34087bc0c3bc1de6d23cf8 (patch) | |
| tree | 4e8ad54c21e4440ddd54a2be4089d7d4807f97ac /internal/outcome/shim.go | |
| parent | 2e806601693e0262641386ce5430d7e8d84b2a77 (diff) | |
internal/outcome: expose pipewire via pipewire-pulse
This no longer exposes the pipewire socket to the container, and instead mediates access via pipewire-pulse. This makes insecure parts of the protocol inaccessible as explained in the doc comment in hst.
Closes #29.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/outcome/shim.go')
| -rw-r--r-- | internal/outcome/shim.go | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/internal/outcome/shim.go b/internal/outcome/shim.go index 59b1236c..a569e7a6 100644 --- a/internal/outcome/shim.go +++ b/internal/outcome/shim.go @@ -15,9 +15,11 @@ import ( "hakurei.app/container" "hakurei.app/container/check" + "hakurei.app/container/fhs" "hakurei.app/container/seccomp" "hakurei.app/container/std" "hakurei.app/hst" + "hakurei.app/internal/pipewire" "hakurei.app/message" ) @@ -266,6 +268,79 @@ func shimEntrypoint(k syscallDispatcher) { // bounds and default enforced in finalise.go z.WaitDelay = state.Shim.WaitDelay + if stateParams.pipewirePulsePath != nil { + zpw := container.NewCommand(ctx, msg, stateParams.pipewirePulsePath, pipewirePulseName) + zpw.Hostname = "hakurei-" + pipewirePulseName + zpw.SeccompFlags |= seccomp.AllowMultiarch + zpw.SeccompPresets |= std.PresetStrict + zpw.Env = []string{ + // pipewire SecurityContext socket path + pipewire.Remote + "=" + stateParams.instancePath().Append("pipewire").String(), + // pipewire-pulse socket directory path + envXDGRuntimeDir + "=" + sp.String(), + } + if msg.IsVerbose() { + zpw.Stdin, zpw.Stdout, zpw.Stderr = os.Stdin, os.Stdout, os.Stderr + } + zpw. + Bind(fhs.AbsRoot, fhs.AbsRoot, 0). + Bind(sp.unwrap(), sp.unwrap(), std.BindWritable). + Proc(fhs.AbsProc).Dev(fhs.AbsDev, true) + socketPath := sp.unwrap().Append("pulse", "native") + innerSocketPath := stateParams.runtimeDir.Append("pulse", "native") + + if err := k.containerStart(zpw); err != nil { + sp.destroy() + printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) }, + "cannot start "+pipewirePulseName+" container:", err) + } + if err := k.containerServe(zpw); err != nil { + sp.destroy() + printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) }, + "cannot configure "+pipewirePulseName+" container:", err) + } + + done := make(chan error, 1) + k.new(func(k syscallDispatcher, msg message.Msg) { done <- k.containerWait(zpw) }) + + socketTimer := time.NewTimer(shimPipeWireTimeout) + for { + select { + case <-socketTimer.C: + sp.destroy() + k.fatal(pipewirePulseName + " exceeded deadline before socket appeared") + break + + case err := <-done: + var exitError *exec.ExitError + if !errors.As(err, &exitError) { + msg.Verbosef("cannot wait: %v", err) + k.exit(127) + } + sp.destroy() + k.fatal(pipewirePulseName + " " + exitError.ProcessState.String()) + break + + default: + if _, err := k.stat(socketPath.String()); err != nil { + if !errors.Is(err, os.ErrNotExist) { + sp.destroy() + k.fatal(err) + break + } + + time.Sleep(500 * time.Microsecond) + continue + } + } + + break + } + + z.Bind(socketPath, innerSocketPath, 0) + z.Env = append(z.Env, "PULSE_SERVER=unix:"+innerSocketPath.String()) + } + if err := k.containerStart(z); err != nil { var f func(v ...any) if logger := msg.GetLogger(); logger != nil { |
