Nginx: 認証サーバーを使う

こちらのページを参考にしました。
[Nginx と自前の認証システムを組み合わせてセキュアなリソースを制限する](https://tech.jxpress.net/entry/2018/08/23/104123)

Docker をクローン

```bash
git clone https://github.com/pistatium/nginx_auth_sample.git
```

起動

```bash
cd nginx_auth_sample
docker-compose up -d
```

起動後の状態

```text
$ sudo docker ps -a
CONTAINER ID   IMAGE               COMMAND                   CREATED         STATUS         PORTS                                     NAMES
06c6054fff49   nginx:alpine        "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes   0.0.0.0:8000->80/tcp, [::]:8000->80/tcp   nginx_auth_sample_nginx_1
e0f0c719dcc4   python:3.6-alpine   "/bin/sh -xcv 'pip i…"   5 minutes ago   Up 4 minutes                                             nginx_auth_sample_backend_1

```

２つのコンテナーが走っています。

<ol>
<li>nginx_auth_sample_nginx_1</li>
Nginx が走っています。
<li>nginx_auth_sample_backend_1</li>
Flask の認証サーバーが走っています。
</ol>

ブラウザーでアクセス
http://localhost:8000/

このままでも、認証サーバーの機能は確認できますが、改造して、分かりやすくします。

## Nginx の改造

ログイン

```bash
docker exec -it nginx_auth_sample_nginx_1 ash
```

```text
$ docker exec -it nginx_auth_sample_nginx_1 ash
/ # cd /var/www/html
/var/www/html #
```

/var/www/html/index.html の改造

```html:/var/www/html/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Nginx auth example</title>
</head>
<body>
<h1>Nginx auth example</h1>

<p>
    <a href='/private/index.html'>/private/index.html</a> is a page requiring lo
</p>
<h2>Preview</h2>
<p>
    <a id='login' href='/auth/login'>[[ Login ]]</a>
    <a id='logout' href='/auth/logout'>[[ Logout ]]</a>
</p>
<hr />
<h2>/auth/is_login/</h2>
<iframe src='/auth/is_login' width="800" height="200">                          
</iframe>                                              
<h2>/private/</h2>                                     
<iframe src='/private/' width="800" height="600">      
</iframe>                                              
<p>Jun/01/2023 AM 09:45</p>                            
</iframe>                                             
</body>                                               
</html>
```

/var/www/html/private/index.html の改造

```html:/var/www/html/private/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Nginx auth sample</title>
</head>
<body>
<h1>Secret Page</h1>
It is a page that only the person who logs in is seen.
....

<img src="/private/cat.jpg">

<br />
<a href="./aaa/">aaa</a><br />
<a href="./bbb/">bbb</a><br />
<br />
Jun/01/2023 AM 09:13<br />
</body>
</html>
```

/var/www/html/private/aaa/index.html
/var/www/html/private/bbb/index.html
を作成

```html:/var/www/html/private/aaa/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>aaa</title>
</head>
<body>
<p>aaa</p>
<p>Jun/01/2023</p>
</body>
</html>
```

```html:/var/www/html/private/bbb/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>bbb</title>
</head>
<body>
<p>bbb</p>
<p>Jun/01/2023</p>
</body>
</html>
```

## Flask （認証サーバーの改造）

ログイン

```bash
docker exec -it nginx_auth_sample_backend_1 ash
```

```text
$ docker exec -it nginx_auth_sample_backend_1 ash
/opt/backend # cd src
/opt/backend/src #
```

/opt/backend/src/app.py の改造
> @app.route('/auth/is_login') だけを改造


```py:/opt/backend/src/app.py
# coding: utf-8

from flask import Flask, request, make_response, redirect, abort

app = Flask(__name__)

SESSION_KEY = 'SESSION'


@app.route('/auth/login')
def login():
    response = make_response(redirect('/'))
    response.set_cookie(SESSION_KEY, '1')  # FIXME
    return response


@app.route('/auth/logout')
def logout():
    response = make_response(redirect('/'))
    response.set_cookie(SESSION_KEY, '', expires=0)  # Delete cookie    
    return response                                                         
@app.route('/auth/is_login')                                            
def is_login():                                                         
    # FIXME                                                             
    if not request.cookies.get(SESSION_KEY):                            
        abort(401)                                                      
    return '*** OK ***'  # 200
```

## ブラウザーでアクセス

http://localhost:8000/
>履歴を消去してテストしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/162f3afb-f34d-0ce1-f097-b4da05e94913.png)

ログインすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/1316e0ff-1d20-d107-b30a-a98bd1538d57.png)

次のURL でも、ログインしているか否かで、表示が異なります。
http://localhost:8000/private/aaa/
http://localhost:8000/private/bbb/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/8517bcbf-528a-cf39-bf13-46d835336103.png)

ログインすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/43b4839a-3f1e-f962-e111-c25d309593a8.png)

## Nginx の設定ファイル

```text:/etc/nginx/conf.d/default.conf
server {
    server_name _;
    listen 80;
    access_log /dev/stdout;
    error_log /dev/stderr warn;
    root /var/www/html;
    index index.html;

    location /private/ {
        auth_request /auth/is_login;
    }
    
    location /auth {
        proxy_pass http://backend:8888;
        proxy_redirect off;
        proxy_set_header Host $http_host;
    }
}
```

## Flask の起動方法

```bash
FLASK_APP=src/app.py flask run -h 0.0.0.0 -p 8888
```

## 確認したバージョン

```text
$ docker -v
Docker version 28.0.4, build b8034c0
```