[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: What are desired semantics for /etc/shells?



Hi!

On Thu, 2021-06-10 at 20:00:02 +0200, Helmut Grohne wrote:
> Due to working on installation bootstrap, I was looking into
> `/etc/shells`.
> 
> Introduction
> ============
> 
> `/etc/shells` contains valid login shells. Some programs match the configured
> shell of a user against this file to check whether a user is a normal user or a
> system users.

When I was looking into this specific case for
<https://wiki.debian.org/Teams/Dpkg/Spec/InstallBootstrap>, and looked
for packages accessing that file, I was surprised by the amount of uses
it had. There was also for example a new usage proposed in
<https://bugs.debian.org/620898> to help remove bash from the essential
set.

> For details on this file refer to `man 5 shells`. On Debian
> systems, it can be managed using the commands `add-shell` and `remove-shell`
> both of which are part of `debianutils`.

BTW I think the BSD man pages are helpful in clarifying its
historically intended use, f.ex:

  <https://www.freebsd.org/cgi/man.cgi?query=shells&sektion=5>

> Inconsistency
> =============
> 
> Some maintainer scripts take care to only run `add-shell` for initial
> configuration or for upgrading from an ancient version that didn't call
> `add-shell`. Others call `add-shell` for every invocation of `postinst`.

[…]

> In practice, all of the packages will add their shells on initial
> configuration. The second and third category will also add their shell on
> package upgrades. Arguably, doing so violates Debian policy section 10.7.3,
> which says that package upgrades must preserve local changes (such as removing
> a shell) on upgrades.

> Desired behaviour
> =================
> 
> This raises the question of what the desired semantics for `/etc/shells` are.
> Do we want the strict interpretation of the policy to be followed and update
> all those packages to conditionalize their `add-shell` invocations? Or is
> `/etc/shells` a simple collection of installed shells and administrators are
> not supposed to mess with it? The latter interpretation somewhat conflicts with
> our policy, so we might have to update it. If `/etc/shells` is not to be messed
> with, maybe it should not live inside `/etc`?

Given the historic BSD and the current usage and documentation on
non-BSD systems, I'd say it's supposed to be the list of valid login
shells on the system, where valid, to me, implies present and also
sanctioned by the sysadmin. So shell packages should add themselves
once on first install, and remove themselves on remove, and should
thus preserve whether the sysadmin disabled the entry.

This makes it possible for the shell to be installed to satisfy
dependencies, while the sysadmin disallowing its usage as a login
shell, as part of its operational policies or similar, for example.

> Declarative packaging
> =====================
> 
> Editing `/etc/shells` by running a command during a maintainer script is less
> than ideal when it comes to declarative packaging. The goal of declarative
> packaging is to make reasoning about packages easier by eliminating the need
> for maintainer scripts as much as possible. One solution for this case could be
> `dpkg-trigger`. All the shell-providing packages could drop a snippet into a
> particular directory and `debianutils` could concatenate them into
> `/etc/shells`. Doing so would delete quite a number of maintainer scripts and
> centralize the chance for introducing bugs and inconsistencies such as the ones
> above into one maintainer script.

As mentioned above, for the installation bootstrap stuff, the first
idea that came to mind was adding support for something like
/etc/shells.d/, but after looking for existing users, realized this
was impractical, as the /etc/shells interfaces is rather entrenched
and much code reads from it directly (either hardcoded or via say
_PATH_SHELLS) instead of going through an API like getusershell(3),
so it could not be abstracted within say glibc.

So I agree that generating it from fragments placed elsewhere (say
under /usr), would be better. As I think this should be kept as
sysadmin editable I don't quite like the symlink approach, which in
addition might cause unforeseen issue. But I think preserving the
content should not be difficult, what I think might work is:

 * We'd have:

   - (N) «/etc/shells»,
   - (O) «/var/lib/<something>/shells» (or wherever) which is the state,
   - (F) «/usr/share/<something>/shells.d/*» (or wherever) which are the
         fragment files the shell packages install.

 * dpkg trigger activates after a package modifies files in (F), runs
   update-shells (or similar), which does:

   1) if (N).new exists, forcibly removes it and (O).new,
   2) if (O).new exists, renames it into (O),
   3) parses (O), or if it does not exist assumed to be the same as (N),
   4) parses (N), preserving comment and blank lines, and for each shell
      line:
      - if it exists on the filesystem, add/keep in (O) and (N),
        (this covers the local sysadmin addition and fragment default
        cases),
      - otherwise drop it from (O) and (N),
        (this covers the local sysadmin and package removal cases),
   5) parses (F), and for each shell entry not processed in 4):
      - if it is in (O) but not in (N), keep it in (O)
        (this covers sysadmin disabling case),
      - if it is in neither (O) nor (N), add to both (O) and (N),
        (this covers the package install case).
   6) write+fsync (N).new, then (O).new,
   7) rename+fsync-dir (N).new into (N), then (O).new into (O).

but, I might have gotten the logic wrong. :)

> I think using triggers has an obvious benefit here, but depending in the
> intended semantics of `/etc/shells`, implementing the part about preserving
> user changes may be difficult. A possible solution could be moving
> `/etc/shells` to `/var` and replacing it with a symbolic link when its contents
> match with the generated one one.

If the above is sane, then it might not be unworkable. :)

> For the installation bootstrap, the trigger-based solution would make any
> ordering issues related to `/etc/shells` fully go away. That would be quite an
> improvement.

Yeah.

Thanks,
Guillem


Reply to: