Nginx Ingress Controllerで特定パスへのアクセスを拒否する

Nginx Ingress Controllerで特定パス、例えば`/admin`のアクセスを拒否する方法のメモ。

Minikube v0.32.0
Kubernetes v1.12.4
quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0

## 設定方法

以下のように、[server-snipet](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#server-snippet)のアノテーションを使って設定できる。ただしserver-snipetは1つのホストにつき1回しか設定できず、同じホストを指定した複数のIngressに定義した場合は古いほうが有効となる。
または[configuration-snippet](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#configuration-snippet)のアノテーションでも設定できる。

（補足）
IBM Cloud Privateではアノテーションのプレフィックスが`ingress.kubernetes.io`となることに注意。

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: liberty
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |-
      location /sample/admin {
          deny all;
      }
spec:
  rules:
  - host: sample.example.com
    http:
      paths:
      - path: /sample
        backend:
          serviceName: liberty
          servicePort: 9080
```

この設定を入れると、Nginxの設定ファイルで`server`の下に設定が入れ込まれる。

```shell-session
$ kubectl exec -it -n kube-system nginx-ingress-controller-6958898f8f-7z9dg cat /etc/nginx/nginx.conf

（省略）

	## start server sample.example.com
	server {
		server_name sample.example.com ;

		listen 80;

		set $proxy_upstream_name "-";

		location /sample/admin {
			deny all;
		}

		location /sample {

			set $namespace      "default";
			set $ingress_name   "liberty";
			set $service_name   "liberty";
			set $service_port   "9080";
			set $location_path  "/sample";

（省略）

		}

（省略）
```

403でアクセス拒否される。

<img width="884" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/86449/2f9373e7-6aaa-b6d5-b9c5-5ed70cf74a2c.png">

なお、`server-snippet`ではなく`configuration-snippet`で指定したとすると`location /sample`の下に設定が入る。`location`を入れ子にすることはできるようなので、この設定でも機能する。この場合特に指定しない場合はエラーコードが503となったが、エラーコードを指定することも可能。

```yaml
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |-
      location /sample/admin {
          deny all;
          return 403;
      }
```

```shell-session
$ kubectl exec -it -n kube-system nginx-ingress-controller-6958898f8f-7z9dg cat /etc/nginx/nginx.conf

（省略）

	## start server sample.example.com
	server {
		server_name sample.example.com ;

		listen 80;

		set $proxy_upstream_name "-";

		location /sample {

（省略）

			location /sample/admin {
				deny all;
				return 403;
			}

			proxy_pass http://upstream_balancer;

			proxy_redirect                          off;

		}

（省略）
```

### rewrite-targetを指定している場合

`nginx.ingress.kubernetes.io/rewrite-target: /`のような設定している場合は、リライトのために正規表現で指定されている`location`設定が優先されてしまうため、上記の記載では上手くいかない。正規表現を使って記載する必要がある。

rewrite-targetを指定していない場合もこちらのような正規表現を使った書き方をしてもよい。

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: liberty
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/server-snippet: |-
      location ~* "^/app1/sample/admin.*" {
          deny all;
      }
spec:
  rules:
  - host: sample.example.com
    http:
      paths:
      - path: /app1
        backend:
          serviceName: liberty
          servicePort: 9080
```

```shell-session
$ kubectl exec -it -n kube-system nginx-ingress-controller-6958898f8f-7z9dg cat /etc/nginx/nginx.conf

（省略）

	## start server sample.example.com
	server {
		server_name sample.example.com ;

		listen 80;

		set $proxy_upstream_name "-";

		location ~* "^/app1/sample/admin.*" {
			deny all;
		}

		location ~* "^/app1\/?(?<baseuri>.*)" {

			set $namespace      "default";
			set $ingress_name   "liberty";
			set $service_name   "liberty";
			set $service_port   "9080";
			set $location_path  "/app1";

（省略）

		}

（省略）

```

## 参考リンク

- [Restrict access to /admin location](https://github.com/nginxinc/kubernetes-ingress/issues/161)
- [nginx locationの評価順序](https://qiita.com/aosho235/items/1acf65246d2a36661021)