Merge pull request 'Make the login shell's execute optional, so a machine may withhold it (hq ADR 0268)' (#169) from withhold-login-shell-execute into main
This commit was merged in pull request #169.
This commit is contained in:
@@ -92,3 +92,26 @@ func TestHoldingNeedsAnAnswer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The control-node withholds the login shell's `execute` (novox/hq ADR 0268): its holder there serves
|
||||||
|
// nothing on the seat, and must not be judged silent for it, as the store's rows read it back.
|
||||||
|
func TestALoginShellWithholdingExecuteIsNotSilent(t *testing.T) {
|
||||||
|
defer catalogue.UseSeats(catalogue.DefaultSeats())
|
||||||
|
var rows []catalogue.Seat
|
||||||
|
for _, s := range catalogue.DefaultSeats() {
|
||||||
|
stored := s
|
||||||
|
stored.Serves = nil
|
||||||
|
for _, v := range s.Serves {
|
||||||
|
v.Optional = false // the store never keeps the mark
|
||||||
|
stored.Serves = append(stored.Serves, v)
|
||||||
|
}
|
||||||
|
rows = append(rows, stored)
|
||||||
|
}
|
||||||
|
catalogue.UseSeats(rows)
|
||||||
|
recorded := []catalogue.Held{{Claim: catalogue.LoginShellSeat, Scope: catalogue.ScopeNode, Node: "anchor", Module: "zsh"}}
|
||||||
|
expected := holdersToHear(catalogue.SeatsWithAProtocol(), recorded, nil, map[string]bool{"anchor": true}, nil, time.Now())
|
||||||
|
if _, asked := expected[catalogue.LoginShellSeat]; asked {
|
||||||
|
t.Fatalf("the login shell's holder is expected to answer, so withholding execute would be said silent: %v",
|
||||||
|
expected[catalogue.LoginShellSeat])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package catalogue
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// The login shell's `execute` runs any command as the operator account, which can become root without a
|
||||||
|
// person, so the operator withheld it on the control-node until a call to it needs a person's approval
|
||||||
|
// (novox/hq ADR 0268). It is withheld per machine by the holder's own setting, so the seat must let a
|
||||||
|
// holder hold it without serving the verb there: `execute` is optional (ADR 0246's mark), and stays so in
|
||||||
|
// a seat row read back from the store, which never keeps the mark.
|
||||||
|
func TestTheLoginShellsExecuteIsOptionalSoAMachineMayWithholdIt(t *testing.T) {
|
||||||
|
check := func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
seat, ok := SeatNamed(LoginShellSeat)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("node-login-shell is not defined")
|
||||||
|
}
|
||||||
|
if len(seat.Serves) != 1 || seat.Serves[0].Name != "execute" {
|
||||||
|
t.Fatalf("the login shell promises %+v, not execute alone", seat.Serves)
|
||||||
|
}
|
||||||
|
if !seat.Serves[0].Optional {
|
||||||
|
t.Fatal("execute is required, so a holder that withholds it on one machine could not hold the seat there")
|
||||||
|
}
|
||||||
|
withholding := Manifest{Module: "zsh", Version: "1", Claims: []Claim{{Name: LoginShellSeat, Scope: ScopeNode}}}
|
||||||
|
if err := CanHold(withholding, seat); err != nil {
|
||||||
|
t.Fatalf("a holder serving no execute is refused: %v", err)
|
||||||
|
}
|
||||||
|
serving := withholding
|
||||||
|
serving.Claims = []Claim{{Name: LoginShellSeat, Scope: ScopeNode, Serves: []string{"execute"}}}
|
||||||
|
if err := CanHold(serving, seat); err != nil {
|
||||||
|
t.Fatalf("a holder serving execute is refused: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("compiled", check)
|
||||||
|
t.Run("read back from the store", func(t *testing.T) {
|
||||||
|
before := seats
|
||||||
|
t.Cleanup(func() { seats = before })
|
||||||
|
UseSeats([]Seat{{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
||||||
|
Serves: []Verb{{Name: "execute"}}}})
|
||||||
|
check(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -256,8 +256,9 @@ var defaultSeats = append([]Seat{
|
|||||||
{Name: EnvironmentSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0203"},
|
{Name: EnvironmentSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0203"},
|
||||||
// The login shell (novox/hq ADR 0204, replacing the module-declared `login-shell` of ADR 0176):
|
// The login shell (novox/hq ADR 0204, replacing the module-declared `login-shell` of ADR 0176):
|
||||||
// the mesh's, so a second shell module claims the seat rather than declaring a second one, and
|
// the mesh's, so a second shell module claims the seat rather than declaring a second one, and
|
||||||
// the seat exists whether or not zsh's definition is registered. `execute` is the contract any
|
// the seat exists whether or not zsh's definition is registered. `execute` is the contract a caller
|
||||||
// node may call; the holder places every module's shell code in its slots.
|
// may call where the machine serves it (optional: ADR 0268); the holder places every module's shell
|
||||||
|
// code in its slots.
|
||||||
{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
||||||
Serves: loginShellVerbs()},
|
Serves: loginShellVerbs()},
|
||||||
// A machine's power (novox/hq ADR 0211): its holder owns logind's power handling, places the
|
// A machine's power (novox/hq ADR 0211): its holder owns logind's power handling, places the
|
||||||
@@ -721,9 +722,6 @@ func uplinkVerbs() []Verb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loginShellVerbs is the contract every holder of node-login-shell serves (novox/hq ADR 0176, ADR
|
|
||||||
// 0204): one command, run the way the operator's own terminal would run it, bounded below the
|
|
||||||
// runtime's thirty-second call limit so a hung command answers rather than times the caller out.
|
|
||||||
// backupVerbs is the node-backup seat's protocol (novox/hq to-be 43): what is kept, take one now,
|
// backupVerbs is the node-backup seat's protocol (novox/hq to-be 43): what is kept, take one now,
|
||||||
// and restore beside the live data — never over it.
|
// and restore beside the live data — never over it.
|
||||||
func backupVerbs() []Verb {
|
func backupVerbs() []Verb {
|
||||||
@@ -745,9 +743,18 @@ func backupVerbs() []Verb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loginShellVerbs is the contract of node-login-shell (novox/hq ADR 0176, ADR 0204): one command, run
|
||||||
|
// the way the operator's own terminal would run it, bounded below the runtime's thirty-second call limit
|
||||||
|
// so a hung command answers rather than times the caller out.
|
||||||
|
//
|
||||||
|
// **`execute` is optional** (novox/hq ADR 0268). It runs any command as the operator account, which can
|
||||||
|
// become root without a person, so a machine may withhold it: the control-node does, through the
|
||||||
|
// holder's own `execute` setting, until a call to it needs a person's approval (hq research 039). The mark
|
||||||
|
// is ADR 0246's: a holder that does not serve the verb on a machine still holds the seat there, and its
|
||||||
|
// silence on the bus is not judged — a holder withholding it serves nothing on the seat.
|
||||||
func loginShellVerbs() []Verb {
|
func loginShellVerbs() []Verb {
|
||||||
return []Verb{
|
return []Verb{
|
||||||
{Name: "execute", Description: "Run one command on this machine as the operator account, in a " +
|
{Name: "execute", Optional: true, Description: "Run one command on this machine as the operator account, in a " +
|
||||||
"non-interactive login shell in its home; answers with what it printed and how it exited.",
|
"non-interactive login shell in its home; answers with what it printed and how it exited.",
|
||||||
Input: schema(map[string]string{
|
Input: schema(map[string]string{
|
||||||
"command": "the command line, as you would type it",
|
"command": "the command line, as you would type it",
|
||||||
|
|||||||
Reference in New Issue
Block a user