From 0eb32eb19c6b12bdb0bf9109eca0a24b80c0930d Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Thu, 17 Oct 2024 16:03:12 +0100 Subject: [PATCH 1/5] Report missing signature --- .../instmgr/InstMgrListUpdatesHandler.java | 4 ++ .../instmgr/InstMgrPrepareRevertHandler.java | 4 ++ .../instmgr/InstMgrPrepareUpdateHandler.java | 4 ++ pom.xml | 2 +- .../TestInstallationManager.java | 43 +++++++++++++++++-- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java index efaf649265b..547a71934b4 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java @@ -27,6 +27,7 @@ import org.wildfly.core.instmgr.logging.InstMgrLogger; import org.wildfly.installationmanager.ArtifactChange; import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.MissingSignatureException; import org.wildfly.installationmanager.Repository; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; @@ -177,6 +178,9 @@ public void handleResult(OperationContext.ResultAction resultAction, OperationCo throw new OperationFailedException(e.getLocalizedMessage()); } catch (OperationFailedException | RuntimeException e) { throw e; + } catch (MissingSignatureException e) { + throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", + e.getDescription()), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java index eaffed1b9bc..9c653d553ba 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java @@ -26,6 +26,7 @@ import org.jboss.dmr.ModelType; import org.wildfly.core.instmgr.logging.InstMgrLogger; import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.MissingSignatureException; import org.wildfly.installationmanager.Repository; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; @@ -147,6 +148,9 @@ public void execute(OperationContext context, ModelNode operation) throws Operat throw new OperationFailedException(e.getLocalizedMessage()); } catch (OperationFailedException | RuntimeException e) { throw e; + } catch (MissingSignatureException e) { + throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", + e.getDescription()), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java index ee1aad22d4f..021bf00f79f 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java @@ -29,6 +29,7 @@ import org.jboss.dmr.ModelType; import org.wildfly.core.instmgr.logging.InstMgrLogger; import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.MissingSignatureException; import org.wildfly.installationmanager.Repository; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; @@ -189,6 +190,9 @@ public void execute(OperationContext context, ModelNode operation) throws Operat throw new OperationFailedException(e.getLocalizedMessage()); } catch (OperationFailedException | RuntimeException e) { throw e; + } catch (MissingSignatureException e) { + throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", + e.getDescription()), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/pom.xml b/pom.xml index a4371fa2245..9b03bf193b3 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ 1.0.1.Final 1.7.0.Final 1.3.0.Final - 1.0.3.Final + 2.0.0.Beta1-SNAPSHOT 8.0.2.Final 2.2.5.Final 2.2.2.Final diff --git a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java index 91b5a3928e4..7f8e7ef1dc1 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java @@ -8,6 +8,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; @@ -15,6 +16,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -22,14 +24,17 @@ import java.util.zip.ZipOutputStream; import org.wildfly.installationmanager.ArtifactChange; +import org.wildfly.installationmanager.CandidateType; import org.wildfly.installationmanager.Channel; import org.wildfly.installationmanager.ChannelChange; +import org.wildfly.installationmanager.FileConflict; import org.wildfly.installationmanager.HistoryResult; import org.wildfly.installationmanager.InstallationChanges; import org.wildfly.installationmanager.ManifestVersion; import org.wildfly.installationmanager.MavenOptions; import org.wildfly.installationmanager.OperationNotAvailableException; import org.wildfly.installationmanager.Repository; +import org.wildfly.installationmanager.TrustCertificate; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.OsShell; @@ -119,10 +124,10 @@ public static void initialize() throws IOException { // History sample data history = new HashMap<>(); - history.put("update", new HistoryResult("update", Instant.now(), "update", "update description")); - history.put("install", new HistoryResult("install", Instant.now(), "install", "install description")); - history.put("rollback", new HistoryResult("rollback", Instant.now(), "rollback", "rollback description")); - history.put("config_change", new HistoryResult("config_change", Instant.now(), "config_change", "config_change description")); + history.put("update", new HistoryResult("update", Instant.now(), "update", "update description", Collections.emptyList())); + history.put("install", new HistoryResult("install", Instant.now(), "install", "install description", Collections.emptyList())); + history.put("rollback", new HistoryResult("rollback", Instant.now(), "rollback", "rollback description", Collections.emptyList())); + history.put("config_change", new HistoryResult("config_change", Instant.now(), "config_change", "config_change description", Collections.emptyList())); // List Updates sample Data @@ -266,11 +271,41 @@ public String generateApplyRevertCommand(Path scriptHome, Path candidatePath, Os return scriptHome + APPLY_REVERT_BASE_GENERATED_COMMAND + candidatePath.toString(); } + @Override + public String generateApplyUpdateCommand(Path scriptHome, Path candidatePath, OsShell shell, boolean noConflictsOnly) throws OperationNotAvailableException { + return scriptHome + APPLY_UPDATE_BASE_GENERATED_COMMAND + candidatePath.toString(); + } + + @Override + public String generateApplyRevertCommand(Path scriptHome, Path candidatePath, OsShell shell, boolean noConflictsOnly) throws OperationNotAvailableException { + return scriptHome + APPLY_REVERT_BASE_GENERATED_COMMAND + candidatePath.toString(); + } + @Override public Collection getInstalledVersions() throws Exception { return installedVersions; } + @Override + public Collection verifyCandidate(Path candidatePath, CandidateType candidateType) throws Exception { + return Collections.emptySet(); + } + + @Override + public void acceptTrustedCertificates(InputStream certificate) throws Exception { + + } + + @Override + public void revokeTrustedCertificate(String keyID) throws Exception { + + } + + @Override + public Collection listCA() throws Exception { + return null; + } + public static void zipDir(Path inputFile, Path target) throws IOException { try (FileOutputStream fos = new FileOutputStream(target.toFile()); ZipOutputStream zos = new ZipOutputStream(fos)) { ZipEntry entry = new ZipEntry(inputFile.getFileName().toString()); From 0154a293daac40e987a60d7b4bde7532eac9ae97 Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Thu, 17 Oct 2024 16:04:32 +0100 Subject: [PATCH 2/5] Adding commands to manager trusted certificates --- .../InstMgrCertificateImportHandler.java | 75 +++++++++++++++ .../InstMgrCertificateParseHandler.java | 87 +++++++++++++++++ .../InstMgrCertificateRemoveHandler.java | 70 ++++++++++++++ .../core/instmgr/InstMgrConstants.java | 6 ++ .../instmgr/InstMgrResourceDefinition.java | 63 +++++++++++++ .../instmgr/cli/AbstractInstMgrCommand.java | 5 +- .../instmgr/cli/AddCertificatesCommand.java | 82 ++++++++++++++++ .../core/instmgr/cli/InstMgrGroupCommand.java | 5 +- .../instmgr/cli/ListCertificatesCommand.java | 93 +++++++++++++++++++ .../cli/RemoveCertificatesCommand.java | 45 +++++++++ .../core/instmgr/LocalDescriptions.properties | 5 + 11 files changed, 534 insertions(+), 2 deletions(-) create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/cli/ListCertificatesCommand.java create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/cli/RemoveCertificatesCommand.java diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java new file mode 100644 index 00000000000..cdaadbba509 --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java @@ -0,0 +1,75 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTACHED_STREAMS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILESYSTEM_PATH; + +import java.io.InputStream; +import java.nio.file.Path; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationDefinition; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleOperationDefinitionBuilder; +import org.jboss.as.controller.registry.OperationEntry; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.spi.InstallationManager; +import org.wildfly.installationmanager.spi.InstallationManagerFactory; + +/** + * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as + * channel changes. + */ +public class InstMgrCertificateImportHandler extends InstMgrOperationStepHandler { + public static final String OPERATION_NAME = "certificate-import"; + + protected static final AttributeDefinition CERT_FILE = SimpleAttributeDefinitionBuilder.create(InstMgrConstants.CERT_FILE, ModelType.INT) + .setStorageRuntime() + .setRequired(true) + .addArbitraryDescriptor(FILESYSTEM_PATH, ModelNode.TRUE) + .addArbitraryDescriptor(ATTACHED_STREAMS, ModelNode.TRUE) + .build(); + + public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, InstMgrResolver.RESOLVER) + .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY) + .setReplyType(ModelType.OBJECT) + .setRuntimeOnly() + .setReplyValueType(ModelType.OBJECT) + .addParameter(CERT_FILE) + .build(); + + InstMgrCertificateImportHandler(InstMgrService imService, InstallationManagerFactory imf) { + super(imService, imf); + } + + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + try { + Path serverHome = imService.getHomeDir(); + MavenOptions mavenOptions = new MavenOptions(null, false); + InstallationManager installationManager = imf.create(serverHome, mavenOptions); + + try (InputStream is = context.getAttachmentStream(CERT_FILE.resolveModelAttribute(context, operation).asInt())) { + installationManager.acceptTrustedCertificates(is); + } + } catch (OperationFailedException | RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, OperationContext.Stage.RUNTIME); + } +} diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java new file mode 100644 index 00000000000..5a16df4ddec --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java @@ -0,0 +1,87 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTACHED_STREAMS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILESYSTEM_PATH; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FINGERPRINT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_STATUS; + +import java.io.InputStream; +import java.nio.file.Path; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationDefinition; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleOperationDefinitionBuilder; +import org.jboss.as.controller.registry.OperationEntry; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.TrustCertificate; +import org.wildfly.installationmanager.spi.InstallationManager; +import org.wildfly.installationmanager.spi.InstallationManagerFactory; + +/** + * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as + * channel changes. + */ +public class InstMgrCertificateParseHandler extends InstMgrOperationStepHandler { + public static final String OPERATION_NAME = "certificate-parse"; + + protected static final AttributeDefinition CERT_FILE = SimpleAttributeDefinitionBuilder.create(InstMgrConstants.CERT_FILE, ModelType.INT) + .setStorageRuntime() + .setRequired(true) + .addArbitraryDescriptor(FILESYSTEM_PATH, ModelNode.TRUE) + .addArbitraryDescriptor(ATTACHED_STREAMS, ModelNode.TRUE) + .build(); + + public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, InstMgrResolver.RESOLVER) + .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY) + .setReplyType(ModelType.OBJECT) + .setRuntimeOnly() + .setReplyValueType(ModelType.OBJECT) + .addParameter(CERT_FILE) + .build(); + + InstMgrCertificateParseHandler(InstMgrService imService, InstallationManagerFactory imf) { + super(imService, imf); + } + + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + try { + Path serverHome = imService.getHomeDir(); + MavenOptions mavenOptions = new MavenOptions(null, false); + InstallationManager installationManager = imf.create(serverHome, mavenOptions); + + try (InputStream is = context.getAttachmentStream(CERT_FILE.resolveModelAttribute(context, operation).asInt())) { + TrustCertificate tc = installationManager.parseCA(is); + + ModelNode entry = new ModelNode(); + entry.get(CERT_KEY_ID).set(tc.getKeyID()); + entry.get(CERT_FINGERPRINT).set(tc.getFingerprint()); + entry.get(CERT_DESCRIPTION).set(tc.getDescription()); + entry.get(CERT_STATUS).set(tc.getStatus()); + context.getResult().set(entry); + } + } catch (OperationFailedException | RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, OperationContext.Stage.RUNTIME); + } +} diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java new file mode 100644 index 00000000000..be6b0e1ac22 --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr; + +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; + +import java.nio.file.Path; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationDefinition; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleOperationDefinitionBuilder; +import org.jboss.as.controller.registry.OperationEntry; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.spi.InstallationManager; +import org.wildfly.installationmanager.spi.InstallationManagerFactory; + +/** + * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as + * channel changes. + */ +public class InstMgrCertificateRemoveHandler extends InstMgrOperationStepHandler { + public static final String OPERATION_NAME = "certificate-remove"; + + protected static final AttributeDefinition KEY_ID = SimpleAttributeDefinitionBuilder.create(CERT_KEY_ID, ModelType.STRING) + .setStorageRuntime() + .setRequired(true) + .build(); + + public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, InstMgrResolver.RESOLVER) + .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY) + .setReplyType(ModelType.OBJECT) + .setRuntimeOnly() + .setReplyValueType(ModelType.OBJECT) + .addParameter(KEY_ID) + .build(); + + InstMgrCertificateRemoveHandler(InstMgrService imService, InstallationManagerFactory imf) { + super(imService, imf); + } + + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + final String keyId = KEY_ID.resolveModelAttribute(context, operation).asString(); + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + try { + Path serverHome = imService.getHomeDir(); + MavenOptions mavenOptions = new MavenOptions(null, false); + InstallationManager installationManager = imf.create(serverHome, mavenOptions); + + installationManager.revokeTrustedCertificate(keyId); + } catch (OperationFailedException | RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, OperationContext.Stage.RUNTIME); + } +} diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java index 2abebd93f1f..a68b5a7c788 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java @@ -15,6 +15,12 @@ public interface InstMgrConstants { Path PREPARED_SERVER_SUBPATH = Paths.get("installation-manager") .resolve("prepared-server"); + String CERT_DESCRIPTION = "description"; + String CERT_FINGERPRINT = "fingerprint"; + String CERT_KEY_ID = "key-id"; + String CERT_STATUS = "status"; + String CERT_FILE = "cert-file"; + String CERTIFICATES = "certificates"; String CHANNEL = "channel"; String CHANNELS = "channels"; String CHANNEL_NAME = "name"; diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java index 890cb8c088a..364ce63bd1f 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java @@ -39,6 +39,7 @@ import org.wildfly.installationmanager.Channel; import org.wildfly.installationmanager.MavenOptions; import org.wildfly.installationmanager.Repository; +import org.wildfly.installationmanager.TrustCertificate; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; @@ -86,6 +87,12 @@ class InstMgrResourceDefinition extends SimpleResourceDefinition { .setRequired(true) .build(); + private static final AttributeDefinition KEY_ID = new SimpleAttributeDefinitionBuilder(InstMgrConstants.CERT_KEY_ID, ModelType.STRING) + .setStorageRuntime() + .setRuntimeServiceNotRequired() + .setRequired(true) + .build(); + private static final AttributeDefinition MANIFEST_GAV = new SimpleAttributeDefinitionBuilder(InstMgrConstants.MANIFEST_GAV, ModelType.STRING) .setAlternatives(InstMgrConstants.MANIFEST_URL) .setStorageRuntime() @@ -115,11 +122,23 @@ class InstMgrResourceDefinition extends SimpleResourceDefinition { .setSuffix("channel") .build(); + private static final ObjectTypeAttributeDefinition CERTIFICATE = ObjectTypeAttributeDefinition.create("certificate", KEY_ID) +// .setValidator(new ChannelValidator()) + .setStorageRuntime() + .setRuntimeServiceNotRequired() + .setRequired(true) + .setSuffix("certificate") + .build(); + private static final AttributeDefinition CHANNELS = ObjectListAttributeDefinition.Builder.of(InstMgrConstants.CHANNELS, CHANNEL) .setStorageRuntime() .setRuntimeServiceNotRequired() .build(); + private static final AttributeDefinition CERTIFICATES = ObjectListAttributeDefinition.Builder.of("certificates", CERTIFICATE) + .setStorageRuntime() + .setRuntimeServiceNotRequired() + .build(); public static PathElement getPath(String name) { return PathElement.pathElement(CORE_SERVICE, name); } @@ -167,11 +186,21 @@ public void registerOperations(ManagementResourceRegistration resourceRegistrati InstMgrCustomPatchRemoveHandler customPatchRemoveHandler = new InstMgrCustomPatchRemoveHandler(imService, imf); resourceRegistration.registerOperationHandler(customPatchRemoveHandler.DEFINITION, customPatchRemoveHandler); + + InstMgrCertificateParseHandler certificateParseHandler = new InstMgrCertificateParseHandler(imService, imf); + resourceRegistration.registerOperationHandler(InstMgrCertificateParseHandler.DEFINITION, certificateParseHandler); + + InstMgrCertificateImportHandler certificateImportHandler = new InstMgrCertificateImportHandler(imService, imf); + resourceRegistration.registerOperationHandler(InstMgrCertificateImportHandler.DEFINITION, certificateImportHandler); + + InstMgrCertificateRemoveHandler certificateRemoveHandler = new InstMgrCertificateRemoveHandler(imService, imf); + resourceRegistration.registerOperationHandler(InstMgrCertificateRemoveHandler.DEFINITION, certificateRemoveHandler); } @Override public void registerAttributes(ManagementResourceRegistration resourceRegistration) { resourceRegistration.registerReadWriteAttribute(CHANNELS, new ReadHandler(), new WriteHandler()); + resourceRegistration.registerReadOnlyAttribute(CERTIFICATES, new CertReadHandler()); } private class WriteHandler implements OperationStepHandler { @@ -364,4 +393,38 @@ public void validateParameter(String parameterName, ModelNode value) throws Oper } } } + + private class CertReadHandler implements OperationStepHandler { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + try { + final ModelNode result = context.getResult(); + Path serverHome = imService.getHomeDir(); + + MavenOptions mavenOptions = new MavenOptions(null, false); + InstallationManager installationManager = imf.create(serverHome, mavenOptions); + + ModelNode mCertificates = new ModelNode().addEmptyList(); + Collection trustedCertificates = installationManager.listCA(); + for (TrustCertificate tc : trustedCertificates) { + ModelNode entry = new ModelNode(); + entry.get(InstMgrConstants.CERT_KEY_ID).set(tc.getKeyID()); + entry.get(InstMgrConstants.CERT_FINGERPRINT).set(tc.getFingerprint()); + entry.get(InstMgrConstants.CERT_DESCRIPTION).set(tc.getDescription()); + entry.get(InstMgrConstants.CERT_STATUS).set(tc.getStatus()); + mCertificates.add(entry); + } + result.set(mCertificates); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, OperationContext.Stage.RUNTIME); + } + } } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AbstractInstMgrCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AbstractInstMgrCommand.java index 578f77b9aeb..85c6d3cdad2 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AbstractInstMgrCommand.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AbstractInstMgrCommand.java @@ -61,6 +61,10 @@ public abstract class AbstractInstMgrCommand implements Command { public static final String COMMAND_NAME = "installer"; diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/ListCertificatesCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/ListCertificatesCommand.java new file mode 100644 index 00000000000..c2873e67ffe --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/ListCertificatesCommand.java @@ -0,0 +1,93 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr.cli; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FINGERPRINT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_STATUS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.aesh.command.CommandDefinition; +import org.aesh.command.CommandException; +import org.aesh.command.CommandResult; +import org.jboss.as.cli.CommandContext; +import org.jboss.as.cli.Util; +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.as.controller.client.Operation; +import org.jboss.as.controller.client.OperationBuilder; +import org.jboss.dmr.ModelNode; +import org.wildfly.core.cli.command.aesh.CLICommandInvocation; +import org.wildfly.core.instmgr.InstMgrConstants; + +@CommandDefinition(name = "certificates-list", description = "List trusted component certificates.", activator = InstMgrActivator.class) +public class ListCertificatesCommand extends AbstractInstMgrCommand { + + @Override + protected Operation buildOperation() { + final ModelNode op = new ModelNode(); + op.get(Util.OPERATION).set(Util.READ_RESOURCE); + op.get(Util.INCLUDE_RUNTIME).set(true); + + return OperationBuilder.create(op, true).build(); + } + + @Override + public CommandResult execute(CLICommandInvocation commandInvocation) throws CommandException, InterruptedException { + final CommandContext ctx = commandInvocation.getCommandContext(); + final ModelControllerClient client = ctx.getModelControllerClient(); + if (client == null) { + ctx.printLine("You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands."); + return CommandResult.FAILURE; + } + + ModelNode response = this.executeOp(commandInvocation.getCommandContext(), this.host); + ModelNode result = response.get(Util.RESULT); + List certificatesMn = result.get(InstMgrConstants.CERTIFICATES).asListOrEmpty(); + + if (certificatesMn.isEmpty()) { + ctx.printLine("No component certificates have been trusted for this installation."); + + return CommandResult.SUCCESS; + } + + ctx.printLine("-------"); + for (ModelNode certificate : certificatesMn) { + ctx.printLine("key ID " + certificate.get(CERT_KEY_ID).asString()); + ctx.printLine("fingerprint " + certificate.get(CERT_FINGERPRINT).asString()); + ctx.printLine("description " + certificate.get(CERT_DESCRIPTION).asString()); + ctx.printLine("status " + certificate.get(CERT_STATUS).asString()); + ctx.printLine("-------"); + } + return CommandResult.SUCCESS; + } + + /** + * Returns a Set with the current channel names available on the server installation + * + * @param ctx + * @param host + * @return + * @throws CommandException + */ + Set getAllChannelNames(CommandContext ctx, String host) throws CommandException { + final Set existingChannelNames = new HashSet<>(); + ModelNode listCmdResponse = this.executeOp(ctx, host); + if (listCmdResponse.hasDefined(RESULT)) { + ModelNode result = listCmdResponse.get(RESULT); + List channels = result.get(InstMgrConstants.CHANNELS).asListOrEmpty(); + for (ModelNode channel : channels) { + existingChannelNames.add(channel.get(NAME).asString()); + } + } + return existingChannelNames; + } +} diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/RemoveCertificatesCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/RemoveCertificatesCommand.java new file mode 100644 index 00000000000..e33ccaa8460 --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/RemoveCertificatesCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr.cli; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; + +import org.aesh.command.CommandDefinition; +import org.aesh.command.CommandException; +import org.aesh.command.CommandResult; +import org.aesh.command.option.Option; +import org.jboss.as.cli.CommandContext; +import org.jboss.as.controller.client.Operation; +import org.jboss.as.controller.client.OperationBuilder; +import org.jboss.dmr.ModelNode; +import org.wildfly.core.cli.command.aesh.CLICommandInvocation; +import org.wildfly.core.instmgr.InstMgrCertificateRemoveHandler; + +@CommandDefinition(name = "certificates-remove", description = "Removes a trusted component certificate.", activator = InstMgrActivator.class) +public class RemoveCertificatesCommand extends AbstractInstMgrCommand { + + @Option(name = CERT_KEY_ID, required = true) + private String keyId; + @Override + protected Operation buildOperation() { + final ModelNode op = new ModelNode(); + + op.get(OP).set(InstMgrCertificateRemoveHandler.DEFINITION.getName()); + op.get(CERT_KEY_ID).set(keyId); + + return OperationBuilder.create(op).build(); + } + + @Override + public CommandResult execute(CLICommandInvocation commandInvocation) throws CommandException, InterruptedException { + final CommandContext ctx = commandInvocation.getCommandContext(); + + this.executeOp(ctx, this.host); + + return CommandResult.SUCCESS; + } +} diff --git a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties index 4b6c3af1692..549341f2d05 100644 --- a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties +++ b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties @@ -82,3 +82,8 @@ installation-manager.custom-patch.upload-custom-patch.custom-patch-file=A custom installation-manager.custom-patch.remove-custom-patch=Removes a custom patch from the base server. It also removes the channel that provides this custom patch and unsubscribes the installation from this channel. installation-manager.custom-patch.remove-custom-patch.manifest=Channel Manifest maven coordinates associated with the custom patch. Expected format is Maven GA (GroupId:ArtifactId). +installation-manager.certificate-list=Lists certificates used to verify components. +installation-manager.certificate-parse=Parses a certificate +installation-manager.certificate-parse.cert-file=A certificate file +installation-manager.certificate-import=Add a certificate to verify installation components. +installation-manager.certificate-remove=Remove a certificate to verify installation components. From adb2652319908472cd9cc712fbaf7321f4d828cb Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Fri, 18 Oct 2024 13:32:04 +0100 Subject: [PATCH 3/5] Add tests --- .../core/instmgr/LocalDescriptions.properties | 4 + .../core/instmgr/InstMgrResourceTestCase.java | 115 ++++++++++++++++++ .../TestInstallationManager.java | 44 ++++++- 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties index 549341f2d05..3ada3c9f96b 100644 --- a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties +++ b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties @@ -86,4 +86,8 @@ installation-manager.certificate-list=Lists certificates used to verify componen installation-manager.certificate-parse=Parses a certificate installation-manager.certificate-parse.cert-file=A certificate file installation-manager.certificate-import=Add a certificate to verify installation components. +installation-manager.certificate-import.cert-file=Path to the certificate that will be used to verify updated components. installation-manager.certificate-remove=Remove a certificate to verify installation components. +installation-manager.certificate-remove.key-id=The ID of a certificate to be removed from the list of certificates trusted to verify updated components. +installation-manager.certificates=Certificates used to verify components. +installation-manager.certificates.certificate.key-id=The ID of a certificate used to verify components. diff --git a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java index 7e65046d51d..ab4a3738bb0 100644 --- a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java +++ b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java @@ -17,6 +17,11 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESPONSE_HEADERS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FINGERPRINT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_STATUS; import java.io.File; import java.io.FileInputStream; @@ -65,7 +70,9 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.wildfly.installationmanager.ArtifactChange; import org.wildfly.installationmanager.Channel; import org.wildfly.installationmanager.ChannelChange; @@ -86,6 +93,9 @@ public class InstMgrResourceTestCase extends AbstractControllerTestBase { static final Path JBOSS_CONTROLLER_TEMP_DIR = JBOSS_HOME.resolve("temp"); static final Path INSTALLATION_MANAGER_PROPERTIES = JBOSS_HOME.resolve("bin").resolve("installation-manager.properties"); + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + @Before public void setupController() throws InterruptedException, IOException { TestInstallationManager.initialized = false; @@ -357,6 +367,111 @@ public void testRemoveChannels() throws Exception { ); } + @Test + public void testReadCertificates() throws Exception { + PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode op = Util.createEmptyOperation(READ_RESOURCE_OPERATION, pathElements); + op.get(INCLUDE_RUNTIME).set(true); + + ModelNode result = executeForResult(op); + Assert.assertTrue(result.isDefined()); + + // validate the result: + List certs = result.get(InstMgrConstants.CERTIFICATES).asListOrEmpty(); + Assert.assertEquals(1, certs.size()); + + // First Channel + ModelNode cert = certs.get(0); + Assert.assertEquals("abcd", cert.get(CERT_KEY_ID).asString()); + Assert.assertEquals("abcd1234", cert.get(CERT_FINGERPRINT).asString()); + Assert.assertEquals("Test Cert", cert.get(CERT_DESCRIPTION).asString()); + Assert.assertEquals("TRUSTED", cert.get(CERT_STATUS).asString()); + } + + @Test + public void testRemoveCertificate() throws Exception { + PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode op = Util.createEmptyOperation(InstMgrCertificateRemoveHandler.OPERATION_NAME, pathElements); + op.get(INCLUDE_RUNTIME).set(true); + op.get(CERT_KEY_ID).set("abcd"); + + executeForResult(op); + + // verify there are no certificates now + pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode listOp = Util.createEmptyOperation(READ_RESOURCE_OPERATION, pathElements); + listOp.get(INCLUDE_RUNTIME).set(true); + + ModelNode result = executeForResult(listOp); + Assert.assertTrue(result.isDefined()); + + // validate the result is an empty list: + List certs = result.get(InstMgrConstants.CERTIFICATES).asListOrEmpty(); + Assert.assertEquals(0, certs.size()); + } + + @Test + public void testAddCertificate() throws Exception { + PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode op = Util.createEmptyOperation(InstMgrCertificateImportHandler.OPERATION_NAME, pathElements); + op.get(INCLUDE_RUNTIME).set(true); + op.get(CERT_FILE).set(0); + final OperationBuilder builder = OperationBuilder.create(op); + final File file = temp.newFile("test.crt"); + Files.writeString(file.toPath(), "key-id:efgh\nfingerprint:efgh5678\ndescription:Another Cert"); + builder.addFileAsAttachment(file); + + executeForResult(builder.build()); + + // verify there are two certificates now + pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode listOp = Util.createEmptyOperation(READ_RESOURCE_OPERATION, pathElements); + listOp.get(INCLUDE_RUNTIME).set(true); + + ModelNode result = executeForResult(listOp); + Assert.assertTrue(result.isDefined()); + + // validate the result has two elements: + List certs = result.get(InstMgrConstants.CERTIFICATES).asListOrEmpty(); + Assert.assertEquals(2, certs.size()); + + // First Channel + ModelNode cert = certs.get(0); + Assert.assertEquals("abcd", cert.get(CERT_KEY_ID).asString()); + Assert.assertEquals("abcd1234", cert.get(CERT_FINGERPRINT).asString()); + Assert.assertEquals("Test Cert", cert.get(CERT_DESCRIPTION).asString()); + Assert.assertEquals("TRUSTED", cert.get(CERT_STATUS).asString()); + + // Second Channel + cert = certs.get(1); + Assert.assertEquals("efgh", cert.get(CERT_KEY_ID).asString()); + Assert.assertEquals("efgh5678", cert.get(CERT_FINGERPRINT).asString()); + Assert.assertEquals("Another Cert", cert.get(CERT_DESCRIPTION).asString()); + Assert.assertEquals("TRUSTED", cert.get(CERT_STATUS).asString()); + } + + @Test + public void testParseCertificate() throws Exception { + PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode op = Util.createEmptyOperation(InstMgrCertificateParseHandler.OPERATION_NAME, pathElements); + op.get(INCLUDE_RUNTIME).set(true); + op.get(CERT_FILE).set(0); + final OperationBuilder builder = OperationBuilder.create(op); + final File file = temp.newFile("test.crt"); + Files.writeString(file.toPath(), "key-id:efgh\nfingerprint:efgh5678\ndescription:Another Cert"); + builder.addFileAsAttachment(file); + + ModelNode result = executeForResult(builder.build()); + Assert.assertTrue(result.isDefined()); + + // validate the result is a new cert: + ModelNode cert = result.asObject(); + Assert.assertEquals("efgh", cert.get(CERT_KEY_ID).asString()); + Assert.assertEquals("efgh5678", cert.get(CERT_FINGERPRINT).asString()); + Assert.assertEquals("Another Cert", cert.get(CERT_DESCRIPTION).asString()); + Assert.assertEquals("TRUSTED", cert.get(CERT_STATUS).asString()); + } + @Test public void testHistoryChannels() throws Exception { PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); diff --git a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java index 7f8e7ef1dc1..ae371a20e9e 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java @@ -5,10 +5,13 @@ package org.wildfly.test.installationmanager; +import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; @@ -20,6 +23,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -49,6 +53,7 @@ public class TestInstallationManager implements InstallationManager { public static List findUpdatesRepositories; public static List findUpdatesChanges; public static List prepareUpdatesRepositories; + private static List lstTrustCertificates; public static Path prepareUpdatesTargetDir; public static List prepareRevertRepositories; @@ -162,6 +167,10 @@ public static void initialize() throws IOException { } } + // certificates sample data + lstTrustCertificates = new ArrayList<>(); + lstTrustCertificates.add(new TrustCertificate("abcd", "abcd1234", "Test Cert", "TRUSTED")); + initialized = true; } } @@ -293,17 +302,50 @@ public Collection verifyCandidate(Path candidatePath, CandidateTyp @Override public void acceptTrustedCertificates(InputStream certificate) throws Exception { + final TrustCertificate trustCertificate = parseCA(certificate); + lstTrustCertificates.add(trustCertificate); } @Override public void revokeTrustedCertificate(String keyID) throws Exception { + final Optional cert = lstTrustCertificates.stream() + .filter(c -> c.getKeyID().equals(keyID)) + .findFirst(); + cert.ifPresent(trustCertificate -> lstTrustCertificates.remove(trustCertificate)); } @Override public Collection listCA() throws Exception { - return null; + return Collections.unmodifiableCollection(lstTrustCertificates); + } + + @Override + public TrustCertificate parseCA(InputStream certificate) throws Exception { + String keyId = null; + String fingerprint = null; + String description = null; + try(BufferedReader reader = new BufferedReader(new InputStreamReader(certificate))) { + while (reader.ready()) { + final String[] line = reader.readLine().split(":"); + switch (line[0]) { + case "key-id": + keyId = line[1]; + break; + case "fingerprint": + fingerprint = line[1]; + break; + case "description": + description = line[1]; + break; + default: + throw new RuntimeException("Unknown line: " + line[0]); + } + } + } + + return new TrustCertificate(keyId, fingerprint, description, "TRUSTED"); } public static void zipDir(Path inputFile, Path target) throws IOException { From 2106ab7dcb9e95b4a9d3919826d5a55428301788 Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Tue, 22 Oct 2024 13:39:48 +0100 Subject: [PATCH 4/5] Check required certificates before performing update --- .../InstMgrCertificateParseHandler.java | 2 +- .../instmgr/InstMgrResourceDefinition.java | 5 +- .../InstMgrUnacceptedCertificateHandler.java | 93 +++++++++++++++++++ .../instmgr/cli/AddCertificatesCommand.java | 8 ++ .../core/instmgr/cli/UpdateCommand.java | 77 +++++++++++++++ .../core/instmgr/LocalDescriptions.properties | 2 + .../core/instmgr/InstMgrResourceTestCase.java | 18 ++++ .../TestInstallationManager.java | 16 +++- 8 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java index 5a16df4ddec..54299229ee4 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java @@ -67,7 +67,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat InstallationManager installationManager = imf.create(serverHome, mavenOptions); try (InputStream is = context.getAttachmentStream(CERT_FILE.resolveModelAttribute(context, operation).asInt())) { - TrustCertificate tc = installationManager.parseCA(is); + TrustCertificate tc = installationManager.parseCertificate(is); ModelNode entry = new ModelNode(); entry.get(CERT_KEY_ID).set(tc.getKeyID()); diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java index 364ce63bd1f..8ba1e987c68 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java @@ -195,6 +195,9 @@ public void registerOperations(ManagementResourceRegistration resourceRegistrati InstMgrCertificateRemoveHandler certificateRemoveHandler = new InstMgrCertificateRemoveHandler(imService, imf); resourceRegistration.registerOperationHandler(InstMgrCertificateRemoveHandler.DEFINITION, certificateRemoveHandler); + + InstMgrUnacceptedCertificateHandler unacceptedcertificateHandler = new InstMgrUnacceptedCertificateHandler(imService, imf); + resourceRegistration.registerOperationHandler(InstMgrUnacceptedCertificateHandler.DEFINITION, unacceptedcertificateHandler); } @Override @@ -408,7 +411,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat InstallationManager installationManager = imf.create(serverHome, mavenOptions); ModelNode mCertificates = new ModelNode().addEmptyList(); - Collection trustedCertificates = installationManager.listCA(); + Collection trustedCertificates = installationManager.listTrustedCertificates(); for (TrustCertificate tc : trustedCertificates) { ModelNode entry = new ModelNode(); entry.get(InstMgrConstants.CERT_KEY_ID).set(tc.getKeyID()); diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java new file mode 100644 index 00000000000..6701f251a02 --- /dev/null +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.instmgr; + +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; + +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Collection; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationDefinition; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleOperationDefinitionBuilder; +import org.jboss.as.controller.registry.OperationEntry; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.spi.InstallationManager; +import org.wildfly.installationmanager.spi.InstallationManagerFactory; +import org.xnio.streams.Streams; + +/** + * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as + * channel changes. + */ +public class InstMgrUnacceptedCertificateHandler extends InstMgrOperationStepHandler { + public static final String OPERATION_NAME = "unaccepted-certificates"; + + protected static final AttributeDefinition OFFLINE = SimpleAttributeDefinitionBuilder.create(InstMgrConstants.OFFLINE, ModelType.BOOLEAN) + .setStorageRuntime() + .setDefaultValue(ModelNode.FALSE) + .setRequired(false) + .build(); + + public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, InstMgrResolver.RESOLVER) + .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY) + .setReplyType(ModelType.OBJECT) + .setRuntimeOnly() + .setReplyValueType(ModelType.OBJECT) + .addParameter(OFFLINE) + .build(); + + InstMgrUnacceptedCertificateHandler(InstMgrService imService, InstallationManagerFactory imf) { + super(imService, imf); + } + + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + try { + final Path serverHome = imService.getHomeDir(); + final Path controllerTempDir = imService.getControllerTempDir(); + final boolean offline = OFFLINE.resolveModelAttribute(context, operation).asBoolean(); + final MavenOptions mavenOptions = new MavenOptions(null, offline); + final InstallationManager installationManager = imf.create(serverHome, mavenOptions); + + final Collection downloadedCerts = installationManager.downloadRequiredCertificates(); + + final ModelNode mCertificates = new ModelNode().addEmptyList(); + int i=0; + for (InputStream is : downloadedCerts) { + final Path certFile = controllerTempDir.resolve("required-cert-" + i++ + ".crt"); + try (FileOutputStream fos = new FileOutputStream(certFile.toFile())) { + Streams.copyStream(is, fos); + is.close(); + } + + final ModelNode entry = new ModelNode(); + entry.get(CERT_FILE).set(certFile.toAbsolutePath().toString()); + mCertificates.add(entry); + } + + final ModelNode result = context.getResult(); + result.set(mCertificates); + } catch (OperationFailedException | RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, OperationContext.Stage.RUNTIME); + } +} diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java index 12879dd2e45..1ff6746259f 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java @@ -34,6 +34,14 @@ public class AddCertificatesCommand extends AbstractInstMgrCommand { @Option(name = "non-interactive") private boolean nonInteractive; + public AddCertificatesCommand() { + } + + public AddCertificatesCommand(File certFile, boolean nonInteractive) { + this.certFile = certFile; + this.nonInteractive = nonInteractive; + } + @Override protected Operation buildOperation() { final ModelNode op = new ModelNode(); diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java index 52ebf8993db..9c916b6eb6b 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java @@ -5,10 +5,18 @@ package org.wildfly.core.instmgr.cli; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; +import static org.wildfly.core.instmgr.InstMgrConstants.OFFLINE; + import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; @@ -23,9 +31,12 @@ import org.jboss.as.cli.operation.ParsedCommandLine; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.Operation; +import org.jboss.as.controller.client.OperationBuilder; import org.jboss.dmr.ModelNode; import org.wildfly.core.cli.command.aesh.CLICommandInvocation; +import org.wildfly.core.instmgr.InstMgrCertificateParseHandler; import org.wildfly.core.instmgr.InstMgrConstants; +import org.wildfly.core.instmgr.InstMgrUnacceptedCertificateHandler; @CommandDefinition(name = "update", description = "Apply the latest available patches on a server instance.", activator = InstMgrActivator.class) public class UpdateCommand extends AbstractInstMgrCommand { @@ -68,6 +79,13 @@ public CommandResult execute(CLICommandInvocation commandInvocation) throws Comm final Boolean optNoResolveLocalCache = cmdParser.hasProperty("--" + NO_RESOLVE_LOCAL_CACHE_OPTION) ? noResolveLocalCache : null; final Boolean optUseDefaultLocalCache = cmdParser.hasProperty("--" + USE_DEFAULT_LOCAL_CACHE_OPTION) ? useDefaultLocalCache : null; + // call the download handler + Collection pendingCertificates = getPendingCertificates(ctx); + // call the import handler + if (!importPendingCertificates(pendingCertificates, ctx, commandInvocation)) { + return CommandResult.SUCCESS; + } + ListUpdatesAction.Builder listUpdatesCmdBuilder = new ListUpdatesAction.Builder() .setNoResolveLocalCache(optNoResolveLocalCache) .setUseDefaultLocalCache(optUseDefaultLocalCache) @@ -146,6 +164,65 @@ public CommandResult execute(CLICommandInvocation commandInvocation) throws Comm return CommandResult.SUCCESS; } + private boolean importPendingCertificates(Collection pendingCertificates, CommandContext ctx, CLICommandInvocation commandInvocation) throws CommandException, InterruptedException { + commandInvocation.println("The update is configured to verify the integrity of updated components, but following certificates need to be trusted:"); + + for (Path pendingCertificate : pendingCertificates) { + final ModelNode modelNode = this.executeOp(buildParseOperation(pendingCertificate), ctx, this.host).get(RESULT); + + commandInvocation.println("key-id: " + modelNode.get(InstMgrConstants.CERT_KEY_ID)); + commandInvocation.println("fingerprint: " + modelNode.get(InstMgrConstants.CERT_FINGERPRINT)); + commandInvocation.println("description: " + modelNode.get(InstMgrConstants.CERT_DESCRIPTION)); + commandInvocation.println(""); + + } + final String input = commandInvocation.inputLine(new Prompt("Import these certificates y/N ")); + if ("y".equals(input)) { + commandInvocation.print("Importing a trusted certificate"); + + for (Path pendingCertificate : pendingCertificates) { + new AddCertificatesCommand(pendingCertificate.toFile(), false).executeOp(ctx, this.host); + } + + return true; + } else { + commandInvocation.print("Importing canceled."); + return false; + } + } + + protected Operation buildParseOperation(Path pendingCertificate) { + final ModelNode op = new ModelNode(); + final OperationBuilder operationBuilder = OperationBuilder.create(op); + + op.get(OP).set(InstMgrCertificateParseHandler.DEFINITION.getName()); + op.get(CERT_FILE).set(0); + operationBuilder.addFileAsAttachment(pendingCertificate.toFile()); + + return operationBuilder.build(); + } + + private Collection getPendingCertificates(CommandContext ctx) throws CommandException { + if (confirm || dryRun) { + // skip the check in non-interactive runs because the certificate cannot be accepted either way + // the update will fail if certificate is required and will print error message + return Collections.emptyList(); + } + + final ModelNode op = new ModelNode(); + + op.get(OP).set(InstMgrUnacceptedCertificateHandler.DEFINITION.getName()); + op.get(OFFLINE).set(offline); + + final ModelNode modelNode = executeOp(OperationBuilder.create(op).build(), ctx, this.host); + + final List paths = modelNode.get(RESULT).asListOrEmpty(); + return paths.stream() + .map(n->n.get(CERT_FILE).asString()) + .map(Path::of) + .collect(Collectors.toList()); + } + private void printListUpdatesResult(CLICommandInvocation commandInvocation, List changesMn) { if (changesMn.isEmpty()) { commandInvocation.println("No updates found"); diff --git a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties index 3ada3c9f96b..142353efdbf 100644 --- a/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties +++ b/installation-manager/src/main/resources/org/wildfly/core/instmgr/LocalDescriptions.properties @@ -91,3 +91,5 @@ installation-manager.certificate-remove=Remove a certificate to verify installat installation-manager.certificate-remove.key-id=The ID of a certificate to be removed from the list of certificates trusted to verify updated components. installation-manager.certificates=Certificates used to verify components. installation-manager.certificates.certificate.key-id=The ID of a certificate used to verify components. +installation-manager.unaccepted-certificates=Downloads certificates listed in the server channels, but not trusted yet. +installation-manager.unaccepted-certificates.offline=Only resolve certificates available locally. diff --git a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java index ab4a3738bb0..e68df71e459 100644 --- a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java +++ b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java @@ -11,10 +11,13 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATIONS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RECURSIVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESPONSE_HEADERS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; @@ -1545,6 +1548,21 @@ public void removeNonExistentCustomPatch() throws IOException { ); } + @Test + public void downloadUnacceptedCertificates() throws IOException { + TestInstallationManager.initialize(); + + PathAddress pathElements = PathAddress.pathAddress(CORE_SERVICE, InstMgrConstants.TOOL_NAME); + ModelNode op = Util.createEmptyOperation(InstMgrUnacceptedCertificateHandler.OPERATION_NAME, pathElements); + + ModelNode rsp = getController().execute(op, null, null, null); + Assert.assertEquals(SUCCESS, rsp.get(OUTCOME).asString()); + + final String certText = Files.readString(Path.of(rsp.get(RESULT).asList().get(0).get(CERT_FILE).asString())); + + Assert.assertEquals("test cert", certText); + } + /** * Creates and upload a custom patch associated to the customPatchManifest passed as argument. * It will use as Zip content the content available in the "test-repo-one" resource directory. diff --git a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java index ae371a20e9e..1839a0616f1 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java @@ -6,14 +6,15 @@ package org.wildfly.test.installationmanager; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; @@ -302,7 +303,7 @@ public Collection verifyCandidate(Path candidatePath, CandidateTyp @Override public void acceptTrustedCertificates(InputStream certificate) throws Exception { - final TrustCertificate trustCertificate = parseCA(certificate); + final TrustCertificate trustCertificate = parseCertificate(certificate); lstTrustCertificates.add(trustCertificate); } @@ -317,12 +318,12 @@ public void revokeTrustedCertificate(String keyID) throws Exception { } @Override - public Collection listCA() throws Exception { + public Collection listTrustedCertificates() throws Exception { return Collections.unmodifiableCollection(lstTrustCertificates); } @Override - public TrustCertificate parseCA(InputStream certificate) throws Exception { + public TrustCertificate parseCertificate(InputStream certificate) throws Exception { String keyId = null; String fingerprint = null; String description = null; @@ -348,6 +349,13 @@ public TrustCertificate parseCA(InputStream certificate) throws Exception { return new TrustCertificate(keyId, fingerprint, description, "TRUSTED"); } + @Override + public Collection downloadRequiredCertificates() throws Exception { + final String cert = "test cert"; + final ByteArrayInputStream bais = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8)); + return List.of(bais); + } + public static void zipDir(Path inputFile, Path target) throws IOException { try (FileOutputStream fos = new FileOutputStream(target.toFile()); ZipOutputStream zos = new ZipOutputStream(fos)) { ZipEntry entry = new ZipEntry(inputFile.getFileName().toString()); From 3158ecdbc111735424a881980329acc24ce7da86 Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Tue, 29 Oct 2024 15:48:35 +0000 Subject: [PATCH 5/5] Clean up and fixes --- .../ModelDescriptionConstants.java | 1 + .../installation-manager/main/module.xml | 1 + .../InstMgrCertificateImportHandler.java | 33 +++++++++++-- .../InstMgrCertificateParseHandler.java | 7 ++- .../InstMgrCertificateRemoveHandler.java | 5 +- .../core/instmgr/InstMgrConstants.java | 2 + .../instmgr/InstMgrListUpdatesHandler.java | 3 +- .../instmgr/InstMgrPrepareRevertHandler.java | 3 +- .../instmgr/InstMgrPrepareUpdateHandler.java | 3 +- .../instmgr/InstMgrResourceDefinition.java | 3 +- .../InstMgrUnacceptedCertificateHandler.java | 49 +++++++++++++------ .../instmgr/cli/AddCertificatesCommand.java | 8 +-- .../core/instmgr/cli/UpdateCommand.java | 42 +++++++--------- .../core/instmgr/logging/InstMgrLogger.java | 6 +++ .../instmgr/cli/command_resources.properties | 26 +++++++++- .../core/instmgr/InstMgrResourceTestCase.java | 7 ++- pom.xml | 2 +- .../TestInstallationManager.java | 2 +- 18 files changed, 138 insertions(+), 65 deletions(-) diff --git a/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java b/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java index 04f524ffb84..ba209e5746c 100644 --- a/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java +++ b/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java @@ -84,6 +84,7 @@ public class ModelDescriptionConstants { public static final String CAPABILITY_REFERENCE = "capability-reference"; public static final String CAPABILITY_REFERENCE_PATTERN_ELEMENTS = "capability-reference-pattern-elements"; public static final String CAPABILITY_REGISTRY = "capability-registry"; + public static final String CERTIFICATE_INFO = "certificate-info"; public static final String CHILD_TYPE = "child-type"; public static final String CHILDREN = "children"; public static final String CLASSIFICATION = "classification"; diff --git a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/installation-manager/main/module.xml b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/installation-manager/main/module.xml index 920f26e0fe4..61d41c6b4f3 100644 --- a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/installation-manager/main/module.xml +++ b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/installation-manager/main/module.xml @@ -25,5 +25,6 @@ + diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java index cdaadbba509..67aaff0e50e 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateImportHandler.java @@ -7,8 +7,11 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTACHED_STREAMS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILESYSTEM_PATH; +import static org.wildfly.core.instmgr.InstMgrConstants.CERTIFICATE_CONTENT; +import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import org.jboss.as.controller.AttributeDefinition; @@ -21,23 +24,28 @@ import org.jboss.as.controller.registry.OperationEntry; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; +import org.wildfly.core.instmgr.logging.InstMgrLogger; import org.wildfly.installationmanager.MavenOptions; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; /** - * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as - * channel changes. + * Operation handler to import a certificate used to validate components installed during updates. + * The certificate has to be an ASCII-armored PGP public certificate. */ public class InstMgrCertificateImportHandler extends InstMgrOperationStepHandler { public static final String OPERATION_NAME = "certificate-import"; protected static final AttributeDefinition CERT_FILE = SimpleAttributeDefinitionBuilder.create(InstMgrConstants.CERT_FILE, ModelType.INT) .setStorageRuntime() - .setRequired(true) + .setRequired(false) .addArbitraryDescriptor(FILESYSTEM_PATH, ModelNode.TRUE) .addArbitraryDescriptor(ATTACHED_STREAMS, ModelNode.TRUE) .build(); + protected static final AttributeDefinition CERT_CONTENT = SimpleAttributeDefinitionBuilder.create(CERTIFICATE_CONTENT, ModelType.STRING) + .setStorageRuntime() + .setRequired(false) + .build(); public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, InstMgrResolver.RESOLVER) .withFlags(OperationEntry.Flag.HOST_CONTROLLER_ONLY) @@ -61,8 +69,23 @@ public void execute(OperationContext context, ModelNode operation) throws Operat MavenOptions mavenOptions = new MavenOptions(null, false); InstallationManager installationManager = imf.create(serverHome, mavenOptions); - try (InputStream is = context.getAttachmentStream(CERT_FILE.resolveModelAttribute(context, operation).asInt())) { - installationManager.acceptTrustedCertificates(is); + final Integer streamIndex = CERT_FILE.resolveModelAttribute(context, operation).asIntOrNull(); + final String certificateContent = CERT_CONTENT.resolveModelAttribute(context, operation).asStringOrNull(); + + if (streamIndex != null && certificateContent != null) { + throw InstMgrLogger.ROOT_LOGGER.mutuallyExclusiveOptions(CERT_FILE.getName(), CERT_CONTENT.getName()); + } + + if (streamIndex != null) { + try (InputStream is = context.getAttachmentStream(streamIndex)) { + installationManager.acceptTrustedCertificates(is); + } + } else if (certificateContent != null) { + try (InputStream is = new ByteArrayInputStream(certificateContent.getBytes(StandardCharsets.UTF_8))) { + installationManager.acceptTrustedCertificates(is); + } + } else { + // throw exception } } catch (OperationFailedException | RuntimeException e) { throw e; diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java index 54299229ee4..73dfe3a0e52 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateParseHandler.java @@ -31,8 +31,7 @@ import org.wildfly.installationmanager.spi.InstallationManagerFactory; /** - * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as - * channel changes. + * Operation handler to parse the certificate file and read the certificate information. Expects am ASCII-armored PGP public key. */ public class InstMgrCertificateParseHandler extends InstMgrOperationStepHandler { public static final String OPERATION_NAME = "certificate-parse"; @@ -67,9 +66,9 @@ public void execute(OperationContext context, ModelNode operation) throws Operat InstallationManager installationManager = imf.create(serverHome, mavenOptions); try (InputStream is = context.getAttachmentStream(CERT_FILE.resolveModelAttribute(context, operation).asInt())) { - TrustCertificate tc = installationManager.parseCertificate(is); + final TrustCertificate tc = installationManager.parseCertificate(is); - ModelNode entry = new ModelNode(); + final ModelNode entry = new ModelNode(); entry.get(CERT_KEY_ID).set(tc.getKeyID()); entry.get(CERT_FINGERPRINT).set(tc.getFingerprint()); entry.get(CERT_DESCRIPTION).set(tc.getDescription()); diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java index be6b0e1ac22..e8d6ae2105d 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrCertificateRemoveHandler.java @@ -24,8 +24,9 @@ import org.wildfly.installationmanager.spi.InstallationManagerFactory; /** - * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as - * channel changes. + * Operation handler to remove one of imported component certificates. The certificate is identified by it's ID (in hex format). + * Once the certificate is removed, the user will have to re-import it to apply and updates including a component + * signed by that certificate. */ public class InstMgrCertificateRemoveHandler extends InstMgrOperationStepHandler { public static final String OPERATION_NAME = "certificate-remove"; diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java index a68b5a7c788..dccd9f4a058 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrConstants.java @@ -20,6 +20,8 @@ public interface InstMgrConstants { String CERT_KEY_ID = "key-id"; String CERT_STATUS = "status"; String CERT_FILE = "cert-file"; + String CERTIFICATE = "certificate"; + String CERTIFICATE_CONTENT = "certificate-content"; String CERTIFICATES = "certificates"; String CHANNEL = "channel"; String CHANNELS = "channels"; diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java index 547a71934b4..9eb5da7d3ad 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrListUpdatesHandler.java @@ -179,8 +179,7 @@ public void handleResult(OperationContext.ResultAction resultAction, OperationCo } catch (OperationFailedException | RuntimeException e) { throw e; } catch (MissingSignatureException e) { - throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", - e.getDescription()), e); + throw InstMgrLogger.ROOT_LOGGER.componentSignedWithUnknownCertificate(e.getDescription(), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java index 9c653d553ba..05b89fb3fea 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareRevertHandler.java @@ -149,8 +149,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat } catch (OperationFailedException | RuntimeException e) { throw e; } catch (MissingSignatureException e) { - throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", - e.getDescription()), e); + throw InstMgrLogger.ROOT_LOGGER.componentSignedWithUnknownCertificate(e.getDescription(), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java index 021bf00f79f..a3cd98db673 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrPrepareUpdateHandler.java @@ -191,8 +191,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat } catch (OperationFailedException | RuntimeException e) { throw e; } catch (MissingSignatureException e) { - throw new OperationFailedException(String.format("One of the signatures in the update is signed by an unknown public key %s. Please import the key using import operation and try again.", - e.getDescription()), e); + throw InstMgrLogger.ROOT_LOGGER.componentSignedWithUnknownCertificate(e.getDescription(), e); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java index 8ba1e987c68..66fdfc562f0 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrResourceDefinition.java @@ -122,8 +122,7 @@ class InstMgrResourceDefinition extends SimpleResourceDefinition { .setSuffix("channel") .build(); - private static final ObjectTypeAttributeDefinition CERTIFICATE = ObjectTypeAttributeDefinition.create("certificate", KEY_ID) -// .setValidator(new ChannelValidator()) + private static final ObjectTypeAttributeDefinition CERTIFICATE = ObjectTypeAttributeDefinition.create(InstMgrConstants.CERTIFICATE, KEY_ID) .setStorageRuntime() .setRuntimeServiceNotRequired() .setRequired(true) diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java index 6701f251a02..b1d4e70b8e3 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/InstMgrUnacceptedCertificateHandler.java @@ -5,10 +5,18 @@ package org.wildfly.core.instmgr; -import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CERTIFICATE_INFO; +import static org.wildfly.core.instmgr.InstMgrConstants.CERTIFICATE_CONTENT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FINGERPRINT; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_KEY_ID; +import static org.wildfly.core.instmgr.InstMgrConstants.CERT_STATUS; -import java.io.FileOutputStream; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Collection; @@ -23,13 +31,13 @@ import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.wildfly.installationmanager.MavenOptions; +import org.wildfly.installationmanager.TrustCertificate; import org.wildfly.installationmanager.spi.InstallationManager; import org.wildfly.installationmanager.spi.InstallationManagerFactory; -import org.xnio.streams.Streams; /** - * Operation handler to get the history of the installation manager changes, either artifacts or configuration metadata as - * channel changes. + * Operation handler to get the certificates potentially required by the update or revert operation. Those certificates + * are listed by the channel and may be used to sign the components included in the channel. */ public class InstMgrUnacceptedCertificateHandler extends InstMgrOperationStepHandler { public static final String OPERATION_NAME = "unaccepted-certificates"; @@ -59,7 +67,6 @@ public void execute(OperationContext context, ModelNode operation) throws Operat public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { try { final Path serverHome = imService.getHomeDir(); - final Path controllerTempDir = imService.getControllerTempDir(); final boolean offline = OFFLINE.resolveModelAttribute(context, operation).asBoolean(); final MavenOptions mavenOptions = new MavenOptions(null, offline); final InstallationManager installationManager = imf.create(serverHome, mavenOptions); @@ -67,17 +74,31 @@ public void execute(OperationContext context, ModelNode operation) throws Operat final Collection downloadedCerts = installationManager.downloadRequiredCertificates(); final ModelNode mCertificates = new ModelNode().addEmptyList(); - int i=0; for (InputStream is : downloadedCerts) { - final Path certFile = controllerTempDir.resolve("required-cert-" + i++ + ".crt"); - try (FileOutputStream fos = new FileOutputStream(certFile.toFile())) { - Streams.copyStream(is, fos); - is.close(); + final ModelNode mCert = new ModelNode(); + try (BufferedInputStream bis = new BufferedInputStream(is)) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (int result = bis.read(); result != -1; result = bis.read()) { + buf.write((byte) result); + } + + final String certContent = buf.toString(StandardCharsets.UTF_8); + mCert.get(CERTIFICATE_CONTENT).set(certContent); + try (final ByteArrayInputStream bais = new ByteArrayInputStream(certContent.getBytes(StandardCharsets.UTF_8))) { + final TrustCertificate tc = installationManager.parseCertificate(bais); + final ModelNode info = new ModelNode(); + info.get(CERT_KEY_ID).set(tc.getKeyID()); + info.get(CERT_FINGERPRINT).set(tc.getFingerprint()); + info.get(CERT_DESCRIPTION).set(tc.getDescription()); + info.get(CERT_STATUS).set(tc.getStatus()); + context.getResult().set(info); + + mCert.get(CERTIFICATE_INFO).set(info); + } } - final ModelNode entry = new ModelNode(); - entry.get(CERT_FILE).set(certFile.toAbsolutePath().toString()); - mCertificates.add(entry); + + mCertificates.add(mCert); } final ModelNode result = context.getResult(); diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java index 1ff6746259f..6117a23deb8 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/AddCertificatesCommand.java @@ -59,7 +59,7 @@ protected Operation buildParseOperation() { final OperationBuilder operationBuilder = OperationBuilder.create(op); op.get(OP).set(InstMgrCertificateParseHandler.DEFINITION.getName()); - op.get("cert-file").set(0); + op.get(CERT_FILE).set(0); operationBuilder.addFileAsAttachment(certFile); return operationBuilder.build(); @@ -76,15 +76,15 @@ public CommandResult execute(CLICommandInvocation commandInvocation) throws Comm commandInvocation.println("fingerprint: " + modelNode.get(InstMgrConstants.CERT_FINGERPRINT)); commandInvocation.println("description: " + modelNode.get(InstMgrConstants.CERT_DESCRIPTION)); - final String input = commandInvocation.inputLine(new Prompt(String.format("Import this certificate y/N"))); + final String input = commandInvocation.inputLine(new Prompt("Import this certificate y/N")); if (nonInteractive || "y".equals(input)) { commandInvocation.print("Importing a trusted certificate"); + + this.executeOp(ctx, this.host); } else { commandInvocation.print("Importing canceled."); } - this.executeOp(ctx, this.host); - return CommandResult.SUCCESS; } } diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java index 9c916b6eb6b..0707526283a 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/cli/UpdateCommand.java @@ -5,9 +5,10 @@ package org.wildfly.core.instmgr.cli; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CERTIFICATE_INFO; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; -import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; +import static org.wildfly.core.instmgr.InstMgrConstants.CERTIFICATE_CONTENT; import static org.wildfly.core.instmgr.InstMgrConstants.OFFLINE; import java.io.File; @@ -16,7 +17,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; @@ -34,7 +34,7 @@ import org.jboss.as.controller.client.OperationBuilder; import org.jboss.dmr.ModelNode; import org.wildfly.core.cli.command.aesh.CLICommandInvocation; -import org.wildfly.core.instmgr.InstMgrCertificateParseHandler; +import org.wildfly.core.instmgr.InstMgrCertificateImportHandler; import org.wildfly.core.instmgr.InstMgrConstants; import org.wildfly.core.instmgr.InstMgrUnacceptedCertificateHandler; @@ -80,9 +80,9 @@ public CommandResult execute(CLICommandInvocation commandInvocation) throws Comm final Boolean optUseDefaultLocalCache = cmdParser.hasProperty("--" + USE_DEFAULT_LOCAL_CACHE_OPTION) ? useDefaultLocalCache : null; // call the download handler - Collection pendingCertificates = getPendingCertificates(ctx); + Collection pendingCertificates = getPendingCertificates(ctx); // call the import handler - if (!importPendingCertificates(pendingCertificates, ctx, commandInvocation)) { + if (!pendingCertificates.isEmpty() && !importPendingCertificates(pendingCertificates, ctx, commandInvocation)) { return CommandResult.SUCCESS; } @@ -164,15 +164,15 @@ public CommandResult execute(CLICommandInvocation commandInvocation) throws Comm return CommandResult.SUCCESS; } - private boolean importPendingCertificates(Collection pendingCertificates, CommandContext ctx, CLICommandInvocation commandInvocation) throws CommandException, InterruptedException { + private boolean importPendingCertificates(Collection pendingCertificates, CommandContext ctx, CLICommandInvocation commandInvocation) throws CommandException, InterruptedException { commandInvocation.println("The update is configured to verify the integrity of updated components, but following certificates need to be trusted:"); - for (Path pendingCertificate : pendingCertificates) { - final ModelNode modelNode = this.executeOp(buildParseOperation(pendingCertificate), ctx, this.host).get(RESULT); + for (ModelNode pendingCertificate : pendingCertificates) { + final ModelNode certificateInfo = pendingCertificate.get(CERTIFICATE_INFO); - commandInvocation.println("key-id: " + modelNode.get(InstMgrConstants.CERT_KEY_ID)); - commandInvocation.println("fingerprint: " + modelNode.get(InstMgrConstants.CERT_FINGERPRINT)); - commandInvocation.println("description: " + modelNode.get(InstMgrConstants.CERT_DESCRIPTION)); + commandInvocation.println("key-id: " + certificateInfo.get(InstMgrConstants.CERT_KEY_ID)); + commandInvocation.println("fingerprint: " + certificateInfo.get(InstMgrConstants.CERT_FINGERPRINT)); + commandInvocation.println("description: " + certificateInfo.get(InstMgrConstants.CERT_DESCRIPTION)); commandInvocation.println(""); } @@ -180,8 +180,8 @@ private boolean importPendingCertificates(Collection pendingCertificates, if ("y".equals(input)) { commandInvocation.print("Importing a trusted certificate"); - for (Path pendingCertificate : pendingCertificates) { - new AddCertificatesCommand(pendingCertificate.toFile(), false).executeOp(ctx, this.host); + for (ModelNode pendingCertificate : pendingCertificates) { + this.executeOp(buildImportOperation(pendingCertificate.get(CERTIFICATE_CONTENT).asString()), ctx, host); } return true; @@ -191,18 +191,17 @@ private boolean importPendingCertificates(Collection pendingCertificates, } } - protected Operation buildParseOperation(Path pendingCertificate) { + protected Operation buildImportOperation(String certificateContent) { final ModelNode op = new ModelNode(); final OperationBuilder operationBuilder = OperationBuilder.create(op); - op.get(OP).set(InstMgrCertificateParseHandler.DEFINITION.getName()); - op.get(CERT_FILE).set(0); - operationBuilder.addFileAsAttachment(pendingCertificate.toFile()); + op.get(OP).set(InstMgrCertificateImportHandler.DEFINITION.getName()); + op.get(CERTIFICATE_CONTENT).set(certificateContent); return operationBuilder.build(); } - private Collection getPendingCertificates(CommandContext ctx) throws CommandException { + private Collection getPendingCertificates(CommandContext ctx) throws CommandException { if (confirm || dryRun) { // skip the check in non-interactive runs because the certificate cannot be accepted either way // the update will fail if certificate is required and will print error message @@ -216,11 +215,8 @@ private Collection getPendingCertificates(CommandContext ctx) throws Comma final ModelNode modelNode = executeOp(OperationBuilder.create(op).build(), ctx, this.host); - final List paths = modelNode.get(RESULT).asListOrEmpty(); - return paths.stream() - .map(n->n.get(CERT_FILE).asString()) - .map(Path::of) - .collect(Collectors.toList()); + // certificate-content & certificate-info + return modelNode.get(RESULT).asListOrEmpty(); } private void printListUpdatesResult(CLICommandInvocation commandInvocation, List changesMn) { diff --git a/installation-manager/src/main/java/org/wildfly/core/instmgr/logging/InstMgrLogger.java b/installation-manager/src/main/java/org/wildfly/core/instmgr/logging/InstMgrLogger.java index fa9d6639bc7..6e0a8a23442 100644 --- a/installation-manager/src/main/java/org/wildfly/core/instmgr/logging/InstMgrLogger.java +++ b/installation-manager/src/main/java/org/wildfly/core/instmgr/logging/InstMgrLogger.java @@ -13,6 +13,7 @@ import org.jboss.as.controller.OperationFailedException; import org.jboss.logging.BasicLogger; import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; import org.jboss.logging.annotations.LogMessage; import org.jboss.logging.annotations.Message; import org.jboss.logging.annotations.MessageLogger; @@ -105,6 +106,11 @@ public interface InstMgrLogger extends BasicLogger { @Message(id = 25, value = "Cannot report installation channels: '%s'") void failedToFindInstallationChannels(Exception failure); + @Message(id = 26, value = "One of the signatures in the update is signed by an unknown public key '%s'. Please import the key using import operation and try again.") + OperationFailedException componentSignedWithUnknownCertificate(String keyID, @Cause Throwable cause); + + @Message(id = 27, value = "You cannot use the '%s' option with the '%s' option because they are mutually exclusive.") + OperationFailedException mutuallyExclusiveOptions(String optionName1, String optionName2); //////////////////////////////////////////////// // Messages without IDs diff --git a/installation-manager/src/main/resources/org/wildfly/core/instmgr/cli/command_resources.properties b/installation-manager/src/main/resources/org/wildfly/core/instmgr/cli/command_resources.properties index f339861ac3f..f513af56f35 100644 --- a/installation-manager/src/main/resources/org/wildfly/core/instmgr/cli/command_resources.properties +++ b/installation-manager/src/main/resources/org/wildfly/core/instmgr/cli/command_resources.properties @@ -194,4 +194,28 @@ installer.remove-custom-patch.description=\ installer.remove-custom-patch.option.manifest.description=\ Location of the channel manifest artifact associated with custom patch that will be removed. A manifest defines the versions and artifacts that will be available from the channel that consumes this custom patch. \ - Specify the location as a Maven GA coordinate (groupId:artifactId). \ No newline at end of file + Specify the location as a Maven GA coordinate (groupId:artifactId). + +# LIST COMPONENT CERTIFICATES +installer.certificates-list.description=\ + Lists all public keys imported into the server. The public keys are used to verify components installed during update and revert operation. + +# ADD COMPONENT CERTIFICATES +installer.certificates-add.description=\ + Imports a new public key to be used to verify components during update and revert operations. Once imported, as long as the key is not \ + expired or revoked, any artifact signed by it, will be treated as a trusted component. + +installer.certificates-add.option.cert-file.description=\ + Path to the file containing an ASCII-armored GPG public key to be imported. + +installer.certificates-add.option.non-interactive.description=\ + Switches off a user prompt asking to confirm the public key validity. Note, the user needs to be sure the file contains a valid key when using this option. + +# REMOVE COMPONENT CERTIFICATES +installer.certificates-remove.description=\ + Removes one of the public keys used to verify components during update and revert operations. + +installer.certificates-remove.option.key-id.description=\ + The ID in a hexadecimal form of a public key to be removed from the list of trusted component certificates. \ + Once the key is removed, if an update or revert operation tries to use component signed by this key, the user will be warned \ + and the update will be rejected unless the uses re-imports the key. \ No newline at end of file diff --git a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java index e68df71e459..7963fe56d2b 100644 --- a/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java +++ b/installation-manager/src/test/java/org/wildfly/core/instmgr/InstMgrResourceTestCase.java @@ -20,6 +20,7 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; +import static org.wildfly.core.instmgr.InstMgrConstants.CERTIFICATE_CONTENT; import static org.wildfly.core.instmgr.InstMgrConstants.CERT_DESCRIPTION; import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FILE; import static org.wildfly.core.instmgr.InstMgrConstants.CERT_FINGERPRINT; @@ -1558,9 +1559,11 @@ public void downloadUnacceptedCertificates() throws IOException { ModelNode rsp = getController().execute(op, null, null, null); Assert.assertEquals(SUCCESS, rsp.get(OUTCOME).asString()); - final String certText = Files.readString(Path.of(rsp.get(RESULT).asList().get(0).get(CERT_FILE).asString())); + final String certText = rsp.get(RESULT).asList().get(0).get(CERTIFICATE_CONTENT).asString(); - Assert.assertEquals("test cert", certText); + Assert.assertEquals("key-id:abcd\n" + + "fingerprint:abcd1234\n" + + "description:Missing Cert", certText); } /** diff --git a/pom.xml b/pom.xml index 9b03bf193b3..16cd5b30174 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ 1.0.1.Final 1.7.0.Final 1.3.0.Final - 2.0.0.Beta1-SNAPSHOT + 2.0.0.Beta2-SNAPSHOT 8.0.2.Final 2.2.5.Final 2.2.2.Final diff --git a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java index 1839a0616f1..3226d076074 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/installationmanager/TestInstallationManager.java @@ -351,7 +351,7 @@ public TrustCertificate parseCertificate(InputStream certificate) throws Excepti @Override public Collection downloadRequiredCertificates() throws Exception { - final String cert = "test cert"; + final String cert = "key-id:abcd\nfingerprint:abcd1234\ndescription:Missing Cert"; final ByteArrayInputStream bais = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8)); return List.of(bais); }