diff --git a/frontend/pages/hosts/components/form.tsx b/frontend/pages/hosts/components/form.tsx
index aca6272..61dba79 100644
--- a/frontend/pages/hosts/components/form.tsx
+++ b/frontend/pages/hosts/components/form.tsx
@@ -172,9 +172,18 @@ const IncusFormFields = ({ form }: MiscFormFieldProps) => {
/>
{type === "lxc" && (
-
-
-
+ <>
+
+
+
+
+
+
+ >
)}
>
);
diff --git a/frontend/pages/hosts/schema/form.ts b/frontend/pages/hosts/schema/form.ts
index 18bbc4b..f677242 100644
--- a/frontend/pages/hosts/schema/form.ts
+++ b/frontend/pages/hosts/schema/form.ts
@@ -44,6 +44,7 @@ const incusSchema = hostSchema.merge(
metadata: z.object({
type: z.enum(["lxc", "qemu"]),
instance: z.string().min(1, { message: "Instance name is required" }),
+ user: z.coerce.number().nullish(),
shell: z.string().nullish(),
}),
})
diff --git a/server/app/ws/term_incus.go b/server/app/ws/term_incus.go
index 20b8871..ad7cd90 100644
--- a/server/app/ws/term_incus.go
+++ b/server/app/ws/term_incus.go
@@ -15,6 +15,7 @@ import (
type IncusWebsocketSession struct {
Type string `json:"type"` // "qemu" | "lxc"
Instance string `json:"instance"`
+ User *int `json:"user"`
Shell string `json:"shell"`
}
@@ -23,7 +24,10 @@ func (i *IncusWebsocketSession) NewTerminal(c *websocket.Conn, incus *lib.IncusS
i.Shell = "/bin/sh"
}
- exec, err := incus.InstanceExec(i.Instance, []string{i.Shell}, true)
+ exec, err := incus.InstanceExec(i.Instance, []string{i.Shell}, &lib.IncusInstanceExecOptions{
+ Interactive: true,
+ User: i.User,
+ })
if err != nil {
return err
}
diff --git a/server/lib/incus.go b/server/lib/incus.go
index d75cfc9..958edfb 100644
--- a/server/lib/incus.go
+++ b/server/lib/incus.go
@@ -67,6 +67,11 @@ func (i *IncusServer) Fetch(method string, url string, cfg *IncusFetchConfig) ([
return io.ReadAll(resp.Body)
}
+type IncusInstanceExecOptions struct {
+ Interactive bool
+ User *int
+}
+
type IncusInstanceExecRes struct {
ID string
Operation string
@@ -74,17 +79,23 @@ type IncusInstanceExecRes struct {
Secret string
}
-func (i *IncusServer) InstanceExec(instance string, command []string, interactive bool) (*IncusInstanceExecRes, error) {
+func (i *IncusServer) InstanceExec(instance string, command []string, options *IncusInstanceExecOptions) (*IncusInstanceExecRes, error) {
url := fmt.Sprintf("/1.0/instances/%s/exec?project=default", instance)
+ var user *int
+ if options != nil && options.User != nil {
+ user = options.User
+ }
+
body, err := i.Fetch("POST", url, &IncusFetchConfig{
Body: map[string]interface{}{
"command": command,
- "interactive": interactive,
+ "interactive": options != nil && options.Interactive,
"wait-for-websocket": true,
"environment": map[string]string{
"TERM": "xterm-256color",
},
+ "user": user,
},
})
if err != nil {