Integração do produto de contêiner ao Serviço de medição do AWS Marketplace usando o AWS SDK para Java - AWS Marketplace

Integração do produto de contêiner ao Serviço de medição do AWS Marketplace usando o AWS SDK para Java

Você pode usar o AWS SDK para Java para se integrar ao Serviço de medição do AWS Marketplace no momento. A medição contínua do uso de software é processada automaticamente pelo Metering Control Plane do AWS Marketplace. O software não precisa realizar ações específicas de medição, exceto chamar RegisterUsage uma vez para medição do uso de software para iniciar. Esse tópico fornece uma implementação de exemplo usando o AWS SDK para Java para se integrar à ação RegisterUsage do Serviço de medição do AWS Marketplace.

RegisterUsage deve ser chamado imediatamente no momento de execução de um contêiner. Se você não registrar o contêiner nas primeiras 6 horas após a execução do contêiner, o Serviço de medição do AWS Marketplace não fornecerá nenhuma garantia de medição dos meses anteriores. No entanto, a medição continuará durante o mês atual até que o contêiner termine.

Para obter o código-fonte completo, consulte Exemplo de Java do RegisterUsage. Muitas dessas etapas se aplicam independentemente da linguagem AWS SDK.

Exemplos de etapa para a integração do Serviço de medição do AWS Marketplace
  1. Faça login no Portal de gerenciamento do AWS Marketplace.

  2. Em Assets (Ativos), selecione Containers (Contêineres) para começar a criar um produto de contêiner. A criação do produto gera o código para a integração do produto com a imagem de contêiner. Para obter mais informações sobre configuração de permissões do IAM, consulte AWS MarketplacePermissões de API de medição e autorização do .

  3. Faça download do SDK do AWS Java público.

    Importante

    Para chamar as APIs de medição do Amazon EKS, é necessário usar um SDK da AWS compatível e executar em um cluster do Amazon EKS que executa o Kubernetes 1.13 ou posterior.

  4. (Opcional) Se estiver fazendo a integração à ação RegisterUsage e quiser realizar a verificação da assinatura digital, você precisará configurar a biblioteca de verificação de assinatura BouncyCastle no caminho de classe do aplicativo.

    Se quiser usar JSON Web Token (JWT), você também deverá incluir bibliotecas JWT Java no caminho de classe do aplicativo. Usar JWT oferece uma abordagem mais simples em relação à verificação de assinatura, embora não seja necessário, e é possível usar BouncyCastle autônomo em vez disso. Não importa se você usa JWT ou BouncyCastle, é preciso usar um sistema de compilação, como Maven, para incluir dependências transitivas de BouncyCastle ou JWT no caminho de classe do aplicativo.

    // 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. Chame RegisterUsage de cada imagem de contêiner paga na oferta do produto. ProductCode e PublicKeyVersion são parâmetros obrigatórios, e todas as outras entradas são opcionais. Esta é uma carga útil de exemplo para 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 }
    nota

    É possível ver problemas transitórios na conexão com o Serviço de medição do AWS Marketplace. O AWS Marketplace recomenda fortemente a implementação de novas tentativas por até 30 minutos, com recuo exponencial, para evitar interrupções de curto prazo ou problemas de rede.

  6. RegisterUsage gera uma assinatura digital RSA-PSS usando SHA-256 que é possível usar para verificar a autenticidade da solicitação. A assinatura inclui os seguintes campos: ProductCode, PublicKeyVersion e Nonce. Para verificar a assinatura digital, você deve manter esses campos da solicitação. Este código é uma resposta de exemplo para uma chamada 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. Recompile uma nova versão da imagem de contêiner que inclua a chamada RegisterUsage, marque o contêiner e o envie para qualquer registro que seja compatível com o Amazon ECS ou o Amazon EKS, como o Amazon ECR ou o Amazon ECR Public. Se você estiver usando o Amazon ECR, verifique se a conta que executa a tarefa do Amazon ECS ou o pod do Amazon EKS tem permissões no repositório do Amazon ECR. Caso contrário, ocorrerá uma falha na inicialização.

  8. Crie um perfil do IAM que conceda permissão para o contêiner chamar RegisterUsage, conforme definido no código a seguir. Forneça esse perfil do IAM no parâmetro Função da tarefa da definição de tarefa do Amazon ECS ou de pod do Amazon EKS.

    JSON
    { "Version":"2012-10-17", "Statement": [ { "Action": [ "aws-marketplace:RegisterUsage" ], "Effect": "Allow", "Resource": "*" } ] }
  9. Crie uma definição de tarefa do Amazon ECS ou de pod do Amazon EKS que faça referência ao contêiner integrado ao AWS Marketplace e ao perfil do IAM criada na etapa 7. Habilite o registro em log do AWS CloudTrail na definição de tarefa, caso você queira vê-lo.

  10. Crie um cluster do Amazon ECS ou Amazon EKS para executar sua tarefa ou pod. Para obter mais informações sobre como criar um cluster do Amazon ECS, consulte Criar um cluster no Guia do desenvolvedor do Amazon Elastic Container Service. Para obter mais informações sobre como criar um cluster do Amazon EKS (usando o Kubernetes versão 1.1.3.x ou posterior), consulte Criar um cluster do Amazon EKS.

  11. Configure o cluster do Amazon ECS ou Amazon EKS e execute a definição de tarefa do Amazon ECS ou o pod do Amazon EKS que você criou, na região da Região da AWS us-east-1. É somente durante este processo de teste, antes de o produto estar ativo, que você precisa usar essa região.

  12. Você pode começar a criar seu produto de contêiner assim que obtiver uma resposta válida de RegisterUsage. Se tiver dúvidas, entre em contato com a equipe de Operações do vendedor do AWS Marketplace.

Exemplo de Java do RegisterUsage

O exemplo a seguir usa o serviço de medição do AWS SDK para Java e do AWS Marketplace para chamar a operação RegisterUsage. A verificação de assinatura é opcional, mas se quiser executar a verificação de assinatura, você deverá incluir as bibliotecas de verificação de assinatura digital obrigatórias. Esse exemplo é apenas para fins de ilustração.

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