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

nginx: Rewrite target converts to location ~* /path #294

Closed
kaspernissen opened this issue Feb 17, 2017 · 2 comments · Fixed by #296
Closed

nginx: Rewrite target converts to location ~* /path #294

kaspernissen opened this issue Feb 17, 2017 · 2 comments · Fixed by #296
Labels

Comments

@kaspernissen
Copy link

nginx-ingress-controller version: 0.9.0-beta1

We are on the migration path towards a Kubernetes orchestrated setup, and are using Ingress with the nginx-ingress-controller. Which by the way, works pretty awesome! Thanks!

We are running into issues when using the annotation rewrite-target. When trying to hit the url: https://api.dev.example.com/documents/someservice/config I would expect the requests to be forwarded to the default path in my ingress: / - however it hits the /someservice. When looking into the template.go I see the following:

// buildLocation produces the location string, if the ingress has redirects
// (specified through the ingress.kubernetes.io/rewrite-to annotation)
func buildLocation(input interface{}) string {
	location, ok := input.(*ingress.Location)
	if !ok {
		return slash
	}

	path := location.Path
	if len(location.Redirect.Target) > 0 && location.Redirect.Target != path {
		return fmt.Sprintf("~* %s", path)
	}

	return path
}

Which results in an nginx.conf as follows:

    server {
        server_name api.dev.example.com;
        listen [::]:80;
        listen 442  ssl http2;
        # PEM sha: f299e5bed3d598b9e68ff81fd436869c46c72292
        ssl_certificate                         /ingress-controller/ssl/dev-cert.pem;
        ssl_certificate_key                     /ingress-controller/ssl/dev-cert.pem;

        more_set_headers                        "Strict-Transport-Security: max-age=15724800; includeSubDomains; preload";
        location ~* /someservice {
            set $proxy_upstream_name "dev-someservice-80";

            port_in_redirect off;

            # enforce ssl on server side
            if ($scheme = http) {
                return 301 https://$host$request_uri;
            }

            client_max_body_size                    "1m";

            proxy_set_header Host                   $host;

            # Pass Real IP
            proxy_set_header X-Real-IP              $remote_addr;

            # Allow websocket connections
            proxy_set_header                        Upgrade           $http_upgrade;
            proxy_set_header                        Connection        $connection_upgrade;

            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host       $host;
            proxy_set_header X-Forwarded-Port       $pass_port;
            proxy_set_header X-Forwarded-Proto      $pass_access_scheme;

            # mitigate HTTPoxy Vulnerability
            # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
            proxy_set_header Proxy                  "";

            # Custom headers

            proxy_connect_timeout                   5s;
            proxy_send_timeout                      60s;
            proxy_read_timeout                      60s;

            proxy_redirect                          off;
            proxy_buffering                         off;
            proxy_buffer_size                       "4k";

            proxy_http_version                      1.1;
	rewrite /someservice/(.*) /$1 break;
	rewrite /someservice / break;
	proxy_pass http://dev-someservice-80;

        }

As I understand, this means that there will be a willcard before /someservice, which is not what I expect. I would expect that the path is api.dev.example.com/someservice and then a post wildcard.

Steps to reproduce

  1. Create the following ingress object
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: api
  namespace: dev
  annotations:
    ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: nginx
spec:
  tls:
  - hosts:
    - api.dev.example.com
    secretName: cert

  rules:
  - host: api.dev.example.com
    http:
      paths:
      - path: /anotherservice
        backend:
          serviceName: anotherservice
          servicePort: 80
      - path: /someservice
        backend:
          serviceName: someservice
          servicePort: 80
      - path: /
        backend:
          serviceName: api
          servicePort: 3000
  1. Create the nginx-ingress-controller daemonset:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: nginx-ingress-lb
  namespace: dev
spec:
  template:
    metadata:
      labels:
        name: nginx-ingress-lb
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '10254'
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.1
        name: nginx-ingress-lb
        imagePullPolicy: Always
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          timeoutSeconds: 1
        # use downward API
        env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
        ports:
        - containerPort: 80
          hostPort: 80
        - containerPort: 443
          hostPort: 443
        args:
        - /nginx-ingress-controller
        - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
  1. Add a service to create a ELB in AWS
apiVersion: v1
kind: Service
metadata:
  name: api-elb
  namespace: dev
  labels:
    environment: dev
    dns: route53
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "3600"
    domainName: "api.dev.example.com"

spec:
  type: LoadBalancer
  ports:

  - port: 443
    targetPort: 443
    protocol: TCP
    name: elb-http-nginx-ingress-lb
  selector:
    name: nginx-ingress-lb

Am i using the rewrite-target annotation wrong? And Is there some way to work around this?

@aledbf
Copy link
Member

aledbf commented Feb 17, 2017

@kaspernissen thank you for the detailed report. The current code generates a regular expression that matches any part of the URL. The location location ~* /someservice should be location ~* ^/someservice to avoid errors.

Can you test if the image quay.io/aledbf/nginx-ingress-controller:0.63 solves the issue?
Thanks

Edit: the image ^^ contains the PR296

@kaspernissen
Copy link
Author

@aledbf I can confirm that this fixes the issue. Thank you very much.

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

Successfully merging a pull request may close this issue.

2 participants