使用 將容器產品與 AWS Marketplace Metering Service 整合 AWS SDK for Java - AWS Marketplace

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 將容器產品與 AWS Marketplace Metering Service 整合 AWS SDK for Java

您可以使用 AWS SDK for Java 與 AWS Marketplace Metering Service 整合。軟體使用的連續計量由 自動處理 AWS Marketplace Metering Control Plane。 除了呼叫RegisterUsage一次以開始計量軟體使用之外,您的軟體不需要執行任何計量特定動作。本主題提供使用 AWS SDK for Java 與AWS Marketplace 計量服務 RegisterUsage動作整合的範例實作。

RegisterUsage 必須在啟動容器時立即呼叫 。如果您未在容器啟動的前 6 小時內註冊容器,AWS則 Marketplace Metering Service 不會為前幾個月提供任何計量保證。不過,計量將在當月繼續,直到容器結束為止。

如需完整的原始程式碼,請參閱 RegisterUsage Java 範例。無論使用何種語言, AWS SDK這些步驟都適用。

AWS Marketplace Metering Service 整合的範例步驟
  1. 登入 AWS Marketplace 管理入口網站

  2. 資產選擇容器以開始建立新的容器產品。建立產品會產生產品的產品程式碼,以便與您的容器映像整合。如需設定IAM許可的相關資訊,請參閱 AWS Marketplace 計量和權益 API 權限

  3. 下載公有 AWS Java SDK

    重要

    若要APIs從 Amazon 呼叫計量EKS,您必須使用支援的 AWS SDK,並在執行 Kubernetes 1.13 或更新版本的 Amazon EKS叢集上執行。

  4. (選用) 如果您要與 RegisterUsage動作整合,而且想要執行數位簽章驗證,則需要在應用程式類別路徑中設定BouncyCastle簽章驗證程式庫。

    如果您想要使用 JSON Web 權杖 (JWT),您還必須在應用程式類別路徑中包含 JWT Java 程式庫。使用 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. RegisterUsage 從您的產品方案中的每個付費容器映像呼叫 。 ProductCodePublicKeyVersion是必要的參數,所有其他輸入都是選用的。以下是 的範例承載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 }
    注意

    在連線至 AWS Marketplace Metering Service 時,可能會看到暫時性問題。 AWS Marketplace 強烈建議在指數級關閉的情況下,實作重試最多 30 分鐘,以避免短期中斷或網路問題。

  6. RegisterUsage 會使用 SHA-256 產生 RSA-PSS 數位簽章,可用來驗證請求真實性。簽章包含下列欄位: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呼叫、標記容器,並將其推送至與 Amazon ECS或 Amazon 相容的任何容器登錄檔EKS,例如 Amazon ECR或 Amazon ECR Public。如果您使用的是 Amazon ECR,請確定啟動 Amazon ECS任務或 Amazon EKS Pod 的帳戶具有 Amazon ECR儲存庫的許可。否則,啟動會失敗。

  8. 建立IAM角色,以授予容器呼叫 的許可RegisterUsage,如下列程式碼所定義。您必須在 Amazon 任務ECS或 Amazon EKS Pod 定義的任務角色參數中提供此IAM角色。

    { "Version": "2012-10-17", "Statement": [ { "Action": [ "aws-marketplace:RegisterUsage" ], "Effect": "Allow", "Resource": "*" } ] }
  9. 建立 Amazon ECS任務或 Amazon EKS Pod 定義,該定義參考與 整合的容器, AWS Marketplace 並參考您在步驟 7 中建立IAM的角色。如果您想要查看 AWS CloudTrail 記錄,您應該啟用任務定義的記錄。

  10. 建立 Amazon ECS或 Amazon EKS叢集來執行您的任務或 Pod。如需建立 Amazon ECS叢集的詳細資訊,請參閱 Amazon Elastic Container Service 開發人員指南 中的建立叢集。如需建立 Amazon EKS叢集的詳細資訊 (使用 Kubernetes 1.1.3.x 版或更新版本),請參閱建立 Amazon EKS叢集。

  11. 在 us-east-1 中設定 Amazon ECS或 Amazon EKS叢集,並啟動您建立的 Amazon ECS任務定義或 Amazon EKS Pod AWS 區域。只有在此測試程序中,產品上線之前,您才必須使用此區域。

  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(); } } }