Entitlement server and protected repos support

Philip Withnall philip at tecnocode.co.uk
Thu May 23 12:33:25 UTC 2019


On Thu, 23 May 2019, at 08:19, Alexander Larsson wrote:
> On Thu, May 23, 2019 at 12:56 AM Damián Nohales <damian at endlessm.com> wrote:
> > Then we have the P2P case:
> >
> > 1. A ref is about to be pulled (repo_pull is called).
> > 2. The P2P code path is used (we have a collection_id)
> > 3. ostree_repo_find_remotes_async is called
> > 4. For each finder result, we set a new "OstreeRepoFinderResult {
> > GVariant *options }" field that will override the common
> > ostree_repo_pull_from_remotes_async's options argument. That new field
> > is populated with the Bearer token in the http-headers field of the
> > finder result option.
> > 5. ostree_repo_pull_from_remotes_async will use the options specific
> > to the remote to fetch the objects.

I don’t think you want to extend the OstreeRepoFinderResult struct, because it’s specific to a remote, whereas you want to use a different bearer token for each ref/commit.

Instead, add an option to ostree_repo_pull_from_remotes_async() which maps refs (or commit checksums) to an a(ss) of HTTP headers to use when pulling that commit metadata. Each set of HTTP headers would contain the relevant bearer token header.

> I think this will do the right thing for the actual upstream remote.
> However, the p2p case is interesting, because what does it even mean
> in terms of a private repo? What ensures that some random p2p node
> doesn't give you the commits even without the bearer token? Maybe that
> node did do an authenticated download, and now its just another commit
> in the local repo that it is being nice to the peers on the local
> network by giving out.

We have three options:
 1) Allow P2P peers to forward paid-for apps without any checks. Not really an option.
 2) Don’t allow P2P peers to forward paid-for apps at all.
 3) Allow P2P peers to forward paid-for apps on receipt of a valid bearer token, with some validation of the token.

(2) is probably the easiest option for the first iteration of developing this, but we presumably want to allow (3) in the future. So the bearer tokens need to be designed so that either:
 (a) a peer Bob can validate that a token from another peer Alice actually comes from that peer, that it was issued by a token server which Bob trusts, and that it hasn’t expired
 (b) Bob can forward a token from Alice to the token server (which Bob must trust) and have the token server validate it

(a) would allow full decentralisation, and is probably necessary if we want this to work for machines which are completely disconnected from the internet and which receive their apps over USB sticks. (b) probably isn’t significantly simpler.

So, I think (and please check this carefully — it’s only initial musing), this means that the token needs to:
 • Contain a public identifier for the user it’s generated for, which can be checked by peers of that user’s machine. Perhaps the machine’s IP address, for example.
 • Contain a public identifier for the token server which generated the token (so that peers can check whether they trust it).
 • Contain an expiry timestamp on some global clock.
 • The entire payload is signed by the key associated with that token server.

> I guess nothing really technicallty disallows a node from ignoring
> that some app should not be freely downloadable, but at the very least
> we should have some kind of idea how a "proper" p2p node should handle
> this.

Sure. And it’s always possible to copy the binaries for an app across to another computer and run them using `bwrap` manually. We can only go so far. :-)

> > The only thing I cannot see integrating well to upstream is the
> > mechanism to get the credentials to talk to the entitlements server.
> > In our specific case, these credentials lives in the user's keychain,
> > so it's fetched using libsecret and an Endless specific SecretSchema.
> >
> > So maybe we can create some sort of entitlements-server credentials
> > providers that are dynamically loaded as plugins so each distro can
> > provide their own .so files with the specific implementations?
> 
> Yeah, this is really the crux. Once we have a token everything is
> pretty simple, but getting the token fundamentally depends on details
> about the authentication system used by the particular system.
> 
> On a system like Endless (or even RHEL) your host OS would already
> have some kind of entilement which is kept up-to date by the OS
> itself. This could easily be passed by flatpak to a http API with some
> standard API, which returns the token you would use for the pull.
> 
> I imagine the for each remote you configure a url to the authenticator
> service, and a path name to the entitlement (or alternatively a
> commandline you run to generate the entilement). Then flatpak would
> load/run this, and send the extracted entitlement data to the
> configured url. Flatpak wouldn't need to understand the entitlement
> data, it would just forward it, and while the API is generic the
> backend is specific and will know the entitlement format.

I think providing a command to retrieve the token would be a workable first implementation. It would have to be execed once per token/entitlement server, per `flatpak` exec; which is pretty reasonable.

> However, that only works because there is some kind of constantly
> running entitlement system. What about systems that don't do this.
> Consider for instance if steam was using flatpak, and you ran flatpak
> update. How would it know to pass the steam username and password to
> the steam auth server? I wonder if we can define a standardized
> interactive way to solve this. This sounds *exactly* like OAuth2, so
> maybe we can use that? Like, have a standardized API in libflatpak
> that allows the client to implement oauth for the authentication
> stuff. I mean, it would mean having the client having to render a
> webpage for the login, but in practice you just take a dep on some
> webkit thing.

Implementing an OAuth provider in flatpak seems a bit over the top to start with, unless you’re suggesting going with loadable plugins for authentication? Otherwise you end up implementing OAuth as a program calling interface, or over D-Bus.

With the Steam example, what’s to stop the main Steam process from storing its auth token somewhere accessible to flatpak after it’s logged in, just like EOS or CentOS? Or are you envisaging that the Steam client wouldn’t be modified to support flatpak even if they provide a flatpak repo on a server?

Overall, if we can distil all authentication flows down to producing a bearer token, then I think we could standardise on that as the external API which flatpak requires third parties to provide. That becomes a question of whether you think all third parties are going to use the same token server as proposed here, or something custom.

Alternatively, we could use libsecret as the API for turning a token server URI into a bearer token to send to that server: we could look up a well-known bearer token value from a secret, which is itself looked up by the token server URI. That would mean a dependency on libsecret in flatpak, though.

It would be good to know how elementary’s authentication setup for paid-for apps fits in here, since they will presumably be interested in integrating too. CC Cassidy, although I think he’s away at the moment, so if anybody else from elementary is listening in; now’s your time to respond. :-)

Philip


More information about the Flatpak mailing list