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

Last reviewed 2021-04-30 UTC

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

Questo tutorial presuppone che tu conosca 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 su un cluster GKE.
  • Eseguire il deployment di un server MySQL su un cluster kOps.
  • Configura i gateway in uscita e in entrata per esporre un server mediante mTLS.
  • Verifica l'accesso a un server MySQL utilizzando un client MySQL in esecuzione in cluster o VPC diversi.

Costi

In questo documento vengono utilizzati 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 essere idonei a una prova senza costi aggiuntivi.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

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

  1. Nella console Google Cloud, vai alla pagina del selettore progetto.

    Vai al selettore progetti

  2. Seleziona o crea un progetto Google Cloud.

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  4. Nella console Google Cloud, vai a Cloud Shell.

    Vai a Cloud Shell

    Nella parte inferiore della console Google Cloud, si apre una sessione Cloud Shell che mostra un prompt della riga di comando. Cloud Shell è un ambiente shell con Google Cloud CLI già installato, che include Google Cloud CLI. 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 la regione e la zona per le risorse di computing:
    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 la regione e us-central1-b per la zona. Puoi eseguire il deployment in una regione a tua scelta.

  8. Imposta i ruoli IAM (Identity and Access Management) richiesti. Se sei un Proprietario progetto, disponi di tutte le autorizzazioni necessarie per completare l'installazione. In caso contrario, chiedi all'amministratore di concederti i ruoli IAM (Identity and Access Management) eseguendo questo comando 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 \
        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 Google Cloud 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 questi 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 (in base alle 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 quanto segue:

    • 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 in 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 di Terraform che non viene mostrata nella console, ma su cui è possibile eseguire query, ad esempio ~/bin/terraform output server_token.

  6. Recupera il file del cluster di server kubeconfig dalla directory terraform. Quindi uniscilo al file di configurazione 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 per i due cluster per un uso futuro:

    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 è (nuovamente) 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 di server:

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

Configurazione del lato client

Come menzionato nella guida concettuale, 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 instradare in modo specifico solo quel traffico alla sua destinazione (il database MySQL in un container). In genere, puoi farlo utilizzando un servizio in Kubernetes. In questo caso, devi catturare questo traffico all'interno della comunicazione mesh. Per rilevare il traffico, utilizza gli elementi Istio per creare una definizione speciale del servizio. Sei tu a definire i seguenti elementi:

  • Gateway in uscita
  • Voce di servizio
  • Servizio virtuale
  • Certificati TLS (come secret)
  • Regole della destinazione
  1. In Cloud Shell, recupera l'indirizzo IP del gateway in entrata sul lato server eseguendo una query sull'indirizzo IP del bilanciatore del carico del servizio istio-ingressgateway utilizzando il contesto del gateway in entrata ($SERVER_CLUSTER, che hai 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 la parte dell'indirizzo IP dell'host, devi creare un nome di dominio completo (FQDN). Questo passaggio è necessario perché, per funzionare correttamente, i certificati richiedono un nome di dominio.

    In questo tutorial utilizzerai il servizio DNS con caratteri jolly nip.io per creare un nome di dominio completo 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 nome di dominio completo, puoi iniziare a definire la parte Istio del cluster client.

Crea il gateway in uscita

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

Gateway in uscita in ascolto del 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
    

    Fai attenzione alle porte. Hai utilizzato le porte default qui per lo switch dei server in uscita, che è 15443. Se vuoi utilizzare un'altra porta, devi modificare l'oggetto service del gateway in uscita per aggiungere le porte personalizzate.

    L'opzione hosts definisce l'endpoint, ovvero dove deve essere diretto il traffico.

Definisci la voce di servizio

Il passaggio successivo consiste nel comunicare al mesh di servizi il servizio esterno. Istio dispone di un proprio registry in cui archivia gli endpoint di servizio per il mesh. Se Istio viene installato su 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 Istio, come mostrato nel diagramma seguente.

Aggiunta di un endpoint al registro Istio mediante una definizione delle voci 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 il tipo di traffico previsto (MySQL L4 - livello di rete quattro, utilizzando la porta 3306). Definite anche che la comunicazione passerà alla rete "mesh esterna". Nella sezione degli endpoint, definisci che il flusso deve andare verso l'indirizzo di dominio completo $SERVICE_URL che hai impostato in precedenza e che è mappato al gateway in entrata nel cluster di server (kOps).

Definisci il servizio virtuale

Un servizio virtuale è un insieme di regole di routing del traffico da applicare quando viene indirizzato un host. Ogni regola di routing definisce i criteri di corrispondenza per il traffico di un protocollo specifico. Se il traffico ha una corrispondenza, viene inviato a un servizio di destinazione denominato (o a un sottoinsieme o a una versione dello stesso) definito nel registro. Per ulteriori informazioni, consulta la documentazione di Istio.

Definire 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 definizione seguente, 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 assegnagli il nome 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 applicare la configurazione modificando l'opzione gateways nel file YAML.

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

Nella sezione successiva definirai come gestire il traffico in uscita dal proxy di produzione client, match.gateways.mesh. Puoi anche definire come indirizzare il traffico dal traffico in uscita al 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 instradare il traffico al servizio esterno, devi definire i criteri di traffico da applicare. Il servizio virtuale che hai appena definito gestisce contemporaneamente due casi di routing. Una gestisce il traffico dal proxy sidecar al gateway in uscita, mentre l'altra gestisce il traffico dal gateway in uscita al servizio esterno.

Per creare corrispondenze tra questi casi e le regole di destinazione, sono necessarie due regole distinte. Il seguente diagramma 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 la comunicazione 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 la modalità di routing del 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.

Crea 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 la comunicazione TLS reciproca 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 che archivia 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 certificati al secret:

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

    La best practice attuale per la distribuzione dei certificati è seguita qui utilizzando il servizio di individuazione secret (SDS) anziché il montaggio di 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 avrai più bisogno dei diritti di accesso in lettura (RBAC) per il secret dei gateway.

  4. Aggiungi i certificati a DestinationRule e assegnagli il 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 già creato il secret. Il secret assicura che i certificati vengano utilizzati per la crittografia mTLS dal gateway in uscita all'endpoint esterno.

Una volta completati questi passaggi, la configurazione lato client è completa e si presenta come nel diagramma seguente.

Configurazione lato client finale.

Configurazione lato server

Come discusso nella guida concettuale, per il lato server devi configurare il gateway in entrata in Anthos Service Mesh. Prima di creare i file necessari, è consigliabile rivedere quali componenti sono necessari per realizzare la parte server della soluzione.

Essenzialmente, vuoi identificare il traffico in entrata in base all'origine e al certificato. Inoltre, vuoi instradare specificamente solo quel traffico verso la sua destinazione, il database MySQL in un container. In genere per eseguire questa operazione viene utilizzato un servizio in Kubernetes, ma in questo esempio devi identificare il traffico in entrata all'interno della comunicazione mesh, quindi è necessaria una definizione speciale di un 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, hai bisogno degli stessi certificati per proteggere la comunicazione per il gateway in entrata. A questo scopo, archivia i certificati in un secret e definisci questo secret con l'oggetto gateway in entrata.

  1. In Cloud Shell, copia i file generati nella posizione da cui stai eseguendo 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 server secret:

    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 di 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 che possa decriptare e verificare la comunicazione TLS utilizzando i certificati.

Definire un gateway in entrata che ispeziona il traffico per verificare se è adatto ai criteri di sicurezza e di forwarding.

Questo diagramma mostra dove si trova il gateway in entrata nel cluster. Il traffico attraversa e viene ispezionato se soddisfa i criteri di sicurezza e di inoltro.

  1. In Cloud Shell, usa 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 devi specificare se vuoi utilizzare mTLS. Per assicurarti che questa operazione funzioni come previsto, devi fornire il secret che hai creato, che contiene i certificati.

  3. Abilita la porta 13306 applicando una patch al servizio in entrata. Per abilitare questa porta, crea il seguente file JSON e assegnagli il 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. Per farlo, utilizza un oggetto interno mesh: il servizio virtuale.

Definisci il servizio virtuale

Come già detto, il servizio virtuale è una definizione di pattern di corrispondenza del traffico che modellano il traffico all'interno del tuo mesh. Devi identificare e inoltrare correttamente il traffico dal gateway in entrata verso il servizio DB MySQL, come illustrato nel diagramma seguente.

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

  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 sulla versione L4, devi fornire un flag tcp per descrivere il traffico MySQL. Questo flag indica fondamentalmente alla mesh che L4 viene utilizzato per questo traffico.

    Potresti aver notato che il servizio in entrata utilizza la porta 13306 per inoltrare il traffico. Il servizio virtuale lo acquisisce 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 di server:

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

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

È importante capire che anche l'inoltro interno del mesh viene eseguito utilizzando la crittografia, ma in questo caso la crittografia si basa su certificati interni del mesh. La terminazione di mTLS avviene al gateway.

Ora puoi disporre di un modo di comunicare completamente e reciprocamente con il tuo server MySQL. Questa forma di crittografia è trasparente per il client e il server MySQL, quindi non sono necessarie modifiche all'applicazione. Ciò semplifica le attività come la modifica o la rotazione dei certificati. Inoltre, questo metodo di comunicazione può essere utilizzato in molti scenari diversi.

Test della configurazione

Ora che hai configurato sia il lato client che il lato server, puoi provare la configurazione.

Test del flusso di traffico dal lato client al lato server.

È il momento di generare traffico dal lato client al lato server. Devi seguire il flusso di traffico e assicurarti che 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 sul 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 avere il seguente aspetto:

    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 collegato e aggiunto la tabella, esci dalla connessione MySQL e dal pod:

    exit
    exit
    

    Devi digitare exit due volte: per prima cosa, per uscire dalla connessione DB, e per la seconda volta, per uscire dal pod. Se il pod smette di rispondere all'uscita, premi Ctrl+C per uscire dalla shell bash.

Seguendo questi passaggi, dovresti aver generato un output di logging significativo che ora puoi analizzare ulteriormente.

Nella sezione successiva, verificherai che il traffico lato client passi il proxy e il gateway in uscita. Puoi anche verificare se riesci a vedere il traffico che entra sul 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 visualizzare 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 vedere che il client richiede il server in esecuzione sull'indirizzo IP 34.66.165.46sulla porta 3306. La richiesta viene inoltrata (outbound) a istio-egressgateway in ascolto sulla porta 15443 dell'indirizzo IP 192.168.1.4. Hai definito questo forwarding nel tuo servizio virtuale (client-virtual-service.yaml).

  2. Leggi i log del 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 vedere che la richiesta client instradata a istio-egressgateway in ascolto sulla porta 192.168.1.4 dell'indirizzo IP 15443 viene ulteriormente instradata all'esterno del mesh di servizi al servizio esterno in ascolto sull'indirizzo IP 34.66.165.46 sulla porta 13306. Hai definito questo forwarding nella seconda parte del tuo servizio virtuale (client-virtual-service.yaml).

Testa il gateway in entrata lato server

  1. In Cloud Shell, sul lato server, visualizza i log del 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 vedere che la richiesta del client esterno instradata a istio-ingressgateway in ascolto sulla porta 34.66.165.46 dell'indirizzo IP 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 del proxy Istio:

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

    L'output è simile al seguente:

    [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 di 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 Recupero dei log degli accessi di Envoy.

  3. Testa il database per visualizzare 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: per prima cosa, per uscire dalla connessione DB, e poi per uscire dal pod.

Verifica l'accesso omettendo i certificati

Ora che hai testato e verificato che l'accesso funzioni utilizzando i certificati inseriti, testa anche l'opposto: cosa succede se ometti il gateway in uscita e l'inserimento del certificato. Questo test è anche detto test negativo.

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

  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, viene visualizzato 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é questo pod omette il gateway in uscita e sta cercando di raggiungere il gateway in entrata ($SERVICE_URL) direttamente tramite 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
    

    Questo dimostra che il nome di dominio completo è risolvibile e che la connessione non riuscita è effettivamente dovuta all'inserimento del certificato mancante.

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

    exit
    exit
    

Ulteriori indagini

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

Ora che sai come utilizzare un mesh di servizi per garantire comunicazioni sicure, ti consigliamo di provarlo con un'applicazione che abbia bisogno di uno scambio sicuro di dati e in cui vuoi 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 di questo tutorial. Questa tecnica funziona anche nella piattaforma GKE Enterprise tra due cluster Kubernetes esterni.

I mesh di servizi sono un modo eccellente per aumentare la sicurezza all'interno del cluster e dei 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 (come un fornitore di servizi 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 tuo progetto.

Elimina il progetto

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi 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