OpenVPNサーバコンテナを作成する

ブリッジ接続のDockerコンテナ環境にした際、従来からインストールされていたOpenVPN(tap)と競合し、Dockerサービスのリブート後にOpenVPNも再起動する必要が生じました。
また、OpenVPN導入手順の棚卸のため、OpenVPN環境をコンテナ内に移植することにしました。

構成

以下のような構成にしてみます。
従来、OpenVPNはブリッジ(tap)モードで動作していましたが、モバイルデバイス(iPhone/Android)からの接続も行いたいため、今回はルーティング(tun)モードで動作させることとします。

証明書の作成

OpenVPNの動作に必要な証明書などを作成します。

easyRSAのインストール

# yum install epel-release
# yum --enablerepo=epel install easy-rsa

esayRSA設定

# cd /usr/share/easy-rsa/2.0/
# cp -p vars vars.org
# vi vars

以下のように組織名等を記載します。

-------鍵の基本情報(デフォルトから変更せず)
export KEY_SIZE=2048     ※鍵の長さ2048bit
export CA_EXPIRE=3650     ※有効期限10年
export KEY_EXPIRE=3650

-------組織名等を変更
export KEY_COUNTRY="JP"
export KEY_PROVINCE="Tokyo"
export KEY_CITY="Tama-city"
export KEY_ORG="kizawa.info"
export
KEY_EMAIL="xxxxx@kizawa.info"
export KEY_OU="Development Studio"

設定変更を反映

# source vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /usr/share/easy-rsa/2.0/keys
# ./clean-all    ←⼀応実⾏

自己認証局証明書の作成

認証局の証明書、秘密鍵の作成を行う。

# ./build-ca

対話式に情報を求められますが、全てEnterで問題ありません。

サーバ証明書・鍵の作成

OpenVPNサーバの証明書、秘密鍵の作成を行う。

# ./build-key-server server

DH暗号パラメータの生成

DH(ディフィー・ヘルマン)暗号パラメータの作成を行う。

# ./build-dh

TLS認証鍵の作成

TLS認証鍵の生成にはOpenVPNが必要

# openvpn --genkey --secret /etc/openvpn/keys/ta.key

設定ファイルの準備

証明書の準備

作成した以下の証明書や鍵ファイルを同一ディレクトリにコピーしておきます。

ca.crt 認証局の証明書
server.key このOpenVPNサーバの秘密鍵
server.crt このOpenVPNサーバの証明書
dh2048.pem dh(ディフィー・ヘルマン)鍵 2048bit
ta.key TLS認証鍵

OpenVPN設定ファイル(openvpn.conf)

OpenVPNサーバの設定ファイルを以下のように準備します。

# 動作モード&ポート
proto       udp
dev         tun0
port        1194

# クライアント接続制御
server      192.168.101.0 255.255.255.0
ifconfig-pool-persist ipp.txt
max-clients 5
keepalive   10 120
comp-lzo
cipher      AES-256-CBC

# 鍵情報
ca          keys/ca.crt
key         keys/server.key
cert        keys/server.crt
dh          keys/dh2048.pem
tls-auth    keys/ta.key 0
persist-key
persist-tun

# クライアントへのpush情報
push "route 192.168.100.0 255.255.255.0"
push "dhcp-option DNS 192.168.100.2"
push "dhcp-option DOMAIN kizawa.info"

# クライアント同士の通信を許可する
client-to-client

# ログ出力
verb 3
status      openvpn-status.log
log         /var/log/openvpn.log
log-append  /var/log/openvpn.log

ログローテーション設定(openvpn-logrotate.conf)

ついでにログローテーションの設定も。

/var/log/openvpn.log {
        missingok
        rotate 53
        compress
        postrotate
            systemctl restart openvpn 2>&1 > /dev/null || true
        endscript
}

イメージ生成&コンテナ起動

Dockerfile

以下のような内容となりました。
なお、SSHログイン可能としたコンテナをベースとしています。

FROM            centos:sshdenable
MAINTAINER      Tomotaka Kizawa

# epel有効化
RUN     yum install -y epel-release && yum clean all

# インストール
RUN     yum --enablerepo=epel -y install openvpn logrotate cronie && yum clean all
RUN     yum update -y && yum clean all

# 設定ファイルの埋め込み
ADD     openvpn.conf /etc/openvpn/openvpn.conf

# 鍵ファイルの埋め込み
RUN     mkdir /etc/openvpn/keys
ADD     ca.crt /etc/openvpn/keys/
ADD     server.key /etc/openvpn/keys/
ADD     server.crt /etc/openvpn/keys/
ADD     dh2048.pem /etc/openvpn/keys/
ADD     ta.key /etc/openvpn/keys/
RUN     chmod 600 /etc/openvpn/keys/*.key

# 起動スクリプト変更
RUN     cp -p /usr/lib/systemd/system/openvpn@.service /usr/lib/systemd/system/openvpn.service
RUN     sed -ri '/ExecStart/i ExecStartPre=/bin/echo 1 > /proc/sys/net/ipv4/ip_forward' /usr/lib/systemd/system/openvpn.service
RUN     sed -ri '/ExecStart/a ExecStopPost=/bin/echo 0 > /proc/sys/net/ipv4/ip_forward' /usr/lib/systemd/system/openvpn.service
RUN     sed -ri '/PIDFile/s/%i.pid/openvpn.pid/g' /usr/lib/systemd/system/openvpn.service
RUN     sed -ri '/ExecStart/s/%i.pid/openvpn.pid/g' /usr/lib/systemd/system/openvpn.service
RUN     sed -ri '/ExecStart/s/%i.conf/openvpn.conf/g' /usr/lib/systemd/system/openvpn.service
RUN     systemctl enable openvpn

# logrotate設定
ADD     openvpn-logrotate.conf /etc/logrotate.d/openvpn
RUN     logrotate /etc/logrotate.conf
RUN     systemctl enable crond

ビルド

以下のコマンドでイメージを生成しました。
上記で準備した各種ファイルは同一ディレクトリに保存しておいてください。

# docker build -f ./openvpn.dockerfile -t centos:openvpn --no-cache=true .

コンテナ起動

生成したイメージからコンテナを起動します。

# docker run --privileged -d --name openvpn --hostname openvpn --net shared_nw --ip 192.168.100.3 --restart=always centos:openvpn /sbin/init

結果

上位ルータでのポートフォワード先、及びルーティング設定の追加を行い無事に移植が完了しました。
構築手順を棚卸する意味でも、Dockerfileを書いてみるのは良いことですね。

サーバ秘密鍵を埋め込むのか、ホストOSから見せるのか判断に悩んだのですが、可搬性(管理性)を高める意味から埋め込むことにしました。

またCRL鍵失効リストの管理ができていないことが課題になりますね。
別場所(ホストOSあるいは別のコンテナ)で生成されている失効リストを定期的に参照し、取り込むシェルを組み込む必要があるかと思います。

補足 2017/5

ホスト側のシステムアップデート後にopenvpnデーモンが起動しなくなりました。
詳細な原因は不明ですが、pidファイル設置場所が存在しなくなっていましたので/usr/lib/systemd/system/openvpn.serviceを以下の通り修正し-serviceを追加ことで解決しました。

[Unit]
Description=OpenVPN Robust And Highly Flexible Tunneling Application On %I
After=network.target

[Service]
PrivateTmp=true
Type=forking
PIDFile=/var/run/openvpn-server/openvpn.pid
ExecStartPre=/bin/echo 1 > /proc/sys/net/ipv4/ip_forward
ExecStopPost=/bin/echo 0 > /proc/sys/net/ipv4/ip_forward
ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn-server/openvpn.pid --cd /etc/openvpn/ --config openvpn.conf
ExecStopPost=/bin/echo 0 > /proc/sys/net/ipv4/ip_forward

[Install]
WantedBy=multi-user.target