ARM5系 QNAP の wget で "Unable to establish SSL connection."

<h1>OpenSSL で <code>alert handshake failure</code> エラー</h1>

> QNAP に SSH で接続して `wget` しようとすると以下のエラーでダウンロードできません。
>
> ```shell-session
> [~] # wget https://*****.com/******_linux-arm.tar.gz
> --2021-02-17 12:11:46--  https://*****.com/******_linux-arm.tar.gz
> Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
> Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
> OpenSSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
> Unable to establish SSL connection.
> 
> [~] # wget --version | head -n1
> GNU Wget 1.16 built on linux-gnueabi.
> ```

どうも HTTPS でダウンロードする際の `SSL handshake` に失敗して **SSL 接続が確立できない**でいるようです。

- <sub><sup>環境: QNAP TS-119P+ (v4.3.3.1432, 2020/10/06), Entware-std v1.03</sup></sub>

## TL; DR <sub><sup><sub><sup>（今北産業）</sup></sub></sup></sub>

1. QNAP のデフォルトの `wget` のバージョンが古い。
2. 認証局リスト（`ca-certificates`）が登録されていない。
2. `opkg` で `wget` と `ca-certificates` をインストールして再ログイン後リトライしてください。

- <sup><sub>`opkg`: QNAP のパッケージマネージャー。詳細は以下を参照<br>[ARM 系 QNAP に git/php7/golang などをパッケージでコマンドインストールする [Entware で Opkg 編]](https://qiita.com/KEINOS/items/f832ada264257300e4d7) @ Qiita</sup></sub>

## TS; DR <sub><sup><sub><sup>（細かいこと）</sup></sub></sup></sub>

### `--verbose --debug` でエラーの具体的な内容を確認する

```shell-session
[~] # wget --verbose --debug https://*****.com/******_linux-arm.tar.gz
DEBUG output created by Wget 1.16 on linux-gnueabi.

--2021-02-17 12:32:58--  https://*****.com/******_linux-arm.tar.gz
Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Caching *****.com => XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
Created socket 3.
Releasing 0xffffffff (new refcount 1).
Initiating SSL handshake.
SSL handshake failed.
OpenSSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Closed fd 3
Unable to establish SSL connection.
```

上記の内容から、`Connecting to ... connected.` でソケット（443ポート）への接続まではできているものの、その後の証明書の相互確認（`SSL handshake`）が `SSL handshake failed.` で失敗していることがわかります。

そして、接続先が求めて来ている認証方式（プロトコル）が `OpenSSL: 〜 routines:ssl3_read_bytes:sslv3 alert handshake failure` により `SSLv3` であることがわかります。

### 切り分け（試してみたこと）

#### `--no-check-certificate` で証明書を使わないで試してみる → <font color=red>NG</font>

QNAP のアップデートは行っているのですがローカルの証明書が壊れているのかもと思い、危険なのですが `--no-check-certificate` で証明書を使わないで試してみました。

```shell-session
[~] # wget --no-check-certificate https://*****.com/******_linux-arm.tar.gz
--2021-02-17 12:20:14--  https://*****.com/******_linux-arm.tar.gz
Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
OpenSSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Unable to establish SSL connection.
```

失敗します。となると、証明書の問題ではなく認証方式なのかと踏んでみました。

#### `--secure-protocol=SSLv3` で認証方式（プロトコル）を決めうちしてみる → <font color=red>NG</font>

まずは、そもそも現在の `wget` が `SSLv3` に対応しているのか確認する必要があります。

```shell-session
[~] # # 使えるプロトコルの確認（--secure-protocol）
[~] # wget --help | grep SSL            
HTTPS (SSL/TLS) options:
       --secure-protocol=PR        choose secure protocol, one of auto, SSLv2,
                                     SSLv3, TLSv1, TLSv1_1, TLSv1_2 and PFS
       --random-file=FILE          file with random data for seeding the SSL PRNG
       --ciphers=STR           Set the priority string (GnuTLS) or cipher list string (OpenSSL) directly.
                                   The format and syntax of this string depend on the specific SSL/TLS engine.
       --ftps-resume-ssl               resume the SSL/TLS session started in the control connection when
```

`SSLv3` があるので大丈夫そうです。しかし、プロトコルを決めうちしてもダメでした。デフォルトで `auto` なので `SSLv3` が選ばれていたようです。

```
[~] # wget --secure-protocol=SSLv3 https://*****.com/******_linux-arm.tar.gz
--2021-02-17 12:24:04--  https://*****.com/******_linux-arm.tar.gz
Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
OpenSSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Unable to establish SSL connection.
```

となると、振り出しに戻って「証明書の問題」に絞ってみます。

#### `ca-certificates` を入れてみる → <font color=red>NG</font>

証明書は壊れていないと過程すると、次に考えられるのが「そもそも必要な証明書が入っていない」パターンです。

つまり、証明する先がわからないので「接続先が発行した公開鍵が正しいものであるかを証明するデータがない」ケースです。この証明を行うのが `CA` と呼ばれる[認証局](https://ja.wikipedia.org/wiki/%E8%AA%8D%E8%A8%BC%E5%B1%80)です。

しかし Docker のイメージなどではよくあるのですが、必要最低限の認証局のみで、主要な認証局が追加されていないことがあります。その場合にハンドシェイクが失敗することが、まれによくあります。ダウンロードした接続先の公開鍵が正しいものであることを確認したくても、肝心の認証局がわからないからです。

一般的には、パッケージマネージャーなどで `ca-certificates` パッケージで最新の主要な認証局一覧を取得・更新します。

そこで QNAP で使えるパッケージマネージャーの `opkg` コマンドで、`ca-certificates` がインストールできるか確認します。（[opkg コマンドは別途インストールが必要です](https://qiita.com/KEINOS/items/f832ada264257300e4d7)）

```shell-session:パッケージの検索と確認
[~] # # パッケージの検索
[~] # opkg find ca-certificates
ca-certificates - 20200601-1 - System CA certificates

[~] # # パッケージ情報の確認
[~] # opkg info ca-certificates
Package: ca-certificates
Version: 20200601-1
Depends: libc, libssp, librt, libpthread
Provides: ca-certs
Status: unknown ok not-installed
Section: base
Architecture: all
Size: 125728
Filename: ca-certificates_20200601-1_all.ipk
Description: System CA certificates
```

幸いなことに `ca-certificates` パッケージは提供されているようです。

```shell-session:パッケージのインストール
[~] # opkg install ca-certificates
Installing ca-certificates (20200601-1) to root...
Downloading http://bin.entware.net/armv5sf-k3.2/ca-certificates_20200601-1_all.ipk
Configuring ca-certificates.
```

しかし、残念なことにダウンロードできませんでした。

```shell-session:ca-certificatesを入れた状態でwget
[~] # wget https://*****.com/******_linux-arm.tar.gz
--2021-02-17 12:24:04--  https://*****.com/******_linux-arm.tar.gz
Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
OpenSSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Unable to establish SSL connection.
```

そうなると、次に考えられるのが `wget` のバージョンです。

現在の `wget` のバージョンは v1.16 で 2014 年のものです。2021/02/17 現在、`GNU wget` v1 系の[最新は v1.21](https://ftp.gnu.org/gnu/wget/)です。これは、[SHA-1 や MD5 の脆弱性](https://qiita.com/KEINOS/items/c92268386d265042ea16#md5-%E3%81%A8-sha-1-%E3%81%AE%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A4%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)が指摘される前の古いバージョンで、いささか古いバージョンすぎると思い、なるべく新しい `wget` のバージョンにしてみました。

#### `wget` を更新してみる → <font color=green>OK</font>

`opkg` に `wget` があったので、インストールしてみました。

```shell-session:opkgでwgetをインストール
[~] # opkg install wget
Installing wget (1.20.3-4) to root...
Downloading http://bin.entware.net/armv5sf-k3.2/wget_1.20.3-4_armv5-3.2.ipk
Configuring wget.
```

QNAP 同梱（デフォルト）の `wget` とは別の場所にインストールされるためパスが変わる（環境変数の `PATH` に追加される）ので、ログインしなおして反映させます。

`wget` のバージョンは最新ではありませんが、比較的新しい安定版の v1.20.3 がインストールされました。

```shell-session:wgetのバージョン確認
[~] # wget --version | head -n1
GNU Wget 1.20.3 built on linux-gnu.
```

今度は、ダウンロードも正常にできました。 :tada:

```shell-session:wgetで再度ダウンロード
[~] # wget https://*****.com/******_linux-arm.tar.gz                 
--2021-02-17 15:14:00--  https://*****.com/******_linux-arm.tar.gz
Resolving *****.com... XXX.XXX.XXX.XXX, ffff:ffff:ffff::ffff
Connecting to *****.com|XXX.XXX.XXX.XXX|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 21087228 (20M) [application/gzip]
Saving to: '******_linux-arm.tar.gz'

******_linux-arm.tar.gz                          100%
```

逆に、`ca-certificates` のインストールが不要なのかと思い `opkg remove ca-certificates` でアンインストールしてみましたが、エラーが再発したので、`wget` のアップデートと `ca-certificates` のインストールが必要、と言う結論になりました。