LAN上にhttps対応のwebサーバを建て、Androidスマホのchromeから警告が表示されない状態でアクセスした手順

## 初めに
 プログレッシブwebアプリ(PWA)を展開するには、HTTPS通信でつながる環境が必要になります。
 レンタルサーバを借りることなく、LAN上でPWAを展開、動作確認できる手段はないかと、調査した結果です。
 
## 環境
- Windows10 Home
- Node.js(v12.13.0)
- Androidスマホ (KYOCERA TORQUE® G03 Androidバージョン 6.0.1)
- Visual Studio Code
- wifi環境
 
## Windows Subsystem for Linux(WSL)のインストール
https通信を行うにあたり、DNSサーバの構築、サーバ証明書の作成が必要です。
当方が調査したところ、DNSサーバの構築にはUnbound、サーバ証明書の作成にはOpenSSLが見つかりました。Unbound、OpenSSLはともに、Windwos版もありますが、仮想Ubuntuを用いたほうが、壁にぶつかることが少なく進めることができました。のちに、下記参考記事を見つけ、仮想UbuntuではなくWSLを用いたほうがよりスムーズでした。そのWSLをインストールします。
(参考)[【連載】WSL2、Visual Studio Code、DockerでグッとよくなるWindows開発環境 〜 その1：まずは概要 〜 \| SIOS Tech. Lab](https://tech-lab.sios.jp/archives/21023#Windows_Subsystem_for_Linux)

##### 1. Windows PowerShellを管理者権限で起動
##### 2. コマンドを実行する

```
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```
再起動確認が現れるので、再起動します。

##### 3. Ubuntu 20.04LTSをインストールする
  MicroSoftStoreを起動し、Ubuntu 20.04LTSを検索し、インストールします。
  

## Unboundを用いて、LAN上にDNSサーバを建てる
(参考)[Ubuntu Serverで内向きDNSをUnboundを使ってやってみたのでメモ - Qiita](https://qiita.com/umaxiaotian/items/b7f8f4866468d5c400b3)
当手順は、Ubuntuでの作業になります。
当手順にて、Windows10をDNSサーバとして機能するようにします。
##### 1. Windowsのスタートボタンより、「Ubuntu 20.04LTS」を起動する
##### 2. Unboundをインストールする
  下記コマンドを実行します。

```
sudo apt install unbound
```

##### 3. 設定ファイルのサンプルをコピーする
  下記コマンドを実行します。

```
sudo cp /usr/share/doc/unbound/examples/unbound.conf /etc/unbound/unbound.conf.d
```

##### 4. 設定ファイルを編集する
  下記コマンドを実行し設定ファイルを開きます。
  
```
sudo nano /etc/unbound/unbound.conf.d/unbound.conf
```

開いた設定ファイルを編集します。

```command:unboud.conf
server: 
  verbosity: 1

  interface: 0.0.0.0

  access-control: 127.0.0.0/23 allow
  #以下は環境に応じて変更する
  #当方の環境が192.168.0.xxx系統であったので、192.168.0.0/23 allowとしています。
  #各々の環境に合わせて設定してください。
  access-control: 192.168.0.0/23 allow

  #ドメイン名とIPアドレスを紐づける
  #この例では、192.168.0.100にexample.comというドメイン名を紐づけています。
  local-data: "example.com. IN A 192.168.0.100"

python: 
remote-control:
forward-zone:
  name: "."
  forward-addr: 8.8.8.8
  forward-addr: 1.1.1.1
  forward-addr: 1.0.0.1
```
##### 5. Unboudを開始する

```command
sudo service unbound start
```
エラーが発生した場合、設定ファイルの記述に誤りがあると思われます。
以下は、設定ファイルにエラーがないか確認するコマンドです。

```
sudo unbound-checkconf
```

※サービス開始前に上記チェックコマンドを実行しても、正しく動作しませんでした。一度サービス開始コマンドを実行すれば、チェックコマンドも正しく動作しました。

他 当ステップで随時使用するコマンド

- Unboudの停止

```
sudo service unbound stop
```

- Unboundの再起動

```
sudo service unbound restart
```

- Ubuntu上にインストールされているサービスの状態確認

```
sudo service --status-ll
```
            
## OPENSSLをインストールし、自己証明書を作成する
当手順は、Ubuntuでの作業になります。
(参考)
[SAN(Subject Alternative Name) のオレオレ証明書 - Qiita](https://qiita.com/nis_nagaid_1984/items/b8f87d41ea108d47af61)
[Android端末にインストール可能なDER形式の自己証明書作成 - Qiita](https://qiita.com/_takeshi_24/items/eb9cdb978e143284de7c)
##### 1. Windowsのスタートボタンより、「Ubuntu 20.04LTS」を起動する
##### 2. OpenSSLをインストールする
  下記コマンドを実行します。

```
sudo apt install openssl
```

##### 3. keyファイルを作成する

```
openssl genrsa -des3 -out server.key 2048
```

設定するパスワードを要求されるので入力します。
ここで設定するパスワードは後に必要なので控えておきます。

```
Enter pass phrase for server.key:
#キーボードで入力しても画面には表示されませんが、入力は受け付けられています。

#再入力を求められるので、再度パスワードを入力します。
Verifing - Enter pass phrase for server.key:
```

##### 4. CSRファイルを作成する (CSR:キー発行依頼ファイル)

```
openssl req -new -key server.key -out server.csr
```

途中に国名、会社名等聞かれます。適当に入力します。
ただし、FQDN名は、設定したいドメイン名で設定する必要があります。
前述のUnboundの設定ファイル(unbound.conf)で設定したドメイン名と同じである必要があります。

##### 5. keyファイルからパスワードを除去する
    元のファイルをコピーし、バックアップserver.key.orgを作成します。

```
cp -p server.key server.key.org
```

パスワードを除去します。

```
openssl rsa -in server.key -out server.key
```

##### 6. SANに設定するFQDNを書く
san.txtを作成します。

```
sudo touch san.txt
```
san.txtを編集します。

```
sudo nano san.txt
```
```text:san.txt
#example.comの部分は任意に変更する。
subjectAltName = DNS:example.com

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:TRUE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
```

##### 7. 証明書ファイルを生成する

```
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt -extfile san.txt
```
上記コマンドの"-days 365"の部分は、証明書の有効期間と思われます。

##### 8. 確認
下記コマンドを実行し、エラーがないか確認します。

```
openssl x509 -text -in server.crt -noout
```

##### 9. DER形式に変換(Android端末にインストールする際に必要)

```
openssl x509 -in server.crt -out server.der.crt -outform der
```

上記手順でWSL上に生成したserver.key、server.crt、server.der.crtは、Windowsのエクスプローラより"\\\\wsl$\Ubuntu-20.04\home\ユーザ名"にアクセスすることで参照可能です。

## Node.jsにてhttpsサーバを建てる

当例では、httpsというプロジェクトフォルダを作成し、作業するものとします。
当手順は、Windowsでの作業になります。
プロジェクトフォルダをVisual Studio Code(以下VSCode)で起動します。
##### 1. プロジェクトの初期化
  VSCodeのターミナル画面で下記コマンドを実行します。

```
npm init -y
```

##### 2. 必要なパッケージのインストール
  VSCodeのターミナル画面で下記コマンドを順次実行します。
  プロジェクトフォルダにnode_modulesフォルダが作成され、必要なファイルがダウンロードされます。

```
npm install --save express

npm install --save-dev @types/express
npm install --save-dev typescript
npm install --save-dev ts-loader 
npm install --save-dev webpack
npm install --save-dev webpack-cli
npm install --save-dev webpack-node-externals
```

##### 3. 各種設定ファイルの作成
    - tsconfig.json
      typescriptのトランスパイルの設定ファイルです。

```json:tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true, 
    "noImplicitAny": true, 
    "module": "es2015", 
    "target": "es2017", 
    "jsx": "react", 
    "lib": ["es2018", "dom"], 
    "moduleResolution": "node", 
    "removeComments": true, 
    "strict": true, 
    "noUnusedLocals": false, 
    "noUnusedParameters": false, 
    "noImplicitReturns": true, 
    "noFallthroughCasesInSwitch": true, 
    "strictFunctionTypes": false 
  }
}
```

- webpack.config.js

webpackの設定ファイルwebpack.config.jsを作成します。

```javascript:webpack.config.js
const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: 'development', 
  target: 'node', 
  externals: [ nodeExternals() ], 
  devtool: 'eval-source-map', 
  module: {
    rules: [
      {
        loader: 'ts-loader', 
        test: /\.ts$/, 
        exclude: [/node_modules/], 
        options: { configFile: 'tsconfig.json'}
      }
    ]
  }, 
  resolve: {
    extensions: ['.ts', '.js', '.json']
  }, 
  entry: './src/server.ts', 
  output: {
    filename: 'server.js', 
    path: path.resolve(__dirname, 'dist')
  }, 
  node: {
    __dirname: false
  }
};
```

-  packckage.jsonにコマンドの登録

package.jsonにコマンドを登録しておきます。

```json:package.json
{
  ...略...
  "scripts": {
    ...
    "start": "node ./dist/server.js", 
    "build": "webpack --config webpack.config.js"
  },
  ...略...
}      
```      

##### 4. コーディング

プロジェクトフォルダにsrcフォルダを作成し、そこにserver.tsファイルを作成し以下のようにコーディングします。

```typescript:src/server.ts
import * as Express from 'express';
import * as path from 'path';
import * as HTTPS from 'https';
import * as fs from 'fs';

const app = Express();

//スマホからexample.comにアクセスした際、文字列"Hello World"を返します。
app.get('/', (req: Express.Request, res: Express.Response) => {
  return res.send('Hello World');
});

//スマホにサーバ証明書をダウンロードできるように、ルーティングを設定します。
app.get('/download-cert', (req: Express.Request, res: Express.Response) => {
  res.download(path.join(__dirname, 'ssl/server.der.crt'));
});
const portNo = process.env.portNo || 443;

const serverOptions: HTTPS.ServerOptions = {
  key: fs.readFileSync(path.join(__dirname, 'ssl/server.key')),  
  cert: fs.readFileSync(path.join(__dirname, 'ssl/server.crt')), 
};

HTTPS.createServer(serverOptions, app).listen(portNo as number, () => {
  console.log(`Server runging on PortNo:${portNo}`);
}); 

export default app;
```

VSCodeのターミナル画面で、以下のコマンドを実行し、ビルドします。プロジェクトフォルダにdistフォルダが生成され、server.jsファイルが生成されます。

```
npm run build
```

##### 5. server.key、server.crt、server.der.crtの配置
  プロジェクトフォルダのdistフォルダに、sslというフォルダを作成し、server.key、server.crt、server.der.crtを配置します。



## Androidスマホにサーバ証明書をインストールし、https通信でアクセスする
当手順はAndroidスマホにおける操作手順になります。
私のスマホ KYOCERA TORQUE® G03にて検証しました。
Windows10端末と、Androidスマホが同一wifi上に接続されているものとします。
##### 1. スマホのIPアドレスを控える

- ホーム画面より"設定"をタップ。Wi-Fiをタップ
Wi-Fiの設定画面が開きます。

- 画面右上の設定ボタン(右上の・が縦に3つ並んでいるボタン)をタップし、"詳細設定"をタップ
  "Wi-Fiの詳細設定"画面が起動します。
 画面下部にスクロールすると、割り当てられているIPアドレスが表示されているので控えます。

##### 2. DNSサーバのアドレスを設定する

- ホーム画面より"設定"をタップ。Wi-Fiをタップ
Wi-Fiの設定画面が開きます。

- 一覧より、接続しているWi-Fiを長押しする  
"ネットワークを削除"、"ネットワークを変更"の選択肢が表示されるので"ネットワークを変更"をタップします。

- "詳細設定項目"を設定する

      - IP設定で"静的"を選択する
      - IPアドレスを設定する
        
         上記手順で控えたIPアドレスを設定します。
      - DNS1を設定する
        UnboundでDNSサーバを建てたWindows端末のIPアドレスを指定します。
      - DNS2を設定する
        元のDNSサーバのIPアドレスを指定します。

##### 3. サーバ証明書をインストールする
- chromeを立ち上げ"https://example.com/download-cert"にアクセスする
**この接続ではプライバシーが保護されません**というセキュリティ警告画面が表示されます。
画面下部の"詳細設定"をタップ、"example.comにアクセスする(安全ではありません)"をタップします。
**インストールできませんでした。証明書ファイルが読み取れませんでした**というメッセージが表示されます。以下の手順に進みます。

- ホーム画面より"設定"をタップ、設定画面で"セキュリティ"をタップ

- セキュリティ画面で"ストレージからインストール"をタップ
一覧の中に、"server.der.crt"があるのでタップします。
"証明書の名前を指定する"画面が開くので、適当な証明書名を入力し、OKをタップします。

##### 4. chormeからhttps://example.comにアクセスする    
 警告が表示されず、"Hello World"が表示されれば、成功です。
  
     
## 最後に
長い手順を踏んだ割には最終的にスマホの画面に"Hello World"が表示されるだけですが、これによりLAN内限定のPWAを展開することが可能になります。
余談ですが、当記事、私の初投稿となります。今後も少しずつ自分なりに身についた事柄をまとめて投稿していこうと思います。
      
  

## 参考資料
当記事は下記記事を参考にさせていただきました。
- [【図解】よく分かるデジタル証明書(SSL証明書)の仕組み 〜https通信フロー,発行手順,CSR,自己署名(オレオレ)証明書,ルート証明書,中間証明書の必要性や扱いについて〜 \| SEの道標](https://milestone-of-se.nesuke.com/sv-advanced/digicert/digital-certification-summary/)
- [【連載】WSL2、Visual Studio Code、DockerでグッとよくなるWindows開発環境 〜 その1：まずは概要 〜 \| SIOS Tech. Lab](https://tech-lab.sios.jp/archives/21023#Windows_Subsystem_for_Linux)
- [SAN(Subject Alternative Name) のオレオレ証明書 - Qiita](https://qiita.com/nis_nagaid_1984/items/b8f87d41ea108d47af61)
- [Android端末にインストール可能なDER形式の自己証明書作成 - Qiita](https://qiita.com/_takeshi_24/items/eb9cdb978e143284de7c)
- [Ubuntu Serverで内向きDNSをUnboundを使ってやってみたのでメモ - Qiita](https://qiita.com/umaxiaotian/items/b7f8f4866468d5c400b3)

ありがとうございました。