Protezione e crittografia delle comunicazioni tra cluster Anthos utilizzando Anthos Service Mesh

Questo tutorial descrive come utilizzare i gateway in entrata e in uscita Anthos Service Mesh per proteggere il traffico tra cluster utilizzando mTLS (Transport Layer Security) comune. Il tutorial è rivolto agli amministratori di cluster Kubernetes che sono responsabili degli aspetti di rete, sicurezza e piattaforma. I controlli descritti qui potrebbero essere particolarmente utili per le organizzazioni con requisiti di sicurezza più elevati o per soddisfare i requisiti normativi. Questo tutorial è accompagnato da una guida concettuale complementare.

Questo tutorial presuppone che tu abbia familiarità con Kubernetes e Anthos Service Mesh.

Obiettivi

  • Utilizza Terraform per configurare l'infrastruttura:
    • Crea una rete VPC personalizzata con due subnet private.
    • Crea due cluster Kubernetes con Anthos Service Mesh abilitato:
    • Registra i cluster in GKE Hub.
  • Eseguire il deployment di un client MySQL sul cluster GKE.
  • Eseguire il deployment di un server MySQL su un cluster kOps.
  • Configura i gateway in entrata e in uscita per esporre un server tramite mTLS.
  • Testa l'accesso a un server MySQL utilizzando un client MySQL in esecuzione in cluster o VPC diversi.

Costi

Questo tutorial utilizza i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono beneficiare di una prova gratuita.

Al termine di questo tutorial, puoi evitare una fatturazione continua eliminando le risorse che hai creato. Per scoprire di più, vedi Pulizia.

Prima di iniziare

Per questo tutorial è necessario un progetto Google Cloud. Puoi crearne uno nuovo oppure selezionare un progetto già creato:

  1. In Google Cloud Console, vai alla pagina del selettore dei progetti.

    Vai al selettore progetti

  2. Seleziona o crea un progetto Google Cloud.

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è abilitata su un progetto.

  4. Nella console, vai a Cloud Shell.

    Vai a Cloud Shell

    Nella parte inferiore della console si apre una sessione Cloud Shell che mostra un prompt della riga di comando. Cloud Shell è un ambiente shell con l'interfaccia a riga di comando di Google Cloud già installata, incluso l'interfaccia a riga di comando di Google Cloud. L'inizializzazione della sessione può richiedere alcuni secondi.

  5. In Cloud Shell, assicurati di lavorare nel progetto che hai creato o selezionato:
    export PROJECT_ID=PROJECT_ID
    gcloud config set project ${PROJECT_ID}
    

    Sostituisci PROJECT_ID con l'ID progetto.

  6. Crea una variabile di ambiente per l'indirizzo email che utilizzi per Google Cloud:
    export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
    

    Sostituisci GOOGLE_CLOUD_EMAIL_ADDRESS con l'indirizzo email che utilizzi in Google Cloud.

  7. Imposta l'area geografica e la zona per le risorse di calcolo:
    export REGION=us-central1
    export ZONE=us-central1-b
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    

    Questo tutorial utilizza us-central1 per l'area geografica e us-central1-b per la zona. Puoi eseguire il deployment in un'area geografica di tua scelta.

  8. Imposta i ruoli IAM necessari. Se sei un proprietario del progetto, hai tutte le autorizzazioni necessarie per completare l'installazione. In caso contrario, chiedi all'amministratore di concederti i ruoli IAM (Gestione di identità e accessi) eseguendo il comando seguente in Cloud Shell:
    ROLES=(
    'roles/container.admin' \
    'roles/gkehub.admin' \
    'roles/iam.serviceAccountAdmin' \
    'roles/iam.serviceAccountKeyAdmin' \
    'roles/resourcemanager.projectIamAdmin' \
    'roles/compute.securityAdmin' \
    'roles/compute.instanceAdmin' \
    'roles/storage.admin' \
    'roles/serviceusage.serviceUsageAdmin'
    )
    for role in "${ROLES[@]}"
    do
     gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \
      --role="$role"
    done
    
  9. Abilita le API necessarie per il tutorial:
    gcloud services enable \
        anthos.googleapis.com \
        anthosgke.googleapis.com \
        anthosaudit.googleapis.com \
        compute.googleapis.com \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        serviceusage.googleapis.com \
        stackdriver.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshtelemetry.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com
    

Preparazione dell'ambiente

  1. In Cloud Shell, clona il seguente repository:

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    cd anthos-service-mesh-samples/docs/mtls-egress-ingress
    
  2. Aggiorna Terraform per il tuo ambiente. Per impostazione predefinita, la console include Terraform 0.12. Questo tutorial presuppone che tu abbia installato Terraform 0.13.5 o versioni successive. Puoi utilizzare temporaneamente un'altra versione di Terraform eseguendo i seguenti comandi:

    mkdir ~/bin
    curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip
    unzip ~/bin/terraform.zip -d ~/bin/
    
  3. Vai alla sottocartella terraform e inizializza Terraform:

    cd terraform/
    ~/bin/terraform init
    
  4. Crea un file terraform.tfvars (basato sulle variabili di ambiente create in precedenza):

    cat << EOF > terraform.tfvars
    project_id = "${PROJECT_ID}"
    region = "${REGION}"
    zones = ["${ZONE}"]
    EOF
    

    Nel passaggio successivo, creerai l'infrastruttura iniziale. Per farlo, devi creare e applicare il piano di esecuzione Terraform per questa configurazione. Gli script e i moduli di questo piano creano i seguenti elementi:

    • Una rete VPC personalizzata con due subnet private
    • Due cluster Kubernetes con Anthos Service Mesh abilitato
    • Un cluster GKE
    • Un cluster kOps in esecuzione nella rete VPC personalizzata

    Il piano di esecuzione registra anche i cluster su GKE Hub.

  5. Esegui il piano di esecuzione:

    ~/bin/terraform plan -out mtls-terraform-plan
    ~/bin/terraform apply "mtls-terraform-plan"
    

    L'output è simile al seguente:

    Apply complete! Resources: 27 added, 0 changed, 0 destroyed.
    Outputs:
    server_token = <sensitive>
    

    La parte <sensitive> è una variabile di output Terraform non mostrata nella console, ma che è possibile interrogare, ad esempio ~/bin/terraform output server_token.

  6. Recupera il file kubeconfig del cluster di server dalla directory terraform. Quindi uniscilo al file di configurazione di client-cluster:

    cd ..
    export KUBECONFIG=client-server-kubeconfig
    cp ./terraform/server-kubeconfig $KUBECONFIG
    gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
    

    Il file client-server-kubeconfig ora contiene la configurazione per entrambi i cluster, che puoi verificare eseguendo questo comando:

    kubectl config view -ojson | jq -r '.clusters[].name'
    

    L'output è il seguente:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    
  7. Ottieni il contesto dei due cluster per utilizzarli in un secondo momento:

    export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client)
    export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server)
    echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
    

    L'output è di nuovo il seguente:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    

    Ora puoi utilizzare questi nomi di cluster come contesto per ulteriori comandi kubectl.

  8. Fai riferimento al cluster client:

    kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
    
  9. Fai riferimento al cluster del server:

    kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
    

Configurazione lato client

Come accennato nella guida ai concetti, il lato client richiede la configurazione del gateway in uscita in Anthos Service Mesh.

In questa sezione configurerai gli elementi Anthos Service Mesh per identificare il traffico esterno in base alla sua origine e utilizzare un certificato personalizzato per criptare le comunicazioni. Inoltre, vuoi indirizzare in modo specifico solo quel traffico alla sua destinazione (il database MySQL in un container). In genere, è necessario farlo utilizzando un servizio in Kubernetes. In questo caso, devi catturare il traffico all'interno della comunicazione mesh. Per rilevare il traffico, utilizza gli elementi Istio per creare una definizione speciale del servizio. Tu definisci i seguenti elementi:

  • Gateway in uscita
  • Voce del servizio
  • Servizio virtuale
  • Certificati TLS (come secret)
  • Regole di destinazione
  1. In Cloud Shell, recupera l'indirizzo IP del gateway in entrata sul lato server inviando una query all'indirizzo IP del bilanciatore del carico del servizio istio-ingressgateway utilizzando il contesto del lato server ($SERVER_CLUSTER, creato in precedenza):

    INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    

    Poiché INGRESS_HOST è solo l'indirizzo IP dell'host, devi creare un nome di dominio completo (FQDN). Si tratta di un passaggio necessario perché, per poter funzionare correttamente, i certificati richiedono un nome di dominio.

    In questo tutorial, utilizzerai il servizio DNS jolly nip.io per creare un FQDN per l'indirizzo IP in entrata. Questo servizio consente di creare il nome di dominio completo senza possedere un dominio.

  2. Archivia l'URL del servizio FQDN in una variabile di ambiente:

    export SERVICE_URL="${INGRESS_HOST}.nip.io"
    

    Ora, con SERVICE_URL definito come FQDN, puoi iniziare a definire la parte Istio del cluster client.

Crea il gateway in uscita

Per iniziare, crea il gateway in uscita per rilevare il traffico destinato al servizio esterno.

Gateway in uscita in ascolto di traffico destinato al servizio esterno.

  1. In Cloud Shell, crea il seguente file YAML e assegnagli il nome client-egress-gateway.yaml:

    cat <<EOF > client-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: istio-egressgateway-mysql
    spec:
     selector:
       istio: egressgateway
     servers:
     - port:
         number: 15443
         name: tls
         protocol: TLS
       hosts:
       - $SERVICE_URL
       tls:
         mode: ISTIO_MUTUAL
    EOF
    
  2. Applica il file YAML precedente al cluster client:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
    

    Presta attenzione ai trasferimenti. Hai usato le porte default qui per lo switch dei server in uscita, che è 15443. Se vuoi utilizzare una porta diversa, devi modificare l'oggetto service del gateway in uscita per aggiungere le porte personalizzate.

    L'opzione hosts definisce l'endpoint, cioè il punto in cui il traffico dovrebbe essere diretto.

Definisci la voce del servizio

Il passaggio successivo consiste nel comunicare al mesh di servizi il servizio esterno. Istio ha un proprio registro in cui archivia gli endpoint di servizio per il mesh. Se Istio è installato sopra Kubernetes, i servizi definiti nel cluster vengono aggiunti automaticamente al registro Istio. Con la definizione della voce di servizio, aggiungi un nuovo endpoint al registro di Istio, come illustrato nel diagramma seguente.

Aggiunta di un endpoint al registro Istio utilizzando una definizione di voce di servizio.

  1. In Cloud Shell, crea il seguente file YAML e assegnagli il nome client-service-entry.yaml:

    cat <<EOF > client-service-entry.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
     name: mysql-external
    spec:
     hosts:
       - $SERVICE_URL
     location: MESH_EXTERNAL
     ports:
       - number: 3306
         name: tcp
         protocol: TCP
       - number: 13306
         name: tls
         protocol: TLS
     resolution: DNS
     endpoints:
       - address: $SERVICE_URL
         ports:
           tls: 13306
    EOF
    
  2. Applica il file YAML precedente al cluster client:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
    

    La definizione del servizio client in questo file YAML indica al servizio quale tipo di traffico prevede (MySQL L4 - livello rete 4, utilizzando la porta 3306). Devi anche specificare che la comunicazione sarà esterna. Nella sezione Endpoint definisci che il flusso deve essere indirizzato all'indirizzo FQDN $SERVICE_URL impostato in precedenza e che è mappato al gateway in entrata nel cluster del server (kOps).

Definisci il servizio virtuale

Un servizio virtuale è un insieme di regole di routing del traffico da applicare quando viene risolto un problema di host. Ogni regola di routing definisce criteri di corrispondenza per il traffico di un protocollo specifico. Se il traffico corrisponde, viene inviato a un servizio di destinazione denominato (o a un suo sottoinsieme o versione) definito nel Registro di sistema. Per ulteriori informazioni, consulta la documentazione di Institute.

Definisci un servizio virtuale che indichi a Istio come applicare il routing per il traffico che raggiunge il servizio esterno.

La definizione del servizio virtuale indica a Istio come applicare il routing per il traffico che raggiunge il servizio esterno. Con la seguente definizione, indichi al mesh di instradare il traffico dal client al gateway in uscita sulla porta 15443. Dal gateway in uscita, instrada il traffico all'host $SERVICE_URL sulla porta 13306 (dove è in ascolto il gateway in entrata lato server).

  1. Crea il seguente file YAML e denominalo client-virtual-service.yaml:

    cat <<EOF > client-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: direct-mysql-through-egress-gateway
    spec:
     hosts:
       - $SERVICE_URL
     gateways:
       - istio-egressgateway-mysql
       - mesh
     tcp:
       - match:
           - gateways:
               - mesh
             port: 3306
         route:
           - destination:
               host: istio-egressgateway.istio-system.svc.cluster.local
               subset: mysql
               port:
                 number: 15443
             weight: 100
       - match:
           - gateways:
               - istio-egressgateway-mysql
             port: 15443
         route:
           - destination:
               host: $SERVICE_URL
               port:
                 number: 13306
             weight: 100
    EOF
    
  2. Applica la definizione YAML al cluster client:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
    

    Puoi specificare i gateway a cui deve essere applicata la configurazione modificando l'opzione gateways nel file YAML.

    La parte importante di questa definizione è l'uso della parola riservata mesh, che implica tutte le sidecar nel mesh. In base alla documentazione di Istio, quando questo campo viene omesso, viene utilizzato il gateway predefinito (mesh), applicando la regola a tutti i file collaterali nel mesh. Se fornisci un elenco di nomi di gateway, le regole si applicano solo ai gateway. Per applicare le regole a gateway e file collaterali, specifica mesh come nome del gateway.

Nella sezione successiva definirai come gestire il traffico proveniente dal proxy dei prodotti client, match.gateways.mesh. Puoi anche definire la modalità di routing del traffico dal traffico in uscita verso il servizio esterno utilizzando l'opzione match.gateways.istio-egressgateway-mysql.

Definisci una regola di destinazione (dal client al gateway in uscita)

Ora che hai definito come indirizzare il traffico al servizio esterno, devi definire i criteri di traffico da applicare. Il servizio virtuale che hai appena definito gestisce due richieste di routing contemporaneamente. Uno gestisce il traffico dal proxy sidecar al gateway in uscita, mentre l'altro gestisce il traffico dal gateway in uscita al servizio esterno.

Per abbinare questi casi alle regole di destinazione, sono necessarie due regole separate. Il diagramma seguente mostra la prima regola, che gestisce il traffico dal proxy al gateway in uscita. In questa definizione, indichi ad Anthos Service Mesh di utilizzare i certificati predefiniti per le comunicazioni mTLS.

Regola di destinazione che definisce come gestire il traffico dal proxy sidecar al gateway in uscita.

  1. In Cloud Shell, crea il seguente file YAML e assegnagli il nome client-destination-rule-to-egress-gateway.yaml:

    cat <<EOF > client-destination-rule-to-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mysql
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
        - name: mysql
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
              - port:
                  number: 15443
                tls:
                  mode: ISTIO_MUTUAL
                  sni: $SERVICE_URL
    EOF
    
  2. Applica la definizione YAML precedente al cluster client:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
    

    Nel file YAML precedente, hai utilizzato l'opzione hosts per definire come instradare il traffico dal proxy client al gateway in uscita. Inoltre, hai configurato il mesh in modo che utilizzi ISTIO_MUTUAL sulla porta 15443, che è una delle porte aperte automaticamente al gateway in uscita.

Creare una regola di destinazione (dal gateway in uscita al servizio esterno)

Il seguente diagramma mostra la seconda regola di destinazione, che indica al mesh come gestire il traffico dal gateway in uscita al servizio esterno.

Seconda regola di destinazione che definisce come gestire il traffico dal gateway in uscita al servizio esterno.

Devi indicare al mesh di utilizzare i certificati inseriti per le comunicazioni TLS reciproche con il servizio esterno.

  1. In Cloud Shell, dalla directory anthos-service-mesh-samples/docs/mtls-egress-ingress, crea i certificati:

     ./create-keys.sh
    

    Assicurati di fornire una password quando lo script la richiede.

  2. Copia i file generati nella directory corrente:

    cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem
    cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem
    cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
    
  3. Crea un secret Kubernetes per archiviare i certificati in modo che possano essere utilizzati come riferimento in un secondo momento nel gateway:

     kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \
      generic client-credential \
      --from-file=tls.key=client-$SERVICE_URL.key.pem \
      --from-file=tls.crt=client-$SERVICE_URL.cert.pem \
      --from-file=ca.crt=ca-chain.cert.pem
    

    Il comando precedente aggiunge i seguenti file di certificato al secret:

    client-$SERVICE_URL.key.pem
    client-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

    L'attuale best practice per la distribuzione dei certificati è seguita dall'utilizzo del servizio di rilevamento segreto (SDS) anziché dei montaggi dei file. Questa pratica evita di riavviare i pod quando aggiungi un nuovo certificato. A partire da Istio 1.8/1.9, con questa tecnica non hai più bisogno dei diritti di accesso in lettura (RBAC) per il secret dei gateway.

  4. Aggiungi i certificati al DestinationRule e assegnagli un nome client-destination-rule-to-external-service.yaml:

    cat <<EOF > client-destination-rule-to-external-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
     name: originate-mtls-for-mysql
    spec:
     host: $SERVICE_URL
     trafficPolicy:
       loadBalancer:
         simple: ROUND_ROBIN
       portLevelSettings:
       - port:
           number: 13306
         tls:
           mode: MUTUAL
           credentialName: client-credential
           sni: $SERVICE_URL
    EOF
    
  5. Applica la definizione YAML precedente al cluster client:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
    

    Questa regola funziona solo se hai creato prima il secret. Il secret assicura che i certificati vengano utilizzati per la crittografia mTLS dal gateway in uscita verso l'endpoint esterno.

Dopo aver completato questi passaggi, la configurazione lato client è completata e ha l'aspetto del seguente diagramma.

Configurazione lato client finale.

Configurazione lato server

Come spiegato nella guida sui concetti, per il lato server è necessario configurare il gateway in entrata in Anthos Service Mesh. Prima di creare i file necessari, è una buona idea controllare quali componenti sono necessari per realizzare la parte server della soluzione.

Sostanzialmente, vuoi identificare il traffico in entrata in base alla sua origine e al certificato. Vuoi anche indirizzare in modo specifico solo quel traffico alla sua destinazione: il tuo database MySQL in un container. In genere utilizzi un servizio in Kubernetes per questo, ma in questo esempio devi identificare il traffico in entrata all'interno della comunicazione mesh, quindi hai bisogno di una definizione speciale di servizio che includa quanto segue:

  • Certificati TLS (come secret)
  • Gateway in entrata
  • Servizio virtuale

Crea il secret per il gateway in entrata

Come hai fatto per il gateway in uscita, per proteggere le comunicazioni per il gateway in entrata devi avere gli stessi certificati. Per farlo, archivia i certificati in un secret e definisci questo secret con il tuo oggetto gateway in entrata.

  1. In Cloud Shell, copia i file generati nel percorso da cui esegui il comando successivo:

    cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem
    cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
    
  2. Crea il secret del server:

    kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \
        generic mysql-credential \
        --from-file=tls.key=server-$SERVICE_URL.key.pem \
        --from-file=tls.crt=server-$SERVICE_URL.cert.pem \
        --from-file=ca.crt=ca-chain.cert.pem
    

    Hai appena aggiunto i seguenti file del certificato al secret:

    server-$SERVICE_URL.key.pem
    server-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

Definisci il gateway in entrata

Per ricevere il traffico dal cluster lato client, devi specificare un gateway in entrata in grado di decriptare e verificare la comunicazione TLS utilizzando i certificati.

Definisci un gateway in entrata che controlla il traffico per verificare che soddisfi i criteri di sicurezza e di inoltro.

Questo diagramma mostra la posizione del gateway in entrata nel cluster. Il traffico passa e viene controllato se soddisfa i criteri di sicurezza e inoltro.

  1. In Cloud Shell, utilizza il seguente file YAML e assegnagli il nome server-ingress-gatway.yaml:

    cat <<EOF > server-ingress-gatway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: gateway-mysql
    spec:
     selector:
       istio: ingressgateway # Istio default gateway implementation
     servers:
     - port:
         number: 13306
         name: tls-mysql
         protocol: TLS
       tls:
         mode: MUTUAL
         credentialName: mysql-credential
       hosts:
       - "$SERVICE_URL"
    EOF
    
  2. Applica la definizione YAML precedente al cluster client:

    kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
    

    Presta attenzione alla sezione tls: perché è particolarmente importante. In questa sezione definisci se vuoi utilizzare mTLS. Per assicurarti che l'operazione funzioni come previsto, devi fornire il secret che hai creato, contenente i certificati.

  3. Abilita la porta 13306 applicando patch al servizio in entrata. Puoi abilitare questa porta creando il seguente file JSON e assegnandogli un nome gateway-patch.json:

    cat <<EOF > gateway-patch.json
    [{
      "op": "add",
      "path": "/spec/ports/0",
      "value": {
        "name": "tls-mysql",
        "protocol": "TCP",
        "targetPort": 13306,
        "port": 13306
      }
    }]
    EOF
    
  4. Applica la patch al servizio gateway:

    kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
    

Ora che hai aperto la porta sul gateway in entrata, devi recuperare il traffico proveniente dal nuovo gateway in entrata e indirizzarlo al pod del database. A tal fine, utilizza un oggetto interno mesh: il servizio virtuale.

Definisci il servizio virtuale

Come spiegato in precedenza, il servizio virtuale è una definizione di pattern di corrispondenza del traffico che formano il traffico all'interno del tuo mesh. Devi identificare e inoltrare correttamente il traffico dal gateway in entrata al servizio MySQL DB, come mostrato nel diagramma seguente.

Identificazione e inoltro del traffico dal gateway in entrata al servizio MySQL.

  1. In Cloud Shell, crea il seguente file YAML e assegnagli il nome server-virtual-service.yaml:

    cat <<EOF > server-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: mysql-virtual-service
    spec:
     hosts:
       - "$SERVICE_URL"
     gateways:
       - gateway-mysql
     tcp:
       - route:
         - destination:
             port:
               number: 3306
             host: mysql.default.svc.cluster.local
    EOF
    

    È importante che questo servizio virtuale faccia riferimento al gateway in entrata da cui proviene il traffico. Il gateway è denominato gateway-mysql.

    Poiché questo servizio virtuale viene applicato su L4, devi fornire un flag tcp per descrivere il tuo traffico MySQL. Questo flag indica fondamentalmente la rete L4 utilizzata per questo traffico.

    Forse avrai notato che il servizio in entrata utilizza la porta 13306 per inoltrare il traffico. Il servizio virtuale lo rileva e lo traduce in 3306.

    Infine, inoltri il traffico al server MySQL nel cluster Kubernetes del server. Per questo esempio, il server è in ascolto sulla porta MySQL standard 3306.

  2. Applica la definizione YAML al cluster del server:

    kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
    

Queste due definizioni decriptano la richiesta del client MySQL incapsulata da mTLS e la inoltrano al database MySQL all'interno del mesh.

È importante comprendere che l'inoltro interno mesh utilizza anche la crittografia, ma in questo caso la crittografia si basa sui certificati interni mesh. La terminazione mTLS avviene al gateway.

Ora puoi comunicare in modo completo e reciproco con il server MySQL. Questa forma di crittografia è trasparente per il client e il server MySQL, quindi non sono necessarie modifiche alle applicazioni. In questo modo, la modifica o la rotazione dei certificati è molto semplice. Questo modo di comunicazione può essere utilizzato per molti scenari diversi.

Test della configurazione

Ora che hai configurato i lati del client e del server, puoi testare la configurazione.

Testare il flusso di traffico dal lato client al lato server.

È ora di generare traffico dal lato client al lato server. Vuoi seguire il flusso del traffico e assicurarti che il traffico venga instradato, criptato e decriptato come previsto.

  1. In Cloud Shell, esegui il deployment del server MySQL nel cluster di server:

    kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
    
  2. Avvia un client MySQL nel cluster client:

    kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
    

    All'avvio del container, viene visualizzata una shell, che dovrebbe essere simile alla seguente:

    root@mysql-client-1:/#
    
  3. Connettiti al server MySQL:

    mysql -pyougottoknowme -h $SERVICE_URL
    
  4. Utilizza i seguenti comandi per aggiungere un database e una tabella:

    CREATE DATABASE test_encrypted_connection;
    USE test_encrypted_connection;
    CREATE TABLE message (id INT, content VARCHAR(20));
    INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
    
  5. Dopo aver connesso e aggiunto la tabella, esci dalla connessione MySQL e dal pod:

    exit
    exit
    

    Devi digitare un'uscita due volte: prima, per uscire dalla connessione DB, e poi per uscire dal pod. Se il pod smette di rispondere all'uscita, premi Ctrl + C per annullare l'uscita dalla shell bash.

Se segui questi passaggi, dovresti aver generato alcuni output di logging significativi che puoi ora analizzare ulteriormente.

Nella sezione successiva verificherai che il traffico lato client stia passando il proxy e il gateway in uscita. Verifica anche se puoi vedere il traffico che entra nel lato server attraverso il gateway in entrata.

Testare il proxy lato client e il gateway in uscita

  1. In Cloud Shell, sul proxy lato client, verifica di poter vedere i log del proxy Istio:

    kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
    

    L'output di debug è simile al seguente:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
    

    Premi Ctrl+C per uscire dall'output del log.

    In questa voce di log puoi notare che il client richiede che il server in esecuzione sull'indirizzo IP 34.66.165.46sulla porta 3306. La richiesta è inoltrata (outbound) a istio-egressgateway in ascolto sull'indirizzo IP 192.168.1.4 porta 15443. Hai definito questo inoltro nel tuo servizio virtuale (client-virtual-service.yaml).

  2. Leggi i log proxy del gateway in uscita:

    kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
    

    L'output di debug è simile al seguente:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
    

    Premi Ctrl+C per uscire dall'output del log.

    In questa voce di log puoi notare che la richiesta client instradata verso istio-egressgateway in ascolto sulla porta IP dell'indirizzo 192.168.1.4 15443 è ulteriormente instradata all'esterno del mesh di servizi verso il servizio esterno in ascolto sull'indirizzo IP 34.66.165.46 sulla porta 13306.. Hai definito questo inoltro nella seconda parte del tuo servizio virtuale (client-virtual-service.yaml).

Testare il gateway in entrata lato server

  1. In Cloud Shell, sul lato server, visualizza i log proxy del gateway in entrata:

    kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
    

    L'output nel log è simile al seguente:

    [2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
    

    Premi Ctrl+C per uscire dall'output del log.

    In questa voce di log puoi notare che la richiesta del client esterno instradata a istio-ingressgateway in ascolto sulla porta IP dell'indirizzo 34.66.165.46 13306 è ulteriormente instradata al servizio MySQL all'interno del mesh identificato dal nome del servizio mysql.default.svc.cluster.local sulla porta 3306.. Hai definito questo forwarding nel gateway in entrata (server-ingress-gateway.yaml).

  2. Per il server MySQL, visualizza i log proxy di Istio:

    kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
    

    L'output ha il seguente aspetto:

    [2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
    

    Premi Ctrl+C per uscire dall'output del log.

    In questa voce di log puoi vedere la chiamata in entrata al server del database MySQL in ascolto sulla porta 3306 dell'indirizzo IP 100.96.4.8. La chiamata proviene dal pod in entrata con l'indirizzo IP 100.96.1.3.

    Per ulteriori informazioni sul formato di logging di Envoy, vedi Utilizzo dei log di accesso di Envoy.

  3. Testa il tuo database per vedere l'input generato:

    MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}')
    kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
    
  4. Verifica il database creato:

    mysql -pyougottoknowme
    USE test_encrypted_connection;
    SELECT * from message;
    

    L'output è simile al seguente:

    +------+-----------+
    | id   | content   |
    +------+-----------+
    |    1 | Crypto Hi |
    +------+-----------+
    1 row in set (0.00 sec)
    
  5. Esci dal database MySQL:

    exit
    exit
    

    Devi digitare exit due volte: innanzitutto per uscire dalla connessione DB e poi per uscire dal pod.

Testa l'accesso omettendo i certificati

Ora che hai testato e verificato che l'accesso funziona usando i certificati inseriti, prova anche il contrario: cosa succede se ometti il gateway in uscita e l'inserimento del certificato. Questo test è anche chiamato test negativo.

Puoi eseguire questo test avviando un altro pod in uno spazio dei nomi senza inserimento del proxy laterale.

  1. In Cloud Shell, crea un nuovo spazio dei nomi:

    kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
    
  2. Crea un pod e avvia una shell interattiva all'interno del container:

    kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \
    mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \
    -n unencrypted --restart=Never -- /bin/bash
    
  3. Prova a connetterti al database dopo l'avvio della shell interattiva:

    mysql -pyougottoknowme -h $SERVICE_URL
    

    Dopo 30 secondi, vedi un output simile al seguente:

    Warning: Using a password on the command line interface can be insecure.
    ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
    

    Questo avviso è previsto perché in questo pod manca il gateway in uscita e sta cercando di raggiungere il gateway in entrata ($SERVICE_URL) direttamente in Internet.

  4. Prova a risolvere l'indirizzo IP del servizio:

    resolveip $SERVICE_URL
    

    L'output è simile al seguente. Il tuo indirizzo IP sarà diverso.

    IP address of 104.154.164.12.nip.io is 104.154.164.12
    

    Ciò dimostra che il nome di dominio completo è risolvibile e che la connessione non riuscita è effettivamente dovuta alla mancanza di un certificato.

  5. Esci dalla connessione MySQL e dal pod server MySQL:

    exit
    exit
    

Ulteriore indagine

Un argomento non trattato in questo tutorial è che in genere le configurazioni in uscita sono di proprietà di un ruolo o di un'organizzazione diversi, perché sono ospitate nello spazio dei nomi istio-system. Configurare le autorizzazioni Kubernetes RBAC in modo che solo gli amministratori di rete possano creare e modificare direttamente le risorse trattate in questo tutorial.

Ora che sai come utilizzare un mesh di servizi per garantire la comunicazione sicura, puoi provare con un'applicazione che deve scambiare dati in modo sicuro e controllare la crittografia fino al livello del certificato. Per iniziare, puoi installare Anthos Service Mesh.

Prova a utilizzare due cluster GKE e combinali utilizzando la tecnica in questo tutorial. Questa tecnica funziona anche nella piattaforma Anthos tra due cluster Kubernetes stranieri.

I mesh di servizi sono un modo eccellente per aumentare la sicurezza all'interno del cluster e per i servizi esterni. Un ultimo caso d'uso da provare è avere un endpoint mTLS che non sia un secondo cluster Kubernetes ma un provider di terze parti (ad esempio un provider di pagamento).

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, puoi eliminare il progetto.

Elimina il progetto

  1. Nella console, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto da eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Passaggi successivi