とりぴよ日記

最近あった出来事やプログラミング・ガジェット・インフラの話

openvpnを使ってaws環境にVPNサーバを比較的低コストで構築する方法

家のネットワークとAWS環境をVPNで繋ぎたくて、openvpnVPN環境を構築してみました。いろんなサイトを参考にしながら構築しているので、その手順をまとめたいと思います。中小企業であれば、専用のVPN機器を購入しなくてもこれで十分ではないかと思います。VPNにかかるコストはt2.nanoの稼働コストのみなので、比較的安い費用でvpnを構築できます。

f:id:ha107chan:20170723204801p:plain

上図の構成の通り、VPNサーバをAWS環境上に構築します。この構成を取ることで、

  • ユーザはVPNサーバを通してSSHやHTTP通信を行えるようになり、サービスを不必要に外部に公開しなくても良くなります。例えば、データベースサーバをMacなどから直接いじりたいときも、データベースサーバをインターネット環境に公開する必要はありません。
  • ユーザ個別に証明書と鍵を発行することでプロジェクトから抜けたユーザがAWS環境にアクセス出来ないように個別にアクセス制御が出来ます。


このVPNサーバは、1個のVPCに1個構築するイメージで構築しています。ですので、複数のVPCVPNサーバを利用したい場合は、VPNサーバを各VPCごとに構築する必要がありますのでお気をつけください。

VPN 環境の構築

VPNサーバの構築

VPNサーバ用のEC2インスタンスを起動

そんなに高いスペックでなくても、数人のユーザで利用するのであれば負荷はほとんどなく利用できます。
spec

  • AMI:amzn-ami-hvm-2017.03.1.20170623-x86_64-gp2, SSD Volume Type
  • Subnet: subnet1 (subnet1は、インターネットと通信できるようにInternet Gatewayがルーティングテーブルに設定されている必要がある。)
  • instance type: t2.nano
  • root volume: ebs

security groupを設定
以下のInbound設定をする

# vpn接続で利用するポート
TCP/1194
# vpnサーバにsshログインして作業するために開ける、作業が終わったら閉じても構わない
SSH/TCP/22

elastic IPを設定
VPNサーバのグローバルIPアドレスを固定するために、AWSのコンソールから設定する

日本時間に修正

clockファイルの修正

vi /etc/sysconfig/clock

# ZONE="UTC"
ZONE="Japan"
UTC=true

シンボリックリンクの修正

ln -sf /usr/share/zoneinfo/Japan /etc/localtime
OS内のネットワーク設定の修正

ip forwardingの有効化

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -w net.ipv4.ip_forward=1
cat /proc/sys/net/ipv4/ip_forward # 1となっていることを確認

ip masqueradeの有効化

echo "iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE" >> /etc/rc.local (ip masqueradeの起動時の有効化)
openvpnのインストー

パッケージのアップデート

yum update -y

openvpnパッケージのインストー

yum install -y openvpn

easy-rsaパッケージのインストー

yum install easy-rsa -y --enablerepo=epel
証明書の作成

CA証明書と鍵の作成

cd /usr/share/easy-rsa/2.0
vi vars

# 組織情報とあうように適切な値を入力する
export KEY_COUNTRY="JP"
export KEY_PROVINCE="Tokyo"
export KEY_CITY="Ota-ku"
export KEY_ORG="Cat Inc."
export KEY_EMAIL="admin@piyopiyo.com"
export KEY_OU="Development Unit"

. ./vars
./clean-all # "NOTE: If you run ./clean-all, I will be doing a rm -rf on /usr/share/easy-rsa/2.0/keys" message will be shown.
./build-ca # use default value

サーバ証明書と鍵の作成

./build-key-server server  # set "common name" => vpn host name

クライアント証明書と鍵の作成

  • プロンプトが表示されたときは、基本的にそのままEnterを押していけば大丈夫
  • 下記では、tamaというユーザ向けのクライアント証明書と鍵を作成する例
./build-key tama

Diffie Hellmanパラメータの生成

# 少し生成に時間がかかる
./build-dh
証明書失効のための設定
  • ダミー証明書を作成して失効する、一度実施しないと証明書失効リストファイルが作成されない
cd /usr/share/easy-rsa/2.0
. ./vars
./build-key dummy # use default value
./revoke-full dummy (keys/crl.pem ファイルが作成される)
openvpnサーバの設定

server.confファイルの編集
protoをudpにしているサイトが多いけれど、tcpに変更したほうが接続が安定したのでtcpにしている

sudo cp /usr/share/doc/openvpn-*/sample/sample-config-files/server.conf /etc/openvpn/

vi /etc/openvpn/server.conf

# 以下の設定で上書きする
port 1194
proto tcp
dev tun
ca /usr/share/easy-rsa/2.0/keys/ca.crt
cert /usr/share/easy-rsa/2.0/keys/server.crt
key /usr/share/easy-rsa/2.0/keys/server.key  # This file should be kept a secret
dh /usr/share/easy-rsa/2.0/keys/dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 10.1.0.0 255.255.0.0" #VPC内部のプライベートIPレンジが10.1.0.0/16の場合
keepalive 10 120
comp-lzo
max-clients 20
persist-key
persist-tun
status openvpn-status.log
log-append  /var/log/openvpn.log
verb 3
crl-verify /usr/share/easy-rsa/2.0/keys/crl.pem

openvpn自動起動の有効化

chkconfig openvpn on

openvpnサーバの起動

openvpn /etc/openvpn/server.conf

サーバ再起動

  • サーバを再起動してもopenvpnは正常に稼働するか確認するためにサーバを再起動
reboot

VPNクライアントの設定

VPNサーバを構築できたらVPNに繋げるようにクライアント側の設定をする

tunnelbrickのインストー

homebrewのcaskを利用してインストー

brew cask install tunnelblick
証明書と鍵を入手

(ユーザ名).key, (ユーザ名).crt, ca.crtの3ファイルが必要になるのでVPNサーバからscpなどで取得しておく

client.confファイルの作成
  • Macのhomeディレクトリの下に、.vpnというディレクトリを作成して、 (ユーザ名).key, (ユーザ名).crt, ca.crtの3ファイルを置く
  • .vpnディレクトリの配下などで、client.confファイルを作成する(ファイル名は好きな名前に変更しても大丈夫)
  • client.conf
client
dev tun
proto tcp
remote (vpnサーバのグローバルIPアドレス) 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca (ca.crtのファイルパス)
cert ((ユーザ名).crtのファイルパス)
key ((ユーザ名).keyのファイルパス)
remote-cert-tls server
comp-lzo
verb 3
tunnelbrickによる接続
  • tunnelbrickを起動する
  • client.confのファイルを画面右上のtunnelbrickのアイコンにドラッグアンドドロップする
  • 表示されてきたプロンプト画面でOnly Meをクリックする
  • Macのパスワード入力を求められるので入力する。これでVPNサーバとクライアントでコネクションを貼れるようになる。

VPNの運用

新しいクライアントの追加作業

新しくvpnを使うユーザが増えた場合は以下のように作業します。
生成された(新しいユーザ名) .crt, (新しいユーザ名) .key と ca.crtのファイルを新しいユーザに共有してあげてください。

cd /usr/share/easy-rsa/2.0
. ./vars
./build-key (新しいユーザ名) # use default value
* openvpnの再起動は不要

既存のクライアントの削除作業

退職したなどの理由で、特定のユーザがvpnを使えないようにするには以下のように作業します。

cd /usr/share/easy-rsa/2.0
. ./vars
./revoke-full (削除するユーザ 名) (keys/crl.pem ファイルが作成される)
* openvpnの再起動は不要

raspberry pi 3にclamavを導入する

最近、raspberry piのセキュリティが気になって色々と設定しているのですが、今回はclamavの設定を書きます。ドキュメントなど読むと、clamavには2種類の動かし方があるようです。

1. scanのときにプロセスを立ち上げる方法
2. デーモンを立ち上げておく方法

1の方法でファイルをscanするときは、clamscanコマンド。2の方法でファイルをscanするときは、clamdscanコマンドを利用します。この2種類のコマンドを比較すると、scanの速度に違いがありました。
(ちなみに、自分が見つけた中では公式のサイトのpdfドキュメントが一番詳しく記載されていました。https://github.com/vrtadmin/clamav-faq/raw/master/manual/clamdoc.pdf


raspberry pi 3上で732個のファイル(ファイルの合計の大きさは2.4GB)のscanを比較した場合

clamscan: 3min19sec
clamdscan: 2min9sec


デーモンを立ち上げていると、一定のメモリ量が常に消費されてしまいますが、scan時のパフォーマンスはデーモンモードのほうが良さそうです。一番避けたい設定はclamdをデーモンで立ち上げているのに、scanはclamscanで実行している場合です。こうすると、scan時にメモリを追加で消費することになってしまい、scanのスピードも遅くなります。
www.riscascape.net


clamavは、指定されたファイルをscanして、データベースに登録されたウイルス署名と一致したファイルがないかどうか確認します。ですので、ウイルス署名のデータベース情報は定期的に更新される必要があります。freshclamコマンドはインターネットら最新のウイルス署名情報を取得してくれます。freshclamにもデーモンモードがあり、デーモンで起動していると、定期的にウイルス署名情報を取得するようです。

以上を踏まえて、自分の場合は、以下のようにraspberry piclamavを設定しました。これで、webコンテンツ以下のファイルに対して毎日深夜の時間帯にスキャンをかけ、virusが検知されたらslackに通知されるようになります。

1. install clamav

apt-get update
apt-get install clamav clamav-daemon

2. update virus definition database

freshclam

3. create script file (hooks.slack.com のURLは適宜変更)

cd /root
vi virusscan.sh
------------------------------------------------
#!/bin/bash
PATH=/usr/bin:/bin

# virus scan
CLAMSCANTMP=`mktemp`
TIME=`date`
MESSAGE="Virus is detected!!: ${TIME}"
clamdscan /var/www/html/data > $CLAMSCANTMP 2>&1

# report slack
grep FOUND$ $CLAMSCANTMP && \
curl -X POST --data-urlencode 'payload={"channel": "#alerts", "username": "alerts", "text": "'"${MESSAGE}"'", "icon_emoji": ":dog:"}' https://hooks.slack.com/services/XXXXXXXX/XXXXXXXX/XXXXXXXX
rm -f $CLAMSCANTMP
------------------------------------------------
chmod 700 virusscan.sh

4. set cron job

crontab -e
------------------------------------------------
00 04 * * * sh /root/virusscan.sh
------------------------------------------------


ドキュメントを読むと、以下のコマンドがあったのでメモも兼ねて記載しておきます。

clamconf

  • clamavの設定情報を表示する
root@raspberrypi:~# clamconf | head
Checking configuration files in /etc/clamav

Config file: clamd.conf
-----------------------
LogFile = "/var/log/clamav/clamav.log"
StatsHostID = "auto"
StatsEnabled disabled
StatsPEDisabled = "yes"
StatsTimeout = "10"
LogFileUnlock disabled

clamtop

  ClamdTOP version 0.99.2   Mon Jul 17 23:41:19 2017
NO CONNTIME LIV IDL QUEUE  MAXQ   MEM HOST           ENGINE DBVER DBTIME
 1 00:00:14   1   0     0     0  471M local          0.99.2 23571 2017-07-17 17h
Details for Clamd version:  ClamAV 0.99.2/23571/Mon Jul 17 17:08:59 2017
Primary threads: live   1 idle   0 max  12                                                                                                  ┌───────────────────────────────────────┐
 [|||                                 ]                                                                                                     │Mem:  heap    1M mmap    0M unused   0M│
Queue:     0 items      0 max                                                                                                               │Libc: used    1M free    0M total    1M│
 [                                    ]                                                                                                     │Pool: count    1 used  470M total  470M│
                                                                                                                                            │[||||||||||||||||||||||||||||||||||||] │
                                                                                                                                            └───────────────────────────────────────

dockerコンテナのrailsコンソールで日本語入力をコンテナ再ビルドなしで出来るようにする方法

日本語がdockerコンテナのrailsコンソール上で入力できないので調べてみると、
以下のようにbuildし直すと出来るようになると記事がありました。
http://qiita.com/suin/items/856bf782d0d295352e51

ただ、再ビルドするのも面倒なので、
以下のようにrailsコンソールの実行時にenvで環境変数を指定することで日本語入力出来るようになりました。

root@b2de32d57cf1:/app# env LANG=C.UTF-8 rails c
Running via Spring preloader in process 104
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> a = "日本語が入力できる!!"
=> "日本語が入力できる!!"

raspberry pi 3のリソース利用状態をmonitorixでグラフ表示する

rasbperry pi 3 のリソース利用状態の推移を監視したかったので、monitorixというツールを入れました。

f:id:ha107chan:20170622231339p:plain

インストール方法 (rootユーザで実行)

1. パッケージのインデックス情報を更新する

apt-get update


2. monitorixに必要な関連パッケージをインストールする

apt-get install rrdtool perl libwww-perl \
libmailtools-perl libmime-lite-perl librrds-perl \
libdbi-perl libxml-simple-perl libhttp-server-simple-perl \
libconfig-general-perl libio-socket-ssl-perl

3. monitorixのdeb パッケージをインストールする

cd /usr/local/src
wget http://www.monitorix.org/monitorix_3.9.0-izzy1_all.deb
openssl sha1 monitorix*.deb # compare sha1 value with http://www.monitorix.org/downloads.html
dpkg -i monitorix*.deb
apt-get -f install # fix broken dependency if exists

4. browserから raspberry pi 3のサーバにアクセスする
http://(raspberry pi 3のipアドレス):8080/index.html


minitorixウェブページのポート番号を変更する方法
僕の動かしているraspberry pi 3ではすでに別のプロセスが8080ポートを利用しているので、ポート番号を8080 => 8081に変更しました。

1. monitorix.confの設定ファイルの変更をする

vi /etc/monitorix/monitorix.conf

----------------------------------------------------------------------------------------------
@@ -30 +30 @@
-       port = 8080
+       port = 8081
----------------------------------------------------------------------------------------------

2. monitorixを再起動

systemctl restart monitorix

中国やロシアなど特定の国からのアクセスを拒否するiptablesの設定方法

中国やロシアからのアクセスを弾く設定を、
ファイアウォール構築(iptables) - CentOSで自宅サーバー構築
を参考にして自宅のraspberry pi 3 に設定してみました。

1. country_iptables.shとupdate_country_iptables.sh をgistから取得する
block ip address by country unit · GitHub

2. ファイルにrootユーザでの実行権限を付与する
chmod 755 country_iptables.sh update_country_iptables.sh

3. country_iptables.shをrootユーザで実行する

sh country_iptables.sh
  • 問題なく実行されると、iptables -nL でiptablesのruleがたくさん設定されていることが確認出来ると思います。
  • country_iptables.sh は、http://nami.jp/ipv4bycc/cidr.txt.gzを利用して、日本のグローバルIPアドレスからのアクセスを許可して、悪いことをすることが多い国からのアクセスを拒否します。
  • raspberry pi 3向けに、最期にiptablesの設定読み込みと設定ファイルの更新を行っています。この最期の処理はCentOSなどではおそらくうまく動きません。

4. cron jobを設定してupdate_country_iptables.shを定期的に実行するようにする

00 03 * * * sh /home/pi/script/update_country_iptables.sh
  • 国ごとのグローバルIPアドレスは定期的に変わるようなので、http://nami.jp/ipv4bycc/cidr.txt.gz の更新をチェックしてiptablesの設定を修正しています。ただ、差分が大きいと自動実行は怖いので50行以上の差分の場合は自動更新はされないようになっています
  • IPTABLES_SCRIPTにはcountry_iptables.shファイルのパスを指定
  • WEBHOOK_URLには通知したいslack channelのurlを設定
  • このスクリプトを毎日実行していると、http://nami.jp/ipv4bycc/cidr.txt.gz に悪意あるコンテンツが埋め込まれたときに勝手に実行されてしまうので、クリティカルなシステムの場合はcron jobに登録せずに手動での実行のほうがいいかもしれないです

sshログインされたことをslackに通知する方法

外出先からssh接続出来るようにraspberrypiのsshポートを公開するようにしました。
念のため、不正ログインをすぐに検知出来るように、sshログインされるとslackのalerts channelに通知される設定をいれました。

1. alertsというchannelを作成する

2. Incoming WebHooksの設定をする

  • いろんなサイトに設定方法はあるので細かいやりかたは省略
  • Incoming WebHooksの設定では、customize nameにalertsを指定している

3. /etc/ssh/sshrcファイルを配置する

  • WEBHOOK_URLには正しいwebhookのurlを記載する

gist.github.com

4. sshrcのパーミッションを変更する
chmod 755 /etc/ssh/sshrc

これで、sshログインするとslackのalerts channel に通知されるようになります。


参考
http://qiita.com/futoase/items/3e57b10adbff04290342

Raspberry Pi 3が自宅ネットワーク内の他デバイスと通信出来ないようにiptableを設定

外部に公開しているraspberry piを踏み台にして、自宅ネットワーク内のデバイスが攻撃を受けないように、iptablesに以下設定をいれました。

実行順序(もちろん、rootユーザで)
1. iptablesの設定を追加(もしかしたら、この前にiptables関連のパッケージインストールが必要だったかも。。。)

# 自宅のホームネットワークのIP
localnetwork='192.168.0.0/24'
router='192.168.0.1'
pi='192.168.0.12'

# raspberry pi とルーターは通信できるようにする
/sbin/iptables -A OUTPUT -s $pi -d $router -j ACCEPT

# 自宅ネットワークの機器から、raspberry piに対してsshは出来るようにしておく
/sbin/iptables -A OUTPUT -p tcp -s $pi --sport 22 -d $localnetwork -j ACCEPT

# 自宅ネットワークの機器から、raspberry pi上の80port, 81portで動いているプロセスに接続出来るようにしておく
/sbin/iptables -A OUTPUT -p tcp -s $pi --sport 80 -d $localnetwork -j ACCEPT
/sbin/iptables -A OUTPUT -p tcp -s $pi --sport 81 -d $localnetwork -j ACCEPT

# raspberry pi から自宅ネットワークの機器へ許可していない通信をしようとするとログが出力されるようにする
/sbin/iptables -N LOGGING
/sbin/iptables -A LOGGING -j LOG --log-level warning --log-prefix "DROP:" -m limit
/sbin/iptables -A OUTPUT -s $pi -d $localnetwork -j LOGGING

# 上記のssh, http以外で、自宅ネットワークの機器に通信しようとするとパケットがドロップされるようにしておく
/sbin/iptables -A OUTPUT -s $pi -d $localnetwork -j DROP

2. 上記のiptablesの設定をサーバが再起動されても反映されるように、/etc/iptables/rules.v4 ファイルに書き出す

iptables-save >> /etc/iptables/rules.v4

3. iptablesによってドロップされたパケットのログが/var/log/messagesではなくて、/var/log/iptables.logに出力されるように修正

  • 1行目は、DROP: というプレフィックスであれば、/var/log/iptables.logに書き出す意味
  • 2行目は、/var/log/messagesへの二重の書き出しを防ぐ
vi /etc/rsyslog.d/iptables.conf
--------------------------------
:msg, contains, "DROP:" -/var/log/iptables.log
& ~
--------------------------------

rsyslogデーモンをrestart

service rsyslog restart

4. logが出力されるか確認

# 適当な自宅ネットワークのipにping
ping 192.168.0.5
# ログが出力されたか見る
cat /var/log/iptables.log


参考にしたページ
iptablesテンプレート集 改訂版(1):スタティックなパケットフィルタリング (3/4) - @IT
Change the IPTables log file « Networking How To's