Exposer des applications Kubernetes via la Gateway API dans GKE
Comment rendre les applications exécutées dans GKE accessibles publiquement via la Gateway API, avec des certificats TLS wildcard managés et une protection WAF Cloud Armor.
Vue d'ensemble
Si vous ne savez pas ce que la Gateway API , voici ce que le Groupe d'Intérêt Spécial (SIG) dédié au Réseau dans Kubernetes dit à ce sujet :
Gateway API est un projet officiel de Kubernetes axé sur le routage L4 et L7 dans Kubernetes. Ce projet représente la nouvelle génération d'API Kubernetes Ingress, Load Balancing et Service Mesh. Dès le départ, il a été conçu pour être générique, expressif et orienté rôles.
En d'autres termes, la Gateway API est une spécification qui vise à améliorer et à normaliser la mise en réseau des services dans Kubernetes. C'est l'avenir de l'exposition des applications Kubernetes. Pour en savoir plus, consultez Présentation de la Gateway API .
Ensuite, nous utiliserons l' Implémentation de la Gateway API dans GKE pour exposer publiquement certaines de nos applications exécutées dans GKE.
Création de la Gateway
Activation de la fonctionnalité Gateway API
Avant de créer la ressource Gateway, nous devons nous assurer que la fonctionnalité Gateway API est activée sur le cluster GKE. Jetez un œil à Activer la Gateway API sur les clusters GKE ou Vérifier l'activation de la Gateway API sur les clusters GKE pour celà.
Pour ceux qui utilisent le cluster GKE standard, voici la commande à utiliser pour activer la Gateway API sur un cluster existant :
gcloud container clusters update <cluster_name> --gateway-api=standard --location=<cluster_region>
Une fois activée, nous devrions voir les CRD liées à la Gateway API :
$ kubectl api-resources | grep gateway
gatewayclasses gc gateway.networking.k8s.io/v1beta1 false GatewayClass
gateways gtw gateway.networking.k8s.io/v1beta1 true Gateway
httproutes gateway.networking.k8s.io/v1beta1 true HTTPRoute
referencegrants refgrant gateway.networking.k8s.io/v1beta1 true ReferenceGrant
gcpgatewaypolicies networking.gke.io/v1
Sélection de la GatewayClass
Nous devons également choisir une GatewayClass pour la ressource Gateway que nous allons créer. La GatewayClass choisie déterminera les capacités/fonctionnalités disponibles pour la Gateway. Voir Capacités/fonctionnalités disponibles de la Gateway selon la classe pour en savoir plus.
Actuellement, la GatewayClass avec le plus de fonctionnalités est la gke-l7-global-external-managed. C'est celle que nous utiliserons pour créer la ressource Gateway. Voici une liste des ressources GatewayClass actuellement disponibles pour notre cluster GKE :
$ kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
gke-l7-global-external-managed networking.gke.io/gateway True 54m
gke-l7-gxlb networking.gke.io/gateway True 54m
gke-l7-regional-external-managed networking.gke.io/gateway True 54m
gke-l7-rilb networking.gke.io/gateway True 54m
Voici le résultat d'un describe sur la ressource GatewayClass gke-l7-global-external-managed :
$ kubectl describe gatewayclass/gke-l7-global-external-managed
Name: gke-l7-global-external-managed
Namespace:
Labels: <none>
Annotations: <none>
API Version: gateway.networking.k8s.io/v1beta1
Kind: GatewayClass
Metadata:
Creation Timestamp: ***
Generation: 1
Resource Version: 27032
UID: 6062d521-6d0f-46f7-bc0d-19d3c968d4f2
Spec:
Controller Name: networking.gke.io/gateway
Description: Preview class for new L7 External Load Balancers.
Status:
Conditions:
Last Transition Time: ***
Message:
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 55m sc-gateway-controller gke-l7-global-external-managed
# Result on older versions
$ kubectl describe gatewayclass/gke-l7-global-external-managed
Name: gke-l7-global-external-managed
Namespace:
Labels: <none>
Annotations: <none>
API Version: gateway.networking.k8s.io/v1beta1
Kind: GatewayClass
Metadata:
Creation Timestamp: *****
Generation: 1
Managed Fields:
API Version: gateway.networking.k8s.io/v1beta1
Fields Type: FieldsV1
fieldsV1:
f:spec:
.:
f:controllerName:
f:description:
Manager: GoogleGKEGatewayController
Operation: Update
Time: *****
API Version: gateway.networking.k8s.io/v1beta1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:conditions:
k:{"type":"Accepted"}:
f:lastTransitionTime:
f:message:
f:observedGeneration:
f:reason:
f:status:
Manager: GoogleGKEGatewayController
Operation: Update
Subresource: status
Time: *****
Resource Version: 585627605
UID: f67bc0b6-5da4-441d-bad6-cdf9a4195f56
Spec:
Controller Name: networking.gke.io/gateway
Description: Preview class for new L7 External Load Balancers.
Status:
Conditions:
Last Transition Time: *****
Message:
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Events: <none>
La ressource Gateway
Créons maintenant la Gateway. Voici le manifeste « gateway.yml » :
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: testgwapi-global-external-managed
namespace: default
spec:
gatewayClassName: gke-l7-global-external-managed
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
Pour obtenir la liste de tous les champs pris en charge par la ressource « Gateway » GKE, consultez Champs d'API de la Gateway API pris en charge par la ressource Gateway de GKE par GatewayClass.
Appliquons le manifeste et voyons ce qui se passe :
$ kubectl apply -f gateway.yml
gateway.gateway.networking.k8s.io/testgwapi-global-external-managed created
$ kubectl get gateway -w
NAME CLASS ADDRESS PROGRAMMED AGE
testgwapi-global-external-managed gke-l7-global-external-managed Unknown 17s
testgwapi-global-external-managed gke-l7-global-external-managed Unknown 82s
testgwapi-global-external-managed gke-l7-global-external-managed 34.111.146.21 True 82s
testgwapi-global-external-managed gke-l7-global-external-managed 34.111.146.21 True 114s
$ kubectl get events
LAST SEEN TYPE REASON OBJECT MESSAGE
2m23s Normal ADD gateway/testgwapi-global-external-managed default/testgwapi-global-external-managed
61s Normal UPDATE gateway/testgwapi-global-external-managed default/testgwapi-global-external-managed
29s Normal SYNC gateway/testgwapi-global-external-managed SYNC on default/testgwapi-global-external-managed was a success
Après avoir créé la ressource « Gateway », un équilibreur de charge HTTP/HTTPS Google Cloud a été automatiquement provisionné par le Contrôleur de Gateway GKE.
Cet équilibreur de charge pourra acheminer le trafic externe vers les pods des clusters GKE selon nos configurations de routage « Gateway API ». Notez qu'une adresse IP publique aléatoire a également été automatiquement attribuée à l'équilibreur de charge. Cette adresse IP apparaît dans la colonne « ADDRESS » du résultat de la commande « kubectl get gateway » précédente.
Utilisation d'une adresse IP statique sur la Gateway
Sans spécifier une adresse IP statique publique déjà réservée à utiliser sur la « Gateway », une adresse sera automatiquement réservée et attribuée à l'équilibreur de charge L7 provisionné suite à la création de la ressource « Gateway ».
L'adresse IP réservée automatiquement sera également libérée dès la suppression de la Gateway. Ainsi, si, pour une raison quelconque, nous souhaitons supprimer et recréer la Gateway, son adresse IP pourrait changer.
Pour éviter cela, nous pouvons utiliser notre propre adresse IP statique publique réservée sur la « Gateway ». L'adresse IP statique publique peut être réservée depuis la Page de réservation d'adresses IP publiques de la console GCP ou en utilisant la CLI « gcloud » comme suit :
gcloud compute addresses create ADDRESS_NAME --project=GCP_PROJECT_ID --global
Pour voir l'adresse IP, utilisez :
gcloud compute addresses list --filter="name=( 'ADDRESS_NAME' )" --project=GCP_PROJECT_ID
Une fois l'adresse IP réservée, nous devons indiquer à la Gateway qu'elle doit l'utiliser pendant ou après sa création. Pour cela, nous devons renseigner le champ « gateway.spec.addresses » dans le manifeste de la passerelle comme suit :
kind: Gateway
...
spec:
(...)
addresses:
- type: NamedAddress
value: ADDRESS_NAME
Configuration de TLS sur la Gateway
Vue d'ensemble
Dans cette section, nous allons configurer TLS sur la Gateway pour sécuriser les communications avec les clients accédant à nos applications. Nous utiliserons le Gestionnaire de certificats Google pour générer des certificats TLS (y compris des certificats wildcard) qui seront automatiquement renouvelés.
Le Listener HTTPS
Nous avons besoin d'un nouveau listener sur la Gateway pour le protocole HTTPS. Voici le manifeste permettant d'ajouter ce listener :
kind: Gateway
(...)
spec:
gatewayClassName: gke-l7-global-external-managed
listeners:
(...)
- name: https
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: All
Création d'une autorisation DNS
Avant de créer le certificat TLS géré par Google avec le Gestionnaire de certificats Google, nous devons créer une autorisation DNS et ajouter l'entrée DNS requise à l'intérieur de la zone DNS correspondante, afin de prouver que nous possédons le nom de domaine pour lequel nous souhaitons générer un certificat TLS.
Créons une autorisation DNS appelée « testgatewayapi » et affichons son contenu :
$ gcloud certificate-manager dns-authorizations create testgatewayapi --domain="testgatewayapi.hackerstack.org"
Create request issued for: [testgatewayapi]
Waiting for operation [projects/(...)] to complete...done.
Created dnsAuthorization [testgatewayapi].
$ gcloud certificate-manager dns-authorizations describe testgatewayapi
createTime: '***'
dnsResourceRecord:
data: 8bf4bd32-c3c7-4af1-ac25-7b38e07807f6.10.authorize.certificatemanager.goog.
name: _acme-challenge.testgatewayapi.hackerstack.org.
type: CNAME
domain: testgatewayapi.hackerstack.org
name: projects/***/locations/global/dnsAuthorizations/testgatewayapi
type: FIXED_RECORD
updateTime: '***'
Cette autorisation DNS nous demande de créer un enregistrement DNS « CNAME » appelé « _acme-challenge.testgatewayapi.hackerstack.org. » ayant pour valeur « 8bf4bd32-c3c7-4af1-ac25-7b38e07807f6.10.authorize.certificatemanager.goog. ».
Après avoir créé l'enregistrement DNS, vérifions en effectuant une requête DNS :
$ dig +short -t CNAME _acme-challenge.testgatewayapi.hackerstack.org
8bf4bd32-c3c7-4af1-ac25-7b38e07807f6.10.authorize.certificatemanager.goog.
Ça a l'air bien. Nous sommes prêts à générer le certificat TLS pour « testgatewayapi.hackerstack.org » et « *.testgatewayapi.hackerstack.org » avec le Gestionnaire de certificats Google.
Nous utilisons l'autorisation DNS pour la vérification de la propriété des domaines car elle permet la création de certificats TLS indépendamment de la ressource Gateway et prend en charge les certificats génériques (wildcards). Pour en savoir plus sur les différentes méthodes d'autorisation de domaine, consultez Autorisation de domaine ou Autorisation de domaine pour les certificats gérés par Google.
Création du certificat TLS géré par Google
Nous utilisons le Gestionnaire de certificats Google pour cela.
Générons un certificat TLS pour les domaines « testgatewayapi.hackerstack.org » et « *.testgatewayapi.hackerstack.org », en utilisant l'autorisation DNS précédemment créée appelée « testgatewayapi » :
$ gcloud certificate-manager certificates create testgatewayapi --domains="*.testgatewayapi.hackerstack.org,testgatewayapi.hackerstack.org" --dns-authorizations=testgatewayapi
Create request issued for: [testgatewayapi]
Waiting for operation [projects/***/locations/global/operations/operation-***] to complete...done.
Created certificate [testgatewayapi].
$ gcloud certificate-manager certificates list
NAME SUBJECT_ALTERNATIVE_NAMES DESCRIPTION SCOPE EXPIRE_TIME CREATE_TIME UPDATE_TIME
testgatewayapi *.testgatewayapi.hackerstack.org *** ***
testgatewayapi.hackerstack.org
Pour voir l’état de la génération du certificat, nous utilisons la commande suivante :
$ gcloud certificate-manager certificates describe testgatewayapi
createTime: '***'
managed:
authorizationAttemptInfo:
- domain: '*.testgatewayapi.hackerstack.org'
state: AUTHORIZING
- domain: testgatewayapi.hackerstack.org
state: AUTHORIZING
dnsAuthorizations:
- projects/***/locations/global/dnsAuthorizations/testgatewayapi
domains:
- '*.testgatewayapi.hackerstack.org'
- testgatewayapi.hackerstack.org
state: PROVISIONING
name: projects/***/locations/global/certificates/testgatewayapi
sanDnsnames:
- '*.testgatewayapi.hackerstack.org'
- testgatewayapi.hackerstack.org
updateTime: '***'
Une fois le certificat généré (cela a pris environ 6 minutes dans ce cas), nous devrions voir quelque chose comme ceci :
createTime: '***'
expireTime: '***'
managed:
authorizationAttemptInfo:
- domain: '*.testgatewayapi.hackerstack.org'
state: AUTHORIZED
- domain: testgatewayapi.hackerstack.org
state: AUTHORIZED
dnsAuthorizations:
- projects/***/locations/global/dnsAuthorizations/testgatewayapi
domains:
- '*.testgatewayapi.hackerstack.org'
- testgatewayapi.hackerstack.org
state: ACTIVE
name: projects/***/locations/global/certificates/testgatewayapi
pemCertificate: |
-----BEGIN CERTIFICATE-----
MIIFpDCCBIygAwIBAgIQQP/U4oP/gZASSvJLrgPhTDANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
(...)
IkoroZytqlFE5vKpJfN0tvxsS877ZnXhEtcAjvlqzRoeq2CjmecBl85009LAQd0f
az2mOclaqYlrxP2APwJIvJLoTJ8Y5TsGiPcHcGFcSQEEyaoAx9vUBpd/Dwvxn5Bp
VYomKUfHZyw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFjDCCA3SgAwIBAgINAgCOsgIzNmWLZM3bmzANBgkqhkiG9w0BAQsFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
(...)
JDwRjW/656r0KVB02xHRKvm2ZKI03TglLIpmVCK3kBKkKNpBNkFt8rhafcCKOb9J
x/9tpNFlQTl7B39rJlJWkR17QnZqVptFePFORoZmFzM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
(...)
+qduBmpvvYuR7hZL6Dupszfnw0Skfths18dG9ZKb59UhvmaSGZRVbNQpsg3BZlvi
d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8=
-----END CERTIFICATE-----
sanDnsnames:
- '*.testgatewayapi.hackerstack.org'
- testgatewayapi.hackerstack.org
updateTime: '***'
Si la génération du certificat échoue à cause d'un problème avec l'autorisation DNS, nous devrions voir quelque chose comme ceci :
createTime: '***'
managed:
authorizationAttemptInfo:
- domain: testgatewayapi.hackerstack.org
failureReason: CONFIG
state: FAILED
dnsAuthorizations:
- projects/***/locations/global/dnsAuthorizations/testgatewayapi
domains:
- testgatewayapi.hackerstack.org
provisioningIssue:
reason: AUTHORIZATION_ISSUE
state: PROVISIONING
name: projects/***/locations/global/certificates/testgatewayapi
sanDnsnames:
- testgatewayapi.hackerstack.org
updateTime: '***'
Voir Gestion des certificats Google Certificate Manager pour plus d'informations.
Ajout du certificat TLS dans une map de certificats
Nous avons besoin d'une map de certificatspour stocker un ou plusieurs de nos certificats, géré par Google ou non, afin d'avoir un endroit unique à partir duquel la « Gateway » récupérera les certificats pour nos applications utilisant le protocole HTTPS.
Créons et affichons une « map de certificats » appelée « testgatewayapi » :
$ gcloud certificate-manager maps create testgatewayapi
Waiting for 'operation-***' to complete...done.
Created certificate map [testgatewayapi].
$ gcloud certificate-manager maps list
NAME ENDPOINTS DESCRIPTION CREATE_TIME
testgatewayapi - ***
$ gcloud certificate-manager maps describe testgatewayapi
createTime: '***'
name: projects/***/locations/global/certificateMaps/testgatewayapi
updateTime: '***'
Pour ajouter des certificats dans la « map de certificats » pour des noms de domaine spécifiques, nous devons créer des entrées de map de certificats comme suit:
$ gcloud certificate-manager maps entries create testgatewayapi1 --map="testgatewayapi" --certificates="testgatewayapi" --hostname="testgatewayapi.hackerstack.org"
Waiting for 'operation-***' to complete...done.
Created certificate map entry [testgatewayapi1].
$ gcloud certificate-manager maps entries create testgatewayapi2 --map="testgatewayapi" --certificates="testgatewayapi" --hostname="*.testgatewayapi.hackerstack.org"
Waiting for 'operation-***' to complete...done.
Created certificate map entry [testgatewayapi2].
Nous avons créé deux « entrées de map de certificats » appelées « testgatewayapi1 » et « testgatewayapi2 » référençant respectivement les certificats TLS pour les domaines « testgatewayapi.hackerstack.org » et « *.testgatewayapi.hackerstack.org ».
$ gcloud certificate-manager maps entries list --map="testgatewayapi"
NAME DESCRIPTION HOSTNAME MATCHER CERTIFICATES STATE CREATE_TIME
testgatewayapi1 testgatewayapi.hackerstack.org testgatewayapi ACTIVE ***
testgatewayapi2 *.testgatewayapi.hackerstack.org testgatewayapi ACTIVE ***
Ensuite, nous allons indiquer à la Gateway d'utiliser la map de certificats « testgatewayapi » comme magasin de certificats TLS. Consultez Gestion de map de certificats et Gestion des entrées de map de certificats pour plus d'informations.
Utilisation du certificat TLS sur la passerelle
Pour dire à la « Passerelle » qui map de certificats pour utiliser la recherche de certificats pour les noms de domaine de nos applications accessibles via l'écouteur HTTPS, nous utilisons la clé « networking.gke.io/certmap » dans le champ « metadata.annotations » des ressources « Gateway » comme suit :
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
(...)
annotations:
networking.gke.io/certmap: testgatewayapi # Name of the certificate map
spec:
(...)
Après cela, lorsque la « passerelle » reçoit des requêtes HTTPS pour l'un des noms de domaine présents dans la « carte de certificats », elle examinera l'« entrée de la carte de certificats » pour ce domaine pour obtenir le nom du certificat à utiliser pour configurer les sessions TLS.
Création de HTTPRoutes
La ressource « HTTPRoute » peut être utilisée pour acheminer le trafic HTTP ou HTTPS vers nos charges de travail d'applications Kubernetes via la « passerelle ».
Sur la « passerelle » que nous avons créée précédemment, nous avons utilisé le champ « gateway.spec.allowedRoutes » pour autoriser les ressources « HTTPRoute » de n'importe quel espace de noms du cluster, pour utiliser la « passerelle » pour acheminer le trafic vers les applications backend.
Voici le manifeste d'un exemple d'application exécutée à l'intérieur du cluster que nous allons exposer via la « passerelle » :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
type: ClusterIP
Voici le manifeste que nous utilisons pour créer la ressource « HTTPRoute » pour exposer l'application via la « passerelle » :
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: nginx-https
namespace: default
spec:
parentRefs:
- kind: Gateway
name: testgwapi-global-external-managed # Name of the Gateway to use
namespace: default
sectionName: https # Which Gateway listener to use
hostnames:
- testgatewayapi.hackerstack.org
rules:
- backendRefs:
- name: nginx # Name of the backend service on which traffic will be routed
kind: Service
namespace: default # Namespace where the backend service resides
port: 80
weight: 100
Pour plus d'informations sur les champs de ressources « HTTPRoute » que nous pouvons utiliser et une explication détaillée de chacun d'eux, consultez Champs d'API de passerelle pris en charge par la ressource GKE HTTPRoutes par classe de passerelle.
Jetons un œil à l’état de la ressource « HTTPRoute » :
$ kubectl get httproute
NAME HOSTNAMES AGE
nginx-https ["testgatewayapi.hackerstack.org"] 35m
$ kubectl describe httproute/nginx-https
Name: nginx-https
Namespace: default
Labels: <none>
Annotations: <none>
API Version: gateway.networking.k8s.io/v1beta1
Kind: HTTPRoute
Metadata:
Creation Timestamp: ***
Generation: 2
Resource Version: 93649
UID: ef84cf65-37d7-4b32-94ec-e4926596c0ee
Spec:
Hostnames:
testgatewayapi.hackerstack.org
Parent Refs:
Group: gateway.networking.k8s.io
Kind: Gateway
Name: testgwapi-global-external-managed
Namespace: default
Section Name: https
Rules:
Backend Refs:
Group:
Kind: Service
Name: nginx
Namespace: default
Port: 80
Weight: 100
Matches:
Path:
Type: PathPrefix
Value: /
Status:
Parents:
Conditions:
Last Transition Time: ***
Message:
Observed Generation: 2
Reason: Accepted
Status: True
Type: Accepted
Last Transition Time: ***
Message:
Observed Generation: 2
Reason: ReconciliationSucceeded
Status: True
Type: Reconciled
Controller Name: networking.gke.io/gateway
Parent Ref:
Group: gateway.networking.k8s.io
Kind: Gateway
Name: testgwapi-global-external-managed
Namespace: default
Section Name: https
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 17m sc-gateway-controller default/nginx-https
Normal UPDATE 2m1s sc-gateway-controller default/nginx-https
Normal SYNC 9s sc-gateway-controller Bind of HTTPRoute "default/nginx-https" to ParentRef {Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Namespace: "default",
Name: "testgwapi-global-external-managed",
SectionName: "https",
Port: nil} was a success
Normal SYNC 9s sc-gateway-controller Reconciliation of HTTPRoute "default/nginx-https" bound to ParentRef {Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Namespace: "default",
Name: "testgwapi-global-external-managed",
SectionName: "https",
Port: nil} was a success
La ressource « nginx-https » et « HTTPRoute » a été correctement liée à la passerelle. Essayons d'atteindre l'application Nginx via la passerelle en effectuant une requête HTTPS :
$ curl -v https://testgatewayapi.hackerstack.org
* Trying 34.111.146.21:443...
* TCP_NODELAY set
* Connected to testgatewayapi.hackerstack.org (34.111.146.21) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.testgatewayapi.hackerstack.org
* start date: ***
* expire date: ***
* subjectAltName: host "testgatewayapi.hackerstack.org" matched cert's "testgatewayapi.hackerstack.org"
* issuer: C=US; O=Google Trust Services LLC; CN=GTS CA 1D4
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x559aaa0680e0)
> GET / HTTP/2
> Host: testgatewayapi.hackerstack.org
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< server: nginx/1.25.4
< date: ***
< content-type: text/html
< content-length: 615
< last-modified: ***
< etag: "65cce434-267"
< accept-ranges: bytes
< via: 1.1 google
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host testgatewayapi.hackerstack.org left intact
Nous avons réussi à atteindre notre application nginx via la « passerelle » en utilisant le protocole HTTPS.
Protection des backends de la passerelle avec Cloud Armor
Vue d'ensemble
La routine shell Google Cloud Armor Un pare-feu d'applications Web (WAF) peut être utilisé pour protéger les applications exposées via l'API Gateway. Consultez Services Google Cloud supplémentaires pour la compatibilité avec la « Gateway » en fonction de la « GatewayClass » utilisée.
Pour utiliser le WAF afin de protéger les applications backends « Gateway », nous devons :
-
créer un ou plusieurs les politiques de sécurité comprenant règles de sécurité des politiques.
-
indiquez à la « passerelle » d'utiliser une « politique de sécurité » spécifique pour protéger un service backend spécifique en créant un Politique de GCPBackend ressource à l'intérieur du cluster GKE.
Ensuite, nous allons exposer le Boutique de jus OWASP application Web vulnérable via la « passerelle » que nous avons créée précédemment, effectuez une attaque par injection SQL réussie sur la page de connexion, puis protégez cette application contre les attaques par injection SQL en utilisant l'une des Règles WAF préconfigurées de Google Cloud Armor. Nous allons ensuite créer un règle de politique Cloud Armor personnalisée pour bloquer des demandes spécifiques.
Utilisation de l'application vulnérable OWASP Juiceshop comme backend
Voici les manifestes que nous utilisons pour déployer le Boutique de jus OWASP application web vulnérable :
apiVersion: apps/v1
kind: Deployment
metadata:
name: juiceshop
namespace: default
labels:
app: juiceshop
spec:
replicas: 1
selector:
matchLabels:
app: juiceshop
template:
metadata:
labels:
app: juiceshop
spec:
containers:
- name: juiceshop
image: bkimminich/juice-shop
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: juiceshop
namespace: default
labels:
app: juiceshop
spec:
ports:
- port: 80
targetPort: 3000
name: http
selector:
app: juiceshop
type: ClusterIP
Après cela, nous rendons l'application disponible via la « Passerelle » en créant la ressource « HTTPRoute » suivante :
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: juiceshop-https
namespace: default
spec:
parentRefs:
- kind: Gateway
name: testgwapi-global-external-managed # Name of the Gateway to use
namespace: default
sectionName: https # Which Gateway listener to use
hostnames:
- "testgatewayapi.hackerstack.org"
rules:
- backendRefs:
- name: juiceshop # Name of the backend service on which traffic will be routed
kind: Service
namespace: default # Namespace where the backend service resides
port: 80
weight: 100
Vérification:
$ curl https://testgatewayapi.hackerstack.org
<!--
~ Copyright (c) 2014-2023 Bjoern Kimminich & the OWASP Juice Shop contributors.
~ SPDX-License-Identifier: MIT
--><!DOCTYPE html><html lang="en"><head>
<meta charset="utf-8">
<title>OWASP Juice Shop</title>
<meta name="description" content="Probably the most modern and sophisticated insecure web application">
(...)
</script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body></html>
Protection de l'application Juiceshop contre les attaques par injection SQL
La page de connexion du Boutique de jus OWASP l'application est vulnérable à certaines attaques par injection SQL. ' OR TRUE-- dans le champ connexion/e-mail avec n'importe quel caractère dans le champ mot de passe, connectez-nous en tant qu'utilisateur « admin ».
Bloquons cette attaque avec le WAF.
Tout d'abord, nous créons un politique de sécurité , l’aspect économique règle de politique de sécurité pour bloquer les attaques par injection SQL. Nous utilisons la méthode « sqli-v33-stable ». règle prédéfinie pour cela. Les règles prédéfinies proviennent de la Ensemble de règles de base OWASP ModSecurity. Voici les commandes :
# Create the Cloud Armor security policy
$ gcloud compute security-policies create protect
# Create a rule inside the security policy to block sql injections
$ gcloud compute security-policies rules create 1000 \
--security-policy protect \
--expression "evaluatePreconfiguredExpr('sqli-v33-stable')" \
--action deny-403
Pour plus d'informations sur les autorisations requises pour la configuration des politiques de sécurité, consultez Politiques de sécurité Permissions IAM et Politiques de sécurité et autorisations IAM personnalisées.
La priorité de la règle est « 1 000 ». Les règles de sécurité les moins prioritaires sont évaluées en premier. Consultez Ordre d'évaluation des règles de politiques de sécurité pour en savoir plus.
Pour en savoir plus sur la création, la mise à jour et la suppression des politiques et des règles de politiques Cloud Armor, consultez Gérer les politiques de sécurité de Cloud Armor, Gérer les règles des politiques de sécurité Cloud Armor et Exemples de politiques.
Ensuite, nous devons indiquer à la « passerelle » d’utiliser la politique de sécurité « protéger » pour le service backend « juiceshop » en créant un Politique de GCPBackend Ressource:
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
name: protect-juiceshop
namespace: default
spec:
default:
securityPolicy: protect
logging:
enabled: true
sampleRate: 500000
targetRef:
group: ""
kind: Service
name: juiceshop
Sans la section « spec.default.logging » activant la journalisation, les journaux Cloud Armor des requêtes bloquées ne seront pas visibles dans Google Cloud Logging. Pour en savoir plus sur les paramètres de configuration de la journalisation, consultez la section Journalisation de l'accès HTTP à la passerelle.
$ kubectl get gcpbackendpolicies
NAME AGE
protect-juiceshop 47m
$ kubectl describe gcpbackendpolicies/protect-juiceshop
Name: protect-juiceshop
Namespace: default
Labels: <none>
Annotations: <none>
API Version: networking.gke.io/v1
Kind: GCPBackendPolicy
Metadata:
Creation Timestamp: ***
Generation: 1
Resource Version: 58233
UID: 9fcacbb7-d874-4b7e-bf40-b5ca93c6681e
Spec:
Default:
Security Policy: protect
Target Ref:
Group:
Kind: Service
Name: juiceshop
Status:
Conditions:
Last Transition Time: ***
Message:
Reason: Attached
Status: True
Type: Attached
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 47m sc-gateway-controller default/protect-juiceshop
Normal SYNC 49s (x14 over 46m) sc-gateway-controller Application of GCPGatewayPolicy "default/protect-juiceshop" was a success
Notez qu'une seule ressource GCPBackendPolicy peut être associée à un service backend Gateway. Jetez un œil à Dépannage de la passerelle GCPBackendPolicy pour en savoir plus.
Testons la protection en effectuant à nouveau l'attaque par injection SQL. L'attaque est bloquée avec succès par le WAF (Web Application Firewall) :

Utilisation de règles WAF Cloud Armor personnalisées
Maintenant, nous voulons bloquer les demandes en fonction de attributs spécifiques comme les IP sources, les en-têtes de requêtes... Pour cela, nous devons créer une règle de politique de sécurité personnalisée. Voici la règles de référence linguistique nous utilisons pour cela.
Créons une règle pour bloquer les requêtes contenant « user-agent : robot » dans l'en-tête :
$ gcloud compute security-policies rules create 1001 \
--security-policy protect \
--expression "has(request.headers['user-agent']) && request.headers['user-agent'].contains('robot')" \
--action deny-403
La règle personnalisée est définie avec l'option « --expression ». Une fois la règle appliquée, vérifions :
$ curl -H 'user-agent: robot' https://testgatewayapi.hackerstack.org
<!doctype html><meta charset="utf-8"><meta name=viewport content="width=device-width, initial-scale=1"><title>403</title>403 Forbidden
Pour visualiser et personnaliser les sorties des journaux des politiques de sécurité pour Google Cloud Logging, consultez Entrées des journaux des politiques de sécurité et Journalisation détaillée des politiques de sécurité.
Autres fonctionnalités de Gateway
Les ressources « GCPGatewayPolicy » et « GCPBackendPolicy » pour la « passerelle » sont l'équivalent de Configuration du frontend et Configuration du backend ressources pour le GCP ingress-gce contrôleur ingress.
Ils peuvent être utilisés pour configurer des fonctionnalités supplémentaires telles que :
- Redirection HTTP vers HTTPS
- Contrôle de santé pour les backends de passerelle
- Politiques TLS/SSL mondiales
- ...
Pour une liste complète de ces fonctionnalités et des liens décrivant comment les activer, consultez Sécurité du front-end, Propriétés des services backend et Services Google Cloud supplémentaires.
Pour des fonctionnalités supplémentaires liées au routage et à la gestion du trafic disponibles via la ressource « HTTPRoute », consultez Routage et gestion du trafic.