Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
Aktualizr "Anonymous" Mode #1056
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.
I'd like to propose a new "anonymous" feature for aktualizr with a minimal set of requirements:
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:
Pieces a client would need:
OSTree Pull and Install: The “ostree manager” handles this, and seems to have no uptane dependencies.
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:
Global flags include:
I can't speak to the backend much, but I can talk to the people that can or get them involved directly.
I don't think that's supported in aktualizr now, but we are open to trying other varieties of authentication.
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.
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.
I think we've managed to keep those separate! They should be if they are not already entirely.
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!
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:
NOTE: The branch from my previous comment is now in decent shape and should be easy to review:
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.
Yes, and we could use the
@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.
@patrickvacek - Glad things seem feasible.
I think if we want to move the logic of aktualizr-lite into aktualizr, then changing aktualizr-info with this makes sense.
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).
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
not needed. that's an accident while i was debugging something.
//from a new branch
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.
Actually, I was assuming that we would keep
Would it make sense to move
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
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?
That is a reasonable point.
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.
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:
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).