Chiffrement modulaire du parquet dans Hive - Amazon EMR

Chiffrement modulaire du parquet dans Hive

Le chiffrement modulaire Parquet fournit un contrôle d'accès et un chiffrement au niveau des colonnes afin d'améliorer la confidentialité et l'intégrité des données stockées au format de fichier Parquet. Cette fonctionnalité est disponible dans Amazon EMR Hive à partir de la version 6.6.0.

Les solutions de sécurité et d'intégrité précédemment prises en charge, notamment le chiffrement de fichiers ou le chiffrement de la couche de stockage, sont décrites dans la section Options de chiffrement du guide de gestion Amazon EMR. Ces solutions peuvent être utilisées pour les fichiers Parquet, mais l'exploitation des nouvelles fonctionnalités du mécanisme de cryptage intégré de Parquet permet un accès granulaire au niveau des colonnes, ainsi que des améliorations en termes de performances et de sécurité. Pour en savoir plus sur cette fonctionnalité, consultez la page github d'Apache intitulée Parquet Modular Encryption.

Les utilisateurs transmettent les configurations aux lecteurs et rédacteurs Parquet à l'aide des configurations Hadoop. Les configurations détaillées permettant aux utilisateurs de configurer les lecteurs et les enregistreurs pour activer le chiffrement ainsi que les fonctionnalités avancées sont documentées dans PARQUET-1854 : Properties-driven Interface to Parquet Encryption Management (Interface pilotée par les propriétés pour la gestion du chiffrement de Parquet).

Exemples d'utilisation

L'exemple suivant couvre la création et l'écriture dans une table Hive en utilisant AWS KMS pour gérer les clés de chiffrement.

  1. Implémentez un client KMS pour le service AWS KMS comme décrit dans le document PARQUET-1373 : Outils de gestion des clés de chiffrement. L'exemple suivant montre un extrait de code d'implémentation.

    package org.apache.parquet.crypto.keytools; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.regions.Regions; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; import com.amazonaws.services.kms.model.DecryptRequest; import com.amazonaws.services.kms.model.EncryptRequest; import com.amazonaws.util.Base64; import org.apache.hadoop.conf.Configuration; import org.apache.parquet.crypto.KeyAccessDeniedException; import org.apache.parquet.crypto.ParquetCryptoRuntimeException; import org.apache.parquet.crypto.keytools.KmsClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; public class AwsKmsClient implements KmsClient { private static final AWSKMS AWSKMS_CLIENT = AWSKMSClientBuilder .standard() .withRegion(Regions.US_WEST_2) .build(); public static final Logger LOG = LoggerFactory.getLogger(AwsKmsClient.class); private String kmsToken; private Configuration hadoopConfiguration; @Override public void initialize(Configuration configuration, String kmsInstanceID, String kmsInstanceURL, String accessToken) throws KeyAccessDeniedException { hadoopConfiguration = configuration; kmsToken = accessToken; } @Override public String wrapKey(byte[] keyBytes, String masterKeyIdentifier) throws KeyAccessDeniedException { String value = null; try { ByteBuffer plaintext = ByteBuffer.wrap(keyBytes); EncryptRequest req = new EncryptRequest().withKeyId(masterKeyIdentifier).withPlaintext(plaintext); ByteBuffer ciphertext = AWSKMS_CLIENT.encrypt(req).getCiphertextBlob(); byte[] base64EncodedValue = Base64.encode(ciphertext.array()); value = new String(base64EncodedValue, Charset.forName("UTF-8")); } catch (AmazonClientException ae) { throw new KeyAccessDeniedException(ae.getMessage()); } return value; } @Override public byte[] unwrapKey(String wrappedKey, String masterKeyIdentifier) throws KeyAccessDeniedException { byte[] arr = null; try { ByteBuffer ciphertext = ByteBuffer.wrap(Base64.decode(wrappedKey.getBytes(StandardCharsets.UTF_8))); DecryptRequest request = new DecryptRequest().withKeyId(masterKeyIdentifier).withCiphertextBlob(ciphertext); ByteBuffer decipheredtext = AWSKMS_CLIENT.decrypt(request).getPlaintext(); arr = new byte[decipheredtext.remaining()]; decipheredtext.get(arr); } catch (AmazonClientException ae) { throw new KeyAccessDeniedException(ae.getMessage()); } return arr; } }
  2. Créez vos clés de chiffrement AWS KMS pour le pied de page ainsi que pour les colonnes auxquelles vos rôles IAM ont accès, comme décrit dans la section Création de clés dans le Guide du développeur AWS Key Management Service. Le rôle IAM par défaut est EMR_ECS_Default.

  3. Dans l'application Hive sur un cluster Amazon EMR, ajoutez le client ci-dessus à l'aide de l'instruction ADD JAR, comme décrit dans la documentation Apache Hive Resources. Voici un exemple de déclaration :

    ADD JAR 's3://location-to-custom-jar';

    Une autre méthode consiste à ajouter le fichier JAR à l'auxlib de Hive à l'aide d'une action d'amorçage. Voici un exemple de ligne à ajouter à l'action boostrap :

    aws s3 cp 's3://location-to-custom-jar' /usr/lib/hive/auxlib
  4. Définissez les configurations suivantes :

    set parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory; set parquet.encryption.kms.client.class=org.apache.parquet.crypto.keytools.AwsKmsClient;
  5. Créez une table Hive au format Parquet, spécifiez les clés AWS KMS dans SERDEPROPERTIES et insérez des données :

    CREATE TABLE my_table(name STRING, credit_card STRING) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe’ WITH SERDEPROPERTIES ( 'parquet.encryption.column.key’=<aws-kms-key-id-for-column-1>: credit_card’, 'parquet.encryption.footer.key’='<aws-kms-key-id-for-footer>’) STORED AS parquet LOCATION “s3://<bucket/<warehouse-location>/my_table”; INSERT INTO my_table SELECT java_method ('org.apache.commons.lang.RandomStringUtils','randomAlphabetic',5) as name, java_method ('org.apache.commons.lang.RandomStringUtils','randomAlphabetic',10) as credit_card from (select 1) x lateral view posexplode(split(space(100),' ')) pe as i,x; select * from my_table;
  6. Vérifiez que lorsque vous créez une table externe au même endroit sans accès aux clés AWS KMS (par exemple, l'accès au rôle IAM est refusé), vous ne pouvez pas lire les données.

    CREATE EXTERNAL TABLE ext_table (name STRING, credit_card STRING) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe’ STORED AS parquet LOCATION “s3://<bucket>/<warehouse-location>/my_table”; SELECT * FROM ext_table;
  7. La dernière instruction doit générer l'exception suivante :

    Failed with exception java.io.IOException:org.apache.parquet.crypto.KeyAccessDeniedException: Footer key: access denied