Este documento descreve como configurar o Service Mesh para preservar o IP de origem do cliente durante o acesso aos serviços.
Pré-requisitos
Você possui uma instância do ASM Enterprise ou Ultimate Edition, versão 1.15 ou posterior. Para mais informações, consulte Criar uma instância do ASM e Atualizar uma instância do ASM.
Crie um cluster gerenciado ACK. Para mais informações, consulte Criar um cluster gerenciado ACK.
Implante um gateway de entrada. Para mais informações, consulte Criar um gateway de entrada.
Conecte-se ao cluster usando kubectl. Para mais informações, consulte Obter o arquivo kubeconfig de um cluster e usar kubectl para conectar-se ao cluster.
Informações de fundo
O IP de origem do cliente é utilizado em diversos cenários, incluindo:
Controle de acesso a aplicações: muitas aplicações exigem autenticação adicional quando um usuário faz login de uma região diferente, o que requer a obtenção do IP original do cliente.
Afinidade de sessão simples: realize balanceamento de carga baseado em IP de origem para encaminhar solicitações do mesmo cliente para a mesma instância de serviço.
Logs de acesso e monitoramento: logs e métricas contendo o endereço de origem real ajudam os desenvolvedores a analisar o tráfego e coletar estatísticas.
Balanceadores de carga na nuvem, como o Server Load Balancer (SLB), podem transmitir o IP de origem do cliente para os serviços de backend. O Istio também oferece essa capacidade. No entanto, ao usar o Istio, um proxy sidecar é injetado em cada pod. Esse proxy intercepta todo o tráfego de entrada e o encaminha para sua aplicação por meio de uma conexão local (127.0.0.1). Consequentemente, sua aplicação não consegue visualizar o IP de origem real do cliente.
Implantar aplicações de exemplo
-
Implante a aplicação sleep.
-
Crie um arquivo chamado sleep.yaml com o seguinte conteúdo.
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 -
Execute o comando a seguir para implantar a aplicação sleep.
kubectl -n default apply -f sleep.yaml
-
-
Implante a aplicação httpbin.
-
Crie um arquivo chamado httpbin.yaml com o seguinte conteúdo.
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 -
Execute o comando a seguir para implantar a aplicação httpbin.
kubectl -n default apply -f httpbin.yaml
-
Cenário 1: Tráfego leste-oeste
Etapa 1: Verificar o comportamento padrão do IP de origem do cliente
No Istio, o proxy sidecar de cada serviço intercepta todo o tráfego leste-oeste. Em seguida, esse proxy encaminha as solicitações para o contêiner da aplicação; assim, o endereço de origem visível para a aplicação é o endereço de loopback do proxy, 127.0.0.6.
-
Execute o comando a seguir para verificar o status dos pods.
kubectl -n default get pods -o wideSaída esperada:
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>A saída mostra que o endereço da aplicação sleep é
172.17.X.XXX. -
Execute o comando a seguir para enviar uma solicitação a partir do contêiner sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ipSaída esperada:
{ "origin": "127.0.0.6" }A saída indica que o endereço de origem da solicitação recebida pela aplicação httpbin é o endereço do proxy Envoy
127.0.0.6, e não o endereço da aplicação sleep. -
Confirme se o endereço IP de origem é 127.0.0.6 inspecionando as informações do soquete.
-
Faça login no contêiner httpbin e execute o comando a seguir para instalar o netstat.
apt update & apt install net-tools -
Saia do contêiner httpbin e execute o comando a seguir para visualizar informações sobre a porta 8000.
kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 8000Saída esperada:
tcp 0 0 172.17.X.XXX:8000 127.0.0.6:42691 TIME_WAIT -A saída confirma que o endereço IP de origem é
127.0.0.6.
-
-
Visualize o conteúdo do log de acesso no pod httpbin.
Veja a seguir um exemplo de entrada de log formatada:
{ "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" }O log exibe as seguintes informações:
"downstream_remote_address":"172.17.X.XXX:56160": Endereço da aplicação sleep."downstream_local_address":"172.17.X.XXX:8000": Endereço de destino acessado pela aplicação sleep."upstream_local_address":"127.0.0.6:42169": Endereço local usado pelo proxy Envoy do httpbin para se conectar à aplicação httpbin. Neste ponto, o IP de origem visto pela aplicação é127.0.0.6."upstream_host":"172.17.X.XXX:8000": Endereço de destino acessado pelo proxy Envoy do httpbin.
Etapa 2: Configurar a preservação do IP de origem
Método 1: Usar o modo TPROXY
Configure o deployment do httpbin para usar o modo de interceptação transparente TPROXY.
-
Execute o comando a seguir para modificar o deployment da aplicação httpbin.
kubectl patch deployment -n default httpbin -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/interceptionMode":"TPROXY"}}}}}' -
Execute o comando a seguir para enviar uma solicitação a partir do contêiner sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ipSaída esperada:
{ "origin": "172.17.X.XXX" }A saída demonstra que a aplicação httpbin obtém o endereço IP real da aplicação sleep.
-
Execute o comando a seguir para visualizar informações sobre a porta 8000.
NotaApós a reinicialização do pod, reinstale o netstat.
kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 8000Saída esperada:
tcp 0 0 172.17.X.XXX:8000 172.17.X.XXX:36728 ESTABLISHED -A saída confirma que o endereço IP de origem é
172.17.X.XXX. -
Visualize o conteúdo do log de acesso no pod httpbin.
Veja a seguir um exemplo de entrada de log formatada:
{ "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 }O log apresenta as seguintes informações:
"downstream_remote_address":"172.17.X.XXX:39058": Endereço da aplicação sleep."downstream_local_address":"172.17.X.XXX:8000": Endereço de destino acessado pela aplicação sleep."upstream_local_address":"172.17.X.XXX:46129": Endereço local usado pelo proxy Envoy do httpbin para se conectar à aplicação httpbin, correspondente ao endereço IP da aplicação sleep."upstream_host":"172.17.X.XXX:8000": Endereço de destino acessado pelo proxy Envoy do httpbin.
Método 2: Usar o cabeçalho XFF
A configuração abaixo faz com que o proxy sidecar do lado do servidor adicione um cabeçalho X-Forwarded-For (XFF) às solicitações de entrada. O proxy define o valor desse cabeçalho como o endereço IP real do cliente antes de encaminhar a solicitação para a aplicação. Este método não possui limitações de sistema operacional, mas exige que sua aplicação leia o IP de origem do cliente a partir do cabeçalho X-Forwarded-For (XFF).
-
Aplique o EnvoyFilter a seguir na instância do ASM usando um modelo de EnvoyFilter. Para mais informações, consulte Criar um filtro Envoy usando um modelo de filtro 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 -
Execute o comando a seguir para enviar uma solicitação a partir do contêiner sleep.
kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ip -
Saída esperada:
{ "origin": "172.17.X.XXX" }A saída mostra que o httpbin recebe o endereço IP real da aplicação sleep. A aplicação httpbin consegue fazer isso porque foi projetada para ler o IP de origem do cabeçalho X-Forwarded-For (XFF), agora adicionado pelo proxy sidecar. Este método não funcionará se sua aplicação não conseguir ler o cabeçalho XFF.
Cenário 2: Tráfego norte-sul
No tráfego norte-sul, as solicitações fluem de um cliente, passando por um balanceador de carga e pelo gateway de entrada do Istio, até o serviço de backend. Esse salto adicional pelo gateway de entrada torna a preservação do IP de origem do cliente mais complexa. As seções a seguir descrevem como configurar e verificar a preservação do IP de origem para solicitações HTTP e HTTPS.
Solicitações HTTP
Sem preservação do IP de origem
-
Crie um arquivo chamado http-demo.yaml com o seguinte conteúdo para acessar a aplicação 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 -
Execute o comando a seguir para implantar o gateway e o VirtualService.
kubectl -n default apply -f http-demo.yaml -
Execute o comando a seguir para acessar a aplicação httpbin através do gateway de entrada.
export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') curl http://$GATEWAY_URL:80/ipSaída esperada:
{ "origin": "10.0.0.93" }A saída indica que o endereço IP retornado corresponde a um nó do Kubernetes.
-
Visualize o log de acesso do gateway de entrada.
Veja a seguir um exemplo de entrada de log:
{ "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 }O log exibe as seguintes informações:
"downstream_remote_address":"10.0.0.93:5899": Não é o IP de origem real do cliente."downstream_local_address":"172.17.X.XXX:80": Endereço do pod do gateway de entrada."upstream_local_address":"172.17.X.XXX:54322": Endereço IP do pod do gateway de entrada preservado, mas com número de porta alterado."upstream_host":"172.17.X.XXX:8000": Endereço do pod httpbin.
With source IP preservation
-
Defina a política de tráfego externo como Local. (Ignore esta etapa para clusters que usam o modo de rede Terway.)
Faça login no console do ASM. No painel de navegação à esquerda, escolha .
Na página Mesh Management, clique em no nome da instância do ASM. No painel de navegação à esquerda, escolha .
Na página Ingress Gateway, clique em View YAML à direita do gateway desejado.
-
Na caixa de diálogo Edit, localize a seção spec, defina o campo externalTrafficPolicy como Local e clique em OK.
spec: affinity: {} autoCreateGatewayYaml: false clusterIds: - cf0243f2c3009406xxx compression: {} cpu: {} dnsPolicy: ClusterFirst externalTrafficPolicy: Local gatewayType: ingress
-
Execute o comando a seguir para acessar a aplicação httpbin através do gateway de entrada.
curl http://$GATEWAY_URL:80/ipSaída esperada:
{ "origin": "120.244.xxx.xxx" }A saída confirma que o endereço IP retornado é o IP de origem real do cliente.
-
Visualize o log de acesso do gateway de entrada.
Veja a seguir um exemplo de entrada de log:
{ "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 }O log apresenta as seguintes informações:
"downstream_remote_address":"120.244.XXX.XXX:28504": Endereço de origem do cliente, conforme esperado."downstream_local_address":"172.17.X.XXX:80": Endereço do pod do gateway de entrada."upstream_local_address":"172.17.X.XXX:57498": Endereço do pod do gateway de entrada preservado, mas com número de porta alterado."upstream_host":"172.17.X.XXX:80": Endereço do pod httpbin.
Solicitações HTTPS
Como a seção anterior detalhou as solicitações HTTP, esta seção foca apenas na configuração e verificação para solicitações HTTPS.
-
Crie um arquivo chamado https-demo.yaml com o seguinte conteúdo para acessar a aplicação 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 -
Execute o comando a seguir para implantar o gateway e o VirtualService.
kubectl -n default apply -f https-demo.yaml -
Execute o comando a seguir para acessar a aplicação httpbin através do gateway de entrada.
export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') curl -k https://$GATEWAY_URL:443/ipSaída esperada:
{ "origin": "120.244.XXX.XXX" }A saída confirma que o endereço IP retornado é o IP de origem real do cliente.