Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aktualizr "Anonymous" Mode #1056

Closed
doanac opened this issue Jan 16, 2019 · 17 comments

Comments

@doanac
Copy link
Collaborator

commented Jan 16, 2019

Context

Aktualizr is awesome, but the effort to get things going is large. Part of that complexity is because of Uptane. There are many users who want the TUF security aspects of Aktualizr and don’t need to Director portions.

Additionally, Foundries.io has an open platform where people are free to install their OS. It would be great if anonymous users were able to use the TUF/OSTree logic in Aktualizr for updates and then have the ability to convert device(s) into a full-blow OTA Connect + Uptane setup.

To help prototype possibilities, Foundries.io has created a simple TUF/OSTree project. The idea presented here, is how to get aktualizr to work in a similar fashion.

Requirements

I'd like to propose a new "anonymous" feature for aktualizr with a minimal set of requirements:

  • Backend
    • Can be deployed with docker-compose
    • TUF
      • Docker Notary or OTA-Tuf
    • OSTree
      • Vanilla HTTP Server or TreeHub
    • Doesn’t require authentication (client just uses HTTPS anonymously)
  • Client
    • User can run an “update” command pulls latest OSTree and personality and deploy
    • User can list available updates

Changes to Aktualizr

This needs real experts, but here is doanac’s 20,000 ft level view:

It's probably a new binary that uses libaktualizr. Maybe like hmi_stub is done in-tree now. Then we look at pieces that can operate “anonymously”. By anonymous we mean:

  • Doesn’t talk to device-gateway (so clients don’t need pre-provisioned keys)
  • Only talks to OStree and TUF

Pieces a client would need:
Validating TUF meta-data: The “image repository” seems like the right place and it looks to be stand-alone already. However, the code to use it properly seems to be in the “sotaupdateclient” (updateImagesMeta()). I’m not sure if it would be a good idea to use that class directly since its got Uptane logic (and device-registry) in it. So there might be some work untangling that into a common helper/utility function/method.

OSTree Pull and Install: The “ostree manager” handles this, and seems to have no uptane dependencies.

Nice-to-haves:
Rather than pinning the client’s configuration to a specific OSTree server, it would be nice if the server was an optional field in the TUF “custom” data. Everything else in targets.json should still work just fine.

Proposed Client CLI

Not sure what to call it. I’ve used tuftree in the past, but maybe this is aktualizr-lite or something along those lines. I’ll call it $TOOL below:

$TOOL initialize --help
        Must be run once to configure application. Options:
        --base-tuf <server>
        --base-tuf-ca <ca file>

$TOOL list-base --help
        List available updates to base image

$TOOL status --help
        Describe current state of device.

$TOOL update --help
        Perform an update on the device. Options:
        --base <version> default=latest version found

Global flags include:

    -v Enable verbose logging
    -c <path> Use a non-default configuration location
doanac added a commit to doanac/aktualizr that referenced this issue Jan 16, 2019
sotauptaneclient: Provide access to image-repository targets
This is related to issue:
  advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Jan 16, 2019
sotauptaneclient: Provide access to image-repository targets
This is related to issue:
  advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
@patrickvacek

This comment has been minimized.

Copy link
Member

commented Jan 17, 2019

* Backend

I can't speak to the backend much, but I can talk to the people that can or get them involved directly.

* Doesn’t require authentication (client just uses HTTPS anonymously)

I don't think that's supported in aktualizr now, but we are open to trying other varieties of authentication.

It's probably a new binary that uses libaktualizr.

Agreed. The client-side requirements seem like things that can be done in a wrapper around the libaktualizr API so long as the wrapper application can get the data it needs.

However, the code to use it properly seems to be in the “sotaupdateclient” (updateImagesMeta()). I’m not sure if it would be a good idea to use that class directly since its got Uptane logic (and device-registry) in it. So there might be some work untangling that into a common helper/utility function/method.

We are aware that SotaUptaneClient is a bit of a catch-all. I would love to see it refactored in a more modular fashion to separate out Uptane vs. custom code. So far, the need and/or will have not been strong enough to make it happen. Maybe now is the time to think about parts that can be moved elsewhere.

OSTree Pull and Install: The “ostree manager” handles this, and seems to have no uptane dependencies.

I think we've managed to keep those separate! They should be if they are not already entirely.

Rather than pinning the client’s configuration to a specific OSTree server, it would be nice if the server was an optional field in the TUF “custom” data.

This is an option to consider. We currently don't support multiple servers, but it's something we've discussed, although we haven't made a concrete solution. I'd need to review the TUF spec, but I think this would be an acceptable solution.

Thanks for thinking through this and starting the discussion!

@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 17, 2019

  • Doesn’t require authentication (client just uses HTTPS anonymously)

I don't think that's supported in aktualizr now, but we are open to trying other varieties of authentication.

It can, I have a prototype doing it as show here: #1059 (comment)

@patrickvacek

This comment has been minimized.

Copy link
Member

commented Jan 17, 2019

It can, I have a prototype doing it as show here: #1059 (comment)

Ah, of course. We have tests that basically do the same thing. Not sure what I was really thinking of there, sorry.

doanac added a commit to doanac/aktualizr that referenced this issue Jan 17, 2019
tuf: Expose custom hardwareIds and version
This is related to issue:
  advancedtelematic#1056

Exposing the version and hardwareId fields gives a client application
the ability to find a list of versioned targets that are applicable
to a specific piece of hardware.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Jan 17, 2019
tuf: Expose custom hardwareIds and version
This is related to issue:
  advancedtelematic#1056

Exposing the version and hardwareId fields gives a client application
the ability to find a list of versioned targets that are applicable
to a specific piece of hardware.

Signed-off-by: Andy Doan <andy@foundries.io>
@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 17, 2019

To give everyone something more concrete/tangible to talk about, I've created a minimal proof-of-concept. I doubt any part of it can be upstreamed, but it should give an idea of where I'd like to see things go. Only 5 pretty small commit:

https://github.com/doanac/aktualizr/commits/aktualizr-lite

doanac added a commit to doanac/aktualizr that referenced this issue Jan 18, 2019
tuf: Expose custom hardwareIds and version
This is related to issue:
  advancedtelematic#1056

Exposing the version and hardwareId fields gives a client application
the ability to find a list of versioned targets that are applicable
to a specific piece of hardware.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Jan 19, 2019
sotauptaneclient: Provide access to image-repository targets
This is related to issue:
  advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Jan 22, 2019
sotauptaneclient: Provide access to image-repository targets
This is related to issue:
  advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Jan 22, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 22, 2019

NOTE: The branch from my previous comment is now in decent shape and should be easy to review:

https://github.com/doanac/aktualizr/commits/aktualizr-lite

@simao

This comment has been minimized.

Copy link
Member

commented Jan 23, 2019

* Backend

I see no reason why ota-tuf and treehub could not be deployed via docker-compose. Both of these services use some sort of storage backend and if this uses local storage this also needs to be setup with docker-compose, but that should be doable.

Basic auth is also fairly easy to implement.

This is an option to consider. We currently don't support multiple servers, but it's something we've discussed, although we haven't made a concrete solution. I'd need to review the TUF spec, but I think this would be an acceptable solution.

Yes, and we could use the uri in target.custom. Currently this field is empty in most cases, but it is already handled by ota-tuf.

@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 23, 2019

Basic auth is also fairly easy to implement.

At one point I actually did this. It works okay with treehub, there's a small issue ota-tuf: advancedtelematic/ota-tuf#162 but nothing major.

patriotyk added a commit that referenced this issue Jan 24, 2019
tuf: Expose custom hardwareIds and version
This is related to issue:
  #1056

Exposing the version and hardwareId fields gives a client application
the ability to find a list of versioned targets that are applicable
to a specific piece of hardware.

Signed-off-by: Andy Doan <andy@foundries.io>
@patrickvacek

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

@doanac Finally had some time to take a closer look at your branch. So far it looks quite reasonable.

The biggest issue for me here is making sure we don't make it too easy for a user who wants full Uptane support to accidentally use the anonymous/tuf-only code paths. Your solution as it currently stands actually handles this in a manner I hadn't initially considered: you've entirely bypassed the "proper" libaktualizr API (aktualizr.h) and gone straight to SotaUptaneClient. That's not part of our official API, so we can't make as many promises about guaranteeing stability, but if that doesn't bother you, then I think we've largely covered that concern for now.

Going forward, we can still think about ways to be even smarter about this and/or make the code/API cleaner. We've long considered refactoring SotaUptaneClient in various ways, but even if we don't want to make big changes there, we could consider making a separate tuf-only API via a different class. (As in, aktualizr.h could be kept as the default Uptane API and tuf-only.h (or something more clever than that!) could be created to serve as a parallel API for the tuf-only case.) This might help keep them separate, and we might even be able to programmatically prevent users from mixing them.

Other comments:

  • The status command looks a bit like aktualizr-info. We originally created aktualizr-info back when our default assumption was that aktualizr would run constantly in the background, but now that we are more comfortable with running it with a specific command for a specific purpose, we could consider merging it into aktualizr as well. There's no problem with your design, I'm just thinking if there are ways to combine existing functionality and tools.
  • The logic in initialize_main for discovering the hardware ID is a bit odd to me. It assumes that it was somehow provided by the server instead of provided or initialized by the device itself. Is that really what you expect to happen? It seems a bit backwards to me, but perhaps I'm thinking too much of Uptane.
  • Recreating SotaUptaneClient each time update_main is called isn't a problem, but it does seem a bit wasteful if it were to be called repeatedly in one run.
  • The change to tuf.cc to support to Docker Notary case with the different naming convention will hopefully be unnecessary.
@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 30, 2019

@patrickvacek - Glad things seem feasible.

* The `status` command looks a bit like `aktualizr-info`. We originally created `aktualizr-info` back when our default assumption was that `aktualizr` would run constantly in the background, but now that we are more comfortable with running it with a specific command for a specific purpose, we could consider merging it into `aktualizr` as well. There's no problem with your design, I'm just thinking if there are ways to combine existing functionality and tools.

I think if we want to move the logic of aktualizr-lite into aktualizr, then changing aktualizr-info with this makes sense.

* The logic in `initialize_main` for discovering the hardware ID is a bit odd to me. It assumes that it was somehow provided by the server instead of provided or initialized by the device itself. Is that really what you expect to happen? It seems a bit backwards to me, but perhaps I'm thinking too much of Uptane.

We can totally drop this, and I think for the purpose of an initial PR it would be smart. The issue I run into is that I need to specify my ecu-hardware-id for each device I have. Its a bit cumbersome having to type raspberrypi3-64, especially when the OSTree SHA I'm running is known to my TUF server and has the hwid. So the logic merely saves you the trouble.

The real fix is probably creating a sota.toml with our device that has the right stuff pre-populated (ie - no "init" command needed).

* Recreating SotaUptaneClient each time `update_main` is called isn't a problem, but it does seem a bit wasteful if it were to be called repeatedly in one run.

I'm not quite sure what you are saying here. I think its instantiated once and update_main is only called once per program invocation. Regardless, this will likely change with the merge of logic into aktualizr/main.cc

* The change to tuf.cc to support to Docker Notary case with the different naming convention will hopefully be unnecessary.

not needed. that's an accident while i was debugging something.

Next Steps
I think we have a rough agreement for how this could be merged. Here's how I'm thinking I can proceed. Let me know if this makes sense:

//from a new branch

  1. break out old logic for "status" into aktualizr-info (looks like we could just print out the ostree hash and be good).
  2. break out old logic for "list" and "update" into aktualizr as --running-mode options
  3. meanwhile keep track of #1074 so that I can do a proper job of listing TUF targets.

as delegations get sorted out and merged, i'll try and sort out any changes we may need for iterating over the targets in this new world.

@patrickvacek

This comment has been minimized.

Copy link
Member

commented Jan 31, 2019

I think if we want to move the logic of aktualizr-lite into aktualizr, then changing aktualizr-info with this makes sense.

Actually, I was assuming that we would keep aktualizr-lite as a separate binary for now, although I'm still not sure if that's the best name. If we do see a clear path for migration of active devices from TUF-only to full Uptane, then it might make sense to merge them. Otherwise, combining them risks doing exactly what I'd like to avoid: making an easy path for users who are expecting Uptane to accidentally use TUF-only features.

@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 31, 2019

Actually, I was assuming that we would keep aktualizr-lite

okay - that's probably easier for me anyway and merging the two would be simple. i'll do that.

@cajun-rat

This comment has been minimized.

Copy link
Contributor

commented Jan 31, 2019

Would it make sense to move aktualizr-lite into the main aktualizr binary? My thought is that '-lite' could be considered a type of credentials, specifically 'anonymous'. credentials.zip has URLs to the server in it, and we already have methods for provisioning that onto devices. This would remove the need for users (and testers!) to run aktualizr-lite initialize --base-tuf...

The I can see that this might complicate the control flow of the main update loop in aktualizr, but we'd win in the long term by having the same external interface regardless if the device is connected to a director, or if is in 'lite' mode and fetches the latest updates by itself, the only change being the contents of credentials.zip

@patrickvacek

This comment has been minimized.

Copy link
Member

commented Feb 1, 2019

Would it make sense to move aktualizr-lite into the main aktualizr binary?

Currently, my thought is no. My concern is that I want to prevent users expecting full Uptane support to accidentally use libaktualizr in a way that only gives them TUF. Perhaps I'm being overly cautious, but I think having two separate APIs for these two separate sets of functions is one way to enforce such a boundary. That could still mean that aktualizr-primary could in fact do either thing depending on configuration. However, that again sounds like making the boundary quite thin. I still need some convincing that combining all these things is safe. What is the actual benefit if having the same external interface?

@cajun-rat

This comment has been minimized.

Copy link
Contributor

commented Feb 1, 2019

Would it make sense to move aktualizr-lite into the main aktualizr binary?

Currently, my thought is no. My concern is that I want to prevent users expecting full Uptane support to accidentally use libaktualizr in a way that only gives them TUF. Perhaps I'm being overly cautious, but I think having two separate APIs for these two separate sets of functions is one way to enforce such a boundary. That could still mean that aktualizr-primary could in fact do either thing depending on configuration. However, that again sounds like making the boundary quite thin. I still need some convincing that combining all these things is safe.

That is a reasonable point.

What is the actual benefit if having the same external interface?

The concrete use-case is to publish a binary image and then let people choose between connecting it to OTA Connect or just try it out with updates coming automatically over TUF. I was thinking that we could switch between these by the choice of which credentials.zip they write to the flash.

Arguably, we might want the image to come with the TUF URL burnt into it already, which would require publishing two images. I want to make it easy for users to download the right thing, and asking them questions like 'Do you want updates over TUF or Uptane?' is going to confuse them terribly.

Overall, I'm in favour of merging this PR as it is, and we can work out what the right external interface is once we have a bit of experience with the feature. +1.

@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 1, 2019

The concrete use-case is to publish a binary image and then let people choose between connecting it
to OTA Connect or just try it out with updates coming automatically over TUF. I was thinking that we
could switch between these by the choice of which credentials.zip they write to the flash.

I may be missing something subtle here, but my current thinking for the images Foundries.io built was to pretty much keep things as-is with 2 new things:

  • a /usr/bin/aktualizr-lite binary
  • a /var/sota/lite.toml file that will work with our server

We already include a tool that will allow a user to register with Uptane if they prefer. So, then its just a runtime choice (do i want lite or uptane).

@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 1, 2019

Sort of a tangent, but I thought it might make sense to mention here. I've created a really simple docker-compose for running the OTA Connect parts required for this new mode:

https://github.com/doanac/ota-compose

doanac added a commit to doanac/aktualizr that referenced this issue Feb 2, 2019
imagesrepository: Add a targets iterator
This is related to issue:
      advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 4, 2019
imagesrepository: Add a targets iterator
This is related to issue:
      advancedtelematic#1056

In order for an application to find the latest target, it will need
access to the list of targets found in the image repository.

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 19, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 20, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 20, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 24, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
doanac added a commit to doanac/aktualizr that referenced this issue Feb 27, 2019
aktualizr-lite: Create skeleton application
This adds the skeleton build and CLI arg-parsing logic for a new
command "aktualizr-lite" that can run aktualizr in an anonymous
mode without Uptane and only use TUF and OSTree.

see: advancedtelematic#1056

Signed-off-by: Andy Doan <andy@foundries.io>
@doanac

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 5, 2019

I think we should close this issue now that aktualizr-lite has been merged. Any other issues are probably worth opening a new issue.

@doanac doanac closed this Mar 5, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.