This is an automated email from the ASF dual-hosted git repository. smolnar82 pushed a commit to branch knox_idf in repository https://gitbox.apache.org/repos/asf/knox.git
commit 157af958042d8aaf3a66884abca05dcf1bcabb3a Author: hanicz <[email protected]> AuthorDate: Thu Jul 2 10:56:31 2026 +0200 KNOX-3316: Change downloaded keystore format based on FIPS (#1292) (cherry picked from commit 9c000de3747fd9db626b6a930ac0fac6450cab05) --- .../service/metadata/GeneralProxyInformation.java | 66 ++++++++++++++ .../service/metadata/KnoxMetadataResource.java | 100 +++++++++++++-------- .../general.proxy.information.component.html | 2 +- .../general.proxy.information.component.ts | 7 ++ .../home/app/model/general.proxy.information.ts | 1 + 5 files changed, 138 insertions(+), 38 deletions(-) diff --git a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/GeneralProxyInformation.java b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/GeneralProxyInformation.java index 833f0d341..d15603a69 100644 --- a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/GeneralProxyInformation.java +++ b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/GeneralProxyInformation.java @@ -55,6 +55,10 @@ public class GeneralProxyInformation { @ApiModelProperty(value = "A boolean flag indicating whether Webshell UI should be enabled on the Knox Home page") private String enableWebshell = "false"; + @XmlElement + @ApiModelProperty(value = "The truststore type the homepage should offer for download (e.g. 'jks' or 'bcfks')") + private String truststoreType = "jks"; + public String getVersion() { return version; } @@ -112,4 +116,66 @@ public class GeneralProxyInformation { this.enableWebshell = enableWebshell; } + public String getTruststoreType() { + return truststoreType; + } + + public void setTruststoreType(String truststoreType) { + this.truststoreType = truststoreType; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private final GeneralProxyInformation instance = new GeneralProxyInformation(); + + private Builder() {} + + public Builder version(String version) { + instance.setVersion(version); + return this; + } + + public Builder hostname(String hostname) { + instance.setHostname(hostname); + return this; + } + + public Builder adminUiUrl(String adminUiUrl) { + instance.setAdminUiUrl(adminUiUrl); + return this; + } + + public Builder webShellUrl(String webShellUrl) { + instance.setWebShellUrl(webShellUrl); + return this; + } + + public Builder adminApiBookUrl(String adminApiBookUrl) { + instance.setAdminApiBookUrl(adminApiBookUrl); + return this; + } + + public Builder enableTokenManagement(boolean enableTokenManagement) { + instance.setEnableTokenManagement(Boolean.toString(enableTokenManagement)); + return this; + } + + public Builder enableWebshell(boolean enableWebshell) { + instance.setEnableWebshell(Boolean.toString(enableWebshell)); + return this; + } + + public Builder truststoreType(String truststoreType) { + instance.setTruststoreType(truststoreType); + return this; + } + + public GeneralProxyInformation build() { + return instance; + } + } + } diff --git a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java index ba77c0e5d..23e61a9f3 100644 --- a/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java +++ b/gateway-service-metadata/src/main/java/org/apache/knox/gateway/service/metadata/KnoxMetadataResource.java @@ -54,6 +54,7 @@ import javax.ws.rs.core.Response.Status; import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.dto.HomePageProfile; +import org.apache.knox.gateway.fips.FipsUtils; import org.apache.knox.gateway.i18n.messages.MessagesFactory; import org.apache.knox.gateway.service.definition.Metadata; import org.apache.knox.gateway.service.definition.ServiceDefinitionPair; @@ -98,39 +99,49 @@ public class KnoxMetadataResource { @Produces({ APPLICATION_JSON, APPLICATION_XML }) @Path("info") public GeneralProxyInformation getGeneralProxyInformation() { - final GeneralProxyInformation proxyInfo = new GeneralProxyInformation(); final GatewayServices gatewayServices = (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); - if (gatewayServices != null) { - final ServerInfoService serviceInfoService = gatewayServices.getService(ServiceType.SERVER_INFO_SERVICE); - final String versionInfo = serviceInfoService.getBuildVersion() + " (hash=" + serviceInfoService.getBuildHash() + ")"; - proxyInfo.setVersion(versionInfo); - proxyInfo.setHostname(Hostname.getHostname()); - proxyInfo.setAdminApiBookUrl( - String.format(Locale.ROOT, "https://knox.apache.org/books/knox-%s/user-guide.html#Admin+API", getAdminApiBookVersion(serviceInfoService.getBuildVersion()))); - final GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); - proxyInfo.setAdminUiUrl(getBaseGatewayUrl(config) + "/manager/admin-ui/"); - proxyInfo.setWebShellUrl(getBaseGatewayUrl(config) + "/homepage/webshell-ui/index.html"); - setTokenManagementEnabledFlag(proxyInfo, gatewayServices); - proxyInfo.setEnableWebshell(String.valueOf(config.isWebShellEnabled())); + if (gatewayServices == null) { + return GeneralProxyInformation.builder().build(); } + final GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); + final ServerInfoService serverInfo = gatewayServices.getService(ServiceType.SERVER_INFO_SERVICE); + final String baseGatewayUrl = getBaseGatewayUrl(config); - return proxyInfo; + return GeneralProxyInformation.builder() + .version(serverInfo.getBuildVersion() + " (hash=" + serverInfo.getBuildHash() + ")") + .hostname(Hostname.getHostname()) + .adminUiUrl(baseGatewayUrl + "/manager/admin-ui/") + .webShellUrl(baseGatewayUrl + "/homepage/webshell-ui/index.html") + .adminApiBookUrl(buildAdminApiBookUrl(serverInfo.getBuildVersion())) + .enableTokenManagement(isTokenManagementEnabled(gatewayServices)) + .enableWebshell(config.isWebShellEnabled()) + .truststoreType(preferredTruststoreType()) + .build(); } - private void setTokenManagementEnabledFlag(final GeneralProxyInformation proxyInfo, final GatewayServices gatewayServices) { + private boolean isTokenManagementEnabled(final GatewayServices gatewayServices) { try { final AliasService aliasService = gatewayServices.getService(ServiceType.ALIAS_SERVICE); final List<String> aliases = aliasService.getAliasesForCluster(AliasService.NO_CLUSTER_NAME); final boolean tokenManagementEnabled = aliases.contains(TokenMAC.KNOX_TOKEN_HASH_KEY_ALIAS_NAME); - proxyInfo.setEnableTokenManagement(Boolean.toString(tokenManagementEnabled)); if (!tokenManagementEnabled) { LOG.tokenManagementDisabled(); } + return tokenManagementEnabled; } catch (AliasServiceException e) { LOG.failedToFetchGatewayAliasList(e.getMessage(), e); + return false; } } + private String buildAdminApiBookUrl(String buildVersion) { + return String.format(Locale.ROOT, "https://knox.apache.org/books/knox-%s/user-guide.html#Admin+API", getAdminApiBookVersion(buildVersion)); + } + + private String preferredTruststoreType() { + return FipsUtils.isFipsEnabledWithBCProvider() ? "bcfks" : "jks"; + } + private String getAdminApiBookVersion(String buildVersion) { return buildVersion.replaceAll(SNAPSHOT_VERSION_POSTFIX, "").replaceAll("\\.", "-"); } @@ -142,18 +153,21 @@ public class KnoxMetadataResource { final GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); final Certificate[] certificateChain = config.isSSLEnabled() ? getPublicCertificates() : getSigningkeyCerts(config); if (certificateChain != null) { + final java.nio.file.Path certFilePath; if ("pem".equals(certType)) { - generateCertificatePem(certificateChain, config); - return generateSuccessFileDownloadResponse(pemFilePath); + certFilePath = generateCertificatePem(certificateChain, config); } else if ("jks".equals(certType)) { - generateCertificateJks(certificateChain, config); - return generateSuccessFileDownloadResponse(jksFilePath); + certFilePath = generateCertificateJks(certificateChain, config); } else if ("bcfks".equals(certType)) { - generateCertificateBcfks(certificateChain, config); - return generateSuccessFileDownloadResponse(bcfksFilePath); + certFilePath = generateCertificateBcfks(certificateChain, config); } else { return generateFailureFileDownloadResponse(Status.BAD_REQUEST, "Invalid certification type provided!"); } + if (certFilePath != null && certFilePath.toFile().exists()) { + return generateSuccessFileDownloadResponse(certFilePath); + } + return generateFailureFileDownloadResponse(Status.SERVICE_UNAVAILABLE, + "Could not generate " + certType.toUpperCase(Locale.ROOT) + " public certificate"); } return generateFailureFileDownloadResponse(Status.SERVICE_UNAVAILABLE, "Could not generate public certificate"); } @@ -190,36 +204,48 @@ public class KnoxMetadataResource { return null; } - private void generateCertificatePem(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + private java.nio.file.Path generateCertificatePem(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + if (pemFilePath != null && pemFilePath.toFile().exists()) { + return pemFilePath; + } + final java.nio.file.Path candidate = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.pem"); try { - if (pemFilePath == null || !pemFilePath.toFile().exists()) { - pemFilePath = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.pem"); - X509CertificateUtil.writeCertificatesToFile(certificateChain, pemFilePath.toFile()); - } + X509CertificateUtil.writeCertificatesToFile(certificateChain, candidate.toFile()); + pemFilePath = candidate; + return pemFilePath; } catch (CertificateEncodingException | IOException e) { LOG.failedToGeneratePublicCert("PEM", e.getMessage(), e); + return null; } } - private void generateCertificateJks(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + private java.nio.file.Path generateCertificateJks(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + if (jksFilePath != null && jksFilePath.toFile().exists()) { + return jksFilePath; + } + final java.nio.file.Path candidate = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.jks"); try { - if (jksFilePath == null || !jksFilePath.toFile().exists()) { - jksFilePath = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.jks"); - X509CertificateUtil.writeCertificatesToJks(certificateChain, jksFilePath.toFile(), null); - } + X509CertificateUtil.writeCertificatesToJks(certificateChain, candidate.toFile(), null); + jksFilePath = candidate; + return jksFilePath; } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) { LOG.failedToGeneratePublicCert("JKS", e.getMessage(), e); + return null; } } - private void generateCertificateBcfks(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + private java.nio.file.Path generateCertificateBcfks(Certificate[] certificateChain, GatewayConfig gatewayConfig) { + if (bcfksFilePath != null && bcfksFilePath.toFile().exists()) { + return bcfksFilePath; + } + final java.nio.file.Path candidate = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.bcfks"); try { - if (bcfksFilePath == null || !bcfksFilePath.toFile().exists()) { - bcfksFilePath = Paths.get(gatewayConfig.getGatewaySecurityDir(), "gateway-client-trust.bcfks"); - X509CertificateUtil.writeCertificatesToBcfks(certificateChain, bcfksFilePath.toFile(), null); - } + X509CertificateUtil.writeCertificatesToBcfks(certificateChain, candidate.toFile(), null); + bcfksFilePath = candidate; + return bcfksFilePath; } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) { LOG.failedToGeneratePublicCert("BCFKS", e.getMessage(), e); + return null; } } diff --git a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html index fb3b097ce..1cef2a53c 100644 --- a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html +++ b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html @@ -49,7 +49,7 @@ limitations under the License. <td> <a href="{{ getMetadataAPIUrl('publicCert?type=pem') }}">PEM</a> | - <a href="{{ getMetadataAPIUrl('publicCert?type=jks') }}">JKS</a> + <a href="{{ getMetadataAPIUrl('publicCert?type=' + getTruststoreType()) }}">{{ getTruststoreType().toUpperCase() }}</a> </td> </tr> } diff --git a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts index 9bba05fc0..d2d7ce5c0 100644 --- a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts +++ b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts @@ -111,6 +111,13 @@ export class GeneralProxyInformationComponent implements OnInit { return false; } + getTruststoreType() { + if (this.generalProxyInformation && this.generalProxyInformation.truststoreType) { + return this.generalProxyInformation.truststoreType; + } + return 'jks'; + } + ngOnInit(): void { console.debug('GeneralProxyInformationComponent --> ngOnInit() --> '); this.homepageService.getGeneralProxyInformation() diff --git a/knox-homepage-ui/home/app/model/general.proxy.information.ts b/knox-homepage-ui/home/app/model/general.proxy.information.ts index 3efe82389..0a3184891 100644 --- a/knox-homepage-ui/home/app/model/general.proxy.information.ts +++ b/knox-homepage-ui/home/app/model/general.proxy.information.ts @@ -23,4 +23,5 @@ export class GeneralProxyInformation { adminApiBookUrl: string; enableTokenManagement: string; enableWebshell: string; + truststoreType: string; }
