rpm-rs / rpm

Other
47 stars 25 forks source link

Unable to read & update package header outside of rpm crate #80

Open TommyLike opened 1 year ago

TommyLike commented 1 year ago

Background I am trying to do a remote pgp sign by utilizing this rpm package, but it seems the header and signature functions are public to crate only, how can we read&update these sections outside of rpm package?

package.metadata.header.**write**(&mut header_bytes)?;
package.metadata.signature = Header::<IndexSignatureTag>::**new_signature_header**();
TommyLike commented 1 year ago

@drahnr @dralley could you take a look?

dralley commented 1 year ago

There is a sign() method on Package which is public, is that not usable in your case?

TommyLike commented 1 year ago

@dralley yeah, what I need is to split the content and do a remote signature then assemble them back into signature.

drahnr commented 1 year ago

So from what I understand, you send the hash to a remote entity which ows your signing key rather than signing it locally?

I'd probably recommend to implement a custom Signer https://github.com/rpm-rs/rpm/blob/41723d969242c61b8e752a929e905730a18f2590/src/rpm/signature/traits.rs#L27 where the actual signing is deferred, so https://github.com/rpm-rs/rpm/blob/41723d969242c61b8e752a929e905730a18f2590/src/rpm/signature/pgp.rs#L44 would be invoked on the remote with None rather than SHA256 and you'd have to calculate the hash locally, send it to your signing entity and return it.

dralley commented 1 year ago

@TommyLike I imagine the idea here is to implement obs-sign, or something like it, on your own - yes? That makes sense. A custom client perhaps?

TommyLike commented 1 year ago

@drahnr yeah, I am trying to create rust version of obs sign with more efficient, secure and support more key and file types..

drahnr commented 1 year ago

But then the method of choice should be to implement the mentioned traits. If you have trouble with that, or the API is insufficient, please open another issue :)

I am not sure there is anything to do given the generality of the issue at hand?

TommyLike commented 1 year ago

But then the method of choice should be to implement the mentioned traits. If you have trouble with that, or the API is insufficient, please open another issue :)

I am not sure there is anything to do given the generality of the issue at hand?

the question is in order to sign multiple rpms concurrently we seperated the signing process/trait into three different workers: split file->do remote sign->assemble signature in which we can't simply impl them all in this trait.

drahnr commented 1 year ago

'worker' meaning individual vms/nodes/machines? And that cannot be changed/re-partitioned?

My point is: you don't need to split, you don't need to assemble. All you need to do is sign the thing. If pkg content is living in a DB, then we'd need more info.

But to be frank, it feels like you're trying to form the cold end of the iron, or you need to provide a whole lot more info to make it reasonable to add such an API (a PR would be nice)

dralley commented 1 year ago

@drahnr I believe the basic idea of obs-sign (which I am not fully familiar with, so anyone please feel free to correct me) that you have a client which opens the RPM and makes a checksum of the relevant bits. The client can then pass only this checksum to an external service which manages the signing. The service then returns a signature which the client can inject into the RPM.

This approach avoids uploading the RPM contents themselves and keeps the signing key management contained to a single designated machine, which is easier to lock down.

Imagine a build system where you have multiple build servers. Putting the signing keys on each individual build server opens up more attack surface and opportunity for those keys to be compromised. You could probably dump all the unsigned RPMs onto a shared NFS drive, or upload full RPMs to a signing service, but maybe that involves too much data movement and/or potential for needlessly sharing sensitive data? So instead you just pass around the checksum.

@TommyLike Is that a good summary?

TommyLike commented 1 year ago

@drahnr I believe the basic idea of obs-sign (which I am not fully familiar with, so anyone please feel free to correct me) that you have a client which opens the RPM and makes a checksum of the relevant bits. The client can then pass only this checksum to an external service which manages the signing. The service then returns a signature which the client can inject into the RPM.

This approach avoids uploading the RPM contents themselves and keeps the signing key management contained to a single designated machine, which is easier to lock down.

Imagine a build system where you have multiple build servers. Putting the signing keys on each individual build server opens up more attack surface and opportunity for those keys to be compromised. You could probably dump all the unsigned RPMs onto a shared NFS drive, or upload full RPMs to a signing service, but maybe that involves too much data movement and/or potential for needlessly sharing sensitive data? So instead you just pass around the checksum.

@TommyLike Is that a good summary?

@dralley exactly 😄 , much appreciated for your explanation.

drahnr commented 1 year ago
x ---{ digest }--> y
x <--{ signature }-- y

where x is the location/machine where the rpm is stored, and y is the location/machine the signature is created.

Replacing the signature is doable, the Header<Sign...> is a public field in https://docs.rs/rpm/latest/rpm/struct.RPMPackageMetadata.html - which can built with a signature builder.

Now all you need is digest of the relevant pieces. The code is https://docs.rs/rpm/latest/src/rpm/rpm/builder.rs.html#470-493 and could be copied or be made public (assuming we'd add a few more types to improve type safety).

Technically, there is nothing stopping you from implementing all of this in a type that implements Signer (running on x), given sufficient flexibility of your existing codebase.

If you need further help, then you'd need to provide a whole lot more context on the things you can change, and the constraints you have. Otherwise, that's as much support I can give around using rpm-rs.

I hope this helps

TommyLike commented 1 year ago

@drahnr sure

  1. this is the changes I made from official rpm-rs project: https://github.com/rpm-rs/rpm/compare/master...TommyLike:rpm-rs:master
  2. and this is how we utilize the updated rpm-rs in our demo project: https://github.com/TommyLike/signatrust/blob/main/src/client/file_handler/rpm.rs check the split_data and assemble_data functions
dralley commented 1 year ago

@TommyLike If you do plan to work on this could you take it as assigned?

TommyLike commented 1 year ago

@dralley sure, will create PR when ready.

dralley commented 5 months ago

@TommyLike In https://github.com/rpm-rs/rpm/pull/225 I'm removing all of the implementation that uses legacy signatures and digests, which includes the header + payload ones. That simplifies the problem quite a bit.

I think what you should be able to do is pull the sha256 checksum of the header out of the signature header, and then sign it, then build your new signature header with the sha256 checksum and the signature.

But if you like, I can look at making a function available to just serialize the header so that you can generate whatever checksum you want