ただのメモ

開発で得た知識のアウトプット

【Raspberry Pi 】SoftEther VPNとHTTPサーバを同居させて自分自身にアクセスする

 Raspberry Pi(以下ラズパイ)をVPNサーバとして利用しつつ、自身のHTTPサーバにアクセスするための備忘録です。

VPNでローカルのHTTPサーバを見る

 ラズパイ界隈では、しばしばmjpg-streamerと市販のWebカメラを用いた配信環境を構築する記事が書かれます。

github.com

しかし、mjpg-streamerのカメラ画像をそのまま外部に公開すると、第三者にも見られる可能性が非常に高くなり、個人情報的に好ましくありません。

それに対する一つの解決策として、VPNを用いてローカルのサーバにアクセスする方法があります。VPNでは暗号化は勿論のこと、デバイスによっては証明書認証も利用できます。スマホからはL2TP/IPsecが利用できます。つまり、HTTPサーバのBASIC認証などに比べると極めて安全です。

少し話が変わりますが、Webページをネイティブアプリのようなスタイルで閲覧できる、PWAという機能を利用すれば、VPN接続時にだけ利用できる一種のアプリとして提供できます。

f:id:pit-ray:20210413014441g:plain:h500

ちなみに、iPhoneでVPN接続を開始するには、設定 > VPNという2ステップを踏みます。

f:id:pit-ray:20210329082835j:plain

ここでは、ラズパイの構築パターンを書いていきたいと思います。

有線LANと無線LANがあるモデル

 ラズパイ3 B+やラズパイ4などのモデルです。これらのモデルには、無線LANと有線LANが備わっているため、その両方を利用することで簡単にセットアップできます。勿論、tapデバイスやbr0でごにょごにょすることもありません。

SoftEtherのローカルブリッジの注意事項には、次の記載があります。

Linux オペレーティングシステム内部での制限事項により、VPN 側 (仮想 HUB 側) からローカルブリッジしている LAN カードに割り当てられる IP アドレスに対して通信を行うことはできません。この制限は SoftEther VPN が原因ではなく、Linux の内部構造に原因があります。もし VPN 側 (仮想 HUB 側) から Linux でローカルブリッジに使用しているコンピュータ本体と、何らかの通信を行いたい場合 (たとえば VPN Server / VPN Bridge サービスと HTTP サーバーサービスを両方動作させており、VPN 側からもサーバーサービスにアクセスさせたい場合) は、ローカルブリッジ用の LAN カードを用意して接続し、その LAN カードと既存の LAN カードの両方を物理的に同じセグメントに接続してください (この他の場合においても 「3.6 ローカルブリッジ」 で解説したように、ローカルブリッジに使用する LAN カードはそのための単独のものを用意することが推奨されています)。(出典: 3.6 ローカルブリッジ - SoftEther VPN プロジェクト)

この意味は、SoftEtherにブリッジしたLANカードは出口専用になり、入口としては機能しない(自身にアクセスできない)ということです。したがって、ローカルブリッジ用にeth0、自身へのアクセス用にwlan0を利用します(SoftEther VPNの注意書きにあるように、プロミスキャスモードに対応した無線LAN子機か有線LANが必要であるため、必然的にeth0にブリッジします)。wlan0は同セグメントに接続すればいいです。ただ、ローカルブリッジ用のeth0にIPをふってもしょうがないので、eth0へのIPアドレスの割り当ては任意です。理解としては、eth0をVPN接続後の出口のルート、wlan0をローカルでのHTTPサーバとの通信ルートとして利用します(多分これで理解あってると思う)。

ただ、この構成ではラズパイを有線LANに接続しなければならないため、取り回しが良くありません。特に、センサ類とサーバを全て一つのラズパイにまとめたいときには非常に不便です。

どうしてもWi-Fiだけでやりたい

 無線接続でVPNサーバを構築したい場合、中継機を使えば実質的に無線で稼働させられます。つまり、中継機を利用して無線LANから有線LANへ変換します。現在の無線LANを構成している親機のルータとは別の無線ルータの中継機能を利用するか、専用の中継機を用意します。私の場合、BUFFALOのルータが余っていたため、追加費用はかかりませんでした。

親機に中継機を接続するには、次の手順を踏みます。ちなみに親機がNECのAterm BL1000HW、中継機がBUFFALOのWZR-HP-G302Hの場合です。

  1. 中継機として利用したいルータ(以下、中継機)のスイッチをブリッジモードにする。

  2. ルータのIPアドレスを出荷時に戻すために、リセットボタンを長押しし、初期化する。

  3. PCと中継機をLANケーブルで接続する。(この段階では中継機が親機に接続されていないため、無線でアクセスできないから)

  4. エアーステーション設定ツールにより、中継機を検出するか、中継機の裏面に記載されている、ブリッジ時のルータのIPアドレスにブラウザからアクセスする。(このあたりの手順は中継機のメーカや型番によって異なるため、それぞれの公式ページを参照してください)

  5. PCを接続しているのと同じセグメントに中継機のIPアドレスを固定する

  6. 親機と中継機がどちらもBUFFALOであればAOSS、異なるメーカであればWPSで接続する。この方法でうまくいかない場合は、親機のSSIDとパスワードを手動で入力します。BUFFALOの旧型モデルで有れば、エアーステーション間接続の設定から、新型で有れば中継機能の設定から、親機のSSIDをベタ打ちします。モノによっては、SSIDを自動検出できる場合もありますが、私の場合は検出できなかったです。

  7. これで固定したルータのIPアドレスに親機の無線LANからアクセスできるようになりました。今後は中継機の有線LANしか使わないため、好みで中継機のSSID検出をオフにします。

  8. 中継機を再起動します。

BUFFALOの場合には次のページのように型番ごとに設定方法を確認できます。

中継機能の設定方法(WEX-300) | バッファロー

以上により、PCとWi-Fiを切断しても、有線で中継機と接続すればインターネットに繋がるはずです。うまく行かない場合、親機や中継機にpingを送ったり、ipconfigやifconfigを利用してIPアドレスが正しいかどうか確認します。

 このように、無線LANルータの中継機能や専用の中継器を利用することで、無線LANを有線LANに変換し、実質的にWi-FiでVPNサーバを稼働させることができます。中継器によっては2000円クラスでも手に入ったり、お古のルータに中継機能が搭載されている場合もあります。ただ、無線LANの親機によっては専用の中継器でないと接続できないものがあり、予想以上に費用が掛かってしまう可能性があります。その際には、ラズパイを二つ用意し、VPNサーバを有線LANで接続できる位置に置くことも考慮するべきです。

設定方法

SoftEther VPNのサーバをインストールする

証明書認証を利用する場合には、ソースを書き換えてビルドしたりしますが、とりあえず/usr/local/vpnserver/に一式置き、systemdで起動できるようにすればいいです。この点の解説は、他のブログなどを参照ください。

ラズパイの設定をする

/etc/network/interfacesの下部に次のように追加します。

auto lo
iface lo inet loopback

iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

編集するには、次のようにVimを使うことをお勧めします。

$ sudo apt install vim
$ sudo vim /etc/network/interfaces

合わせて、wpa_supplicant.confにSSIDやら暗号化キーやらを記載します。心配な人は、暗号化キーをハッシュ化しておきましょう。

/etc/dhcpcd.confに次のように追記します。

interface eth0
static ip_address=0.0.0.0

interface wlan0
static ip_address=192.168.0.10/24 # HTTPサーバとVPNサーバ兼用
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

これでラズパイのIPが固定されました。

先ほど説明した通り、ラズパイへのアクセスはWi-Fiを介しますが、デフォルトの省電力機能により不安定になることがあるため、その機能をオフにします。

まず、その機能の状態を確認します。

$ iwconfig
eth0      no wireless extensions.

lo        no wireless extensions.

wlan0     IEEE 802.11  ESSID:"**********"
          Mode:Managed  Frequency:2.412 GHz  Access Point: *************
          Bit Rate=72.2 Mb/s   Tx-Power=31 dBm
          Retry short limit:7   RTS thr:off   Fragment thr:off
          Power Management:on
          Link Quality=45/70  Signal level=-65 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:2  Invalid misc:0   Missed beacon:0

Power Managementがonになっています。次のコマンドでoffになることを確認します。

$ iw dev wlan0 set power_save off

ただ、Power Managementは再起動するとonに戻ってしまうため、起動する毎にこのコマンドが実行されるようにします。起動時にコマンドを実行するには様々な方法がありますが、例としてrc.localを利用します。

$ sudo vim /etc/rc.local

を実行し、次のコマンドを追記します。

# Disable Wi-Fi Power Management
/sbin/iw dev wlan0 set power_save off

これによって、再起動しても省電力機能がoffになります。

SoftEther VPNの設定をする

SoftEther VPNのサーバー管理マネージャでeth0と仮想HUBをローカルブリッジします。

f:id:pit-ray:20210324214022j:plain

親機の設定をする

VPNで接続するデバイスによって次のポートを開放し、wlan0のIPアドレスへフォワーディングします。一般的なルータであれば、親機のIPアドレスをブラウザに打ち込めば設定できるハズです。iptablesなどを利用している場合には、同様に開放します。

接続方法 プロトコル ポート番号
SoftEther VPN Client TCP 5555など(サーバの設定に依存)
iPhoneなどのL2TP/IP接続 UDP 450, 5000

以上で、80ポートで接続すればHTTPサーバに接続され、5555や450, 5000で接続すればVPNサーバによって処理されます。勿論、VPN接続をしている場合でも80ポートでHTTPサーバにアクセスできます。

ここで、VPNサーバのセキュリティを高めるためには、SoftEther Clientによる接続に固有証明書認証を用いたり、L2TP/IP接続のユーザにはアクセスリストで宛先IPアドレスをローカルに限定し、踏み台防止などを行うと良いです。SoftEtherのアクセスリストを利用する場合には、下部に破棄ルールを入れ、上部にローカル許可を入れますが、DHCPを無効化しないためにUDPの67, 68ポートは最優先で許可する必要があります。

今回紹介しなかったものとその指針

有線LANだけ搭載されたラズパイ

 ラズパイ3以前のものは無線LANが無く、物理有線LANのみ搭載されています。この場合、SoftEther VPNの解説記事でよく紹介されているようなtapデバイスとbr0によるブリッジ接続を利用します。

方法1 物理NIC増設する場合(コメントには仮想NICを追加する方法もあり) yunabe.hatenablog.com

方法2 tapデバイスとブリッジを利用する方法 yunabe.hatenablog.com

参考 tapデバイスの理解 blog.treedown.net

今回利用した方法は物理NICを二つ利用する方法であるため、無線LAN子機や有線LANアダプタをドングルしてやれば同様に利用できるはずです。

無線LANだけ搭載されたモデルの場合

ラズパイZeroのWHなどの小型モデルには、有線LANが無く無線LANだけ搭載されています。SoftEther VPNは、公式サイトでも説明がある通り、プロミスキャスモードに対応しているWi-Fiをドングルするか、有線LANアダプタを接続しなければなりません。(残念ながらラズパイのWi-Fiは、プロミスキャスモードに対応していないようです)

自己責任で

 SoftEther VPNは、比較的安全なVPNサーバ/クライアントですが、導入前と導入後では後者のほうが圧倒的にリスクが高くなります。その点注意して自己責任で行ってください。私はネットワークのプロの技術者ではないため、見落としている点がある可能性があります。保身のために言っておきますが、私は一切の責任を負いません

Windows環境でwxWidgetsのインクルードやリンクが失敗する問題 (3.0.x, 3.1.x, Visual Studio 2019, MinGW)

今回はVisual Studio 2019でwxWidgets 3.0.5のビルドが失敗する問題と、MinGW-w64でwxWidgets 3.1.4のビルドが失敗する問題の対策です。

wxWidgets 3.0.5 + Visual Studio 2019

問題

wx/wxcrt.hの中のwxStroll()に関してテンプレートのエラーが吐かれます。wx/wxcrt.hにてwxNEEDS_DECL_BEFORE_TEMPLATEが定義されてないのが問題です。

対策

次のようなヘッダファイルを作り、エラーが生じているソースの先頭でインクルードすればよいです。

#if defined(_MSC_VER) && _MSC_VER >= 1500
#ifndef wxNEEDS_DECL_BEFORE_TEMPLATE
#define wxNEEDS_DECL_BEFORE_TEMPLATE
#endif
#endif


wxWidgets 3.1.4 + MinGW-w64

問題

libwxmsw31u_core.aとのリンクが失敗し、大量のundefined referenceが出力されます。wxWidgetsがMinGWのlibuxtheme.aliboleacc.aとのリンクに失敗しているのが原因です。

対策

MinGWをインストールしたディレクトリからlibuxtheme.aliboleacc.aをコピーし、wxWidgets/lib/gcc_libの中に置きます。MinGW-w64のデフォルトのディレクトリ構成ならば、mingw64/x86_64-w64-mingw32/lib/の中にあります。バッチファイルでコピーする場合には、次のようなスクリプトを書けばよいです。

for /f "usebackq" %%A in (`where.exe mingw32-make`) do set MINGW_MAKE_PATH=%%A

cd libs
@if not exist tmp (
    mkdir tmp
)
powershell cp "%MINGW_MAKE_PATH:~0,-21%\\*-mingw*\\lib\\liboleacc.a" "tmp\\liboleacc.a"
powershell cp "%MINGW_MAKE_PATH:~0,-21%\\*-mingw*\\lib\\libuxtheme.a" "tmp\\libuxtheme.a"

copy "tmp\\liboleacc.a" "wxWidgets\\lib\\gcc_lib\\liboleacc.a"
copy "tmp\\libuxtheme.a" "wxWidgets\\lib\\gcc_lib\\libuxtheme.a"

cd ..

追記

wxWidgetsのv3.1.5ではこの問題が修正されているようです。



素直にVisual Studioを使おう

CMakeのwxWidgetsのモジュールでは、wxWidgets/lib/vc_libwxWidgets/lib/vc_x64_libwxWidgets/lib/gcc_libしか検索を行わないため、MinGWを利用したときに32 bitと64 bitを自前で使い分けないといけません。また、今現在のMinGWでビルドを行うと、古いスタイルのリソースを参照するため、wxWidgetsとVisual StudioのwxWidgetsでは見た目が異なります。

f:id:pit-ray:20210117103246p:plain
wxWidgets (MinGW)
f:id:pit-ray:20210117103625p:plain
wxWidgets (Visual Studio 2019)


GitHubリポジトリにSynopsys Coverity Scan Static Analysisのバッジをつける

今回はGitHubのREADMEでよく見かけるCoverity Scanのバッジをつける流れのメモです。

https://camo.githubusercontent.com/c5ac830f3fb7dfc432f31df74c22978336950978c40ba6ae205a1cec827b31a0/68747470733a2f2f7363616e2e636f7665726974792e636f6d2f70726f6a656374732f32323431372f62616467652e737667 ← コレ

Synopsys Coverity Scan とは

 Coverityは、Synopsys社が提供する静的コード解析ソフトウェアで、Coverity Static Analysisではビルドのプロセスから生成したグラフから網羅的な解析を行います。Coverityではこのような方式をとっていることから、低レベルなエラーなども含めた高精度な解析結果を得ることができます。対応している言語は、C/C++, C#, Java, JavaScript, PHP, Python, .NET Core, ASP.NET, Objective-C, Go, JSP, Ruby, Swift, Fortran, Scala, VB.NET, iOS, TypeScriptなどです。

詳細情報は、以下のリンクから静的解析 (Coverity)のデータシートから見ることができます。
www.synopsys.com

 Coverityは、GitHubで公開されているオープンソースのソフトウェアでは、Coverity Scanというサービスにより無償で利用できます。

今回利用するプロジェクト

今回はCMakeを利用した次のC++プロジェクトに対して解析を行います。特にwxWidgetsという外部ライブラリをリンクしているため、コード解析の例として参考になると思います。
github.com

このソフトウェアは、WindowsをVimのキーバインディングで操作するツールとして公開しているもので、興味のある方は次の記事をご覧ください。
www.pit-ray.com

手順

Coverity Scanは次のサイトから利用します。
scan.coverity.com

GitHubとの連携

まずは、GitHubとのリンクを行うために、右上のSign upからSign in with GitHubを押します。

f:id:pit-ray:20210112173841j:plain

f:id:pit-ray:20210112174038j:plain

すると、Coverity ScanによるGitHubへのOAuthの認証が要求されるので、許可します。
f:id:pit-ray:20210112174406j:plain

プロジェクトの追加

アカウントを連携したら次のリンクから自分のプロジェクトを選択します。
https://scan.coverity.com/projects/new?tab=github_index

そうしたら、ライセンスやホームページURLなどの必要な事項を記入し、Submitします。今回の場合は、自分のリポジトリであるので、RoleをMaintainer/Ownerとしていますが、適宜適切なものを選択します。
f:id:pit-ray:20210112174919j:plain

すると、Project Settingsの設定画面が表示されると思います。ここで、Quick Start GuideからSubmit Buildを選択します。
f:id:pit-ray:20210112175817j:plain

解析

解析のためのファイルはUpload Buildのページからアップロードします。ここでバージョンや説明などの必要な事項を入力します。

f:id:pit-ray:20210112175711j:plain

初めに述べたように、Coverity Static Analysisはビルド時のプロセスを監視して解析用のグラフを生成します。そこで、Coverityビルドツールをダウンロードし、オフラインで解析用のファイルを生成したうえで、そのファイルをアップロードします。このような手順を踏むことで、外部ライブラリなどのプロジェクト固有の環境の変化に対応することができます。

まず、以下のページからビルドツールをダウンロードします。
Coverity Scan - Download the Coverity Scan Build Tool: C/C++


必要な言語のページから、適切なOSを選択します。
f:id:pit-ray:20210112180941j:plain

すると、826 MBのデカい圧縮ファイルがダウンロードできるので、解凍し、そのうちのbinディレクトリのパスを通します

次に、GitHubから自分のリポジトリを落とします。

$ git clone https://github.com/pit-ray/win-vind.git
$ cd win-vind

MinGW + CMakeの場合は、次のように解析グラフを生成します。

$ mkdir debug_s
$ cd debug_s
$ cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" ..
$ cov-build --dir cov-int cmake --build .

最後のcov-buildで、死ぬほど時間がかかりますが気長に待ちます。成功すれば、Compilation units (100%) are ready for analysysと表示され、cov-intディレクトリが生成されます。

このディレクトリをtarコマンドやzipコマンドで圧縮するか、Windowsの標準UIで圧縮します。
f:id:pit-ray:20210112182646j:plain

そうしたら、先ほどの Upload a Project Buiildページのファイル選択から圧縮したcov-int.zipを選択します。アップロードと解析にかなりの時間を必要とするはずなので、また待ちます。

解析が終わると、次のように解析結果を見ることができます。ライブラリも含んで41万行のバカでかいプロジェクトのように表示されています。

f:id:pit-ray:20210112183111j:plain

バッジを貼り付ける

ダッシュボードのProject Settingsを見ると、下部にバッジのHTMLがあるので、README.mdに貼り付けます。
f:id:pit-ray:20210112183554j:plain


以上で終わりです。参考になれば幸いです。