« (back-to-indentation) | Main | GNU id-utils for Windowsはいずこに? »

2005年11月18日

OpenVPN

[ カテゴリ: Technology ]

最近、いろいろtipsをMTに書いてないと云って怒られ気味。 で、なんとOpenVPNについても書いてないことが発覚したので、メモ代わりに。

OpenVPNは、 オープンソースのVPNの実装です。 プロトコルが独自仕様なところがいたいですが、 動作プラットフォームは各種UNIXからMacOSX、Windowsまでをサポートしており、 L2 VPNおよびL3 VPNが可能です。 L2 VPNといえば昨年S**tE***rが一世風靡しましたが、 あれを購入するくらいならOpenVPNのほうがよっぽどいいでしょう。

で、実はOpenVPNは難しいことはなにもありません。 証明書発行してサーバとクライアント双方に仕込めばおしまい。 でもこれじゃつまんないのでいくつかtipsを…

CRLをどうするか

ApacheでHTTPSすると困るのがCRLですが、OpenVPNの場合はどうでしょう? というわけでドキュメントを見てみると次のような記述があります。

When the crl-verify option is used in OpenVPN, the CRL file will be re-read any time a new client connects or an existing client renegotiates the SSL/TLS connection (by default once per hour). This means that you can update the CRL file while the OpenVPN server daemon is running, and have the new CRL take effect immediately for newly connecting clients.

ううむ、偉すぎる。 アクセス頻度が違うのでApacheがこれをやるとえぐいんですけどね。 というわけで、cronかなにかで更新するようにすればおしまい。

CNでフィルタしたい!

そもそも、基本的に証明書ベースの認証は、 証明書を持ってればだれでもおっけーになりがちです。 もちろん、EKUつかってごにょごにょしたりできますが、 現実的にはこのような方法を使っているケースは稀でしょう。 というわけで、CNベースでサービスをフィルタしたいという要望が出てきます。

OpenVPNの場合は、設定ファイルにtls-verifyというのがあります。 以下は、openvpn(8)から抜粋。

Execute shell command cmd to verify the X509 name of a pending TLS connection that has otherwise passed all other tests of certification. cmd should return 0 to allow the TLS handshake to proceed, or 1 to fail.

これをつかって、たとえば以下のようなやっつけスクリプトを書いておきます。 ここではverify-cnとしましょう。

#!/bin/sh
RUBY=/usr/pkg/bin/ruby18

DENY=1
if [ ! -x "${RUBY}" ]; then
  exit ${DENY}
fi

exec ${RUBY} -S -x $0 "$@"
#! ruby

ALLOW, DENY = 0, 1

class VerifyCN
  def initialize(allowlist)
    read_allowlist(allowlist)
  end

  public  ;#

  def verify(depth, x509_name_oneline)
    return ALLOW if depth.to_i != 0
    return DENY \
      unless x509_name_oneline =~ /\/CN=([^\/]+)/
    return DENY \
      if @allowlist_esc.find{|cn_esc| cn_esc == $1 }.nil?
    return ALLOW
  end

  private ;#

  def read_allowlist(allowlist)
    @allowlist = IO.readlines(allowlist)
    @allowlist_esc = @allowlist.collect{|cn| cn.chomp.gsub(/ /, "_") }
  end
end

allowlist = ARGV.shift
verifier = VerifyCN.new(allowlist) \
  rescue exit(DENY)

exit(verifier.verify(*ARGV)) \
  rescue exit(DENY)

で、設定ファイルには、

tls-verify "/path/to/verify-cn /path/to/allowlist"

ここで指定しているallowlistというファイルには、 接続を許可するクライアント証明書のCNを、 1行にひとつずつ記述しておきます。

という具合で、結構融通が利くOpenVPN、 L2モードでもクライアント間通信を制限することもできるし、 TCPでもUDPでも使えます。 TCPの場合はTCP over TCP問題がありますが、 HTTP Proxyを通したりもできるらしい(未検証)。 というわけで、みなさんもいかがですか?

Comments

Post a comment




Remember Me?