Skip to content
This repository has been archived by the owner on Feb 24, 2020. It is now read-only.

trust: only trust pubkeys on first use, prompt for https trust #2033

Merged
merged 1 commit into from Feb 2, 2016

Conversation

cgonyeo
Copy link
Member

@cgonyeo cgonyeo commented Jan 25, 2016

This commit changes rkt's behavior such that when fetching an image
through meta discovery, rkt will only download and trust the gpg public
key for the image if rkt doesn't have any local keys with a matching
prefix. If the key for an image has changed, the user must manually
trust the new key with `rkt trust --prefix`.

In addition, `--trust-keys-from-https` now defaults to false, so the
user is prompted to approve new keys (unless rkt isn't running in a tty,
in which case an error is printed).

New default behavior when fetching an image in a tty:

[nix-shell:~/go/src/github.com/coreos/rkt]$ sudo ./build-rkt-0.15.0/bin/rkt fetch quay.io/coreos/alpine-sh
image: searching for app image quay.io/coreos/alpine-sh
image: remote fetching from URL "https://quay.io/c1/aci/quay.io/coreos/alpine-sh/latest/aci/linux/amd64/"
pubkey: prefix: "quay.io/coreos/alpine-sh"
key: "https://quay.io/aci-signing-key"
gpg key fingerprint is: BFF3 13CD AA56 0B16 A898  7B8F 72AB F5F6 799D 33BC
    Quay.io ACI Converter (ACI conversion signing key) <support@quay.io>
Are you sure you want to trust this key (yes/no)?
yes
Trusting "https://quay.io/aci-signing-key" for prefix "quay.io/coreos/alpine-sh" after fingerprint review.
Added key for prefix "quay.io/coreos/alpine-sh" at "/etc/rkt/trustedkeys/prefix.d/quay.io/coreos/alpine-sh/bff313cdaa560b16a8987b8f72abf5f6799d33bc"
image: downloading signature from https://quay.io/c1/aci/quay.io/coreos/alpine-sh/latest/aci.asc/linux/amd64/
Downloading signature: [=======================================] 473 B/473 B
Downloading ACI: [=============================================] 2.65 MB/2.65 MB
image: signature verified:
  Quay.io ACI Converter (ACI conversion signing key) <support@quay.io>
sha512-a2fb8f390702d3d9b00d2ebd93e7dd1

New default behavior when fetching and not in a tty:

Feb 01 09:36:20 rokot systemd[1]: Started /home/derek/go/src/github.com/coreos/rkt/./build-rkt-0.15.0/bin/rkt fetch quay.io/coreos/alpine-sh.
Feb 01 09:36:20 rokot rkt[10271]: image: searching for app image quay.io/coreos/alpine-sh
Feb 01 09:36:20 rokot rkt[10271]: image: remote fetching from URL "https://quay.io/c1/aci/quay.io/coreos/alpine-sh/latest/aci/linux/amd64/"
Feb 01 09:36:21 rokot rkt[10271]: pubkey: prefix: "quay.io/coreos/alpine-sh"
Feb 01 09:36:21 rokot rkt[10271]: key: "https://quay.io/aci-signing-key"
Feb 01 09:36:21 rokot rkt[10271]: gpg key fingerprint is: BFF3 13CD AA56 0B16 A898  7B8F 72AB F5F6 799D 33BC
Feb 01 09:36:21 rokot rkt[10271]: Quay.io ACI Converter (ACI conversion signing key) <support@quay.io>
Feb 01 09:36:21 rokot rkt[10271]: pubkey: To trust the key for "quay.io/coreos/alpine-sh", do one of the following:
Feb 01 09:36:21 rokot rkt[10271]: pubkey:  - call rkt with --trust-keys-from-https
Feb 01 09:36:21 rokot rkt[10271]: pubkey:  - run: rkt trust --prefix "quay.io/coreos/alpine-sh"
Feb 01 09:36:21 rokot rkt[10271]: image: error adding keys: error reviewing key: unable to ask user to review fingerprint due to lack of tty
Feb 01 09:36:21 rokot rkt[10271]: image: downloading signature from https://quay.io/c1/aci/quay.io/coreos/alpine-sh/latest/aci.asc/linux/amd64/
Feb 01 09:36:21 rokot rkt[10271]: Downloading signature:  0 B/473 B
Feb 01 09:36:21 rokot rkt[10271]: Downloading signature:  473 B/473 B
Feb 01 09:36:21 rokot rkt[10271]: image: If you expected the signing key to change, try running:
Feb 01 09:36:21 rokot rkt[10271]: image:     rkt trust --prefix "quay.io/coreos/alpine-sh"
Feb 01 09:36:21 rokot rkt[10271]: fetch: openpgp: signature made by unknown entity
Feb 01 09:36:21 rokot systemd[1]: run-rdb646d41c49b4f9595431ad80b07c459.service: Main process exited, code=exited, status=1/FAILURE
Feb 01 09:36:21 rokot systemd[1]: run-rdb646d41c49b4f9595431ad80b07c459.service: Unit entered failed state.
Feb 01 09:36:21 rokot systemd[1]: run-rdb646d41c49b4f9595431ad80b07c459.service: Failed with result 'exit-code'.

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 25, 2016

cc @gtank @brianredbeard @pbx0

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 25, 2016

With this setup, trusting new keys replaces existing ones. Multiple keys can still be trusted, but they all need to be added in the same step. I don't know what the use case for multiple public keys being trusted for a given prefix is though, so if people think that's important I can add a flag to rkt trust to allow for adding keys for a prefix (as opposed to replacing keys for a prefix).

@alban
Copy link
Member

alban commented Jan 26, 2016

Thanks!

  • Are the patches rebased on master? If so, rkt should be compiled in build-rkt-0.16.0 instead of build-rkt-0.15.0.
  • When I delete the key manually with rm -f /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190, I cannot trust the key again with rkt trust --prefix coreos.com/etcd if the directory /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/ exists, even empty. Is it the intended behavior? At least, the error message "keys already set for prefix" should be changed because there are no keys in the empty directory.
  • Can users still disable a key by writing an empty file as explained in the Establishing trust documentation?
  • If I trust manually the CoreOS key for all coreos.com images with rkt trust --prefix=coreos.com https://coreos.com/dist/pubkeys/aci-pubkeys.gpg and then exec etcd with rkt run coreos.com/etcd:v2.0.10, then it will ask me if I trust the key even though it is already trusted for the whole prefix coreos.com. Replying no would execute successfully the image anyway because the key is trusted in the "parent" prefix.
  • In the scenario above, when running rkt as a systemd service, I get "error reading input: EOF" because there is no stdin. Is that ok?
$ sudo systemd-run build-rkt-0.16.0+git/bin/rkt run coreos.com/etcd:v2.0.10
Running as unit run-6147.service.
$ sudo journalctl -u  run-6147.service
...
rkt[6148]: rkt: remote fetching from URL "https://github.com/coreos/etcd/releases/download/v2.0.10/etcd-v2.0
rkt[6148]: prefix: "coreos.com/etcd"
rkt[6148]: key: "https://coreos.com/dist/pubkeys/aci-pubkeys.gpg"
rkt[6148]: gpg key fingerprint is: 8B86 DE38 890D DB72 9186  7B02 5210 BD88 8818 2190
rkt[6148]: CoreOS ACI Builder <release@coreos.com>
rkt[6148]: Are you sure you want to trust this key (yes/no)?
rkt[6148]: rkt: error adding keys: error reviewing key: error reading input: EOF
rkt[6148]: rkt: downloading signature from https://github.com/coreos/etcd/releases/download/v2.0.10/etcd-v2.
rkt[6148]: Downloading signature:  0 B/819 B
rkt[6148]: Downloading signature:  819 B/819 B
rkt[6148]: Downloading ACI:  0 B/3.79 MB
rkt[6148]: Downloading ACI:  16.4 KB/3.79 MB
rkt[6148]: Downloading ACI:  1.76 MB/3.79 MB
rkt[6148]: Downloading ACI:  3.79 MB/3.79 MB
rkt[6148]: rkt: signature verified:
rkt[6148]: CoreOS ACI Builder <release@coreos.com>
rkt[6148]: [135212.464297] etcd[4]: 2016/01/26 11:23:36 etcd: no data-dir provided, using default data-dir .
rkt[6148]: [135212.465017] etcd[4]: 2016/01/26 11:23:36 etcd: listening for peers on http://localhost:2380

@alban
Copy link
Member

alban commented Jan 26, 2016

When using --replace-keys, it deletes recursively all the keys for the specified prefix. Should it say which keys it is going to delete and ask confirmation?

For example, if I had keys in several depths of prefixes:

/etc/rkt/trustedkeys/prefix.d/coreos.com/8b86de38890ddb7291867b025210bd8888182190
/etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190
/etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/foo/8b86de38890ddb7291867b025210bd8888182190

rkt trust --prefix=coreos.com https://coreos.com/dist/pubkeys/aci-pubkeys.gpg --replace-keys will delete them all.

@alban
Copy link
Member

alban commented Jan 26, 2016

Fixes #1880

@@ -42,6 +42,14 @@ type Manager struct {
type AcceptOption int
type OverrideOption int

type ErrKeyForPrefixExists struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not an error variable instead of a type? (e.g. pkg/lock/file.go:24)

@krnowak
Copy link
Collaborator

krnowak commented Jan 26, 2016

Is this making rkt run interactive? How does this affect the use of "rkt run" in a systemd unit?

@krnowak
Copy link
Collaborator

krnowak commented Jan 26, 2016

Nevermind, @alban already pointed that out.

for _, p := range pathNamesPrefix {
_, err := os.Stat(p)
if err == nil {
return true, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file exists, the key might still not be there: it could be an empty file in /etc to disable the key in /usr. Arguably, this is an existing bug, but since TrustedKeyPrefixExists is now used for rkt trust, this should be fixed...

@alban
Copy link
Member

alban commented Jan 26, 2016

@peebs
Copy link
Contributor

peebs commented Jan 26, 2016

I didn't consider making rkt run interactive, so instead of asking to trust a key to under rkt run on first go, the error message should probably always point to using rkt trust instead.

I wonder if we even want to be checking for existing matching keys at all at this point. We can't use it to trigger an interactive question for rkt run. Also, since --trust-keys-from-https no longer defaults true, then i'm not sure its necessary to error on new keys and introduce the TOFU model when a flag has already been set to bypass the need to rkt trust in the first place. However, checking if existing keys match a name would be useful be useful in providing more detailed errors messages. Any thoughts?

Also, I agree that rkt trust doesn't need to delete old keys. I don't see as reason to destroy old keys when rkt trusting new keys. I mean it might be useful to do an all at once key rotation, but, it seems like its behavior could be surprising. Probably better left as future work under some sort of rkt revoke UX. (briefly mentioned in this issue).

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 27, 2016

I have no clue why my rkt is building in build-rkt-0.15.0 instead of build-rkt-0.16.0, but the resulting binary is claiming to be version 0.16.0+gitfe93264.

Users should still be able to use empty files to disable keys installed in /usr.

Just added a commit (I'll squash them at the end). Notable changes:

  • Removed --replace-keys, and rkt will now never delete keys. rkt trust will add keys, as before.
  • rkt will only prompt the user for key review when running in a tty
  • rkt will now consider empty files/directories and parent prefixes when determining if keys exist for a given prefix

A log of the tty change:

[nix-shell:~/go/src/github.com/coreos/rkt]$ sudo systemd-run ./build-rkt-0.15.0/bin/rkt run aci.gonyeo.com/nginx
Running as unit run-r8074cdc933274eaf912290964bbbbd9e.service.

[nix-shell:~/go/src/github.com/coreos/rkt]$ sudo journalctl -u run-r8074cdc933274eaf912290964bbbbd9e.service
-- Logs begin at Sat 2015-11-21 20:21:51 EST, end at Wed 2016-01-27 15:08:44 EST. --
Jan 27 15:08:36 rokot systemd[1]: Started /home/derek/go/src/github.com/coreos/rkt/./build-rkt-0.15.0/bin/rkt run aci.gonyeo.com/nginx.
Jan 27 15:08:36 rokot rkt[752]: rkt: using image from local store for image name coreos.com/rkt/stage1-coreos:0.16.0+gitfe93264
Jan 27 15:08:36 rokot rkt[752]: rkt: searching for app image aci.gonyeo.com/nginx
Jan 27 15:08:37 rokot rkt[752]: rkt: remote fetching from URL "https://aci.gonyeo.com/nginx-latest-linux-amd64.aci"
Jan 27 15:08:38 rokot rkt[752]: prefix: "aci.gonyeo.com/nginx"
Jan 27 15:08:38 rokot rkt[752]: key: "https://aci.gonyeo.com/pubkeys.gpg"
Jan 27 15:08:38 rokot rkt[752]: gpg key fingerprint is: 391A 2660 3B7D 1A7B 969B  DB93 8D6A 284F 420B 2594
Jan 27 15:08:38 rokot rkt[752]: subkey fingerprint: 818A 735C A7D6 60F5 F113  8ED8 29A7 820C 14D5 7505
Jan 27 15:08:38 rokot rkt[752]: Derek Gonyeo (ACI signing key) <derek@gonyeo.com>
Jan 27 15:08:38 rokot rkt[752]: To trust the key for "aci.gonyeo.com/nginx", do one of the following:
Jan 27 15:08:38 rokot rkt[752]: - call rkt with --trust-keys-from-https
Jan 27 15:08:38 rokot rkt[752]: - run: rkt trust --prefix "aci.gonyeo.com/nginx"
Jan 27 15:08:38 rokot rkt[752]: rkt: error adding keys: error reviewing key: unable to ask user to review fingerprint due to lack of tty
Jan 27 15:08:38 rokot rkt[752]: rkt: downloading signature from https://aci.gonyeo.com/nginx-latest-linux-amd64.aci.asc
Jan 27 15:08:38 rokot rkt[752]: Downloading signature: 0 B of an unknown total size
Jan 27 15:08:38 rokot rkt[752]: Downloading signature: 473 B of an unknown total size
Jan 27 15:08:38 rokot rkt[752]: Downloading signature: 473 B of an unknown total size
Jan 27 15:08:38 rokot rkt[752]: rkt: If you expected the signing key to change, try running:
Jan 27 15:08:38 rokot rkt[752]: rkt:     rkt trust --prefix "aci.gonyeo.com/nginx"
Jan 27 15:08:38 rokot rkt[752]: run: openpgp: signature made by unknown entity
Jan 27 15:08:38 rokot systemd[1]: run-r8074cdc933274eaf912290964bbbbd9e.service: Main process exited, code=exited, status=1/FAILURE
Jan 27 15:08:38 rokot systemd[1]: run-r8074cdc933274eaf912290964bbbbd9e.service: Unit entered failed state.
Jan 27 15:08:38 rokot systemd[1]: run-r8074cdc933274eaf912290964bbbbd9e.service: Failed with result 'exit-code'.

Thoughts?

@alban
Copy link
Member

alban commented Jan 27, 2016

I have no clue why my rkt is building in build-rkt-0.15.0 instead of build-rkt-0.16.0, but the resulting binary is claiming to be version 0.16.0+gitfe93264.

Strange... does running ./autogen.sh and ./configure again fix this?

@@ -106,6 +106,15 @@ func checkSignature(ks *Keystore, prefix string, signed, signature io.ReadSeeker
return entities, err
}

// DeleteAllTrustedKeysForPrefix deletes all trusted keys for the given prefix
func (ks *Keystore) DeleteAllTrustedKeysForPrefix(prefix string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeleteAllTrustedKeysForPrefix can be deleted now that it is not used anymore.

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 29, 2016

Made the suggested fixes, rebased on master, squashed commits. Should be ready for another pass.

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 29, 2016

Strange... does running ./autogen.sh and ./configure again fix this?

Even stranger: they don't appear to fix this. I even cloned master to a different directory, and did ./autogen.sh && ./configure && make, and it's still dropping the files in the wrong directory.

@krnowak
Copy link
Collaborator

krnowak commented Jan 29, 2016

I have no idea what's wrong in the build-system. Maybe you have the BUILDDIR env variable defined? What does the line in makelib/variables.mk with BUILDDIR ?= say?

@cgonyeo
Copy link
Member Author

cgonyeo commented Jan 29, 2016

Oh wait, it looks like nix-shell is setting the BUILDDIR environment variable. I'm having it give me an environment based on the rkt 0.15.0 package, so that's where that's coming from. Mystery solved.

@krnowak
Copy link
Collaborator

krnowak commented Jan 29, 2016

@dgonyeo: #1758 (comment)

continue
}
if err != nil {
return false, fmt.Errorf("cannot check dir %q: %v", p, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return false, errwrap.Wrap(fmt.Errorf("cannot check dir %q", p), err)

@iaguis
Copy link
Member

iaguis commented Feb 1, 2016

It would be nice if the commit message explained the current behavior a bit better. Including the examples in the PR.

@iaguis
Copy link
Member

iaguis commented Feb 1, 2016

This LGTM (modulo some nits) but I'd like another pair of eyes on it.


if m.TrustKeysFromHTTPS && u.Scheme == "https" {
accept = AcceptForce
}

if accept == AcceptAsk {
if !terminal.IsTerminal(int(os.Stdin.Fd())) || !terminal.IsTerminal(int(os.Stderr.Fd())) {
stdout.Printf("To trust the key for %q, do one of the following:", prefix)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be printed to log, instead of stdout?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting to see a stderr in addition to a stdout. What's the difference between stdout and log?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @blixtra

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log is basically printing to stderr. See https://github.com/dgonyeo/rkt/blob/rkt-trust/rkt/pubkey/pubkey.go#L58

I guess the name log was chosen to minimize number of changed lines when switching from the log package to our rktlog.

This commit changes rkt's behavior such that when fetching an image
through meta discovery, rkt will only download and trust the gpg public
key for the image if rkt doesn't have any local keys with a matching
prefix. If the key for an image has changed, the user must manually
trust the new key with `rkt trust --prefix`.

In addition, `--trust-keys-from-https` now defaults to false, so the
user is prompted to approve new keys (unless rkt isn't running in a tty,
in which case an error is printed).
@cgonyeo
Copy link
Member Author

cgonyeo commented Feb 1, 2016

@iaguis updated the commit message and the PR. Thoughts on the new description / examples?

@cgonyeo
Copy link
Member Author

cgonyeo commented Feb 1, 2016

I'll be spending some time today updating the docs and investigating what tests I could write for this, but I won't be able to work too many hours today due to homework, and tomorrow's a full day of classes. If we want to rush to get this in, help finishing this up would be appreciated.

@alban
Copy link
Member

alban commented Feb 1, 2016

Thanks! Setting the "needs rework" label for the TODO in #2033 (comment) but this new version of the code looks good to me.

@alban
Copy link
Member

alban commented Feb 2, 2016

I am slightly improving the rkt trust tests in #2069 but they should be improved further later in #2070.

The code looks good to me, so I am merging it now.

alban added a commit that referenced this pull request Feb 2, 2016
trust: only trust pubkeys on first use, prompt for https trust
@alban alban merged commit 3201205 into rkt:master Feb 2, 2016
alban added a commit to kinvolk/rkt that referenced this pull request Feb 2, 2016
Global option --trust-keys-from-https was changed to default to false in
rkt#2033

Update the documentation accordingly.
alban added a commit to kinvolk/rkt that referenced this pull request Feb 2, 2016
iaguis added a commit that referenced this pull request Feb 2, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants