mounts: read a password only from the directory the configuration names, and say a domain goes in the username
mesh/merge-gate fail: builds mounts → shanks; no bus step; a manifest the change touches fails the module check: modules/mounts/module.json: this manifest …
mesh/repo-check fail: its merge-check.sh failed: files asking for a setting without saying whether it is trusted: 24
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate fail: builds mounts → shanks; no bus step; a manifest the change touches fails the module check: modules/mounts/module.json: this manifest …
mesh/repo-check fail: its merge-check.sh failed: files asking for a setting without saying whether it is trusted: 24
mesh/delivery superseded: a newer head of the same pull request
A configuration without the passwords directory would have read a path relative to the process's working directory; it is now refused as a fault. Review of mesh-catalog#190 (issue 386, ADR 0283).
This commit is contained in:
@@ -69,7 +69,8 @@ All three are preferences whose default, `none`, names nothing (ADR 0262 allows
|
|||||||
For example `games=smb://192.0.2.76/Share@/mnt/games`.
|
For example `games=smb://192.0.2.76/Share@/mnt/games`.
|
||||||
- `smb-users`: the username each SMB source logs in with, space-separated `name=username`, for example
|
- `smb-users`: the username each SMB source logs in with, space-separated `name=username`, for example
|
||||||
`games=root`. A username is not a secret, so it is a setting (novox/hq ADR 0283). It holds no space,
|
`games=root`. A username is not a secret, so it is a setting (novox/hq ADR 0283). It holds no space,
|
||||||
comma or `=`.
|
comma or `=`. A device that needs a domain takes it in the username, as `DOMAIN\user` or `user@domain`:
|
||||||
|
the credentials file has no `domain=` line.
|
||||||
|
|
||||||
A mount point is an absolute, plainly written path, not a directory of the system's own (`/etc`, `/usr`,
|
A mount point is an absolute, plainly written path, not a directory of the system's own (`/etc`, `/usr`,
|
||||||
`/run`, …). An entry that cannot be read is said, and only that entry is left out.
|
`/run`, …). An entry that cannot be read is said, and only that entry is left out.
|
||||||
|
|||||||
@@ -714,10 +714,15 @@ func (a *Applier) login(cfg Config, source string) (SMBCredential, []health.Wait
|
|||||||
if !hasUser {
|
if !hasUser {
|
||||||
waits = append(waits, health.Wait{Part: part, Setting: "smb-users", What: "the username of the source " + source})
|
waits = append(waits, health.Wait{Part: part, Setting: "smb-users", What: "the username of the source " + source})
|
||||||
}
|
}
|
||||||
|
// Never a relative path: without the directory the mesh places the passwords in, nothing is read.
|
||||||
|
if !filepath.IsAbs(cfg.Passwords) {
|
||||||
|
return SMBCredential{}, nil, "the module's configuration names no directory for its passwords, so " +
|
||||||
|
PasswordSecret(source) + " cannot be read"
|
||||||
|
}
|
||||||
var password string
|
var password string
|
||||||
raw, err := a.M.read(PasswordFile(cfg.Passwords, source))
|
raw, err := a.M.read(PasswordFile(cfg.Passwords, source))
|
||||||
switch {
|
switch {
|
||||||
case cfg.Passwords == "" || errors.Is(err, fs.ErrNotExist):
|
case errors.Is(err, fs.ErrNotExist):
|
||||||
waits = append(waits, health.Wait{Part: part, Secret: PasswordSecret(source), What: "the password of the source " + source})
|
waits = append(waits, health.Wait{Part: part, Secret: PasswordSecret(source), What: "the password of the source " + source})
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return SMBCredential{}, nil, "its password " + PasswordSecret(source) + " could not be read: " + bare(err)
|
return SMBCredential{}, nil, "its password " + PasswordSecret(source) + " could not be read: " + bare(err)
|
||||||
|
|||||||
@@ -681,3 +681,36 @@ func TestAWaitBesideAFaultIsUnhealthy(t *testing.T) {
|
|||||||
t.Errorf("a wait alone: %+v", h)
|
t.Errorf("a wait alone: %+v", h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A password that exists and cannot be read, and a configuration naming no directory for the passwords, are
|
||||||
|
// refused as faults: said without the value and without the path, and nothing is read from a relative path.
|
||||||
|
func TestAPasswordThatCannotBeReadIsAFaultSaidWithoutValueOrPath(t *testing.T) {
|
||||||
|
// A directory where the file should be: read fails, and not as "not there" (as root a mode would not stop it).
|
||||||
|
f := newSystem(t)
|
||||||
|
f.write(ConfigFile, "sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||||
|
f.write("/var/lib/mesh/mounts/smb-password-games.secret/inside", testPassword)
|
||||||
|
st := f.applier(time.Now()).Look(context.Background())
|
||||||
|
if len(st.Shares) != 1 || st.Shares[0].State != StateFailed || !strings.Contains(st.Shares[0].Why, "could not be read") ||
|
||||||
|
strings.Contains(st.Shares[0].Why, "/var/lib") || strings.Contains(st.Shares[0].Why, testPassword) {
|
||||||
|
t.Fatalf("an unreadable password: %+v", st.Shares)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No passwords= and no state=: the source is not set up, and no relative path is read.
|
||||||
|
for _, cfg := range []string{
|
||||||
|
"sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\n",
|
||||||
|
"sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\npasswords=relative/dir\n",
|
||||||
|
} {
|
||||||
|
g := newSystem(t)
|
||||||
|
g.write(ConfigFile, cfg)
|
||||||
|
g.write("smb-password-games.secret", testPassword)
|
||||||
|
g.write("relative/dir/smb-password-games.secret", testPassword)
|
||||||
|
st := g.applier(time.Now()).Look(context.Background())
|
||||||
|
if len(st.Shares) != 1 || st.Shares[0].State != StateFailed || !strings.Contains(st.Shares[0].Why, "names no directory for its passwords") ||
|
||||||
|
strings.Contains(st.Shares[0].Why, testPassword) || strings.Contains(st.Shares[0].Why, "relative") {
|
||||||
|
t.Errorf("%q: %+v", cfg, st.Shares)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(g.root, "/var/lib/mesh/mounts/smb-games.credentials")); !os.IsNotExist(err) {
|
||||||
|
t.Errorf("%q: a credentials file was written", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user