Nordvpn proxyが突然機能しなくなった話

# 背景
[NordvpnをDocker上で動かしてproxyにする](https://qiita.com/holndal/items/16e939bcda0479f7daf9)という記事を書いた後に再度試したら全部ipアドレスがそのままになっていた

https://qiita.com/holndal/items/16e939bcda0479f7daf9

## 発見した経緯
スクレイピング開始前に下記の様なエラーチェックを入れていた
```python
# デフォルトのIPアドレスを取得
response = requests.get('https://ifconfig.me')
pure_ip = response.text

# プロキシ経由でIPアドレスを取得
temp_proxy_address = f"http://localhost:8118"
temp_proxies = {
    'http': temp_proxy_address,
    'https': temp_proxy_address,
}
response = requests.get('https://ifconfig.me', proxies=temp_proxies)
vpn_ip = response.text

assert pure_ip != vpn_ip, "VPNが機能してないよ！"
```

## エラーメッセージ
IPが取得できる時点でproxyとしては動作している。しかしproxyがnordvpnに接続できていないのでとりあえずDockerのエラーログを見に行ってみた

実行コマンド
```bash
docker run -d --cap-add=NET_ADMIN --name=vpn --dns=103.86.96.100 --dns=103.86.99.100 --restart=always -e "COUNTRY=JP" -e "USERNAME=**" -e "PASSWORD=**" -e "LOCAL_NETWORK=192.168.1.0/24" -v /etc/localtime:/etc/localtime:ro -v ovpn-data:/app/ovpn/config -p 8118:8118 jeroenslot/nordvpn-proxy:latest
```

```
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: Removing current cronfile
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: Skipping downloading OVPN files - as they are not older than 120 minute(s).
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: SERVER has not been set, choosing best for you.
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: Creating new cronfile
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: Your country setting will be used. This is set to: JP
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: Your cron settings (*/15 * * * *) will be applied!
2024-08-26 12:23:55 2024-08-26 03:23:55 INFO: The country codes are unknown, getting country codes from API
2024-08-26 12:23:55 parse error: Invalid numeric literal at line 1, column 7
2024-08-26 12:23:56 2024-08-26 03:23:56 INFO: Skipping downloading OVPN files - as they are not older than 120 minute(s).
2024-08-26 12:23:56 2024-08-26 03:23:56 INFO: SERVER has not been set, choosing best for you.
2024-08-26 12:23:56 2024-08-26 03:23:56 INFO: Your country setting will be used. This is set to: JP
2024-08-26 12:23:56 2024-08-26 03:23:56 INFO: The country codes are known, skipping
2024-08-26 12:23:57 parse error: Invalid numeric literal at line 1, column 7
```

というわけでどうやら `Invalid numeric literal at line 1, column 7` が問題な模様。

## ファイルを見る
`/app/ovpn/run` を見ると
```bash
#!/bin/bash
set -e -u -o pipefail

source /app/ovpn/get-ovpn-files.sh
source /app/ovpn/servers_recommendations.sh

. /app/date.sh --source-only
# (続く)
```
とあるので、とりあえず `get-ovpn-files.sh` を見に行く

```bash
# check if the file exists
if [ -f ${OVPN_CONFIG_DIR}/ovpn.zip ]; then
  #the file exists continue checking if its older than two hours.
  if test `find ${OVPN_CONFIG_DIR}/ovpn.zip -mmin +${REFRESH_TIME}`; then
    download_files
  else
    echo "$(adddate) INFO: Skipping downloading OVPN files - as they are not older than ${REFRESH_TIME} minute(s)."
  fi
else
  #the files don't exists continue to download and extract them.
  download_files
fi
```

つまりどうやら `ovpn.zip`は存在する。`ovpn.zip`は120分以内に更新されてるのでスキップされているらしい
`Skipping downloading OVPN files - as they are not older than 120 minute(s).`

問題無さそう。

続いて
`servers_recommendations.sh` を見る。そこそこ長いのでエラーから考えてみる
parse errorなので、そもそもそれが発生しうる箇所に絞ってみると

```bash
# 1. 国コードを取得
curl -s https://nordvpn.com/wp-admin/admin-ajax.php?action=servers_countries -o /tmp/servers_countries
export COUNTRY_CODE=$(cat $JSON_FILE_SERVER_COUNTRIES | jq '.[]  | select(.code == "'${COUNTRY^^}'") | .id')

# 2. nordvpnのAPIを叩いて、推奨サーバーを取得
# つまりはコレ https://api.nordvpn.com/v1/servers/recommendations?limit=10&filters=JP=106
curl -s $SERVER_RECOMMENDATIONS_URL$QUERY_PARAM -o $JSON_FILE
NUMBER_OF_SERVERS="$(jq length $JSON_FILE)"

## 特定のサーバーに設定
DESIRED_SERVER_NUMBER="$(shuf -i 0-$(($NUMBER_OF_SERVERS - 1)) -n 1)"
export SERVER="$(jq -r '.['$DESIRED_SERVER_NUMBER'].hostname' $JSON_FILE)"
export SERVERNAME="$(jq -r '.['$DESIRED_SERVER_NUMBER'].name' $JSON_FILE)"
export LOAD="$(jq -r '.['$DESIRED_SERVER_NUMBER'].load' $JSON_FILE)"
export UPDATED_AT="$(jq -r '.['$DESIRED_SERVER_NUMBER'].updated_at' $JSON_FILE)"
export IP="$(jq -r '.['$DESIRED_SERVER_NUMBER'].station' $JSON_FILE)"
echo "$(jq -r '.['$DESIRED_SERVER_NUMBER'].hostname' $JSON_FILE)"
echo "$(jq -r '.['$DESIRED_SERVER_NUMBER'].hostname' $JSON_FILE)" > /tmp/nordvpn_hostname
```

ログ的にこの2つ。

ログを仕込んでみると
`https://api.nordvpn.com/v1/servers/recommendations?limit=10&filters=JP=106` を叩いた際にjsonではなく、下記のレスポンスが帰ってきてparseに失敗した模様。
```
<html>
    <head><title>429 Too Many Requests</title></head>
    <body>
        <center><h1>429 Too Many Requests</h1></center>
        <hr><center>nginx</center>
    </body>
</html>
```

nordvpnに接続した状態でこのdockerを動かしていたが、別のサーバーに接続し直すと動作しました。

つまり
`VPNに相乗りしてた誰かがnordvpnのAPI叩きまくって429出てた`

が真相だったみたいです。

エラートレースしやすくするためにjsonを変換する前にエラーチェックした方が良いと思いました。

# 学んだ事
- Json parseする前にcurlのステータスコードチェックを入れた方が良い
- https://api.nordvpn.com/v1/servers/recommendations?limit=10&filters=JP=106 という便利なAPIがある


# 宣伝(招待リンク)

[お友達紹介リンク](https://refer-nordvpn.com/ClziUzUtdba) を使用した登録は下記の特典が付くので、もし検討している方はご一考ください。

```
- 1年または2年プランを選ぶと3か月分の無料期間
- 1か月プランを選ぶと1か月分の無料期間
がもらえます。
```

書いていて思いましたが、結構紹介特典がしょぼいので[ハピタス](https://m.hapitas.jp/item/detail/itemid/77465?i=24116723&route=pcText&apn=itemsharelink)経由でダウンロードしてポイント貰った方がお得かもしれません。