電子の密林を開拓する

AmazonLinux で メールサーバーを作る(その4)

今回は MySQL を設定します。
MySQL(含むserver)のインストールは 第一回 で完了している前提です。




・デーモン起動の設定


Apacheと同じく mysqld も自動起動設定されていないようです。
以下のようにして自動起動設定を行います。
$ chkconfig | grep mysql
mysqld          0:off   1:off   2:off   3:off   4:off   5:off   6:off

$ sudo chkconfig mysqld on

$ chkconfig --list mysqld
mysqld          0:off   1:off   2:on    3:on    4:on    5:on    6:off
chkconfig で設定しても、次回のOS起動時に自動起動設定されるだけなので、今すぐmysqlを起動させたいのであれば サーバーOSを再起動する必要があります。あるいは mysqld 自体を手動起動しても良いです。
ここでは手動で起動させます。
$ sudo service mysqld status
mysqld is stopped

$ sudo service mysqld start
Initializing MySQL database: Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h ip-10-150-161-211 password 'new-password'

Alternatively you can run:
/usr/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default. This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/bin/mysqlbug script!

[ OK ]
Starting mysqld: [ OK ]
$
起動は成功するようですが、初期設定とパスワード設定を行え…と警告されていますね。

「support-files/mysql.server を所定の位置にコピーせよ」…というのは、どうやら /etc/init.d/mysql のバッチのことらしいです。これは service コマンド経由で呼び出すものと等価なハズで…起動は成功しているということは コピーそのものは(yumコマンドが?)完了しているということみたいです。

…ということは、残りのパスワード設定(と、不要なテーブルやアカウントの削除)を実施する必要があるようですが…。恐らく、 /usr/bin/mysql_secure_installation を実行すれば代行してくれるものと思われます。
では、さっそく実行してみましょう。

$ sudo /usr/bin/mysql_secure_installation




NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!


In order to log into MySQL to secure it, we'll need the current
password for the root user. If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.

Set root password? [Y/n] Y
New password:
Re-enter new password:

Password updated successfully!
Reloading privilege tables..
... Success!


By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] Y
... Success!

Normally, root should only be allowed to connect from 'localhost'. This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n]
Y
... Success!

By default, MySQL comes with a database named 'test' that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] Y
- Dropping test database...
... Success!
- Removing privileges on test database...
... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] Y
... Success!

Cleaning up...



All done! If you've completed all of the above steps, your MySQL
installation should now be secure.

Thanks for using MySQL!


$
いくつか質問されますが、全部 Yes ですね…。root パスワードは、インストール直後はカラッポらしいので、適切なものを再設定させます。
念の為、この状態で mysql をstop -> start させてみます。
$ sudo service mysqld stop
Stopping mysqld: [ OK ]

$ sudo service mysqld start
Starting mysqld: [ OK ]
特に警告は表示されないのでOKでしょう。




・DBアカウントの作成

次に、DB利用者(PHPプログラマと、そのプログラム)に開放するアカウントとDB領域を作成します。
作成するのはMLサービスらしいので、アカウント名、DB領域名ともに mlservice としておきます。
mysql> create database mlservice;
Query OK, 1 row affected (0.00 sec)

mysql> grant all privileges on mlservice.*
to mlservice@localhost
identified by 'パスワード';
DB接続用アカウントは、権限別に「運用/管理用」「WEB用」「crontabバッチ用」と分離したいところですが、初心者向けのものなので1つだけにしておきます。実サービスで使われるDBではないので…(^^;

アカウント設定が出来たら、利用可能になっているかどうか確認しておきます。
$ mysql -u mlservice -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.31 MySQL Community Server (GPL)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mlservice |
+--------------------+
2 rows in set (0.00 sec)

mysql> use mlservice
Database changed

mysql> show tables;
Empty set (0.00 sec)

mysql> create table test ( x INT );
Query OK, 0 rows affected (0.07 sec)

mysql> show tables;
+---------------------+
| Tables_in_mlservice |
+---------------------+
| test |
+---------------------+
1 row in set (0.00 sec)

mysql> drop table test;
Query OK, 0 rows affected (0.02 sec)

mysql> exit
Bye
とりあえず動作はするので、これでOKと言うことにしておきます。



・PHPからのMySQL接続
MySQLはPHPから利用されることが分かっているので、Apache mod_php と php-cli の両方から接続可能になっているかどうか、確認しておきます。

Apache の document root に以下のようなファイルを作成します。ここでは pdo.php とか名前を付けておきます。
<?
$DBhost = 'localhost';
$DBregion = 'mlservice';
$DBuser = 'mlservice';
$DBpassword = 'パスワード';

$pdo = new PDO( "mysql:host=${DBhost};dbname=${DBregion}", $DBuser, $DBpassword );

$statement = $pdo->query( "show grants' );
$rows = $statement->fetchAll( PDO::FETCH_ASSOC );

var_dump( $rows );

// END of FILE //
作成したら、php-cli で実行してみます。しかし「PHP Fatal error:  Class 'PDO' not found in /var/www/~/pdo.php on line 7」とかエラーが出て、動作しません。なぜだ…(x_x;

「もしかして、何か足りない? 」と思いつつ yum list installed | grep -i pdo を実行してみると、やはりそれらしいモジュールはありません。きっと、pdo と名前が付くものがあるのでしょう。
yum search all php mysql pdo として検索してみると、以下のようなものが見つかります。
php-pdo.i686 : A database access abstraction module for PHP applications
php-mysql.i686 : A module for PHP applications that use MySQL databases
php-mysqlnd.i686 : A module for PHP applications that use MySQL databases
php-mysql と php-mysqlnd と、どちらも DB接続ドライバーで nd の方が NativeDriver という名前で より高速なものらしいので、ソチラを使うことにします。
sudo yum install php-pdo.i686 php-mysqlnd.i686 としてインストールします。

インストール後に 先ほどの pdo.php を再実行すると…。
$ php -f pdo.php
array(2) {
[0]=>
array(1) {
["Grants for mlservice@localhost"]=>
string(112) "GRANT USAGE ON *.* TO 'mlservice'@'localhost' IDENTIFIED BY PASSWORD 'パスワードハッシュ'"
}
[1]=>
array(1) {
["Grants for mlservice@localhost"]=>
string(64) "GRANT ALL PRIVILEGES ON `mlservice`.* TO 'mlservice'@'localhost'"
}
}
それらしい結果が表示されました。

続けて Apache mod_php 側で動作するかどうか確認してみると…。
$curl 'http://~.no-ip.biz/pdo.php'
Fatal error: Class 'PDO' not found in /var/www/~/pdo.php on line 7
先ほどと同じエラーが発生しています。なぜ???
「ああ」と思い直して、Apache を再起動して再度挑戦すると、ウマク表示されました。
$ sudo apachectl -k restart

$ curl 'http://~.no-ip.biz/pdo.php'
array(2) {
[0]=>
array(1) {
["Grants for mlservice@localhost"]=>
string(112) "GRANT USAGE ON *.* TO 'mlservice'@'localhost' IDENTIFIED BY PASSWORD 'パスワードハッシュ'"
}
[1]=>
array(1) {
["Grants for mlservice@localhost"]=>
string(64) "GRANT ALL PRIVILEGES ON `mlservice`.* TO 'mlservice'@'localhost'"
}
}
yum コマンドは、起動中のデーモンに依存関係のあるモジュールをインストールしても、そのデーモンを再起動したりはしてくれないのですね…(^^;


PDO を介して MySQL にもアクセスできるようになったので、これで php->MySQL の接続設定は完了です。



AmazonLinux で メールサーバーを作る(その3)

今回は Postfix を設定し、virtualhost としてメールを受信するようにします。


・Postfix の設定
Postifx自身は既に yum でインストール済みなので、その設定ファイルを確認します。
/etc/postfix/main.cf ですね…。

Postfix が待ち受けするIPアドレス、あるいは名前を決定する必要があるのですが…。
以下のような理由で、
  • DynamicDNSを使用しており、今後 DDNSサービスを乗り換えるかもしれない(のでドメインを固定したくない)
  • virtualdomain に対応させるつもりなので、複数ドメインに対応させたい(かもしれない)
  • EIPを付け替える可能性があるので、IPアドレスでの指定は困難
…というわけで、IPアドレスや名前を特定せず、/etc/postfix/main.cf には以下のように設定します。
inet_protocols = all
※ここでIPアドレス(あるいは名前を指定しておかないと、指定されていないIPアドレスでPostfixが待ち受けしなくなります。外部から接続できなくなったりして、とても困るので忘れないように設定してください。

※mydestination に virtualdomain である「~.no-ip.biz」を追記しておく必要がある…のかと思いましたが、特に記述しておかなくても virtualdomain 宛のメイルが受信できるようですね。後述する virtual ファイルの設定を見て、受け入れ可能ドメインを決定しているのかと思われます。


他には……メールを受信するハズのLinuxアカウント側で .forward ファイルによる maildir とコマンド制御が使いたいので、以下のように/etc/postfix/main.cf へ追記します。
#allow_mail_to...
allow_mail_to_commands = alias,forward,include
allow_mail_to_files = alias,forward,include

ついでに、セキュリティ設定として、SMTPのVRFYコマンド等を禁止しておきます。
#for security
disable_vrfy_command = yes
これで、VRFY コマンドによる「指定されたメールアドレス(の受信者)が存在するか」という問い合わせが出来なくなります。



・virtualhost の設定
PHPプログラムで作成したいのはMLサービスで、任意のアカウントをPHP側で利用したいらしいです。
つまり、今回のEC2インスタンス割り当てられる ~.no-ip.biz というドメイン宛のメールは、特定のLinuxアカウント上で用意されるPHPプログラムで受信したいということになります。

postfix の virtualhost 設定を利用して、*@~.no-ip.biz 宛のメールを すべて 1つのlinux アカウント(今回は dev-user)に配送するように設定します。
/etc/postfix/main.cf の末尾あたりに、以下のように追記します。
#virtualdomians
virtual_maps = hash:/etc/postfix/virtual
追記出来たら、次に /etc/postfix/virtual というファイルを作成し、以下のような記述を追加します。
~.no-ip.biz          sub-domain
@~.no-ip.biz dev-user
一行目で ~.no-ip.biz が virtualdomain として扱われることを宣言し、二行目で ~.no-ip.biz ドメイン宛のすべてのメールを dev-user (linuxアカウント)に配送することを指定します。ファイルを作成したら、postmapコマンドを利用して virtualdomain 設定を postfix が読める形式に変換しておきます。
sudo postmap virtual
これを忘れると Postfix がSMTPで操作できなくなって意味不明になるので注意してください。

postmap を実行したら、Postfix を restart するか reload  しておきます。
$ sudo service postfix reload
Reloading postfix: [ OK ]


・セキュリティの確認
念の為、代表的なセキュリティ問題である「オープンリレイ」状態になっていないかどうか確認しておきます。
実験は、EC2インスタンスではなく、外部のサーバから実施してください。
outerPC$ telnet ~.no-ip.biz 25
Trying **.**.**.**...
Connected to ~.no-ip.biz.
Escape character is '^]'.
220 ip-**-**-**-**.localdomain ESMTP Postfix
HELO localhost.localdomain
250 ip-**-**-**-**.localdomain
RCPT TO: abc@example.com
503 5.5.1 Error: need MAIL command
MAIL FROM: abc@example.com
250 2.1.0 Ok
RCPT TO: abc@example.com
554 5.7.1 <abc@example.com>: Relay access denied

VRFY abc@example.com
502 5.5.1 VRFY command is disabled
少なくともオープンリレイ状態にはなっておらず、VRFYコマンドも拒否されているので、OKと言うことにしておきます。



・受信側の設定
..forward ファイルを用意して、すべてのメールを maildir 形式で保存するようにします。
ex2-user$ sudo su - dev-user

dev-user$ mkdir ~/maildir
dev-user$ cd =/maildir
dev-user$ mkdir new cur tmp
dev-user$ chmod -R u=rwx,og= .

dev-user$ cd ~/
dev-user$ echo '~/maildir/' > .forward
dev-user$ chmod og= .forward

dev-user$ exit
これで、dev-user 宛に送られたメイル(のすべて?)が maildir に保存されるようになります。


・実験
mail送信しようとしましたが…いつも使っているmailコマンドがインストールされてしません。
yum でインストールします。
sudo yum install mailx
パッケージ名末尾の x って何だろう…とか思いますが、あまり気にしないことにします。


インストールできたら、送信実験をします。
echo 'TEST!' | mail -s TEST-TITLE abcdefg@~.no-ip.biz
まず /var/log/maillog を確認してみます。
Aug  6 19:08:13 ip-10-150-161-211 postfix/pickup[1811]: 3A10C2099E: uid=0 from=<root>
Aug 6 19:08:13 ip-10-150-161-211 postfix/cleanup[1988]: 3A10C2099E: message-id=<20130806100813.3A10C2099E@ip-10-150-161-211.localdomain>
Aug 6 19:08:13 ip-10-150-161-211 postfix/qmgr[1812]: 3A10C2099E: from=<root@ip-10-150-161-211.localdomain>, size=486, nrcpt=1 (queue active)
Aug 6 19:08:13 ip-10-150-161-211 postfix/local[1990]: 3A10C2099E: to=<dev-user@ip-10-150-161-211.localdomain>, orig_to=<abcdefg@~.no-ip.biz>, relay=local, delay=0.07, delays=0.05/0.01/0/0.01, dsn=2.0.0, status=sent (delivered to maildir)
Aug 6 19:08:13 ip-10-150-161-211 postfix/qmgr[1812]: 3A10C2099E: removed
キチンと配送されていますね。dev-user 側のmaildir ディレクトリも確認してみましょう。
dev-user$ cd maildir/new/
dev-user$ ls -la
total 16
drwx------ 2 dev-user dev-user 4096 Aug 6 19:08 .
drwx------ 5 dev-user dev-user 4096 Aug 6 18:55 ..
-rw------- 1 dev-user dev-user 622 Aug 6 19:08 1375783693.Vca01I2099dM273460.ip-10-150-161-211
こちらにもキチンとファイルが来ていることが分かります。

次に dev-user の ~/.forward を書き換えて プログラムに渡すようにしてみます。

dev-user$ cat .forward
| "cat - > /home/dev-user/mail.txt"
この状態で *@~.no-ip.biz にメールを送ると dev-user のホームディレクトリに mail.txt というファイルが作成され、そこにメールの中身が書き出されます。




Postfix の設定は以上で完了の予定。
必要であれば、もっとセキュリティをギチギチに固めてください。
どんなセキュリティ設定があるのか分かりませんが…。











AmazonLinux で メールサーバーを作る(その2)


・Apache mod_php の設定
mod_phpの設定を行う…ツモリでしたが、何も設定しなくても *.php が動作する上に phpinfo() がそれらしい値を返しています。結果を見る限り、動作に問題は無いようなので、apache mod_php の初期設定そのものは完了している…ということにしておきます。

細かい php.ini の設定は、以下のよう変更しておきます。
display_errors = Off → On
    ※(PHP入門者の)開発用サーバであるため、エラーが表示された方が都合が良い。

max_memory_limit = 128M → 8M
※特にPHPフレームワークを使わないので、少ないメモリ量でOK。


・サーバのタイムゾーンの変更
phpinfo() の出力を確認している時、Apacheのエラーログに以下のような警告が表示されていました。
PHP Warning:  phpinfo(): 
It is not safe to rely on the system's timezone settings.
You are *required* to use the date.timezone setting
or the date_default_timezone_set() function.
In case you used any of those methods and you are still getting this warning,
you most likely misspelled the timezone identifier.
We selected 'UTC' for 'UTC/0.0/no DST' instead in ~
たぶん、時間設定なのかなーと思い、サーバのローカルタイムを変更します。

AmazonLinux では(CentOSと同じように?) /etc/localtime というファイルにローカルのタイムゾーンが設定されているようなので、これを Asia/Tokyo の設定と置換します。
$ cat /etc/localtime.org
TZif2UTCTZif2UTC
UTC0

$ date
Fri Aug  2 08:53:38 UTC 2013

$ sudo cp /usr/share/zoneinfo/Japan localtime
$ date
Fri Aug  2 17:53:53 JST 2013
そりらしく置き換えられたようです。ただ、アプリケーション起動時にしかこの設定はチェックされないようなので、デーモン類にはローカルタイムが反映されていないことになります。念のため、OS全体を再起動しておいた方が良いと思われます。


しかし、それでも警告は消えませんね…。Apacheのログに出力される日時が期待される日本時間になったぐらいしか効果が無いです。
どうやらphp側の問題みたいです。

/etc/php.ini を編集して以下のように設定します。
[Date]
date.timezone = Asia/Tokyo
本当は (Asia/Tokyoではなく) GMT とか UTC と設定したいところですが、PHP初心者(日本人)向けですので、このように設定しておきます。

sudo apachectl -k restart 実行後に、再度 phpingo() のページへアクセスすると、警告は出なくなりました。



・Apacheがせ自動起動しない?
先ほどの php.ini 設定の過程で、EC2インスタンスを再起動すると、なぜかApacheが停止しています。
Apacheの自動起動設定が無いのでしょうか…。

CentOS 系では chkconfig というコマンドで自動起動の確認と設定が出来るらしいので、それを使ってみます。
$ sudo chkconfig
acpid 0:off 1:off 2:on 3:on 4:on 5:on 6:off
atd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
auditd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
blk-availability 0:off 1:on 2:on 3:on 4:on 5:on 6:off
cloud-init 0:off 1:off 2:on 3:on 4:on 5:on 6:off
cloud-init-user-scripts 0:off 1:off 2:on 3:on 4:on 5:on 6:off
crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off
htcacheclean 0:off 1:off 2:off 3:off 4:off 5:off 6:off
httpd 0:off 1:off 2:off 3:off 4:off 5:off 6:off
ip6tables 0:off 1:off 2:on 3:on 4:on 5:on 6:off
iptables 0:off 1:off 2:on 3:on 4:on 5:on 6:off
irqbalance 0:off 1:off 2:off 3:on 4:on 5:on 6:off
lvm2-monitor 0:off 1:on 2:on 3:on 4:on 5:on 6:off
mdmonitor 0:off 1:off 2:on 3:on 4:on 5:on 6:off
messagebus 0:off 1:off 2:on 3:on 4:on 5:on 6:off
netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off
netfs 0:off 1:off 2:off 3:on 4:on 5:on 6:off
network 0:off 1:off 2:on 3:on 4:on 5:on 6:off
ntpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
ntpdate 0:off 1:off 2:on 3:on 4:on 5:on 6:off
psacct 0:off 1:off 2:off 3:off 4:off 5:off 6:off
racoon 0:off 1:off 2:off 3:off 4:off 5:off 6:off
rdisc 0:off 1:off 2:off 3:off 4:off 5:off 6:off
rsyslog 0:off 1:off 2:on 3:on 4:on 5:on 6:off
saslauthd 0:off 1:off 2:off 3:off 4:off 5:off 6:off
sendmail 0:off 1:off 2:on 3:on 4:on 5:on 6:off
sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
udev-post 0:off 1:on 2:on 3:on 4:on 5:on 6:off
なぜか全部OFFになってますね。自動起動するように設定します。
$ sudo chkconfig httpd on
$ sudo chkconfig --list httpd
httpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
自動起動がONになったようです。
これで、EC2インスタンスを再起動しても、きちんとApacheが動作しています。



ファイルアップロードなどの設定を見直していないような気もしますが、とりあえずはコレでApache mod_php の設定はOKということにしておきます。




次回は Postfix の設定をします。

このページのトップヘ