Tutorial: criptografia no nível do aplicativo no Memorystore para Redis

Neste tutorial, mostramos como criar um aplicativo que se comunica com a API Cloud Key Management Service (Cloud KMS) para criptografar conteúdo armazenado em um repositório Memorystore para Redis no Google Cloud. Para saber mais sobre os conceitos usados neste tutorial, consulte o documento associado a este tutorial, Criptografia no nível do aplicativo: Memorystore para Redis.

Este tutorial é destinado a desenvolvedores de aplicativos e profissionais de segurança familiarizados com o Google Cloud, Linux, serviços de gerenciamento de chaves, Redis, Git, Maven e Java.

O tutorial é estruturado de maneira incremental, semelhante a uma metodologia de desenvolvimento voltado a testes. Primeiro, você executa um teste. Em seguida, você introduz uma alteração que causa uma falha no teste. Por fim, tome as medidas para remover a falha de teste.

Objetivos

  • Crie e teste um par de instâncias do Memorystore para Redis.
  • Converta dados e carregue-os no Redis.
  • Crie seu próprio sistema de gerenciamento de chaves (KMS).
  • Saiba mais sobre o processo de gerenciamento de chave de criptografia.

Custos

Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem ser qualificados para uma avaliação gratuita.

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Para mais informações, consulte Como fazer a limpeza.

Antes de começar

  1. No Console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar a página do seletor de projetos

  2. Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como confirmar se o faturamento está ativado para o projeto.

  3. Ative as APIs Compute Engine, Cloud KMS, and Memorystore for Redis.

    Ative as APIs

  4. No Console do Cloud, ative o Cloud Shell.

    Ativar o Cloud Shell

Como criar uma instância de VM do projeto

  1. No Cloud Shell, defina a configuração da ferramenta de linha de comando gcloud:

    gcloud config set project PROJECT_NAME
    gcloud config set compute/zone us-central1-f
    gcloud config set compute/region us-central1
    

    Substitua PROJECT_NAME pelo nome do projeto do Google Cloud neste tutorial.

  2. Crie uma instância de máquina virtual (VM):

    gcloud compute instances create ale-instance \
        --machine-type=n1-standard-1 \
        --image-family=debian-10 \
        --image-project=debian-cloud \
        --boot-disk-size=200GB
    
  3. No console do usuário, na página de instâncias de VM, clique em SSH para fazer login na instância de VM.

    Botão SSH no Cloud Shell.

    Uma nova janela é aberta, e neste tutorial chama a janela do projeto em vez de janela do Cloud Shell. Para concluir algumas das etapas do tutorial, alterne entre a janela do projeto e a janela do Cloud Shell.

Como instalar o Git e o Maven

  • Na janela do projeto, instale o Git e o Maven:

    sudo apt-get install git wget maven
    

    Você talvez receba o seguinte aviso:

    E: Could not get lock /var/lib/dpkg/lock
    

    Esse erro poderá ocorrer se os scripts de inicialização da VM ainda estiverem em execução. Se você receber esse erro, aguarde um ou dois minutos e execute o comando anterior novamente.

Como instalar o Java Development Kit (JDK)

  1. Na janela do projeto, altere o diretório:

    cd /tmp
    
  2. Faça o download do pacote de instalação do Java:

    wget https://download.java.net/java/GA/jdk14.0.1/664493ef4a6946b186ff29eb326336a2/7/GPL/openjdk-14.0.1_linux-x64_bin.tar.gz
    

    A resposta será semelhante a:

    --2020-05-17 19:21:39--  https://download.java.net/java/GA/jdk14.0.1/664493ef4a6946b186ff29eb326336a2/7/GPL/openjdk-14.0.1_linux-x64_bin.tar.gz
    Resolving download.java.net (download.java.net)... 23.213.168.108
    Connecting to download.java.net (download.java.net)|23.213.168.108|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 198665889 (189M) [application/x-gzip]
    Saving to: 'openjdk-14.0.1_linux-x64_bin.tar.gz'
    oenjdk-14.0.1_linux-x64_  80%[======================>      ] 151.72M  4.86MB/s    eta 8s
    
  3. Exiba o conteúdo do diretório Java Virtual Machine (JVM):

    ls /usr/lib/jvm/
    

    Você verá um resultado parecido com este:

    ls: /usr/lib/jvm/: No such file or directory
    

    Esse resultado indica que a pasta não existe. Se esse for o caso, crie a pasta:

    sudo mkdir /usr/lib/jvm
    
  4. Instale o JDK:

    cd /usr/lib/jvm
    sudo tar xzf /tmp/openjdk-14.0.1_linux-x64_bin.tar.gz
    export JAVA_HOME=/usr/lib/jvm/jdk-14.0.1/
    

Como clonar o repositório tinkCryptoHelper

Nas etapas a seguir, clone o repositório de código do tinkCryptoHelper, o aplicativo Java usado neste tutorial.

  1. Na janela do projeto, acesse o diretório principal:

    cd ~
    
  2. Clone a base de código tinkCryptoHelper:

    git clone https://github.com/google/tinkCryptoHelper.git
    cd tinkCryptoHelper
    

Como criar um build do aplicativo tinkCryptoHelper

Nas etapas a seguir, use a ferramenta mvn para executar sua primeira versão do tinkCryptoHelper do código-fonte Java em um pacote Java executável.

  1. Na janela do projeto, crie o pacote:

    mvn package
    

    A saída termina com o seguinte:

    [INFO] --------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] --------------------------------------------------------------------
    [INFO] Total time: 13.880 s
    [INFO] Finished at: 2020-05-17T19:29:13+00:00
    [INFO] Final Memory: 19M/116M
    [INFO] --------------------------------------------------------------------
    

Como testar o sistema

Nas seções a seguir, você executa testes para avaliar como o sistema se comporta depois de fazer alterações de configuração. Há 14 testes de integração no código no repositório. Cada um testa um comportamento diferente para garantir que o sistema está funcionando corretamente. É importante ressaltar que os testes dependem da configuração. Por exemplo, se você não tem um banco de dados Redis anexado ao sistema, os testes não tentam testar a conectividade do Redis.

Testes básicos de funcionalidade

Depois de criar um build do tinkCryptoHelper, você estará pronto para testar a funcionalidade básica dele. Como você não tem um banco de dados Redis on-line, os testes de conectividade não falham.

  1. Na janela do projeto, execute um teste:

    mvn test
    

    A resposta será semelhante a:

    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    [INFO] Running com.google.samples.kms.ale.AppTest
    AES256_GCM encryption cipherlength: 64
    Envelope encryption cipherlength: 224
    [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.057 s - in com.google.samples.kms.ale.AppTest
    [INFO]
    [INFO] Results:
    [INFO]
    [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0
    [INFO]
    [INFO] --------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] --------------------------------------------------------------------
    [INFO] Total time: 7.087 s
    [INFO] Finished at: 2020-05-17T19:34:21+00:00
    [INFO] Final Memory: 12M/56M
    

    O resultado principal é Tests run: 14, Failures: 0, Errors: 0, Skipped: 0. Esse resultado indica que todos os testes foram executados com sucesso.

Testar a conexão do Redis sem instâncias

A próxima etapa nos testes de integração completa requer que o aplicativo (tinkCryptoHelper) se comunique com o Redis. O aplicativo recebe as informações da string de conexão do Redis de um arquivo prefs.xml. Como parte do primeiro teste que você executou na seção anterior, o aplicativo tinkCryptoHelper criou vários arquivos prefs.xml em vários locais, seguindo a estrutura de classe do código-fonte Java.

Como exercício, você define redisIsOnline como true no arquivo prefs.xml. A instância do Redis não está on-line porque você ainda precisa criá-la. Esse teste faz com que os testes de conectividade falhem.

  1. Na janela do projeto, liste os arquivos XML na estrutura do código-fonte para garantir que você encontre e edite o arquivo prefs.xml correto:

    find ~/.java -name '*.xml'
    

    A resposta será semelhante a:

    /home/USERNAME/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml
    /home/USERNAME/.java/.userPrefs/com/google/samples/kms/prefs.xml
    /home/USERNAME/.java/.userPrefs/com/google/samples/prefs.xml
    /home/USERNAME/.java/.userPrefs/com/google/prefs.xml
    /home/USERNAME/.java/.userPrefs/com/prefs.xml
    

    Nessa saída, USERNAME é o nome de usuário que você usou para fazer login.

  2. Para se preparar para testar o Redis com o Maven, abra o seguinte arquivo com nano (ou outro editor do UNIX, como vi):

    nano ~/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml
    

    O conteúdo desse arquivo de configuração é semelhante ao seguinte:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
    <map MAP_XML_VERSION="1.0">
      <entry key="com.google.samples.kms.ale.AppTest" value="true"/>
    </map>
    
  3. Na seção map do arquivo de configuração, adicione uma segunda entrada:

    <entry key="redisIsOnline" value="true"/>
    

    A seção map agora é semelhante a esta:

    <map MAP_XML_VERSION="1.0">
      <entry key="com.google.samples.kms.ale.AppTest" value="true"/>
      <entry key="redisIsOnline" value="true"/>
    </map>
    
  4. Salve e saia do arquivo.

  5. Execute um teste:

    mvn test
    

    Como o Redis não está on-line, a saída mostra erros semelhantes aos seguintes:

    [ERROR]   AppTest.testRedis3_2:86 » JedisConnection Failed connecting to host 127.0.0.2:...
    [ERROR]   AppTest.testRedis4_0:79 » JedisConnection Failed connecting to host 127.0.0.1:...
    [ERROR]   AppTest.testRedisRoundtripRedis3_2:93->testRedisRoundtripRedisSeries:105->testRedisRoundtripClear:149 » JedisConnection
    [ERROR]   AppTest.testRedisRoundtripRedis4_0:99->testRedisRoundtripRedisSeries:105->testRedisRoundtripClear:149 » JedisConnection
    [INFO]
    

    Você causou uma falha. Na próxima seção, você corrigirá a falha.

Como criar instâncias do Redis

Na etapa anterior, o teste falhou porque não havia uma instância do Redis para se conectar. Para corrigir isso, é preciso criar instâncias do Redis. Para realizar testes com o aplicativo tinkCryptoHelper, são necessárias duas instâncias do Redis de versões diferentes. Os testes são executados nas duas instâncias.

Criar as instâncias do Redis

  1. No Cloud Shell, crie duas instâncias do Redis de versões diferentes:

    gcloud redis instances create redis32 --redis-version redis_3_2 --region us-central1
    gcloud redis instances create redis40 --redis-version redis_4_0 --region us-central1
    

    Leva alguns minutos para criar as instâncias do Redis 3.2 e do Redis 4.0.

  2. Veja informações sobre as instâncias:

    gcloud redis instances list --region us-central1
    

    A resposta será semelhante a:

    INSTANCE_NAME  VERSION    REGION       TIER   SIZE_GB  HOST            PORT  NETWORK  RESERVED_IP        STATUS  CREATE_TIME
    redis32        REDIS_3_2  us-central1  BASIC  1        10.126.141.179  6379  default  10.126.141.176/29  READY   2020-05-17T19:47:15
    redis40        REDIS_4_0  us-central1  BASIC  1        10.55.16.163    6379  default  10.55.16.160/29    READY   2020-05-17T19:50:44
    
  3. Copie os endereços IP da saída. Você precisará dos endereços IP do host nas próximas etapas.

Configurar os endereços IP das instâncias do Redis

O próximo passo é dizer ao aplicativo tinkCryptoHelper para encontrar as instâncias do Redis.

  1. Na janela do projeto, abra o arquivo prefs.xml que você abriu anteriormente:

    nano ~/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml
    
  2. Na seção map do arquivo de configuração, adicione duas linhas após a entrada adicionada anteriormente:

    <entry key="redisHost3_2" value="IP_ADDRESS3.2" />
    <entry key="redisHost4_0" value="IP_ADDRESS4.0" />
    

    Substitua:

    • IP_ADDRESS3.2: o valor da instância redis32 que você copiou anteriormente.
    • IP_ADDRESS4.0: o valor da instância redis40 que você copiou anteriormente.
  3. Salve e saia do arquivo.

  4. Execute um teste:

    mvn test
    

    A resposta será semelhante a:

    Set of 5000 cleartext values took on average 2µs on host 10.126.141.179
    Get of 5000 cleartext values took on average 1µs on host 10.126.141.179
    Set of 5000 encrypted values took on average 40µs on host 10.126.141.179
    ...
    Envelope encryption cipherlength: 224
    [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.312 s - in com.google.samples.kms.ale.AppTest
    [INFO]
    [INFO] Results:
    [INFO]
    [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 12.470 s
    [INFO] Finished at: 2020-05-17T20:55:46+00:00
    [INFO] Final Memory: 12M/56M
    [INFO] ------------------------------------------------------------------------
    

Você concluiu o teste de integração com o Redis.

Converter dados e carregá-los no Redis

Na seção anterior, os testes tinham vários efeitos colaterais. Um deles era a inserção de dados nas instâncias do Redis. Esse processo foi necessário para concluir os testes de integração. Se você quiser testar o sistema com seus próprios dados, as etapas a seguir demonstram como usar o tinkCryptoHelper para criptografar os dados e, em seguida, atualizar o banco de dados do Redis com seus próprios dados. Um arquivo de amostra data.csv é fornecido no repositório Git, mas é possível criar seu próprio.

  1. Na janela do projeto, crie um arquivo JAR:

    mvn assembly:single
    
  2. Converta o arquivo data.csv em um arquivo compatível com o comando import do Redis:

    $JAVA_HOME/bin/java -jar target/cryptoHelper-1.0-jar-with-dependencies.jar data.csv redis_import.txt
    

    O arquivo redis_import.txt contém uma série de comandos que podem ser usados para inserir seus dados no Redis.

  3. Instale as ferramentas do Redis, que incluem uma interface de linha de comando (CLI):

    sudo apt-get install redis-tools
    
  4. Se você não quiser manter os dados existentes no banco de dados Redis, faça o seguinte:

    1. Usando a CLI do Redis, conecte-se ao Redis:

      redis-cli -h IP_ADDRESS
      

      Substitua IP_ADDRESS pelo endereço IP que você copiou anteriormente.

    2. Exclua os dados no banco de dados:

      FLUSHALL
      exit
      
  5. Carregue os dados:

    cat redis_import.txt | redis-cli -h IPADDRESS --pipe
    
  6. Para testar se os dados estão lá, conecte-se ao Redis novamente e consiga a chave:

    1. Conecte-se ao Redis:

      redis-cli -h IP_ADDRESS
      
    2. Consiga a chave:

      get 1
      

      A saída é esta:

      Hello World
      

      Se você usou seus próprios dados, a chave e o valor provavelmente são diferentes.

Como criar o próprio KMS

A chave de criptografia KMS neste tutorial pertence a um projeto do Cloud do Google chamado tink-test-infrastructure, não ao projeto que você criou. Esse projeto de amostra tem um KMS usado para disponibilizar a chave de criptografia para uso público.

Para criar suas próprias instâncias e chaves do KMS, crie uma conta de serviço e associe a conta ao papel de IAM do criptografador/descriptografador do Cloud KMS CryptoKey (roles/cloudkms.cryptoKeyEncrypterDecrypter). Com esse papel, a conta de serviço pode criar um keyring do KMS e adicionar chaves.

Configurar uma conta de serviço

  1. No Cloud Shell, crie uma nova conta de serviço:

    gcloud iam service-accounts create tink-509 \
        --description="Account for KMS" \
        --display-name="tinkAccount"
    
  2. No Console do Cloud, acesse a página IAM e administrador.

    Acessar a página "IAM e administrador"

  3. Clique em Adicionar e adicione a conta de serviço tink-509 ao papel do criptografador/descriptografador do Cloud KMS CryptoKey.

    Como adicionar um papel do IAM no Console do Cloud.

  4. Salve a alteração e saia do Console do Cloud.

  5. Na janela do projeto, salve um arquivo de chave de credenciais JSON para a nova conta de serviço:

    1. Faça backup do arquivo de chave de credenciais antigo:

      mv kmsServiceAccountCredentials.json kmsServiceAccountCredentials.json.old
      
    2. Verifique se você fez login. Execute o comando e siga as instruções do OAuth:

      gcloud config set project PROJECT_ID
      gcloud auth login
      

      Substitua PROJECT_ID pelo ID do projeto do Cloud.

    3. Crie o novo arquivo de chave de credenciais:

      gcloud iam service-accounts keys create kmsServiceAccountCredentials.json \
          --iam-account tink-509@app-lev-enc.iam.gserviceaccount.com
      

      A resposta será semelhante a:

      created key [c09dfea3892d6c309333f1998caf35845dc50608] of type [json] as [kmsServiceAccountCredentials.json] for [tink-509@app-lev-enc.iam.gserviceaccount.com]
      

Criar um keyring e uma chave do Cloud KMS

  1. No Cloud Shell, crie um keyring do Cloud KMS:

    gcloud kms keyrings create "unit-and-integration-testing" --location "global"
    gcloud kms keys create aead-key --purpose=encryption --location "global" \
        --keyring unit-and-integration-testing
    
  2. Adicione o papel de IAM roles/cloudkms.cryptoKeyEncrypterDecrypter a uma política do IAM:

    gcloud kms keys add-iam-policy-binding \
        aead-key --location global --keyring unit-and-integration-testing \
        --member serviceAccount:tink-509@app-lev-enc.iam.gserviceaccount.com \
        --role roles/cloudkms.cryptoKeyEncrypterDecrypter
    

    Com esse papel, sua conta de serviço acessa as chaves.

Limpar dados anteriores do Redis

Nos testes anteriores, o banco de dados Redis foi preenchido com dados de amostra, mas a criptografia era baseada em uma chave mais antiga. É necessário limpar os dados e excluir a chave mais antiga.

  1. No Cloud Shell, liste todas as instâncias do Redis:

    gcloud redis instances list --region us-central1
    

    Anote o endereço IP das instâncias que você criou.

  2. Conecte-se às duas instâncias do Redis:

    redis-cli -h IP_ADDRESS
    

    Execute o comando anterior duas vezes, substituindo IP_ADDRESS pelo endereço IP de cada instância.

  3. Exclua os dados no banco de dados:

    FLUSHALL
    exit
    

    Quando você executa o teste final posteriormente neste tutorial, os dados nas tabelas do Redis são criptografados com as novas chaves.

Especificar onde o tinkCryptoHelper encontra o novo KMS

  1. Na janela do projeto, abra o arquivo pref.xml do KMS usando um editor do UNIX:

    nano ~/.java/.userPrefs/com/google/samples/kms/prefs.xml
    
  2. Adicione a seguinte linha como segunda entrada:

    <entry key="keyResourceIdUri" value="gcp-kms://projects/PROJECT_ID/locations/global/keyRings/unit-and-integration-testing/cryptoKeys/aead-key"/>
    

    Substitua PROJECT_ID pelo código do projeto.

Realizar um teste final

  • Na janela do projeto, execute testes:

    mvn test
    

    A resposta será semelhante a:

    ...
    Envelope encryption cipherlength: 224
    [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.312 s - in com.google.samples.kms.ale.AppTest
    ...
    

    Você criou o KMS e as chaves corretamente para testar o tinkCrytoHelper.

Limpeza

Para evitar cobranças dos recursos usados neste tutorial na conta do Google Cloud, exclua o projeto.

Exclua o projeto

  1. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

A seguir