Cette rubrique explique comment configurer Service Mesh afin de préserver l'adresse IP source du client lors de l'accès à vos services.
Prérequis
Vous disposez d'une instance ASM Enterprise ou Ultimate Edition, version 1.15 ou ultérieure. Pour plus d'informations, consultez les rubriques Créer une instance ASM et Mettre à niveau une instance ASM.
Un cluster ACK managé est créé. Pour plus d'informations, consultez la rubrique Créer un cluster ACK managé.
Une passerelle d'entrée est déployée. Pour plus d'informations, consultez la rubrique Créer une passerelle d'entrée.
Vous êtes connecté au cluster à l'aide de kubectl. Pour plus d'informations, consultez la rubrique Obtenir le fichier kubeconfig d'un cluster et utiliser kubectl pour se connecter au cluster.
Contexte
L'adresse IP source du client est utilisée dans de nombreux scénarios, notamment :
Contrôle d'accès aux applications : par exemple, de nombreuses applications imposent une authentification supplémentaire lorsqu'un utilisateur se connecte depuis une région différente, ce qui nécessite l'obtention de l'adresse IP d'origine du client.
Affinité de session simple : vous pouvez effectuer un équilibrage de charge basé sur l'adresse IP source pour acheminer les requêtes provenant du même client vers la même instance de service.
Journalisation et surveillance des accès : les journaux d'accès et les métriques de surveillance contenant l'adresse source réelle aident les développeurs à analyser le trafic et à recueillir des statistiques.
Les équilibreurs de charge cloud tels que Server Load Balancer (SLB) peuvent transmettre l'adresse IP source du client aux services backend. Istio doit également offrir cette fonctionnalité. Toutefois, lorsque vous utilisez Istio, un proxy sidecar est injecté dans chaque pod. Ce proxy intercepte tout le trafic entrant et le transfère à votre application via une connexion locale (127.0.0.1). Par conséquent, votre application ne peut pas voir l'adresse IP source réelle du client.
Déployer des exemples d'applications
-
Déployez l'application sleep.
-
Créez un fichier nommé sleep.yaml avec le contenu suivant.
apiVersion: v1 kind: ServiceAccount metadata: name: sleep --- apiVersion: v1 kind: Service metadata: name: sleep labels: app: sleep service: sleep spec: ports: - port: 80 name: http selector: app: sleep --- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: terminationGracePeriodSeconds: 0 serviceAccountName: sleep containers: - name: sleep image: curlimages/curl command: ["/bin/sleep", "3650d"] imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /etc/sleep/tls name: secret-volume volumes: - name: secret-volume secret: secretName: sleep-secret optional: true -
Exécutez la commande suivante pour déployer l'application sleep.
kubectl -n default apply -f sleep.yaml
-
-
Déployez l'application httpbin.
-
Créez un fichier nommé httpbin.yaml avec le contenu suivant.
apiVersion: v1 kind: Service metadata: name: httpbin labels: app: httpbin spec: ports: - name: http port: 8000 selector: app: httpbin --- apiVersion: apps/v1 kind: Deployment metadata: name: httpbin spec: replicas: 1 selector: matchLabels: app: httpbin version: v1 template: metadata: labels: app: httpbin version: v1 spec: containers: - image: docker.io/citizenstig/httpbin imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 8000 -
Exécutez la commande suivante pour déployer l'application httpbin.
kubectl -n default apply -f httpbin.yaml
-
Scénario 1 : Trafic est-ouest
Étape 1 : Vérifier le comportement par défaut de l'adresse IP source du client
Dans Istio, le proxy sidecar de chaque service intercepte tout le trafic est-ouest. Ce proxy transfère ensuite les requêtes au conteneur d'application, de sorte que l'adresse source visible par l'application est l'adresse de bouclage du proxy, 127.0.0.6.
-
Exécutez la commande suivante pour vérifier l'état des pods.
kubectl -n default get pods -o wideRésultat attendu :
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpbin-c85bdb469-4ll2m 2/2 Running 0 3m22s 172.17.X.XXX cn-hongkong.10.0.0.XX <none> <none> sleep-8f764df66-q7dr2 2/2 Running 0 3m9s 172.17.X.XXX cn-hongkong.10.0.0.XX <none> <none>Le résultat indique que l'adresse de l'application sleep est
172.17.X.XXX. -
Exécutez la commande suivante pour envoyer une requête depuis le conteneur sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ipRésultat attendu :
{ "origin": "127.0.0.6" }Le résultat montre que l'adresse source de la requête reçue par l'application httpbin est l'adresse du proxy Envoy
127.0.0.6, et non l'adresse de l'application sleep. -
Confirmez que l'adresse IP source est 127.0.0.6 en inspectant les informations de socket.
-
Connectez-vous au conteneur httpbin et exécutez la commande suivante pour installer netstat.
apt update & apt install net-tools -
Quittez le conteneur httpbin et exécutez la commande suivante pour afficher les informations relatives au port 8000.
kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 8000Résultat attendu :
tcp 0 0 172.17.X.XXX:8000 127.0.0.6:42691 TIME_WAIT -Le résultat indique que l'adresse IP source est
127.0.0.6.
-
-
Consultez le contenu du journal d'accès dans le pod httpbin.
Voici un exemple d'entrée de journal formatée :
{ "trace_id":null, "bytes_received":0, "upstream_host":"172.17.X.XXX:8000", "authority":"httpbin:8000", "downstream_remote_address":"172.17.X.XXX:56160", "upstream_service_time":"1", "upstream_transport_failure_reason":null, "istio_policy_status":null, "path":"/ip", "bytes_sent":28, "request_id":"4501a50a-dab0-44c9-b52c-2a4f425a****", "protocol":"HTTP/1.1", "method":"GET", "duration":1, "start_time":"2022-11-22T16:09:30.394Z", "user_agent":"curl/7.86.0-DEV", "upstream_local_address":"127.0.0.6:42169", "response_flags":"-", "route_name":"default", "response_code":200, "upstream_cluster":"inbound|80||", "x_forwarded_for":null, "downstream_local_address":"172.17.X.XXX:8000", "requested_server_name":"outbound_.8000_._.httpbin.default.svc.cluster.local" }Le journal affiche les informations suivantes :
"downstream_remote_address":"172.17.X.XXX:56160": l'adresse de l'application sleep."downstream_local_address":"172.17.X.XXX:8000": l'adresse de destination à laquelle l'application sleep accède."upstream_local_address":"127.0.0.6:42169": l'adresse locale utilisée par le proxy Envoy httpbin pour se connecter à l'application httpbin. À ce stade, l'adresse IP source visible par l'application est127.0.0.6."upstream_host":"172.17.X.XXX:8000": l'adresse de destination à laquelle le proxy Envoy httpbin accède.
Étape 2 : Configurer la préservation de l'adresse IP source
Méthode 1 : Utiliser le mode TPROXY
Configurez le déploiement httpbin pour utiliser le mode d'interception transparente TPROXY.
-
Exécutez la commande suivante pour modifier le déploiement de l'application httpbin.
kubectl patch deployment -n default httpbin -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/interceptionMode":"TPROXY"}}}}}' -
Exécutez la commande suivante pour envoyer une requête depuis le conteneur sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ipRésultat attendu :
{ "origin": "172.17.X.XXX" }Le résultat montre que l'application httpbin peut obtenir l'adresse IP réelle de l'application sleep.
-
Exécutez la commande suivante pour afficher les informations relatives au port 8000.
RemarqueAprès le redémarrage du pod, vous devez réinstaller netstat.
kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 8000Résultat attendu :
tcp 0 0 172.17.X.XXX:8000 172.17.X.XXX:36728 ESTABLISHED -Le résultat indique que l'adresse IP source est
172.17.X.XXX. -
Consultez le contenu du journal d'accès dans le pod httpbin.
Voici un exemple d'entrée de journal formatée :
{ "route_name":"default", "bytes_received":0, "trace_id":null, "request_id":"1ccabe60-63cf-469b-8565-99cac546****", "upstream_cluster":"inbound|80||", "response_flags":"-", "protocol":"HTTP/1.1", "upstream_transport_failure_reason":null, "requested_server_name":"outbound_.8000_._.httpbin.default.svc.cluster.local", "response_code":200, "user_agent":"curl/7.86.0-DEV", "start_time":"2022-11-22T16:03:32.803Z", "path":"/ip", "authority":"httpbin:8000", "bytes_sent":31, "downstream_remote_address":"172.17.X.XXX:39058", "upstream_service_time":"1", "method":"GET", "downstream_local_address":"172.17.X.XXX:8000", "duration":1, "upstream_host":"172.17.X.XXX:8000", "istio_policy_status":null, "upstream_local_address":"172.17.X.XXX:46129", "x_forwarded_for":null }Le journal affiche les informations suivantes :
"downstream_remote_address":"172.17.X.XXX:39058": l'adresse de l'application sleep."downstream_local_address":"172.17.X.XXX:8000": l'adresse de destination à laquelle l'application sleep accède."upstream_local_address":"172.17.X.XXX:46129": l'adresse locale utilisée par le proxy Envoy httpbin pour se connecter à l'application httpbin, qui correspond à l'adresse IP de l'application sleep."upstream_host":"172.17.X.XXX:8000": l'adresse de destination à laquelle le proxy Envoy httpbin accède.
Méthode 2 : Utiliser l'en-tête XFF
La configuration suivante permet au proxy sidecar côté serveur d'ajouter un en-tête X-Forwarded-For (XFF) aux requêtes entrantes. Le proxy définit la valeur de cet en-tête sur l'adresse IP réelle du client avant de transférer la requête à l'application. Cette méthode ne présente aucune limitation liée au système d'exploitation, mais elle nécessite que votre application lise l'adresse IP source du client depuis l'en-tête X-Forwarded-For (XFF).
-
Appliquez le EnvoyFilter suivant à l'instance ASM en utilisant un modèle EnvoyFilter. Pour plus d'informations, consultez la rubrique Créer un filtre Envoy à l'aide d'un modèle de filtre Envoy.
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: enable-xff-for-sidecar-inbound namespace: istio-system # Change this to the namespace where your gateway is located. labels: asm-system: "true" provider: "asm" spec: configPatches: - applyTo: NETWORK_FILTER match: proxy: proxyVersion: "^1.*" context: SIDECAR_INBOUND listener: name: "virtualInbound" filterChain: filter: name: "envoy.filters.network.http_connection_manager" patch: operation: MERGE value: typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" use_remote_address: true -
Exécutez la commande suivante pour envoyer une requête depuis le conteneur sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ip -
Résultat attendu :
{ "origin": "172.17.X.XXX" }Le résultat indique que httpbin reçoit l'adresse IP réelle de l'application sleep. L'application httpbin peut effectuer cette opération car elle est conçue pour lire l'adresse IP source depuis l'en-tête X-Forwarded-For (XFF), ajouté désormais par le proxy sidecar. Cette méthode ne fonctionnera pas si votre application ne peut pas lire l'en-tête XFF.
Scénario 2 : Trafic nord-sud
Pour le trafic nord-sud, les requêtes transitent du client vers le service backend en passant par un équilibreur de charge et la passerelle d'entrée Istio. Ce saut supplémentaire via la passerelle d'entrée complexifie la préservation de l'adresse IP source du client. Les sections suivantes décrivent comment configurer et vérifier la préservation de l'adresse IP source pour les requêtes HTTP et HTTPS.
Requêtes HTTP
Sans préservation de l'adresse IP source
-
Créez un fichier nommé http-demo.yaml contenant le code suivant pour accéder à l'application httpbin via HTTP.
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gw-httpprotocol namespace: default spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: http number: 80 protocol: HTTP --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin namespace: default spec: gateways: - httpbin-gw-httpprotocol hosts: - '*' http: - route: - destination: host: httpbin port: number: 8000 -
Exécutez la commande suivante pour déployer la passerelle et le VirtualService.
kubectl -n default apply -f http-demo.yaml -
Exécutez la commande suivante pour accéder à l'application httpbin via la passerelle d'entrée.
export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') curl http://$GATEWAY_URL:80/ipRésultat attendu :
{ "origin": "10.0.0.93" }Le résultat indique que l'adresse IP renvoyée correspond à celle d'un nœud Kubernetes.
-
Consultez le journal d'accès de la passerelle d'entrée.
Voici un exemple d'entrée de journal :
{ "upstream_service_time":"1", "response_code":200, "protocol":"HTTP/1.1", "bytes_sent":28, "upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local", "start_time":"2022-11-23T03:29:20.017Z", "istio_policy_status":null, "upstream_transport_failure_reason":null, "trace_id":null, "route_name":null, "request_id":"292903be-a889-4d5d-83a0-ab1f5d1a****", "method":"GET", "upstream_host":"172.17.X.XXX:8000", "duration":1, "path":"/ip", "downstream_local_address":"172.17.X.XXX:80", "authority":"47.242.XXX.XX", "user_agent":"curl/7.79.1", "downstream_remote_address":"10.0.0.93:5899", "upstream_local_address":"172.17.X.XXX:54322", "requested_server_name":null, "x_forwarded_for":"10.0.0.93", "response_flags":"-", "bytes_received":0 }Le journal affiche les informations suivantes :
"downstream_remote_address":"10.0.0.93:5899": Il ne s'agit pas de l'adresse IP source réelle du client."downstream_local_address":"172.17.X.XXX:80": Adresse du pod de la passerelle d'entrée."upstream_local_address":"172.17.X.XXX:54322": L'adresse IP du pod de la passerelle d'entrée est conservée, mais le numéro de port est modifié."upstream_host":"172.17.X.XXX:8000": Adresse du pod httpbin.
With source IP preservation
-
Définissez la stratégie de trafic externe sur Local. (Vous pouvez ignorer cette étape pour les clusters utilisant le mode réseau Terway.)
Connectez-vous à la console ASM. Dans le volet de navigation de gauche, choisissez .
Sur la page Mesh Management, cliquez sur le nom de l'instance ASM. Dans le volet de navigation de gauche, choisissez .
Sur la page Ingress Gateway, cliquez sur View YAML à droite de la passerelle cible.
-
Dans la boîte de dialogue Edit, recherchez la section spec, définissez le champ externalTrafficPolicy sur Local, puis cliquez sur OK.
spec: affinity: {} autoCreateGatewayYaml: false clusterIds: - cf0243f2c3009406xxx compression: {} cpu: {} dnsPolicy: ClusterFirst externalTrafficPolicy: Local gatewayType: ingress
-
Exécutez la commande suivante pour accéder à l'application httpbin via la passerelle d'entrée.
curl http://$GATEWAY_URL:80/ipRésultat attendu :
{ "origin": "120.244.xxx.xxx" }Le résultat indique que l'adresse IP renvoyée correspond à l'adresse IP source réelle du client.
-
Consultez le journal d'accès de la passerelle d'entrée.
Voici un exemple d'entrée de journal :
{ "istio_policy_status":null, "upstream_transport_failure_reason":null, "path":"/ip", "x_forwarded_for":"120.244.XXX.XXX", "route_name":null, "method":"GET", "duration":2, "downstream_remote_address":"120.244.XXX.XXX:28504", "bytes_received":0, "upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local", "bytes_sent":34, "protocol":"HTTP/1.1", "response_flags":"-", "upstream_local_address":"172.17.X.XXX:57498", "upstream_service_time":"2", "request_id":"9c0295d4-e77f-4a3a-b292-e5c58d92****", "start_time":"2022-11-23T03:24:04.413Z", "response_code":200, "trace_id":null, "authority":"47.242.XXX.XX", "user_agent":"curl/7.79.1", "downstream_local_address":"172.17.X.XXX:80", "upstream_host":"172.17.X.XXX:80", "requested_server_name":null }Le journal affiche les informations suivantes :
"downstream_remote_address":"120.244.XXX.XXX:28504": Adresse source du client, comme prévu."downstream_local_address":"172.17.X.XXX:80": Adresse du pod de la passerelle d'entrée."upstream_local_address":"172.17.X.XXX:57498": L'adresse du pod de la passerelle d'entrée est conservée, mais le numéro de port est modifié."upstream_host":"172.17.X.XXX:80": Adresse du pod httpbin.
Requêtes HTTPS
La section précédente ayant détaillé les requêtes HTTP, cette section se concentre uniquement sur la configuration et la vérification des requêtes HTTPS.
-
Créez un fichier nommé https-demo.yaml contenant le code suivant pour accéder à l'application httpbin via HTTPS.
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gw-https namespace: default spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: https number: 443 protocol: HTTPS tls: credentialName: myexample-credential mode: SIMPLE --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin-https namespace: default spec: gateways: - httpbin-gw-https hosts: - '*' http: - route: - destination: host: httpbin port: number: 8000 -
Exécutez la commande suivante pour déployer la passerelle et le VirtualService.
kubectl -n default apply -f https-demo.yaml -
Exécutez la commande suivante pour accéder à l'application httpbin via la passerelle d'entrée.
export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') curl -k https://$GATEWAY_URL:443/ipRésultat attendu :
{ "origin": "120.244.XXX.XXX" }Le résultat indique que l'adresse IP renvoyée correspond à l'adresse IP source réelle du client.