install-service: grant venv base interpreter (pyvenv.cfg home) outside COMFY_DIR
This commit is contained in:
@@ -64,11 +64,16 @@ func Run(run func(ctx context.Context) error) error {
|
||||
// directives apply. The proxy needs nothing but outbound TCP/UDP and the
|
||||
// notify socket, so it loses nothing. A managed ComfyUI (comfyDir) gets a
|
||||
// BindPaths hole through ProtectHome/ProtectSystem: it reads its venv and
|
||||
// writes output/temp/user data under COMFY_DIR.
|
||||
// writes output/temp/user data under COMFY_DIR. A venv whose base
|
||||
// interpreter (pyvenv.cfg home) lives outside COMFY_DIR gets an additional
|
||||
// read-only bind.
|
||||
func renderUnit(exePath, configPath, comfyDir string) string {
|
||||
bind := ""
|
||||
if comfyDir != "" {
|
||||
bind = "BindPaths=" + comfyDir + "\n"
|
||||
if home := comfyVenvHome(comfyDir); home != "" {
|
||||
bind += "BindReadOnlyPaths=" + home + "\n"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=gpu-turnstile GPU arbitration proxy for Ollama and ComfyUI
|
||||
|
||||
@@ -375,6 +375,13 @@ func grantAll(exe, configPath string) error {
|
||||
if err := grantAccessTree(comfyDir, "(OI)(CI)(M)"); err != nil {
|
||||
return err
|
||||
}
|
||||
// uv venvs (Comfy-Desktop) redirect to a base interpreter that
|
||||
// can live outside COMFY_DIR; read+execute suffices for it.
|
||||
if home := comfyVenvHome(comfyDir); home != "" {
|
||||
if err := grantAccessTree(home, "(OI)(CI)(RX)"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -496,7 +503,7 @@ func grantAccessTree(path, perms string) error {
|
||||
func runIcacls(path, perms string, recursive bool) error {
|
||||
args := []string{path, "/grant", virtualAccount + ":" + perms}
|
||||
if recursive {
|
||||
fmt.Printf("granting %s modify access to %s (large trees can take minutes)\n", virtualAccount, path)
|
||||
fmt.Printf("granting %s %s access to %s (large trees can take minutes)\n", virtualAccount, perms, path)
|
||||
args = append(args, "/T")
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// comfyVenvHome returns the base interpreter directory of the Python venv at
|
||||
// <comfyDir>/.venv when that directory lives outside comfyDir, "" otherwise.
|
||||
// uv-created venvs (Comfy-Desktop) ship a redirector python.exe whose real
|
||||
// interpreter is the pyvenv.cfg "home" tree — typically a sibling of
|
||||
// COMFY_DIR, which a sandbox/ACL covering COMFY_DIR alone does not reach.
|
||||
func comfyVenvHome(comfyDir string) string {
|
||||
data, err := os.ReadFile(filepath.Join(comfyDir, ".venv", "pyvenv.cfg"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
k, v, ok := strings.Cut(line, "=")
|
||||
if !ok || strings.TrimSpace(k) != "home" {
|
||||
continue
|
||||
}
|
||||
home := strings.TrimSpace(v)
|
||||
if home == "" {
|
||||
return ""
|
||||
}
|
||||
if st, err := os.Stat(home); err != nil || !st.IsDir() {
|
||||
return ""
|
||||
}
|
||||
rel, err := filepath.Rel(comfyDir, home)
|
||||
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
return home
|
||||
}
|
||||
return "" // inside comfyDir: already covered by the COMFY_DIR grant
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComfyVenvHome(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
comfy := filepath.Join(root, "ComfyUI")
|
||||
outside := filepath.Join(root, "standalone-env")
|
||||
inside := filepath.Join(comfy, "runtime")
|
||||
for _, d := range []string{filepath.Join(comfy, ".venv"), outside, inside} {
|
||||
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
cfg := filepath.Join(comfy, ".venv", "pyvenv.cfg")
|
||||
|
||||
write := func(home string) {
|
||||
if err := os.WriteFile(cfg, []byte("home = "+home+"\nversion_info = 3.13.0\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
write(outside)
|
||||
if got := comfyVenvHome(comfy); got != outside {
|
||||
t.Fatalf("outside home: got %q, want %q", got, outside)
|
||||
}
|
||||
|
||||
write(inside)
|
||||
if got := comfyVenvHome(comfy); got != "" {
|
||||
t.Fatalf("inside home: got %q, want empty", got)
|
||||
}
|
||||
|
||||
write(filepath.Join(root, "does-not-exist"))
|
||||
if got := comfyVenvHome(comfy); got != "" {
|
||||
t.Fatalf("missing home: got %q, want empty", got)
|
||||
}
|
||||
|
||||
if got := comfyVenvHome(filepath.Join(root, "no-venv")); got != "" {
|
||||
t.Fatalf("no pyvenv.cfg: got %q, want empty", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user