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

feat: remote signing server and signature generalization #278

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

RaitoBezarius
Copy link
Member

@RaitoBezarius RaitoBezarius commented Jan 3, 2024

This PR is composed of ~four changes:

  • generalization of signature schemes in the tool
  • generalization of how to assemble stubs in the shared library
  • a remote signature server that sends you signed stub in exchange for signature requests
  • an implementation inside lzbt-systemd of remote signing

This enables moving the local signature to another server… or even maybe a hardware security token!

In the future, the server can ask for attestations or any kind of things to give the signed stub.

Note

Initrd secrets are not supported by the remote signer

This is out of scope for this PR and will probably not supported as I plan to bring systemd credentials
first inside of NixOS and remove the legacy initrd secrets which are a broken feature.

What were alternatives?

  • Offer a way to send the secret to the signer: NAK because an attacker could present a secret being a trivial privilege escalation binary.
  • Force the secret to be present on the signer: NAK because this would require at least a policy to allow / disallow access to a given secret based on the requester, this is too big for this PR.
    Therefore, initrd secrets will NOT work with the remote signer.

@RaitoBezarius
Copy link
Member Author

Replaces #228, I need to fix more conflicts and it should be good.

@RaitoBezarius RaitoBezarius force-pushed the signing-client branch 4 times, most recently from 80782c0 to 5dad068 Compare January 4, 2024 22:32
@nikstur nikstur marked this pull request as draft January 21, 2024 12:30
@nikstur
Copy link
Collaborator

nikstur commented Jan 21, 2024

I converted this to a draft since it depends on your fork.

rust/tool/server/Cargo.toml Outdated Show resolved Hide resolved
rust/tool/server/README.md Outdated Show resolved Hide resolved
server.wait_for_open_port(9999)
# Perform a switch to the remote configuration
# and contact the server to get the right bootables.
with subtest("Activation will request for remote signing"):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This means the server has to be available at the time re-building. This probably won't scale well but should be good enough for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, why wouldn't it scale well?

Copy link
Collaborator

Choose a reason for hiding this comment

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

What if the server is unavailable at the time of re-build? Does the re-building just fail? Do you want to add client-side retries? None of the solutions I can think of are super elegant.

Copy link
Member Author

Choose a reason for hiding this comment

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

The normal person usecase would be:

I have a dozen of machines and they all have Secure Boot, I'd like to rebuild switch without all of them having access to the private key, I can just contact my secure server with TPM2 or HSM who has the machinery to perform secure signatures and send it my stuff its way.

If my secure server is down, I cannot sign my new binaries.

What is this really saying is that remote secure signing is load bearing and you need high availability if you don't want to fail rebuilding your system in those scenarios.

High availability can be facilitated on our side by having multiple fallbacks we can try in a certain order, e.g. remote secure signing (convenient, fast, secure!) and then the fallback is you pull your Yubikey and sign it on the "field recovery" certificate or whatever.

There will be, once those PRs land, a whole discussion on how do you seriously manage this at scale.

nix/modules/lanzaboote.nix Outdated Show resolved Hide resolved
lanzasigndCrane = buildRustApp {
pname = "lanzasignd";
src = craneLib.cleanCargoSource ./rust/tool;
doCheck = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should have at least some rudimentary unit/integration tests for the server as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ack

rust/tool/shared/src/signature/mod.rs Outdated Show resolved Hide resolved

/// Verify the signature of a PE binary, provided as bytes.
/// Return true if the signature was verified.
fn verify(&self, pe_binary: &[u8]) -> Result<bool>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's a little odd that the Signer trait can also verify.

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 can make it like signature and derive a VerifyingKey handle and you can call verify on that but it seems like overengineering to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, that'd be overengineering. Is there a higher-level concept that encapsulates both verifying and signing? Not a big issue if we keep the Signer terminology and just add a comment.

Copy link
Member Author

Choose a reason for hiding this comment

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

A notary?

rust/tool/server/src/main.rs Outdated Show resolved Hide resolved
rust/tool/shared/Cargo.toml Outdated Show resolved Hide resolved
rust/tool/server/Cargo.toml Outdated Show resolved Hide resolved
…t packages

Now, it is possible to build any package of the workspace in a fine grained fashion.
We want here to capture the required data to assemble a stub, here is a partial structure
modulo ESP generation paths.

Other pieces of code can consume this structure, validate it before passing it to the PE assembler
and the signer.

We convert everything into owned structures because we cannot really do
deserialization in any context with lifetimes going around, but,
allocations are generally very cheap in this context.
In order to offer more flexible signature mechanisms in lanzaboote,
we need to take a step back and offer a general PE signature trait.

After this, we will be able to plug various different implementations.
@RaitoBezarius
Copy link
Member Author

I converted this to a draft since it depends on your fork.

This is not dependent on a fork anymore, now.

@RaitoBezarius RaitoBezarius marked this pull request as ready for review January 25, 2024 02:15
Remote signing enables a user to request for:

- PE signature of a given store path, which is assumed to be available on the server side
- PE signature oi a stub given by its parameters, which are assumed to be analyzable on the server side
  (i.e. computing hashes is possible.)
- Verifying if a PE signature is correct according to PE signatures and Secure Boot policy
It is now possible to use remote signature inside lzbt-systemd.
This is an example server to perform remote signatures
based on stub parameters provided.
Our lanzaboote integration tests are getting more and more sophisticated and ambitious.

Let's extract them into a "lanzalib", so they can be used with multiple backends.
We build lanzasignd now as part of the flake as an additional software we provide.
Introduces the Secure Boot remote signing server for NixOS.
Lanzaboote boot module now supports using a potential remote signer server,
but this support is limited to the lanzaboote bootables and not the fwupd ones.
A simple test harness for remote signatures with lanzasignd.
We didn't test if there *was* a signature, idempotency of removal of signatures (i.e. removing an non-existent signature
is the identity operation) could fool us into believing we had a signed thing then not signed.
This is relevant for a remote signer who relies on the existence of store paths
remotely, for example.
We fabricated a lot of initrds which were exactly the same as the one in
our store when we had no initrd secrets. This ends this practice.
personal todo:

- returns stub parameters in the toplevel setup
- verify paths for local keypair
- build and sign a stub, verify it using the API, verify it locally (?)
- build a stub, sign a "store path" (actually a temporary location on the same system), verify it using the API
- sign a random file, verify it using the API
@RaitoBezarius
Copy link
Member Author

Personal TODO and expectations for reviewers:

  • I need to update the flake build of signd.
  • I need to fix the wiring up for the integration testing.

@nikstur Do you have anything else you would like me to address for this?

@blitz
Copy link
Member

blitz commented Feb 10, 2024

Given that this PR is currently gigantic and you say it consists of four distinct parts, can we possibly split it in multiple PRs for review?

From my perspective we are also missing overview documentation here.

@RaitoBezarius
Copy link
Member Author

Given that this PR is currently gigantic and you say it consists of four distinct parts, can we possibly split it in multiple PRs for review?

From my perspective we are also missing overview documentation here.

Sure!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants