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

[MRESOLVER-301] Artifact generators SPI and GnuPG signature generator #432

Merged
merged 1 commit into from Feb 26, 2024

Conversation

cstamas
Copy link
Member

@cstamas cstamas commented Feb 19, 2024

Artifact generators and one implementation: GnuPG Signer.

The "signer" generator is simple module for signing artifacts with GnuPG Signer.


https://issues.apache.org/jira/browse/MRESOLVER-301

@cstamas cstamas self-assigned this Feb 19, 2024
@cstamas cstamas marked this pull request as ready for review February 19, 2024 18:00
@slawekjaranowski
Copy link
Member

At first time looks a complicated a bit.

I can test it in next weekend - I need more time for it.

first questions:

  • What is difference about gpg singnature and md5, sha checksums - why not use the same same existing implementation - we simply want generate a checksum or signature

  • What future will be for m-gpg-p when we move generating of signatures to resolver and finaly to Maven core ... yes I see that it is a extension but ...

By the way I think we should discuss such (for me big) architecture change on ML.

@cstamas
Copy link
Member Author

cstamas commented Feb 20, 2024

Q1: checksums: they are tightly coupled into "basic connector". Historically, "connector" was the transport abstraction, but later "connector + transport" was introduced, and connector deals with checksums ONLY. Original issue was "deal with signatures as with checksums" but this is a no-go, as connector is (theoretically) swappable, while true, we have only "basic" connector and no other implementation. IMHO, we should eventually remove the abstraction from connector and accept that "basic" is THE connector we use.
Q2: First, it is an extension, yes. Second, I see no problem with resolver signing, as to me, it seems like "natural fit". If you think about, signer plugins usually do something and "attach" the result. Am fine with both, and as far I am concerned, the two can coexists even (maybe add some logic to signer to remain dormant if signatures discovered).

@cstamas
Copy link
Member Author

cstamas commented Feb 20, 2024

Also, "artifact generator" is one thing, and its use for "signing" is another.

If we remain at "publishing to Central" domain, where PGP signature is enforced, and signing, I am not satisfied with any of existing solutions:

  • maven-sign-plugin uses gpg executable
  • takari-sign-plugin cannot do ED25519 (but have cool ideas)
  • s4u sign plugin unused in ASF (but have cool ideas)

So I just "brought" the best of all here. At least, that was my intent. And yes, IMO, "signing" is natural fit for "artifact generator" and IMO we should not complicate our build/POMs for something that is an expected requirement (is like we'd need to add a plugin to POM to create checksums, something also required to publish to Central).

Also, "signer" is extensible, so it does not have to get GnuPG, it could be something else as well... so in this way, it is not in Maven Core (wired in), but can progress and change, maybe as an extension.

@michael-o michael-o self-requested a review February 21, 2024 12:28
Copy link
Member

@michael-o michael-o left a comment

Choose a reason for hiding this comment

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

I don't have any opinion and cannot object.

@michael-o michael-o self-requested a review February 21, 2024 13:08
Copy link
Member

@michael-o michael-o left a comment

Choose a reason for hiding this comment

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

I'll leave the decision to others.

@cstamas
Copy link
Member Author

cstamas commented Feb 22, 2024

By the way I think we should discuss such (for me big) architecture change on ML.

@slawekjaranowski this is not "big architectural change" at all, just another option. I modified current PR to:

  • enabled GnuPG signer step back, if signatures detected among deployable artifacts (ie bui;d uses plugin that produces .asc signatures)
  • by default signer is disabled (even if present as extension)

Copy link
Contributor

@gnodet gnodet left a comment

Choose a reason for hiding this comment

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

Mostly coding style issues


import static java.util.Objects.requireNonNull;

final class SignerArtifactGenerator implements ArtifactGenerator {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not public instead of final with a public constructor and protected fields ? Why would not people be allowed to extend it ?

@Named(GpgEnvPasswordLoader.NAME)
@Priority(50)
@SuppressWarnings("checkstyle:magicnumber")
public final class GpgEnvPasswordLoader implements GpgSignerFactory.KeyPasswordLoader {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the final keyword needed ?

@Singleton
@Named(GpgFileRingMaterialLoader.NAME)
@Priority(10)
public final class GpgFileRingMaterialLoader implements GpgSignerFactory.KeyRingMaterialLoader {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

* GnuPG {@link Signer} implementation that is kept and reused across session.
*/
@SuppressWarnings("checkstyle:magicnumber")
public final class GpgSigner implements Signer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same...

});
}

private void sign(InputStream content, OutputStream signature) throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

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

protected ?

@@ -151,7 +162,23 @@ private DeployResult deploy(SyncContext syncContext, RepositorySystemSession ses
throw new DeploymentException("Failed to deploy artifacts/metadata: " + e.getMessage(), e);
}

final List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see how final is useful. The static analysis very well knows if a variable is final or not.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@@ -95,53 +106,96 @@ private InstallResult install(SyncContext syncContext, RepositorySystemSession s

RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);

List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request);
final List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
Copy link
Contributor

Choose a reason for hiding this comment

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

Once again ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@cstamas
Copy link
Member Author

cstamas commented Feb 22, 2024

For other a general answer: I do agree with all your comments, but

  • Resolver whole codebase is done as this, and we may want to change all these, but for now there are a lot of package protected and final classes, I did not want to disturb this coherence. But I do agree about non-final/public/protected fields.
  • BUT, extending JSR330 annotated component is a no-no (hence final)
  • I like how these classes are being prevented to be tampered with. If someone needs this, can copy, but I don't want to be hindered a year after, as somecome tells me I broke "compatibility" with his app (where he uses or extends these classes).

So, yes, I agree, but this is why not IMO.

@cstamas
Copy link
Member Author

cstamas commented Feb 23, 2024

Added UT for signer (for now does not assert much). Also verified locally the whole signing workflow along with agent, and it all works nicely, while verified produced signatures with gpg CLI.

The test on purpose use Ed25519 key, as that is where the future moves to.

Copy link
Member

@slawekjaranowski slawekjaranowski left a comment

Choose a reason for hiding this comment

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

There are many new Factories ... do we need all of them?


@Singleton
@Named(SignerArtifactGeneratorFactory.NAME)
public final class SignerArtifactGeneratorFactory implements ArtifactGeneratorFactory {
Copy link
Member

Choose a reason for hiding this comment

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

It is only one implementation of SignerArtifactGeneratorFactory
why we put it in new module ... maybe should be in maven-resolver-impl

*
* @since 2.0.0
*/
public interface SignerFactory {
Copy link
Member

Choose a reason for hiding this comment

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

next factory similar to ArtifactGeneratorFactory

.map(a -> {
HashMap<String, String> properties = new HashMap<>(a.getProperties());
properties.put(
SignerArtifactGeneratorFactory.ARTIFACT_SIGNER_ID,
Copy link
Member

Choose a reason for hiding this comment

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

This property ARTIFACT_SIGNER_ID is only used in this place .... why we add it?

try {
List<Artifact> generatedArtifacts = new ArrayList<>();
for (ArtifactGenerator artifactGenerator : artifactGenerators) {
Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts);
Copy link
Member

Choose a reason for hiding this comment

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

generatedArtifacts is an empty list at beginning - so how we discover artifacts to sign in artifactGenerator.generate

Copy link
Member

Choose a reason for hiding this comment

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

ok - I see we heve materialized artifact list in org.eclipse.aether.generator.signer.SignerArtifactGenerator
It is look too complicated

Comment on lines 37 to 38
<!-- To make Jetty + generator-signer work -->
<javaVersion>17</javaVersion>
Copy link
Member

Choose a reason for hiding this comment

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

I only see that UnixDomainSocketAddress used in GpgAgentPasswordLoader requres JDK 16+
so I don't see connections with Jetty

Copy link
Member Author

Choose a reason for hiding this comment

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

Originally it was 11 as Jetty 10.x we use as client and as server (in http-test) required 11, but now upped to 17.
Will change it: "jetty needs 11, generator-signer needs 17"

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@Named(GpgSignerFactory.NAME)
public final class GpgSignerFactory implements SignerFactory {
public static final String NAME = GpgConfigurationKeys.NAME;
private static final String SIGNER_KEY = GpgSignerFactory.class.getName() + ".signer";
Copy link
Member

Choose a reason for hiding this comment

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

unused constant

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

}

Long keyId = null;
String keyIdStr = ConfigUtils.getString(session, null, CONFIG_PROP_KEY_ID);
Copy link
Member

Choose a reason for hiding this comment

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

keyId should also be provided by environment value

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed, now only exists "loaders" (3 of them: env, conf and agent)

@cstamas
Copy link
Member Author

cstamas commented Feb 24, 2024

@slawekjaranowski general remark:

  • spi defines ArtifactGenerators, there can exist more of those in the system
  • generator-signer hooks into this (is one implementation of generators), and define Signers
  • gpg is one Signer implementation

But also:

  • there may be multiple generators, see how they are ordered, and the idea is that "signer generator" is last, and it should sign all the deployed but also "so far generated" artifacts (if they are relevant).
  • gpg may not be the only one signer either
  • moreover, another signer may sign differently than gpp. Gpg signs each artifact, but another signer may provide one signature for all "relevant" artifacts.

@cstamas
Copy link
Member Author

cstamas commented Feb 24, 2024

@michael-o @slawekjaranowski @gnodet pls review again.

Reworked, as layout was "improper" for signer (is transport and remote repository related). What was actually missing was the ArtifactPredicate (that recognizes artifact hashes and also configured extensions without hashes). This config was never per-remote repository, so it felt right to be moved to new component. Maven2 repo layout updated accordingly. Finally, this change revealed a (doc) bug: remote repo checksums ARE configurable per remote repository, but doco did not reflect that.

@cstamas
Copy link
Member Author

cstamas commented Feb 24, 2024

So to recap, what I tried to explain to Slawek. This PR:

  • defines Artifact Generator SPI (there may be multiple of those present in system)
  • defines Signer Artifact Generator that is one provider for SPI, that defines Signer API (there may be multiple of those present in system)
  • implements GpgSigner that is one Signer implementation provided out of the box

Naturally, as generators are ordered, signer generator is expected to be "among last" and sign not only artifacts in request (install/deploy) but also "so far generated artifacts" (if they are relevant).

@gnodet
Copy link
Contributor

gnodet commented Feb 25, 2024

So to recap, what I tried to explain to Slawek. This PR:

  • defines Artifact Generator SPI (there may be multiple of those present in system)
  • defines Signer Artifact Generator that is one provider for SPI, that defines Signer API (there may be multiple of those present in system)

Now that you put it that way, I'm not really convinced we need a Signer SPI here. The code of the ArtifactGenerator which calls the signers is trivial, so it seems to me that the GpgSigner could directly implement the Artifact Generator SPI.

  • implements GpgSigner that is one Signer implementation provided out of the box

Naturally, as generators are ordered, signer generator is expected to be "among last" and sign not only artifacts in request (install/deploy) but also "so far generated artifacts" (if they are relevant).

@cstamas
Copy link
Member Author

cstamas commented Feb 25, 2024

Now that you put it that way, I'm not really convinced we need a Signer SPI here. The code of the ArtifactGenerator which calls the signers is trivial, so it seems to me that the GpgSigner could directly implement the Artifact Generator SPI.

Agreed, will simplify it more, but the question begs: "generator-signer" module name becomes wrong, no? Should it be maybe "generator-gnupg (or gpg)" instead?

@gnodet
Copy link
Contributor

gnodet commented Feb 25, 2024

Now that you put it that way, I'm not really convinced we need a Signer SPI here. The code of the ArtifactGenerator which calls the signers is trivial, so it seems to me that the GpgSigner could directly implement the Artifact Generator SPI.

Agreed, will simplify it more, but the question begs: "generator-signer" module name becomes wrong, no? Should it be maybe "generator-gnupg (or gpg)" instead?

Yes, I think that would make more sense.

@cstamas cstamas changed the title [MRESOLVER-301] Artifact generators [MRESOLVER-301] Artifact generators SPI and GnuPG signature generator Feb 25, 2024
@cstamas
Copy link
Member Author

cstamas commented Feb 26, 2024

Collapsed code and renamed to generator-gnupg

@cstamas cstamas merged commit e152bf3 into apache:master Feb 26, 2024
4 checks passed
@cstamas cstamas deleted the MRESOLVER-301 branch February 26, 2024 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants