本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用以下方法将您的容器产品与 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 计量服务的示例步骤
-
登录到 AWS Marketplace 管理门户
。 -
从资产中,选择容器以开始创建新容器产品。创建产品会生成产品的产品代码以与您的容器映像集成。有关设置IAM权限的信息,请参阅AWS Marketplace 计量和授权 API 权限。
-
下载公共 AWSJava 版本SDK
。 重要
要APIs从亚马逊调用计量EKS,您必须使用支持的, AWS SDK并在运行 Kubernetes 1.13 或更高版本的亚马逊EKS集群上运行。
-
(可选)如果您要与
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>
-
从您的产品中的每个付费容器映像调用
RegisterUsage
。ProductCode
和PublicKeyVersion
是必需参数,所有其他输入都是可选的。以下是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 分钟的重试,并以指数级退缩,以避免短期中断或网络问题。
-
RegisterUsage
使用 SHA -256 生成一个RSAPSS数字签名,您可以使用该签名来验证请求的真实性。该签名包括以下字段:ProductCode
、PublicKeyVersion
和Nonce
。要验证数字签名,您必须保留请求中的这些字段。以下代码是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..."
-
重建包含
RegisterUsage
调用的容器镜像的新版本,标记容器,然后将其推送到任何与亚马逊ECS或亚马逊兼容的容器注册表EKS,例如亚马逊ECR或亚马逊ECR公众。如果您使用的是亚马逊ECR,请确保启动亚马逊ECS任务的账户或 Amazon EKS pod 具有访问亚马逊ECR存储库的权限。否则,启动失败。 -
创建一个IAM
角色来授予容器调用权限 RegisterUsage
,如以下代码所定义。您必须在 Amazon 任务的任务角色参数或 Amazon ECS EKS pod 定义中提供此IAM角色。{ "Version": "2012-10-17", "Statement": [ { "Action": [ "aws-marketplace:RegisterUsage" ], "Effect": "Allow", "Resource": "*" } ] }
-
创建 Amazon ECS 任务或 Amazon EKS 容器定义,该定义引用已与您在步骤 7 中创建的IAM角色集成的容器 AWS Marketplace 并引用该角色。如果要查看 AWS CloudTrail 日志记录,则应在任务定义中启用日志记录。
-
创建 Amazon ECS 或 Amazon EKS 集群来执行您的任务或容器。有关创建 Amazon ECS 集群的更多信息,请参阅《亚马逊弹性容器服务开发人员指南》中的创建集群。有关创建亚马逊EKS集群(使用 Kubernetes 版本 1.1.3.x 或更高版本)的更多信息,请参阅创建亚马逊集群。EKS
-
在 us-east- AWS 区域 1 中配置亚马逊ECS或亚马逊EKS集群并启动您创建的亚马逊ECS任务定义或亚马逊EKS容器。只有在此测试过程中,在产品上线之前,您才必须使用此区域。
-
当您从
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(); } } }