Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kubernetes NGINX Ingress: Disable external auth for specific path

I would like to be able to disable external authorization for a specific path of my App.

Similiar to this SO: Kubernetes NGINX Ingress: Disable Basic Auth for specific path

Only difference is using an external Auth provider (OAuth via Microsoft Azure) and there is a

This is the path that should be reachable by the public

/MyPublicPath

My ingress.yaml:

apiVersion: extensions/v1beta1 
kind: Ingress
metadata:
  name: myIngressName
  annotations:
    nginx.ingress.kubernetes.io/auth-signin: https://externalprovider/oauth2/sign_in
    nginx.ingress.kubernetes.io/auth-url: https://externalprovider/oauth2/auth
    nginx.ingress.kubernetes.io/auth-request-redirect: https://myapp/context_root/
    nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User, X-Auth-Request-Email, X-Auth-Request-Access-Token, Set-Cookie, Authorization
spec:
  rules:
  - host: myHostName
    http:
      paths:
      - backend: 
          serviceName: myServiceName
          servicePort: 9080
        path: /

Can I have it not hit the https://externalprovider/oauth2/auth url for just that path?

I've tried using ingress.kubernetes.io/configuration-snippet to set auth_basic to value "off" but that appears to be tied to the basic auth directives not the external ones.

like image 232
E. Sloss Avatar asked Jan 01 '23 00:01

E. Sloss


2 Answers

My experiment showed that it's not required to have two ingress-controllers like Crou mentioned in the previous answer.

One Nginx ingress-controller and two Ingress objects are just enough to do the trick.

The experiment didn't cover the whole solution: the auth provider wasn't deployed so we'll see auth request only, but for checking Ingress part it's not really necessary.


Here are the details [TL;DR]:

Ingress-controller was deployed according to the official manual.

Both my1service and my2service are forwarding the traffic to the same Nginx Pod.

I also added rewrite-target annotation because my the destination Pod is serving content on path / only.

Ingress1:

apiVersion: extensions/v1beta1 
kind: Ingress
metadata:
  name: myingress1
  annotations:
    nginx.ingress.kubernetes.io/auth-signin: https://externalprovider/oauth2/sign_in
    nginx.ingress.kubernetes.io/auth-url: https://externalprovider/oauth2/auth
    nginx.ingress.kubernetes.io/auth-request-redirect: https://myapp/context_root/
    nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User, X-Auth-Request-Email, X-Auth-Request-Access-Token, Set-Cookie, Authorization
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myhost.com
    http:
      paths:
      - backend: 
          serviceName: my1service
          servicePort: 80
        path: /

Ingress2:

apiVersion: extensions/v1beta1 
kind: Ingress
metadata:
  name: myingress2
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myhost.com
    http:
      paths:
      - backend: 
          serviceName: my2service
          servicePort: 80
        path: /somepath

Applying them to the cluster gives us the following configuration of ingress-controller: (I skipped not important lines from the nginx.conf content)

As we can see here, different set of rules is used for each location, so it's possible to have an auth for some path and skip the auth for another, or even have different auth providers for different locations on the same HTTP host.

ingress-controller's nginx.conf:

$ kubectl exec -n ingress-nginx ingress-nginx-controller-7fd7d8df56-xx987 -- cat /etc/nginx/nginx.conf > nginx.conf
$ less nginx.conf

http {
        
        ## start server myhost.com
        server {
                server_name myhost.com ;
                
                location /somepath {
                        # this location doesn't use authentication and responds with the backend content page.
                        
                        set $namespace      "default";
                        set $ingress_name   "myingress2";
                        set $service_name   "my2service";
                        set $service_port   "80";
                        set $location_path  "/somepath";
                        
                        set $proxy_upstream_name "default-my2service-80";
                        set $proxy_host          $proxy_upstream_name;
                        set $pass_access_scheme  $scheme;
                        
                }
                
                location = /_external-auth-Lw {
                        internal;
                        
                        # this location is used for executing authentication requests
                        
                        set $proxy_upstream_name "default-my1service-80";

                        proxy_set_header            Host                    externalprovider;
                        proxy_set_header            X-Original-URL          $scheme://$http_host$request_uri;
                        proxy_set_header            X-Original-Method       $request_method;
                        proxy_set_header            X-Sent-From             "nginx-ingress-controller";
                        proxy_set_header            X-Real-IP               $remote_addr;
                        
                        proxy_set_header            X-Forwarded-For        $remote_addr;
                        
                        proxy_set_header            X-Auth-Request-Redirect https://myapp/context_root/;
                        
                        set $target https://externalprovider/oauth2/auth;
                        proxy_pass $target;
                }
                
                location @64e7eef73f135f7a304693e85336f805005c5bc2 {
                        internal;
                        
                        # this location suppose to return authentication error page
                        
                        add_header Set-Cookie $auth_cookie;
                        
                        return 302 https://externalprovider/oauth2/sign_in?rd=$pass_access_scheme://$http_host$escaped_request_uri;
                }
                
                location / {
                        
                        # this location requests for authentication from external source before returning the backend content
                        
                        set $namespace      "default";
                        set $ingress_name   "myingress1";
                        set $service_name   "my1service";
                        set $service_port   "80";
                        set $location_path  "/";
                        
                        
                        set $balancer_ewma_score -1;
                        set $proxy_upstream_name "default-my1service-80";
                        set $proxy_host          $proxy_upstream_name;
                        set $pass_access_scheme  $scheme;
                        
                        set $pass_server_port    $server_port;
                        
                        set $best_http_host      $http_host;
                        set $pass_port           $pass_server_port;
                        
                        set $proxy_alternative_upstream_name "";
                        
                        # this location requires authentication
                        auth_request        /_external-auth-Lw;
                        auth_request_set    $auth_cookie $upstream_http_set_cookie;
                        add_header          Set-Cookie $auth_cookie;
                        auth_request_set $authHeader0 $upstream_http_x_auth_request_user;
                        proxy_set_header 'X-Auth-Request-User' $authHeader0;
                        auth_request_set $authHeader1 $upstream_http_x_auth_request_email;
                        proxy_set_header 'X-Auth-Request-Email' $authHeader1;
                        auth_request_set $authHeader2 $upstream_http_x_auth_request_access_token;
                        proxy_set_header 'X-Auth-Request-Access-Token' $authHeader2;
                        auth_request_set $authHeader3 $upstream_http_set_cookie;
                        proxy_set_header 'Set-Cookie' $authHeader3;
                        auth_request_set $authHeader4 $upstream_http_authorization;
                        proxy_set_header 'Authorization' $authHeader4;
                        
                        set_escape_uri $escaped_request_uri $request_uri;
                        error_page 401 = @64e7eef73f135f7a304693e85336f805005c5bc2;
                        
                        
                }
                
        }
        ## end server myhost.com
        
}

Let's test how it all works:

# ingress-controller IP address is 10.68.0.8

# here I requested / path and internal error and 'externalprovider could not be resolved (3: Host not found)' 
# error tells us that authentication was required, but auth backend is not available. 
# It's expected.

master-node$ curl http://10.68.0.8/ -H "Host: myhost.com"
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx/1.19.1</center>
</body>
</html>

#controller logs:
$ kubectl logs -n ingress-nginx ingress-nginx-controller-7fd7d8df56-xx987

10.68.0.1 - - [21/Jul/2020:13:17:06 +0000] "GET / HTTP/1.1" 502 0 "-" "curl/7.47.0" 0 0.072 [default-my1service-80] [] - - - - 158e2f959af845b216c399b939d7c2b6
2020/07/21 13:17:06 [error] 689#689: *119718 externalprovider could not be resolved (3: Host not found), client: 10.68.0.1, server: myhost.com, request: "GET / HTTP/1.1", subrequest: "/_external-auth-Lw", host: "myhost.com"
2020/07/21 13:17:06 [error] 689#689: *119718 auth request unexpected status: 502 while sending to client, client: 10.68.0.1, server: myhost.com, request: "GET / HTTP/1.1", host: "myhost.com"
10.68.0.1 - - [21/Jul/2020:13:17:06 +0000] "GET / HTTP/1.1" 500 177 "-" "curl/7.47.0" 74 0.072 [default-my1service-80] [] - - - - 158e2f959af845b216c399b939d7c2b6

# Then I sent a request to /somepath and got a reply without necessity 
# to provide any auth headers. 

$ curl http://10.68.0.8/somepath -H "Host: myhost.com"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
</body>
</html>

#controller logs show the successful reply:
10.68.0.1 - - [21/Jul/2020:13:18:29 +0000] "GET /somepath HTTP/1.1" 200 612 "-" "curl/7.47.0" 82 0.002 [default-my2service-80] [] 10.68.1.3:80 612 0.004 200 3af1d3d48c045be160e2cee8313ebf42

like image 179
VASャ Avatar answered Jan 04 '23 01:01

VASャ


I had the same problem and I added below snippet code in my ingress.yaml file and its working.

nginx.ingress.kubernetes.io/auth-snippet: | 
    if ( $request_uri = "/nonmember" ) {
        return 200;
    }
like image 22
krishna_5c3 Avatar answered Jan 04 '23 02:01

krishna_5c3