20 May 2018

feedPlanet Gentoo

Michał Górny: Empty directories, *into, dodir, keepdir and tmpfiles.d

There seems to be some serious confusion around the way directories are installed in Gentoo. In this post, I would like to shortly explain the differences between different methods of creating directories in ebuilds, and instruct how to handle the issues related to installing empty directories and volatile locations.

Empty directories are not guaranteed to be installed

First things first. The standards are pretty clear here:

Behaviour upon encountering an empty directory is undefined. Ebuilds must not attempt to install an empty directory.

PMS 13.2.2 Empty directories (EAPI 7 version)

What does that mean in practice? It means that if an empty directory is found in the installation image, it may or may not be installed. Or it may be installed, and incidentally removed later (that's the historical Portage behavior!). In any case, you can't rely on either behavior. If you really need a directory to exist once the package is installed, you need to make it non-empty (see: keepdir below). If you really need a directory not to exist, you need to rmdir it from the image.

That said, this behavior does makes sense. It guarantees that the Gentoo installation is secured against empty directory pruning tools.

*into

The *into family of functions is used to control install destination for other ebuild helpers. By design, either they or the respective helpers create the install directories as necessary. In other words, you do not need to call dodir when using *into.

dodir

dodir is not really special in any way. It is just a convenient wrapper for install -d that prepends ${ED} to the path. It creates an empty directory the same way the upstream build system would have created it, and if the directory is left empty, it is not guaranteed to be preserved.

So when do you use it? You use it when you need to create a directory that will not be created otherwise and that will become non-empty at the end of the build process. Example use cases are working around broken build systems (that fail due to non-existing directories but do not create them), and creating directories when you want to manually write to a file there.

src_install() {
    # build system is broken and fails
    # if ${D}/usr/bin does not exist
    dodir /usr/bin
    default

    dodir /etc/foo
    sed -e "s:@libdir@:$(get_libdir):" \
        "${FILESDIR}"/foo.conf.in \
        > "${ED}"/etc/foo/foo.conf || die
}

keepdir

keepdir is the function specifically meant for installing empty directories. It creates the directory, and a keep-file inside it. The directory becomes non-empty, and therefore guaranteed to be installed and preserved. When using keepdir, you do not call dodir as well.

Note that actually preserving the empty directories is not always necessary. Sometimes packages are perfectly capable of recreating the directories themselves. However, make sure to verify that the permissions are correct afterwards.

src_install() {
    default

    # install empty directory
    keepdir /var/lib/foo
}

Volatile locations

The keepdir method works fine for persistent locations. However, it will not work correctly in directories such as /run that are volatile or /var/cache that may be subject to wiping by user. On Gentoo, this also includes /var/run (which OpenRC maintainers unilaterally decided to turn into a /run symlink), and /var/lock.

Since the package manager does not handle recreating those directories e.g. after a reboot, something else needs to. There are three common approaches to it, most preferred first:

  1. Application creates all necessary directories at startup.
  2. tmpfiles.d file is installed to create the files at boot.
  3. Init script creates the directories before starting the service (checkpath).

The preferred approach is for applications to create those directories themselves. However, not all applications do that, and not all actually can. For example, applications that are running unprivileged generally can't create those directories.

The second approach is to install a tmpfiles.d file to create (and maintain) the directory. Those files are work both for systemd and OpenRC users (via opentmpfiles) out of the box. The directories are (re-)created at boot, and optionally cleaned up periodically. The ebuild should also use tmpfiles.eclass to trigger directory creation after installing the package.

The third approach is to make the init script create the directory. This was the traditional way but nowadays it is generally discouraged as it causes duplication between different init systems, and the directories are not created when the application is started directly by the user.

Summary

To summarize:

  1. when you install files via *into, installation directories are automatically created for you;
  2. when you need to create a directory into which files are installed in other way than ebuild helpers, use dodir;
  3. when you need to install an empty directory in a non-volatile location (and application can't just create it on start), use keepdir;
  4. when you need to install a directory into a volatile location (and application can't just create it on start), use tmpfiles.d.

20 May 2018 8:03am GMT

13 May 2018

feedPlanet Gentoo

Michał Górny: A short history of Gentoo copyright

As part of the recent effort into forming a new copyright policy for Gentoo, a research into the historical status has been conducted. We've tried to establish all the key events regarding the topic, as well as the reasoning behind the existing policy. I would like to shortly note the history based on the evidence discovered by Robin H. Johnson, Ulrich Müller and myself.

Continue reading

13 May 2018 7:04pm GMT

12 May 2018

feedPlanet Gentoo

Michał Górny: On OpenPGP (GnuPG) key management

Over the time, a number of developers have had problems following the Gentoo OpenPGP key policy (GLEP 63. In particular, the key expiration requirements have resulted in many developers wanting to replace their key unnecessarily. I've been asked to write some instructions on managing your OpenPGP key, and I've decided to go for a full blog post with some less-known tips. I won't be getting into detailed explanations how to use GnuPG though - you may still need to read the documentation after all.

Primary key and subkeys

An OpenPGP key actually consists of one or more pairs of public and private keys - the primary key (or root key, in GLEP 63 naming), and zero or more subkeys. Ideally, the primary key is only used to create subkeys, UIDs, manipulate them and sign other people's keys. All 'non-key' cryptographic operations are done using subkeys. This reduces the wear of the primary key, and the risk of it being compromised.

If you don't use a smartcard, then a good idea would be to move the private part of primary key off-site since you don't need it for normal operation. However, before doing that please remember to always have a revocation certificate around. You will need it to revoke the primary key if you lose it. With GnuPG 2.1, removing private keys is trivial. First, list all keys with keygrips:

$ gpg --list-secret --with-keygrip
/home/you/.gnupg/pubring.kbx
-------------------------------
sec   rsa2048/0xBBC7E6E002FE74E8 2018-05-12 [SC] [expires: 2020-05-11]
      55642983197252C35550375FBBC7E6E002FE74E8
      Keygrip = B51708C7209017A162BDA515A9803D3089B993F0
uid                   [ultimate] Example key 
ssb   rsa2048/0xB7BA421CDCD4AF16 2018-05-12 [E] [expires: 2020-05-11]
      Keygrip = 92230550DA684B506FC277B005CD3296CB70463C

Note that the output may differ depending on your settings. The sec entry indicates a primary key. Once you find the correct key, just look for a file named after its Keygrip in ~/.gnupg/private-keys-v1.d (e.g. B51708C7209017A162BDA515A9803D3089B993F0.key here). Move that file off-site and voilà!

In fact, you can go even further and use a dedicated off-line system to create and manage keys, and only transfer appropriate private keys (and public keyring updates) to your online hosts. You can transfer and remove any other private key the same way, and use --export-key to transfer the public keys.

How many subkeys to use?

Create at least one signing subkey and exactly one encryption subkey.

Signing keys are used to sign data, i.e. to prove its integrity and authenticity. Using multiple signing subkeys is rather trivial - you can explicitly specify the key to use while creating a signature (note that you need to append ! to key-id to force non-default subkey), and GnuPG will automatically use the correct subkey when verifying the signature. To reduce the wear of your main signing subkey, you can create a separate signing subkey for Gentoo commits. Or you can go ever further, and have a separate signing subkey for each machine you're using (and keep only the appropriate key on each machine).

Encryption keys are used to encrypt messages. While technically it is possible to have multiple encryption subkeys, GnuPG does not make that meaningful. When someone will try to encrypt a message to you, it will insist on using the newest key even if multiple keys are valid. Therefore, use only one encryption key to avoid confusion.

There is also a third key class: authentication keys that can be used in place of SSH keys. If you intend to use them, I suggest the same rule as for SSH keys, that is one key for each host holding the keyring. More on using GnuPG for SSH below.

To summarize: use one encryption subkey, and as many signing and authentication subkeys as you need. Using more subkeys reduces individual wear of each key, and makes it easier to assess the damage if one of them gets compromised.

When to create a new key?

One of the common misconceptions is that you need to create a new key when the current one expires. This is not really the purpose of key expiration - we use it mostly to automatically rule out dead keys. There are generally three cases when you want to create a new key:

  1. if the key is compromised,
  2. if the primary key is irrecoverably lost,
  3. if the key uses really weak algorithm (e.g. short DSA key).

Most of the time, you will just decide to prolong the primary key and subkeys, i.e. use the --edit-key option to update their expiration dates. Note that GnuPG is not very user-friendly there. To prolong the primary key, use expire command without any subkeys selected. To prolong one or more subkeys, select them using key and then use expire. Normally, you will want to do this periodically, before the expiration date to give people some time to refresh. Add it to your calendar as a periodic event.

$ gpg --edit-key 0xBBC7E6E002FE74E8
Secret key is available.

sec  rsa2048/0xBBC7E6E002FE74E8
     created: 2018-05-12  expires: 2020-05-11  usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa2048/0xB7BA421CDCD4AF16
     created: 2018-05-12  expires: 2020-05-11  usage: E   
[ultimate] (1). Example key <example@example.com>

gpg> expire
Changing expiration time for the primary key.
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 3y
Key expires at Tue May 11 12:32:35 2021 CEST
Is this correct? (y/N) y

sec  rsa2048/0xBBC7E6E002FE74E8
     created: 2018-05-12  expires: 2021-05-11  usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa2048/0xB7BA421CDCD4AF16
     created: 2018-05-12  expires: 2020-05-11  usage: E   
[ultimate] (1). Example key <example@example.com>

gpg> key 1

sec  rsa2048/0xBBC7E6E002FE74E8
     created: 2018-05-12  expires: 2021-05-11  usage: SC  
     trust: ultimate      validity: ultimate
ssb* rsa2048/0xB7BA421CDCD4AF16
     created: 2018-05-12  expires: 2020-05-11  usage: E   
[ultimate] (1). Example key <example@example.com>

gpg> expire
Changing expiration time for a subkey.
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sun May 12 12:32:47 2019 CEST
Is this correct? (y/N) y

sec  rsa2048/0xBBC7E6E002FE74E8
     created: 2018-05-12  expires: 2021-05-11  usage: SC  
     trust: ultimate      validity: ultimate
ssb* rsa2048/0xB7BA421CDCD4AF16
     created: 2018-05-12  expires: 2019-05-12  usage: E   
[ultimate] (1). Example key <example@example.com>

If one of the conditions above applies to one of your subkeys, or you think that it has reached a very high wear, you will want to replace the subkey. While at it, make sure that the old key is either expired or revoked (but don't revoke the whole key accidentally!). If one of those conditions applies to your primary key, revoke it and start propagating your new key.

Please remember to upload your key to key servers after each change (using --send-keys).

To summarize: prolong your keys periodically, rotate subkeys whenever you consider that beneficial but avoid replacing the primary key unless really necessary.

Using gpg-agent for SSH authentication

If you already have to set up a secure store for OpenPGP keys, why not use it for SSH keys as well? GnuPG provides ssh-agent emulation which lets you use an OpenPGP subkey to authenticate via SSH.

Firstly, you need to create a new key. You need to use the --expert option to access additional options. Use addkey to create a new key, choose one of the options with custom capabilities and toggle them from the default sign+<em<encrypt to authenticate:

$ gpg --expert --edit-key 0xBBC7E6E002FE74E8
Secret key is available.

sec  rsa2048/0xBBC7E6E002FE74E8
     created: 2018-05-12  expires: 2020-05-11  usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa2048/0xB7BA421CDCD4AF16
     created: 2018-05-12  expires: 2020-05-11  usage: E   
[ultimate] (1). Example key <example@example.com>

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 8

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: Sign Encrypt 

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: Encrypt 

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? e

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: 

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? a

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: Authenticate 

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
[...]

Once the key is created, find its keygrip:

$ gpg --list-secret --with-keygrip
/home/mgorny/.gnupg/pubring.kbx
-------------------------------
sec   rsa2048/0xBBC7E6E002FE74E8 2018-05-12 [SC] [expires: 2020-05-11]
      55642983197252C35550375FBBC7E6E002FE74E8
      Keygrip = B51708C7209017A162BDA515A9803D3089B993F0
uid                   [ultimate] Example key <example@example.com>
ssb   rsa2048/0xB7BA421CDCD4AF16 2018-05-12 [E] [expires: 2020-05-11]
      Keygrip = 92230550DA684B506FC277B005CD3296CB70463C
ssb   rsa2048/0x2BE2AF20C43617A0 2018-05-12 [A] [expires: 2018-05-13]
      Keygrip = 569A0C016AB264B0451309775FDCF06A2DE73473

This time we're talking about the keygrip of the [A] key. Append that to ~/.gnupg/sshcontrol:

$ echo 569A0C016AB264B0451309775FDCF06A2DE73473 >> ~/.gnupg/sshcontrol

The final step is to have gpg-agent with --enable-ssh-support started. The exact procedure here depends on the environment used. In XFCE, it involves setting a hidden configuration option:

$ xfconf-query -c xfce4-session -p /startup/ssh-agent/type -n -t string -s gpg-agent

Further reading

12 May 2018 6:40am GMT