スマホ向けにOpenVPNを社内で導入したお話

この記事は ウェブクルー [Advent Calendar 2017の12日目](https://qiita.com/advent-calendar/2017/webcrew)の記事です。
昨日は@hiro_moriさんの「クリティカルレンダリングパスの最適化」でした。

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

## はじめに
本記事では、私がウェブクルーに新卒入社した際の研修で導入したOpenVPNのことをお話します。
※OpenVPNのインストールや基本的な設定は記載しておりません。

## 導入理由
弊社では従業員には基本的にWindowsのPCが支給されます。
そのため、VPNサービスにはWindowsと親和性の高い、SSTPを利用しております。
しかし、もろもろの都合でPCを開けない場面があり、VPNを利用することができないことがあるという声がありました。
(例：営業担当が営業先に移動中、社内の管理ツールでなんらかの設定変更をしたいが、電車でPCを開けないなど。)

そこで、スマホやその他タブレット端末など、手軽にアクセスができるもので対応ができないものかと考えましたが、
SSTPでは実現が難しい為、様々なプラットフォームに対応したOpenVPNを、SSTPとは別の仕組みとして導入しました。

## OpenVPNとは
SSL VPNです。
ルーティングモードかブリッジモード、ID/Password認証の有無、認証のmoduleの導入ができるなど、柔軟な設定が出来ます。
詳しくは公式のウェブページをご覧ください。
https://www.openvpn.jp/

## 社内向けサービスの概要
社内向けのウェブサービスを作成し、サービスを通してVPN接続に必要な証明書の発行、
ワンタイムパスワードの発行を行えるようにしました。
この記事では、下図の②と③の認証部分にフォーカスします。

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/211893/e2bd7675-fa28-62ae-ec80-15b63ea4fec3.png)


## 紛失のリスクと対応
スマホで社内のツールにアクセスをできるようにするとなると、
考慮しなければならないのが**紛失のリスク**です。

このリスクに対して、SSL/TLS VPNの特徴である **証明書**を有効活用し、
VPN接続をしたいときに、専用の社内向けウェブサービスで証明書を発行して、**発行から1日過ぎたら失効**させ、
権限をできるだけ端末に留めさせないことで対応しました。
また、**ワンタイムパスワード**を用いることで、仮に証明書が別の端末に渡ってしまったときにも
それだけで認証ができないようにしました。

### 証明書発行とワンタイムパスワードについて
#### 証明書発行
下記は証明書発行を行うコマンドです。

```shell
# easyrsa3: 簡単に認証局が作成できるユーティリティ
# vars:easyrsaで使用するパラメータの設定ファイル 
# days:失効までの日数
# build-client-full:接続するユーザの証明書を作る際に使うオプション
# nopass:パスフレーズの不要
/usr/local/easyrsa3 --batch --vars=/usr/local/easyrsa3/vars --days=1 build-client-full "ユーザ名" nopass
```

上記のようなコマンドでクライアント証明書を生成し、
接続したい端末にダウンロードさせるウェブサービスの仕組みを作成します。が、そこはウェブサービスの作成方法で、
特筆すべきことでもないのでカットします。

#### ワンタイムパスワードと認証プログラム
ワンタイムパスワードは上記の証明書ダウンロード用のウェブサービスにてログインを行ったときに発行します。

生成のアルゴリズムはRFCにて規定されております。
https://tools.ietf.org/html/rfc4226
https://tools.ietf.org/html/rfc6238
中に実装も記載されているのでぜひご覧ください。

証明書＋ワンタイムパスワードが準備できたら、これらを用いて認証をする仕組みをOpenVPNサーバに導入します。
OpenVPNの.confファイルにて、下記の設定でID/Password認証をONにします。
同時に認証に使うプログラムを指定できます。

```shell:server.conf
# auth_program.py : 今回はpythonで認証プログラムを作りましたが、適宜お選びください。
# via-file : OpenVPNがクライアントから受け取ったIDとパスワードを一時的に保存するファイルです。
auth-user-pass-verify auth_program.py via-file
```

認証プログラムは、上記の引数にある*via-file*からID/Passwordを読み出して
生成したワンタイムパスワードと比較し正しければ0を、正しくなければ1を返すというシンプルなものです。
下記のワンタイムパスワードの認証用の関数は擬似的なものなので、作成したワンタイムパスワードと比較するプログラムをご自身で記述してください。

```py3:auth_program.py
def authOneTimePassword(user_id, one_time_password):
  if (one_time_password が 正しいか検査)
    return True
  else
    return False
    
filename = sys.argv[1]
fp = open(filename)
data = fp.readlines()
fp.close()

input_userid = data[0].rstrip()
input_password = data[1].rstrip()
if (authOneTimePassword(input_userid, input_password)):
  print "[Auth] Success."
  sys.exit(0)
else:
  print "[Auth] Failed."
  sys.exit(1)
```

最終的に１か０が返ればよいので、LDAP認証を入れるなど別の認証方法も使うことが出来ます。

上記のような証明書とワンタイムパスワードの仕組みと社用携帯に導入されているMDM(モバイルデバイス管理ツール)によって、スマホの紛失のリスクへの対応としました。

その他にも、万が一不正なVPN接続をされた場合に、どこにアクセスしたかを追跡するために、
OpenVPNサーバからNAT時にiptablesでログを取るといったことも行いました。

## 最後に
弊社でのVPNスマホ利用への対応について紹介しました。

リスクを考えると少々認証に手間がかかりますが、ユーザに「便利になった」と言っていただけたので、
非常に達成感がある研修でした。

明日は@kouchanneさんです。宜しくお願い致します。

-----

ウェブクルーでは一緒に働いていただける方を随時募集しております。
お気軽にエントリーくださいませ。
[開発エンジニアの募集](https://hrmos.co/pages/1004681658307198976/jobs/0000005)
[フロントエンドエンジニアの募集](http://www.webcrew.co.jp/recruit/creative.html)
[データベースエンジニアの募集](https://hrmos.co/pages/1004681658307198976/jobs/0000023)