BLOGTIMES
2012/05/11

OpenVPN の内蔵パケットフィルタを有効にする

  openvpn 
このエントリーをはてなブックマークに追加

OpenVPN で接続先を限定したかったので、パケットフィルタの設定方法を調べていたら内蔵のパケットフィルタ機能(pf.c)があることに気づいたので設定してみました。
Linux であればもちろん iptables を利用することも出来ますが、今回は OpenVPN に閉じた形にしたかったのでこちらの方法を選択しています。

参考にさせていただいたのは「OpenVPN’s built-in packet filter « \1」。
以下、劣化和訳手順です。

設定の手順

まず、パケットフィルタを有効にするためのプラグインをビルドして、所定の場所に配置します。
/path/to/openvpn_src の部分は openvpn-plugin.h が置いてあるディレクトリ ( openvpn のソースを展開したディレクトリ)に読み替えてください。

INCLUDE="-I/path/to/openvpn_src" CC_FLAGS="-O2 -Wall -g" gcc $CC_FLAGS -fPIC -c $INCLUDE minimal_pf.c && gcc $CC_FLAGS -fPIC -shared -Wl,-soname,minimal_pf.so -o minimal_pf.so minimal_pf.o -lc mkdir -p /etc/openvpn/plugin cp -a minimal_pf.so /etc/openvpn/plugin

次に、クライアントが接続してきたときにパケットフィルタの定義ファイルが読み込まれるように client-connect スクリプトを追加します。
もし既に client-connect スクリプトを使っている場合には、そのスクリプト内から下記のスクリプトが読み込まれるようにしてください。

/etc/openvpn/client-connect.sh

#!/bin/sh set -e DEFAULT_TEMPLATE=dafault.pf TEMPLATE_DIR=/etc/openvpn/pf TEMPLATE="$TEMPLATE_DIR/${common_name}.pf" # create the file OpenVPN wants with the rules for this client if [ -f "$TEMPLATE" ] && [ ! -z "$pf_file" ]; then cp -- "$TEMPLATE" "$pf_file" else cp -- "$TEMPLATE_DIR/$DEFAULT_TEMPLATE" "$pf_file" fi exit 0

今回は上記のようなスクリプトを用いました。ちょっと変なスクリプトになっていますが、OpenVPN の内蔵パケットフィルタは環境変数として $pf_file で渡されてくるファイルを読み込んでフィルタのルールを作成するので、$pf_fileに適用したいパケットフィルタ定義をコピーするようになっています。ちなみに、このスクリプトの終了ステータスが 0 以外の場合にクライアントは接続が拒否されます。これを利用して、本来はフィルタの定義が見つからない場合はクライアントを拒否するのがセキュアですが、今回はファイルが見つからない場合にはデフォルトのルールを適用するようにしてあります。

最後に openvpn の設定ファイルにプラグインの読み込みと、接続時の client-connect の呼出を追加します。
具体的には設定ファイルに下記の内容を追加します。

script-security 2 plugin /etc/openvpn/plugin/minimal_pf.so client-connect /etc/openvpn/client-connect.sh

パケットフィルタの定義ファイル

最後にパケットフィルタの定義ファイルを作成します。今回は /etc/openvpn/pf 以下に定義ファイルを置くことにしました。
ファイルの命名規則はdefault.pf 以外は (common_name).pf としています。

今回は下記のような2種類の定義を用意しました。
 ・デフォルトでは 192.168.1.*のネットワーク内の 192.168.1.8 にのみアクセスを許す。
 ・但し、someuser.example.com の場合は 192.168.1.* の全てのホストにアクセスを許す。

default.pf

[CLIENTS ACCEPT] [SUBNETS ACCEPT] +192.168.1.8 -192.168.1.0/24 -unknown [END]

someuser.example.com.pf

[CLIENTS ACCEPT] [SUBNETS ACCEPT] +192.168.1.0/24 +unknown [END]

少なくとも default.pf は作成する必要があるので、こちらを最低限作成して OpenVPN のデーモンをリスタートすればパケットフィルタが有効になります。

minimal_pf.c

パケットフィルタを有効にするためのプラグインについては「OpenVPN’s built-in packet filter « \1」で紹介されているものをそのまま流用させていただいています。

/* minimal_pf.c * ultra-minimal OpenVPN plugin to enable internal packet filter */ #include <stdio.h> #include <stdlib.h> #include "openvpn-plugin.h" /* dummy context, as we need no state */ struct plugin_context { int dummy; }; /* Initialization function */ OPENVPN_EXPORT openvpn_plugin_handle_t openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) { struct plugin_context *context; /* Allocate our context */ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context)); /* Which callbacks to intercept. */ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ENABLE_PF); return (openvpn_plugin_handle_t) context; } /* Worker function */ OPENVPN_EXPORT int openvpn_plugin_func_v2 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[], void *per_client_context, struct openvpn_plugin_string_list **return_list) { if (type == OPENVPN_PLUGIN_ENABLE_PF) { return OPENVPN_PLUGIN_FUNC_SUCCESS; } else { /* should not happen! */ return OPENVPN_PLUGIN_FUNC_ERROR; } } /* Cleanup function */ OPENVPN_EXPORT void openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) { struct plugin_context *context = (struct plugin_context *) handle; free (context); }

    トラックバックについて
    Trackback URL:
    お気軽にどうぞ。トラックバック前にポリシーをお読みください。[policy]
    このエントリへのTrackbackにはこのURLが必要です→https://blog.cles.jp/item/4941
    Trackbacks
    このエントリにトラックバックはありません
    Comments
    愛のあるツッコミをお気軽にどうぞ。[policy]
    古いエントリについてはコメント制御しているため、即時に反映されないことがあります。
    コメントはありません
    Comments Form

    コメントは承認後の表示となります。
    OpenIDでログインすると、即時に公開されます。

    OpenID を使ってログインすることができます。

    Identity URL: Yahoo! JAPAN IDでログイン