Say the login session a mesh-cli line comes from, and pin the SDK at go/v0.1.9 (hq ADR 0272)
A service running as the operator — the tool runner, a unit of the account's user manager — shares the operator's user id, and was as much the terminal on the control-node as a person typing. The engine now reads the asking process's cgroup and sends its login session's scope (session-<id>.scope under the account's own user-<uid>.slice), or nothing; the controller makes nothing else the terminal. The SDK is pinned at its tag now that go/v0.1.9 exists.
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -35,6 +36,11 @@ type Asked struct {
|
||||
UID uint32 `json:"uid"`
|
||||
// Follow is a call a line runs as, asked about again by the account that asked it (AskTheController).
|
||||
Follow string `json:"follow,omitempty"`
|
||||
// Session is the login session the asking process runs in — its systemd scope, `session-<id>.scope` under the
|
||||
// account's own slice — as the kernel's cgroup says it; empty for anything else: a system service, a user unit,
|
||||
// a process another account's session started. Only a login session is a person at a terminal (novox/hq ADR
|
||||
// 0272): the controller makes nothing else the terminal.
|
||||
Session string `json:"session,omitempty"`
|
||||
}
|
||||
|
||||
// Forward asks the controller and follows the line to what came of it (AskTheController): what it printed, that
|
||||
@@ -84,6 +90,35 @@ func (s *slots) give(uid uint32) {
|
||||
// test can say what the kernel would; it is the only source of the account.
|
||||
var peerOf = peerCredentials
|
||||
|
||||
// sessionOf reads the login session a process runs in, from the kernel. A variable so a test can say.
|
||||
var sessionOf = func(pid int32, uid uint32) string {
|
||||
raw, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return loginSessionIn(string(raw), uid)
|
||||
}
|
||||
|
||||
// loginSessionIn is the login session scope a process's cgroup names, for its own account: the unified hierarchy's
|
||||
// line, `0::/user.slice/user-<uid>.slice/session-<id>.scope`, and nothing below or beside it. Anything else is no
|
||||
// login session — a service under system.slice, a unit under the account's user manager (user@<uid>.service), a
|
||||
// scope of another account's.
|
||||
func loginSessionIn(cgroup string, uid uint32) string {
|
||||
for _, line := range strings.Split(cgroup, "\n") {
|
||||
path, ok := strings.CutPrefix(line, "0::")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
prefix := fmt.Sprintf("/user.slice/user-%d.slice/", uid)
|
||||
scope, ok := strings.CutPrefix(path, prefix)
|
||||
if !ok || strings.Contains(scope, "/") || !strings.HasPrefix(scope, "session-") || !strings.HasSuffix(scope, ".scope") {
|
||||
return ""
|
||||
}
|
||||
return scope
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sameNamespaces refuses a peer whose mount or user namespace is not the engine's. A variable so a test can say.
|
||||
var sameNamespaces = namespacesOf
|
||||
|
||||
@@ -204,5 +239,5 @@ func answer(ctx context.Context, conn *net.UnixConn, held *slots, forward Forwar
|
||||
_ = conn.SetDeadline(time.Now().Add(cli.AskWithin))
|
||||
asking, cancel := context.WithTimeout(ctx, cli.AskWithin-30*time.Second)
|
||||
defer cancel()
|
||||
reply(forward(asking, Asked{Line: req.Line, Account: account, UID: uid}))
|
||||
reply(forward(asking, Asked{Line: req.Line, Account: account, UID: uid, Session: sessionOf(pid, uid)}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user