Search code examples
nginxkubernetesnginx-ingress

Prevent NGINX-ingress from responding to public IP


What's the process for either preventing NGINX from responding to the Public IP, or to get it to redirect to somewhere else - like another URL.

I'm a little stumped as I can't seem to find much in the way of documentation anywhere for this situation. We're using cert-manager too.

Essentially a PEN Test has failed because the public IP is responding with an NGINX/ k8s self-signed cert. We don't want or need that!


Solution

  • It's possible to solve this; easy for HTTP and difficult for HTTPS. The main problem is to issue a certificate for an IP address, not any issuer does that (letsencrypt for example doesn't), so you have to find one or try whichever you use now.

    To handle unknown hosts (like an IP address) you can create an ingress object without host field in rules. This will make the created ingress work as 'default' or 'fallback' rule, thus it will be used when there is no better match by Host header (any ingress with host in rules).

    To create an ingress object you need a service and here's how you can create a dummy service without endpoints:

    apiVersion: v1
    kind: Service
    metadata:
      name: dummy-service
    spec:
      clusterIP: None
    

    Next, the ingress for it:

    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      name: default-ingress
      annotations:
        kubernetes.io/ingress_class: nginx
        nginx.ingress.kubernetes.io/configuration-snippet: |
          # Nginx will place this in server block
    
          # You can redirect all requests somewhere:
          return 301 https://example.com/;
          # or just:
          #return 403;
    spec:
      rules:
        # This rule has no 'host' field and because of that
        # NGINX won't include 'server_name' directive in
        # vhost configuration. What this means is that this
        # ingress rule will be used only if the request
        # comes with 'Host' header for which there is no
        # specific rule (IP-address for example).
        - http:
            paths:
              - backend:
                  servicePort: 80
                  serviceName: dummy-service
    

    At this point you've got redirect (or 403) working for HTTP and HTTPS, although the latter with a dummy certificate. If you managed to issue a certificate for your IP addresses and save it as a secret, the next thing is to make NGINX to use it instead of its default dummy certificate. For that you need to modify ingress controller deployment by adding --default-ssl-certificate argument:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ingress-nginx-controller
    spec:
      template:
        spec:
          containers:
            - name: controller
              args:
                - /nginx-ingress-controller
                # use 'namespace/secret_name' as the value for the argument
                - --default-ssl-certificate=default/ip-cert-secret
    

    Now NGINX will respond to IP addresses with a valid certificate.

    Bonus: if you have a cert-manager Issuer or ClusterIssuer that can issue a certificate for IP address (like self-signed one), you can request a certificate with the following manifest:

    #apiVersion: cert-manager.io/v1
    apiVersion: cert-manager.io/v1beta1
    kind: Certificate
    metadata:
      name: ip-cert
    spec:
      secretName: ip-cert-secret
      duration: 2160h # 90d
      renewBefore: 360h # 15d
      isCA: false
      privateKey:
        algorithm: RSA
        encoding: PKCS1
        size: 2048
      usages:
        - server auth
        - client auth
      commonName: Dummy
      ipAddresses:
      - 10.1.1.13 # fill the list
      issuerRef:
        name: # insert issuer name
        kind: # Issuer or ClusterIssuer 
        group: cert-manager.io