Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URL encoded characters are being parsed by ingress #590

Closed
polycaster opened this issue Jun 17, 2019 · 9 comments
Closed

URL encoded characters are being parsed by ingress #590

polycaster opened this issue Jun 17, 2019 · 9 comments

Comments

@polycaster
Copy link

Describe the bug
During infrastructure auditing it came up that URL encoded strings in the URI didn't match expected behaviour.
So, for example, the following request DELETE example.endpoint/v1/workflows/%3c%2ffoo%3e would return a 404 from NGINX while DELETE example.endpoint/v1/workflows/%3C)foo%3E%20 would result in the expected behaviour where the request gets forwarded to the backend and gets handled there.
If this is done with a URL encoded : it attempts the protocol redirect.

These were the only two offending characters that I found, presumably because they have special meanings.

I'd appreciate if you could tell me if this is a bug, a feature or a misconfiguration.

To Reproduce
Steps to reproduce the behavior:

  1. Deploy the kubernetes-ingress in this repo and configure it.
  2. Add a service as a backend to the ingress
  3. Try to hit the service API with a request that contains a URL encoded string with either the / or : characters.

Expected behavior
I would expect that these URL encoded characters would get passed to the backend to deal with.

Your environment
nginx/nginx-ingress:1.4.3-alpine

Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.5", GitCommit:"2166946f41b36dea2c4626f90a77706f426cdea2", GitTreeState:"clean", BuildDate:"2019-03-25T15:26:52Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.11", GitCommit:"637c7e288581ee40ab4ca210618a89a555b6e7e9", GitTreeState:"clean", BuildDate:"2018-11-26T14:25:46Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}

(although this can be seen on clusters with K8s server version v1.12.7-gke.10 and v1.9.2+coreos.0)
Kubernetes platforms: GKE, Managed and EKS
Using NGINX

Thank you very much for your time.

@pleshakov
Copy link
Contributor

@polycaster
it seems that in your Ingress resource you enabled rewrites annotations. Is it the case?

If it is the case, then NGINX will normalize the URI before sending a request to the backend. For example, workflows/%3c%2ffoo%3e becomes workflows/</foo>. Unfortunately, with the current implementation of the rewrite annotation, we cannot change this behavior.

As a workaround, I can suggest taking a look at this example https://stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637 To achieve the expected behavior, you can customize the rewrite annotation according to the example, which will require creating a custom template. Or you can create a new custom annotation for rewrites.

@polycaster
Copy link
Author

  annotations:
    nginx.org/websocket-services: websocket-service
    nginx.org/proxy-read-timeout: 600s
    nginx.org/proxy-connect-timeout: 36s
    nginx.org/location-snippets: "allow 10.0.0.0/8; deny all;"

These are the only annotations regarding NGINX in the Ingress object, so I don't think that rewrites are enabled.

Unless it always normalizes the URI, in which case, is that the desired behaviour?

@pleshakov
Copy link
Contributor

@polycaster
If there is no rewrite, then the request URI is passed to the backend in the same form as sent by a client when the original request is processed.

Could you possibly share the Ingress resource? Could you possible share the NGINX Ingress Controller access logs for the problematic requests?

@polycaster
Copy link
Author

That is not the behaviour I am observing.

Here's a portion of the logs after sending a whole bunch of those requests:
This spans 3 different pods

nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.20.245 - org [18/Jun/2019:09:09:09 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:10 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:10 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:11 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:11 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:11 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:12 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:12 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:13 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:13 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:14 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:14 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:15 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:16 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:16 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:16 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:09:17 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.69.187 - - [18/Jun/2019:09:11:39 +0000] "PUT /v1/auth/kubernetes/login HTTP/2.0" 200 766 "-" "Go-http-client/2.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.69.187 - - [18/Jun/2019:09:12:07 +0000] "PUT /v1/auth/kubernetes/login HTTP/2.0" 200 766 "-" "Go-http-client/2.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.20.245 - - [18/Jun/2019:09:12:27 +0000] "PUT /v1/auth/kubernetes/login HTTP/2.0" 200 689 "-" "Go-http-client/2.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.152.194 - org [18/Jun/2019:09:13:04 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-sm6rs nginx-ingress-private 10.137.69.187 - org [18/Jun/2019:09:13:05 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-vch7w nginx-ingress-private 10.137.69.187 - org [18/Jun/2019:09:13:06 +0000] "DELETE /v1/workflows/%3Cyouremail%3E%0ACc%3A%3Cyouremail%3E HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"
nginx-ingress-private-76bf4f54d4-2rdpv nginx-ingress-private 10.137.69.187 - org [18/Jun/2019:09:14:49 +0000] "DELETE /v1/workflows/%2a%2f%2a HTTP/2.0" 404 10 "-" "curl/7.58.0" "-"

Ingress configuration:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx-private
    nginx.org/proxy-connect-timeout: 36s
    nginx.org/proxy-read-timeout: 600s
    nginx.org/websocket-services: websocket-service
  name: ingress
spec:
  rules:
  - host: admin-api.cluster.org
    http:
      paths:
      - backend:
          serviceName: admin-api
          servicePort: http
  - host: audit.cluster.org
    http:
      paths:
      - backend:
          serviceName: audit-api
          servicePort: http
  - host: core-api.cluster.org
    http:
      paths:
      - backend:
          serviceName: core-api
          servicePort: http
  - host: customer.cluster.org
    http:
      paths:
      - backend:
          serviceName: websocket-service
          servicePort: websocket
        path: /websocket
      - backend:
          serviceName: website
          servicePort: http
        path: /
  - host: ops.cluster.org
    http:
      paths:
      - backend:
          serviceName: websocket-service-2
          servicePort: websocket
        path: /websocket
      - backend:
          serviceName: workflow
          servicePort: http
        path: /workflow-simulator
      - backend:
          serviceName: admin-website
          servicePort: http
        path: /
  - host: api.cluster.org
    http:
      paths:
      - backend:
          serviceName: api
          servicePort: http
  - host: switchboard.cluster.org
    http:
      paths:
      - backend:
          serviceName: switchboard
          servicePort: webhooks-http
  - host: documentation.cluster.org
    http:
      paths:
      - backend:
          serviceName: documents
          servicePort: http
  - host: bottomline.cluster.org
    http:
      paths:
      - backend:
          serviceName: bottomline
          servicePort: http
  - host: fps.cluster.org
    http:
      paths:
      - backend:
          serviceName: fps
          servicePort: http
  - host: router.cluster.org
    http:
      paths:
      - backend:
          serviceName: router
          servicePort: http
  - host: transfer.cluster.org
    http:
      paths:
      - backend:
          serviceName: transfer
          servicePort: http
  - host: xp.cluster.org
    http:
      paths:
      - backend:
          serviceName: xp
          servicePort: http
  - host: workflows.cluster.org
    http:
      paths:
      - backend:
          serviceName: workflows-api
          servicePort: http
  - host: simulator.cluster.org
    http:
      paths:
      - backend:
          serviceName: simulator
          servicePort: http
  - host: payments.cluster.org
    http:
      paths:
      - backend:
          serviceName: payment
          servicePort: http
  tls:
  - hosts:
    - admin-api.cluster.org
    - audit.cluster.org
    - core-api.cluster.org
    - customer.cluster.org
    - ops.cluster.org
    - api.cluster.org
    - switchboard.cluster.org
    - documents.cluster.org
    - bottomline.cluster.org
    - fps.cluster.org
    - router.cluster.org
    - transfer.cluster.org
    - xp.cluster.org
    - workflows.cluster.org
    - simulator.cluster.org
    - payments.cluster.org
    - documentation.cluster.org

As you can see, no mention to rewrites.

@pleshakov
Copy link
Contributor

@polycaster thx for the additional details. Unfortunately, I'm not able to reproduce this behavior.

Have you made any additional customizations of the IC that affect the generated configuration? For example, a customization of the ConfigMap resource.

Additionally, could you possibly double check if there is any problem with the backend application?

@polycaster
Copy link
Author

polycaster commented Jun 18, 2019

The configmap contains the following entries:

Data
====
http2:
----
true
server-names-hash-bucket-size:
----
1024
server-names-hash-max-size:
----
8192
http-snippets:
----
large_client_header_buffers 4 16k; http2_max_field_size 16k;
Events:  <none>

Not sure if any of them are relevant to the issue we are seeing.
I have double checked that the backend is indeed working and returning expected results with other strings.

Successful request:
DELETE /v1/workflow/%20asd

If it is not possible to find a satisfactory solution to this issue for my case I might attempt to fix it with the stackOverflow post you mentioned above, but i'd rather that be a last resource situation.

Not sure if it helps but here's the generated configuration from within the NGINX container:

server {
	listen 80;
	listen 443 ssl http2;
	ssl_certificate /etc/nginx/secrets/ingress-cert;
	ssl_certificate_key /etc/nginx/secrets/ingress-cert;
	server_tokens on;
	server_name workflows.cluster.org;

	if ($scheme = http) {
		return 301 https://$host:443$request_uri;
	}
	
	location / {
		proxy_http_version 1.1;
		proxy_connect_timeout 36s;
		proxy_read_timeout 600s;
		client_max_body_size 1m;
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Port $server_port;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_buffering on;
		proxy_pass http://workflows.cluster.org-workflows-http;
	}

	location @grpcerror400 { default_type application/grpc; return 400 "\n"; }
	location @grpcerror401 { default_type application/grpc; return 401 "\n"; }
	location @grpcerror403 { default_type application/grpc; return 403 "\n"; }
	location @grpcerror404 { default_type application/grpc; return 404 "\n"; }
	location @grpcerror405 { default_type application/grpc; return 405 "\n"; }
	location @grpcerror408 { default_type application/grpc; return 408 "\n"; }
	location @grpcerror414 { default_type application/grpc; return 414 "\n"; }
	location @grpcerror426 { default_type application/grpc; return 426 "\n"; }
	location @grpcerror500 { default_type application/grpc; return 500 "\n"; }
	location @grpcerror501 { default_type application/grpc; return 501 "\n"; }
	location @grpcerror502 { default_type application/grpc; return 502 "\n"; }
	location @grpcerror503 { default_type application/grpc; return 503 "\n"; }
	location @grpcerror504 { default_type application/grpc; return 504 "\n"; }
	
}

@pleshakov
Copy link
Contributor

@polycaster unfortunately, even with the additional configuration from the configmap, I am still not able to reproduce the issue.

Could you possibly confirm that the problematic requests don't work when the client connects directly to the backend bypassing the Ingress Controller?

Note that for testing I used this image nginxdemos/hello:plain-text (taken from https://github.com/nginxinc/kubernetes-ingress/tree/master/examples/complete-example), which is also NGINX, to confirm what URL the backend receives. In my case, the backend receives the same URL as the NGINX Ingress Controller. Note that the backend responds with a 200 response for any request.

Could you possibly tests the requests with a different backend such as the nginxdemos/hello:plain-text ?

@polycaster
Copy link
Author

Hi @pleshakov, I'd like to thank you for your effort about this.

After you've suggested that you couldn't reproduce the behaviour i've dug a bit more and sent the requests directly to the service pods from within the cluster.
The behaviour was still there which led me to look into other components.

Apparently, the culprit is GRPC gateway that was downstream, and we seem to have been bitten by this long running issue grpc-ecosystem/grpc-gateway#224

The solution is upgrading GRPC gateway to a release where this issue has been fixed.

Thank you once again.

@pleshakov
Copy link
Contributor

@polycaster np!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants