ラズベリーパイ+ flaskアプリ + gunicorn + docker + nginx + オレオレ証明書 + FW で本格的？なwebサーバ構築

## なぜやるか
webの構造を知りたい。

今回の構成

アプリケーション flask
アプリケーション実行環境  wsgi
仮想環境コンテナ Docker 
webサーバ nginx
HTTPS  オレオレ証明書
ファイアウォール ufw



## ラズパイVNC設定
```
sudo apt-get update
sudo apt-get upgrade
```
https://qiita.com/SHIJIMI_EN/items/476399577755da1fcd86

## ラズパイIP固定

https://qiita.com/SHIJIMI_EN/items/78caa649b89a4eb9f92b

## sambaファイル共有

https://qiita.com/SHIJIMI_EN/items/9904ae0f17b5d329f192

## ラズパイに必要なものインストール
【docker】
docker.io
docker-compose
【FW】
ufw
【nginx】
nginx

```
sudo apt install -y docker.io docker-compose ufw nginx
```

## ディレクトリ構成
ユーザーフォルダの下に次のようなフォルダ構造にします。

project
├─App2
└─kajiApp
|    └─flaskr
|        ├─static
|        ├─templates
|        └─__pycache__
└─docker-compose.yml

projectフォルダの中にアプリケーションファイル kajiApp とApp2
今回はkajiAppだけ動かします。今後アプリケーション増やしたときのためにApp2置いときます。スケールすることを想定しています。
docker-composeファイルいれてあります。

アプリはdockerで動かして nginxはコンテナの外で動かします。


#### フォルダ構造を作る。
アプリを作る or コピペ or git clone する。
今回はgit clone
```
cd /home/pcmainte/project
git clone https://github.com/arisaEN/kajiApp.git
```
こんなかんじになった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/355b158f-d4b0-28ed-fc7d-7606c5ba20f2.png)



## Dockerfileの用意
git cloneしたものの中にはいっている。
書き換えます。
```
FROM python:3.9-slim

WORKDIR /app

# 必要なパッケージをインストール
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションコードをコピー
COPY . .

# Gunicornを使用してアプリケーションを起動
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "flaskr:app"]
```

## Docker-Composeファイルの作成
Projectフォルダ下に作ったものに書き込みます。
```
version: '3.8'

services:
  kajiapp:
    build:
      context: ./kajiApp 
      dockerfile: Dockerfile
    volumes:
      - ./kajiApp/flaskr/static:/app/flaskr/static
    container_name: kajiapp
    ports:
      - "127.0.0.1:5000:5000"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

```
ホストマシンの127.0.0.1:5000（ローカルホスト）へのアクセスをコンテナ内のポート5000に転送します。つまり、ホストマシンの127.0.0.1:5000に接続することで、コンテナ内のFlaskアプリケーションにアクセスできるようになります。

## nginx.confファイルの作成
```
user pcmainte;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    upstream kajiApp {
        server 127.0.0.1:5000;
    }
    server {
        listen 80;
        server_name kajiApp.example.com;
        charset UTF-8; 
        location / {
            proxy_pass http://kajiApp;
        }
    }
    server {
        listen 80;
        server_name unko.com;

        location / {
            root /home/pcmainte/project/App2;
            index index.html;
        }
    }
}


 ```
## クライアントにDNS設定
windowsの場合
C:\windows\system32\drivers\etc\hosts ここに管理者として
```
100.64.16.104 kajiapp.example.com
100.64.16.104 unko.com
```
みたいな感じで追加 メモ帳で編集すればOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/15fb9378-6d90-2e7f-528c-d74b62c34019.png)

【windowsの設定】
kajiapp.example.comにアクセスしたら100.64.16.104に行きなさいってこと。
【nginxの設定】
100.64.16.104(nginxサーバ)はkajiapp.example.comというリクエストは127.0.0.1:5000に飛ばしてkajiAppを表示させるという設定になっています。


ここまでできれば、dockerコンテナ立ち上げてアプリ稼働させて、
nginxも稼働させれば、クライアントはkajiapp.example.comとブラウザで入力すればnginxを通してコンテナアプリに接続できます。

テストとして、コンテナ立ち上げてみます。
コンテナ立ち上げ
```
cd project
sudo docker-compose up --build
```
nginxサーバ(ラズパイ)から接続するとつながるはずです。
ブラウザで、  127.0.0.1:5000   enter

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/78c09f53-d2e4-91bb-2f6b-c596fff69f25.png)



繋がりました。クライアントからhttp://100.64.16.104:5000/ には接続できないようにdockerfileとcomposeファイルで設定してあります。
クライアントはnginxを通してdockerコンテナにアクセスさせるためです。
でお次は↓HTTP通信をHTTPS通信にします。
 
 ## HTTPSの設定
今回は自己証明書を使います。通称：オレオレ証明書
nginxサーバで証明書を作って、クライアントには証明書をインストールしてもらいます
↓設定手順

### nginxサーバー上で実行
```
mkdir -p ~/ssl-certs
cd ~/ssl-certs
```
/home/pcmainte/ssl-certs ここに鍵生成されます



-----------------------------
ここからこちらの記事参照
一応本記事でもメモしていますが、こちらの見やすい記事とやっていることは同じです。

https://qiita.com/t-kuni/items/d3d72f429273cf0ee31e





ここに
subjectnames.txt を作る
中身↓
```
subjectAltName = DNS:kajiapp.example.com, IP:127.0.0.1
```



### 秘密鍵の生成
```
openssl genrsa 2048 > server.key
```


### 証明書署名要求（CSR）の作成
```
openssl req -new -key server.key -subj "/C=JP/ST=Some-State/O=Some-Org/CN=kajiapp.example.com" > server.csr
```
パスワード 任意 覚えとく
国コード JP
Province 都道府県名 空白でOK
Locality 市区町村名 空白でOK
Organization 組織名 
Organizational Unit 部署名
Common Name 証明書を使いたいFQDN 今回は kajiapp.example.com
メールアドレス
challenge password 空白でOK
optional company name 空白でOK


### 自己署名証明書の作成
```
openssl x509 -days 3650 -req -extfile subjectnames.txt -signkey server.key < server.csr > server.crt
```
秘密鍵（private.key）が暗号化されている場合に表示されます。秘密鍵を生成する際に設定したパスフレーズを入力する

```
Certificate request self-signature ok
```
証明書署名要求（CSR）の自己署名が正常に完了したということ。

証明書確認
```
openssl x509 -in server.crt -text -noout
```

-----------------------------

### nginxの設定
nginxの設定ファイルに HTTPS接続設定をする
↑の方で設定したnginxのファイルに追記します。 


```
sudo nano /etc/nginx/nginx.conf
```
```
user pcmainte;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    upstream kajiApp {
        server 127.0.0.1:5000;
    }

    server {
        listen 443 ssl;
        server_name kajiapp.example.com;


        ssl_certificate /home/pcmainte/ssl-certs/server.crt;
        ssl_certificate_key /home/pcmainte/ssl-certs/server.key;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';

        location / {
            proxy_pass http://kajiApp;
        }
    }


    server {
        listen 80;
        server_name kajiApp.example.com;
        charset UTF-8; 
        location / {
            proxy_pass http://kajiApp;
        }
    }
    server {
        listen 80;
        server_name unko.com;

        location / {
            root /home/pcmainte/project/App2;
            index index.html;
        }
    }
}

```


nginxを再起動
```
sudo systemctl restart nginx
```


### ブラウザでHTTPS接続

現状だとまだ保護されていない通信となる。 これは、HTTPS通信できてるけど、証明書がブラウザに認証されていなくて疑われている状態。
疑いを晴らす作業をしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/5146ed4a-1f86-1384-3e7c-2f7c7091c254.png)

windowsならばnginxサーバに作った/home/pcmainte/ssl-certs/server.crt これをコピーしてwindowsのローカルにいれる。



crtファイルをwindowsにインストールする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/7b9eb327-edb4-34ea-6dce-08dadf2df30a.png)

windowsでクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/d73221ab-ed4c-407a-8344-7ef211554d55.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/7b694f8f-eb35-a099-f6bf-22769ae1b65e.png)

ローカルコンピューター
証明書をすべて次のストア
参照
信頼されたルート証明書

インストール終わったら再度 https://kajiapp.example.com/ に接続。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/0cf03a2e-a16d-850b-d0a6-30babeb97c3c.png)

完了。
 
 ## UFWの設定
 アクセスする必要のないポートは閉じて、脅威からデバイスを守りましょう。

ポート開放
上から順に
HTTP
HTTPS
VNC
ssh
sambaを使ったクライアントからのファイル共有・操作

```
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 5900/tcp
sudo ufw allow OpenSSH
sudo ufw allow from any to any port 137,138,139,445 proto tcp
sudo ufw allow from any to any port 137,138,139,445 proto udp
```
ファイアウォールを有効化（未有効の場合）
```
sudo ufw enable
```

## docker コンテナ自動起動設定

サーバーのコンソールで打つ。
```
docker update --restart=always kajiapp
```

参考

https://qiita.com/fault/items/a7c1cdd3f7f047ff7b11

 ## 感想
 ◎クライアント側の課題
 クライアントはURLを貼り付けるだけでアプリを見れる環境を作りたい。
 現状クライアント側の操作は：
 ①サーバの自己証明書をクライアントにインストール。
 ②URLに対するサーバのIPアドレスをアプリごとにhostsファイルに書き込み。
 ③URLをブラウザに貼る。

  ①と②をなくすためになにか考えるのが今の課題。


 ◎サーバー側の課題
 アプリごとに証明書を発行しなければいけないのが面倒。



 ## 自分用メモ
nginx , docker ,　nanoで使ったコマンドまとめです。
 
------------------------------------------------------

nginx設定ファイル
sudo nano /etc/nginx/nginx.conf

nginx起動
sudo systemctl start nginx

Nginx を有効化（自動起動設定）
sudo systemctl enable nginx

nginx再起動
sudo systemctl restart nginx

Nginx の動作確認コマンド
sudo systemctl status nginx

ポート仕様状況確認
sudo lsof -i :80


nginxダウン
sudo systemctl stop nginx

ログのパス
/var/log/nginx

nginxの設定ファイル
/etc/nginx


------------------------------------------------------
起動コンテナ一覧
sudo docker ps 

起動・停止コンテナ一覧
sudo docker ps -a

コンテナ削除
docker rm 6c796c021e54

dockerコンテナ起動
sudo docker-compose up --build


docker の中見る
sudo docker exec -it 1b8a90a5fb4e /bin/bash 

dockerのなかのフォルダ確認
ls -l /app/flaskr/static/



docker exec コマンドを実行するには、現在のユーザーが docker グループに属している必要があります。
sudo usermod -aG docker pcmainte


コンテナ内の /app/flaskr/static/ にアクセスする権限付与
sudo docker exec -it kajiapp bash
ls -ld /app/flaskr/static/


chmod -R 755 /app/flaskr/static/


----------------------------------------------------------
nanoエディタ

1行切り取り 削除にも使える
Ctrl K

1行コピー
Alt ^ 


---------------------------------------
HTTPS設定

sudo nano /usr/lib/ssl/openssl.cnf


--------------------------