使用以下方法将您的容器产品与 AWS Marketplace 计量服务集成 AWS SDK for Java - AWS Marketplace

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用以下方法将您的容器产品与 AWS Marketplace 计量服务集成 AWS SDK for Java

您可以使用与 AWS Marketplace 计量服务集成。 AWS SDK for Java 软件使用的连续计量由自动处理 AWS Marketplace Metering Control Plane。 除了调用RegisterUsage一次即可开始计量软件使用情况外,您的软件无需执行任何计量特定的操作。本主题提供了一个使用与AWS Marketplace 计量服务RegisterUsage操作集成的实现示例。 AWS SDK for Java

RegisterUsage 必须在启动容器时立即调用。如果您在集装箱启动的前 6 个小时内没有注册集装箱,AWSMarketplace Metering Service 将不为前几个月提供任何计量保证。但是,计量将在当月继续进行,直到容器结束。

有关完整源代码,请参阅RegisterUsage Java 示例。无论使用哪种 AWS SDK语言,其中许多步骤都适用。

集成 AWS Marketplace 计量服务的示例步骤
  1. 登录到 AWS Marketplace 管理门户

  2. 资产中,选择容器以开始创建新容器产品。创建产品会生成产品的产品代码以与您的容器映像集成。有关设置IAM权限的信息,请参阅AWS Marketplace 计量和授权 API 权限

  3. 下载公共 AWSJava 版本SDK

    重要

    要APIs从亚马逊调用计量EKS,您必须使用支持的, AWS SDK并在运行 Kubernetes 1.13 或更高版本的亚马逊EKS集群上运行。

  4. (可选)如果您要与RegisterUsage操作集成,并且想要执行数字签名验证,则需要在应用程序类路径中配置BouncyCastle签名验证库。

    如果要使用 JSON Web Token (JWT),则还必须在应用程序类路径中包含 JWTJava 库。使用JWT提供了一种更简单的签名验证方法,但不是必需的,您可以 BouncyCastle 改为使用独立版。无论您使用JWT还是 BouncyCastle,都需要使用诸如 Maven 之类的编译系统JWT在应用程序类路径中包含 BouncyCastle 或的传递依赖关系。

    // Required for signature verification using code sample <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.60</version> </dependency> // This one is only required for JWT <dependency> <groupId>com.nimbusds</groupId> <artifactId>nimbus-jose-jwt</artifactId> <version>6.0</version> </dependency>
  5. 从您的产品中的每个付费容器映像调用 RegisterUsageProductCodePublicKeyVersion 是必需参数,所有其他输入都是可选的。以下是 RegisterUsage 的示例负载。

    { "ProductCode" : "string", // (required) "PublicKeyVersion": 1, // (required) "Nonce": "string", // (optional) to scope down the registration // to a specific running software // instance and guard against // replay attacks }
    注意

    在连接到 M AWS arketplace 计量服务时可能会出现暂时性问题。 AWS Marketplace 强烈建议实施最长 30 分钟的重试,并以指数级退缩,以避免短期中断或网络问题。

  6. RegisterUsage使用 SHA -256 生成一个RSAPSS数字签名,您可以使用该签名来验证请求的真实性。该签名包括以下字段:ProductCodePublicKeyVersionNonce。要验证数字签名,您必须保留请求中的这些字段。以下代码是 RegisterUsage 调用的示例响应。

    { "Signature": "<<JWT Token>>" } // Where the JWT Token is composed of 3 dot-separated, // base-64 URL Encoded sections. // e.g. eyJhbGcVCJ9.eyJzdWIMzkwMjJ9.rrO9Qw0SXRWTe // Section 1: Header/Algorithm { "alg": "PS256", "typ": "JWT" } // Section 2: Payload { "ProductCode" : "string", "PublicKeyVersion": 1, "Nonce": "string", "iat": date // JWT issued at claim } // Section 3: RSA-PSS SHA256 signature "rrO9Q4FEi3gweH3X4lrt2okf5zwIatUUwERlw016wTy_21Nv8S..."
  7. 重建包含RegisterUsage调用的容器镜像的新版本,标记容器,然后将其推送到任何与亚马逊ECS或亚马逊兼容的容器注册表EKS,例如亚马逊ECR或亚马逊ECR公众。如果您使用的是亚马逊ECR,请确保启动亚马逊ECS任务的账户或 Amazon EKS pod 具有访问亚马逊ECR存储库的权限。否则,启动失败。

  8. 创建一个IAM角色来授予容器调用权限RegisterUsage,如以下代码所定义。您必须在 Amazon 任务的任务角色参数或 Amazon ECS EKS pod 定义中提供此IAM角色。

    { "Version": "2012-10-17", "Statement": [ { "Action": [ "aws-marketplace:RegisterUsage" ], "Effect": "Allow", "Resource": "*" } ] }
  9. 创建 Amazon ECS 任务或 Amazon EKS 容器定义,该定义引用已与您在步骤 7 中创建的IAM角色集成的容器 AWS Marketplace 并引用该角色。如果要查看 AWS CloudTrail 日志记录,则应在任务定义中启用日志记录。

  10. 创建 Amazon ECS 或 Amazon EKS 集群来执行您的任务或容器。有关创建 Amazon ECS 集群的更多信息,请参阅《亚马逊弹性容器服务开发人员指南》中的创建集群。有关创建亚马逊EKS集群(使用 Kubernetes 版本 1.1.3.x 或更高版本)的更多信息,请参阅创建亚马逊集群。EKS

  11. 在 us-east- AWS 区域 1 中配置亚马逊ECS或亚马逊EKS集群并启动您创建的亚马逊ECS任务定义或亚马逊EKS容器。只有在此测试过程中,在产品上线之前,您才必须使用此区域。

  12. 当您从 RegisterUsage 获得有效的响应时,您可以开始创建您的容器产品。如有问题,请联系 AWS Marketplace 卖家运营团队。

RegisterUsage Java 示例

以下示例使用 AWS SDK for Java 和 AWS Marketplace 计量服务来调用该RegisterUsage操作。签名验证是可选的,但如果您要执行签名验证,则必须包含所需的数字签名验证库。此示例仅用于说明。

import com.amazonaws.auth.PEM; import com.amazonaws.services.marketplacemetering.AWSMarketplaceMetering; import com.amazonaws.services.marketplacemetering.AWSMarketplaceMeteringClientBuilder; import com.amazonaws.services.marketplacemetering.model.RegisterUsageRequest; import com.amazonaws.services.marketplacemetering.model.RegisterUsageResult; import com.amazonaws.util.json.Jackson; import com.fasterxml.jackson.databind.JsonNode; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.interfaces.RSAPublicKey; import java.util.Base64; import java.util.Optional; import java.util.UUID; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * Class for making calls out to AWS Marketplace Metering Service. */ class RegisterUsage { private static final String PRODUCT_CODE = "......."; private final AWSMarketplaceMetering registerUsageClient; private final SignatureVerifier signatureVerifier; private final int publicKeyVersion; public RegisterUsage(final SignatureVerifier signatureVerifier) { this.signatureVerifier = signatureVerifier; this.publicKeyVersion = PublicKeyProvider.PUBLIC_KEY_VERSION; this.registerUsageClient = AWSMarketplaceMeteringClientBuilder.standard().build(); } /** * Shows how to call RegisterUsage client and verify digital signature. */ public void callRegisterUsage() { RegisterUsageRequest request = new RegisterUsageRequest() .withProductCode(PRODUCT_CODE) .withPublicKeyVersion(publicKeyVersion) .withNonce(UUID.randomUUID().toString()); // Execute call to RegisterUsage (only need to call once at container startup) RegisterUsageResult result = this.registerUsageClient.registerUsage(request); // Verify Digital Signature w/o JWT boolean isSignatureValid = this.signatureVerifier.verify(request, result); if (!isSignatureValid) { throw new RuntimeException("Revoke entitlement, digital signature invalid."); } } } /** * Signature verification class with both a JWT-library based verification * and a non-library based implementation. */ class SignatureVerifier { private static BouncyCastleProvider BC = new BouncyCastleProvider(); private static final String SIGNATURE_ALGORITHM = "SHA256withRSA/PSS"; private final PublicKey publicKey; public SignatureVerifier(PublicKeyProvider publicKeyProvider) { this.publicKey = publicKeyProvider.getPublicKey().orElse(null); Security.addProvider(BC); } /** * Example signature verification using the NimbusJOSEJWT library to verify the JWT Token. * * @param request RegisterUsage Request. * @param result RegisterUsage Result. * @return true if the token matches. */ public boolean verifyUsingNimbusJOSEJWT(final RegisterUsageRequest request, final RegisterUsageResult result) { if (!getPublicKey().isPresent()) { return false; } try { JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) getPublicKey().get()); JWSObject jwsObject = JWSObject.parse(result.getSignature()); return jwsObject.verify(verifier) && validatePayload(jwsObject.getPayload().toString(), request, result); } catch (Exception e) { // log error return false; } } /** * Example signature verification without any JWT library support. * * @param request RegisterUsage Request. * @param result RegisterUsage Result. * @return true if the token matches. */ public boolean verify(final RegisterUsageRequest request, final RegisterUsageResult result) { if (!getPublicKey().isPresent()) { return false; } try { String[] jwtParts = result.getSignature().split("\\."); String header = jwtParts[0]; String payload = jwtParts[1]; String payloadSignature = jwtParts[2]; Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, BC); signature.initVerify(getPublicKey().get()); signature.update(String.format("%s.%s", header, payload).getBytes(StandardCharsets.UTF_8)); boolean verified = signature.verify(Base64.getUrlDecoder() .decode(payloadSignature.getBytes(StandardCharsets.UTF_8))); String decodedPayload = new String(Base64.getUrlDecoder().decode(payload)); return verified && validatePayload(decodedPayload, request, result); } catch (Exception e) { // log error return false; } } /** * Validate each value in the returned payload matches values originally * supplied in the request to RegisterUsage. TimeToLiveInMillis and * PublicKeyExpirationTimestamp will have the values in the payload compared * to values in the signature */ private boolean validatePayload(final String payload, final RegisterUsageRequest request, final RegisterUsageResult result) { try { JsonNode payloadJson = Jackson.getObjectMapper().readTree(payload); boolean matches = payloadJson.get("productCode") .asText() .equals(request.getProductCode()); matches = matches && payloadJson.get("nonce") .asText() .equals(request.getNonce()); return matches = matches && payloadJson.get("publicKeyVersion") .asText() .equals(String.valueOf(request.getPublicKeyVersion())); } catch (Exception ex) { // log error return false; } } private Optional<PublicKey> getPublicKey() { return Optional.ofNullable(this.publicKey); } } /** * Public key provider taking advantage of the AWS PEM Utility. */ class PublicKeyProvider { // Replace with your public key. Ensure there are new-lines ("\n") in the // string after "-----BEGIN PUBLIC KEY-----\n" and before "\n-----END PUBLIC KEY-----". private static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\n" + "UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\n" + "HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\n" + "o2kQ+X5xK9cipRgEKwIDAQAB\n" + "-----END PUBLIC KEY-----"; public static final int PUBLIC_KEY_VERSION = 1; public Optional<PublicKey> getPublicKey() { try { return Optional.of(PEM.readPublicKey(new ByteArrayInputStream( PUBLIC_KEY.getBytes(StandardCharsets.UTF_8)))); } catch (Exception e) { // log error return Optional.empty(); } } }