Webサーバーを立てて欲しいというリクエストがあった。
極端に複雑なことはしないようなので、最低限のリソースでサーバーを立ち上げることにした。
どんな環境を作るか
リクエスト
こんな話だった。
- Webサーバーを立てたい。
- コンテンツはローカルのPCで作成し、FTPでアップロードしたい。
- このサーバーはインターネットに公開するつもり。
しまったなーと思っているのは、CGI的なものを使うかどうか聞いていなかったこと。
まぁ、後からでもどうにかなるだろう。
レスポンス
OSの指定はなかったので、Ubuntu server 22.04 LTSを選択。軽くていいんじゃないかなと。
FTPでコンテンツをアップロードするということは、日頃、このサーバーにログインして何かをすることはなさそう…
ユーザーを何人か追加するとして、その人達が作るファイルは、別の人も更新できた方が良いのだろう。
CGIが動き、www-dataユーザーでディレクトリやファイルが作られる仕組みが入るかもしれない。
編集者としてedituserというユーザーを作るとして…
- DocumentRootは/var/www/htmlとする。
- このディレクトリのオーナーのグループをwww-data、ユーザーをwww-dataとする。
- www-dataグループに書き込みの権限を与える。
- SGID(set-group-id)を設定する。
これによって、/var/www/html以下に作成されるファイルとディレクトリは、グループがwww-dataになる。
- edituserをwww-dataグループに所属させる。
- www-dataグループに所属することで、/var/www/htmlディレクトリへの書き込みが可能になる。
- プライマリグループはedituserのままにすることで、ログイン時のumaskが0002になる。
ディレクトリは775、ファイルは664で作られるので、グループに所属するユーザー間の読み書き・実行が可能になる。
という設定の組み合わせにすることで、/var/www/htmlディレクトリ以下を、www-dataグループのユーザーで読み書きできるようにする。
ユーザーを追加するときは、edituserと同じ設定にすればいい。
このサーバーを公開するためには、適当なリバースプロキシーサーバーを立てりゃいいかな。
何より慣れていないのは、FTPサーバー。これさえクリアすれば、どうにかなるだろう。
こんな構成で引き渡すことにした。
環境構築
ホームラボで手順を整理してから、本番環境を構築する。
以下、ホームラボで行った手順。
ファイアウォールの設定
SSHのポートを開放して、ファイアウォールを有効にする。
接続元は適切なものに絞ったりする(from 192.168.0.0/24とか)。
$ sudo ufw allow to any port 22 proto tcp from any comment "SSH"
$ sudo ufw enable
ログの出力を調整する。
/etc/rsyslog.d/20-ufw.conf
# Log kernel generated UFW log messages to file :msg,contains,"[UFW " /var/log/ufw.log # Uncomment the following to stop logging anything that matches the last rule. # Doing this will stop logging kernel generated UFW log messages to the file # normally containing kern.* messages (eg, /var/log/kern.log) & stop
※最後の & stop がコメント化されているので、先頭の#を削除して有効化。
rsyslogを再起動。
$ sudo systemctl restart rsyslog
これで、/var/log/ufw.logだけにログが出力される。
スワップ領域を増やす
メモリーが少なめの設定なので、少し大きめにとってみる。
(標準で2GBもとってあるから、十分といえば十分だけれど)
最初に作られたスワップ領域があったので、削除。
$ sudo swapoff /swap.img $ sudo rm /swap.img
スワップ領域を作る。ここでは4GBにしてみた。
$ sudo fallocate -l 4G /swap.img $ sudo chmod 600 /swap.img $ sudo mkswap /swap.img mkswap: /swap.img: insecure permissions 0644, fix with: chmod 0600 /swap.img Setting up swapspace version 1, size = 4 GiB (4294963200 bytes) no label, UUID=877eb6d1-662f-442e-a453-5257740c1305
スワップ領域を有効にする。
$ sudo swapon /swap.img
確認。
$ free -h total used free shared buff/cache available Mem: 1.9Gi 298Mi 1.2Gi 1.0Mi 359Mi 1.4Gi Swap: 4.0Gi 0B 4.0Gi
再起動後もスワップ領域として使われていることが確認できた。
Apache
WebサーバーとしてApacheをインストール。
Nginxの方が性能が高いみたいだが、こちらに慣れているので。
$ sudo apt install apache2 $ sudo ufw allow to any port 80,443 proto tcp from any comment "WWW"
ブラウザで http://<IP アドレス> にアクセスすると、Apacheのデフォルトページが表示される。
/var/www/htmlディレクトリのオーナーをwww-dataに変更。
あわせて、SGID(set-group-id)を設定する。
$ sudo chown -R www-data:www-data /var/www/html $ sudo chmod g+sw /var/www/html $ ll -d /var/www/html drwxrwsr-x 3 www-data www-data 4096 Sep 23 18:46 /var/www/html/
ホストの定義を新規に作成。
- サーバー名はexample.netとしているので、本番にあわせる。
- SSL証明書と秘密鍵はsnakeoilとしているので、本番で利用するものに差し替える。
- リバースプロキシされたときに、接続元IPアドレスが出力できるように、X-Forwarded-Forをログに出力する。
/etc/apache2/sites-available/content.conf
<VirtualHost *:80> ServerName example.net Redirect permanent / https://example.net </VirtualHost> <VirtualHost *:443> ServerName https://example.net LogFormat "%h %{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined2 ErrorLog ${APACHE_LOG_DIR}/content-error.log CustomLog ${APACHE_LOG_DIR}/content-access.log combined2 SSLEngine on SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key <Directory /var/www/html> Require all granted DirectoryIndex index.html AllowOverride All Options +ExecCGI AddHandler cgi-script .cgi .pl </Directory> </VirtualHost>
デフォルトの設定を外して、作成した設定を有効にする。
$ sudo a2dissite 000-default.conf $ sudo a2ensite content.conf
必要なモジュールを有効にする。
$ sudo a2enmod ssl cgi http2
警告避け。
$ echo "ServerName localhost" | sudo tee /etc/apache2/conf-available/fqdn.conf $ sudo a2enconf fqdn
設定を反映させる。
$ sudo systemctl restart apache2
.htaccessで設定を上書きできる柔軟な設定なのかなと。
編集者を追加
編集者として、対話型のコマンドでedituserを追加する。
パスワードだけを入れて、後は空としている。
$ sudo adduser edituser Adding user `edituser' ... Adding new group `edituser' (1001) ... Adding new user `edituser' (1001) with group `edituser' ... Creating home directory `/home/edituser' ... Copying files from `/etc/skel' ... New password: Retype new password: passwd: password updated successfully Changing the user information for edituser Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n]
編集者をwww-dataグループに追加する。
ask ubuntu / How to create an FTP/SFTP user to manage /var/www which is owned by www-data
$ sudo usermod -a -G www-data edituser $ groups edituser edituser : edituser www-data
※プライマリーグループはedituserのままでOK。
vsftpd
FTPサーバーとして、vsftpdをインストール。
接続元は適切なものに絞ったりする(from 192.168.0.0/24とか)。
$ sudo apt install vsftpd $ sudo ufw allow to any port 21 proto tcp from any comment "FTP" $ sudo ufw allow to any port 61001:61005 proto tcp from any comment "FTP PASV"
※ufwの設定を削除するときは、ufw と allow の間に delete を入れればOK。設定値を変えた場合に、作り直しは可能。
幾つかの設定を入れる。
/etc/vsftpd.conf
write_enable=YES local_umask=002 pasv_min_port=61001 pasv_max_port=61005
※ポートの最初と最後はファイアウォールの設定とあわせて、新規に追加する。
安全接続を目指すなら、以下を設定してFTPSにする。
証明書と秘密鍵にはsnakeoilを使っているが、Apache同様、本番で使用するものに差し替えてもいい。
結局、作業用の接続なのでsnakeoilで暗号化するのでも、問題はないはずだ。
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key ssl_enable=YES strict_ssl_read_eof=NO strict_ssl_write_shutdown=NO
※変更と追記。Qiita / vsftpdにアップロードしたら"426 Failure reading network stream."エラーが出るときの解決法
テストは、一旦ssl_enableをNOにしてコマンドベースで行い、後からFFFTPとかを使ってFTPSを試すのが簡単だと思う。
設定を反映。
$ sudo systemctl restart vsftpd
Windows 11からアクセスしてみる。FTPのみ。
>ftp <IPアドレス> <IPアドレス> に接続しました。 220 (vsFTPd 3.0.5) 200 Always in UTF8 mode. ユーザー (<IPアドレス>:(none)): edituser 331 Please specify the password. パスワード: 230 Login successful. ftp> cd /var/www/html 250 Directory successfully changed. ftp> ls -l 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. -rw-r--r-- 1 33 33 10671 Sep 22 07:23 index.html 226 Directory send OK. ftp: 71 バイトが受信されました 0.00秒 71.00KB/秒。 ftp> quit 221 Goodbye.
Ubuntu 22.04からアクセスしてみる。
FTPの場合
$ ftp <IPアドレス> Connected to <IPアドレス>. 220 (vsFTPd 3.0.5) Name (<IPアドレス>:rohhie): edituser 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> cd /var/www/html 250 Directory successfully changed. ftp> ls 229 Entering Extended Passive Mode (|||61002|) 150 Here comes the directory listing. -rw-r--r-- 1 33 33 10671 Sep 22 07:23 index.html 226 Directory send OK. ftp> quit 221 Goodbye.
FTPSの場合
$ sudo apt install lftp
設定方法はこちらで教えてくれた。
Qiita / 【Linux】ftpへのファイル転送
~/.lftprc
set ftp:ssl-auth TLS set ftp:ssl-force true set ftp:ssl-allow yes set ftp:ssl-protect-list yes set ftp:ssl-protect-data yes set ftp:ssl-protect-fxp yes set ssl:verify-certificate no set cmd:fail-exit yes
$ lftp -u edituser ftp://<IPアドレス> Password: lftp edituser@<IPアドレス>:~> cd /var/www/html cd ok, cwd=/var/www/html lftp edituser@<IPアドレス>:/var/www/html> ls -rw-rw-r-- 1 1001 33 213 Sep 23 19:04 index.html -rw-rw-r-- 1 1001 33 378 Sep 23 19:04 test.php -rwxrwxr-x 1 1001 33 652 Sep 23 19:04 test.pl lftp edituser@<IPアドレス>:/var/www/html> quit
とりあえずのテストはここまで。
読み書きのテストは次で行う。
テスト
テストページを作成して動作を確かめる。
ポータルの作成
とりあえず、index.htmlを待避しておきますか。
$ sudo mv /var/www/html/index.html /var/www
edituserになってポータルページを作成。
$ sudo su - edituser
/var/www/html/index.html
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> </head> <body> <h1>CGIテスト</h1> <p>テスト1: <a href="/test.pl">Perl</a></p> <p>テスト2: <a href="/test.php">PHP</a></p> </body> </html>
PerlのCGIをテスト
PerlのCGIを作成して動かしてみる。
/var/www/html/test.pl
#!/usr/bin/perl use strict; my $host_name = &GetHostByAddr($ENV{'REMOTE_ADDR'}); my($ipaddr) = @_; print <<PAGE; Content-type: text/html\n <!doctype html> <html lang=\"ja\"> <head> <meta charset=\"utf-8\"> </head> <body> <h1>テスト Perl</h1> <p>あなたのIPアドレスは $ENV{'REMOTE_ADDR'} です。</p> <p>あなたのホスト名は、$host_name です。</p> <p></p> <p><a href="/">トップページへ</a></p> </body> </html> PAGE exit; sub GetHostByAddr { my($ip) = @_; my @addr = split(/\./, $ip); my $packed_ip = pack("C4", $addr[0], $addr[1], $addr[2], $addr[3]); my($name) = gethostbyaddr($packed_ip, 2); return $name; }
実行権限を付ける。
$ chmod +x /var/www/html/test.pl
https://<IP アドレス>にアクセスし、CGIを起動してみる。
ホームラボでは、IPアドレスから逆引きしてホスト名が取得できるようになっている。
逆引きできなければ、ホスト名が空になる。
動いているようだ。
PHPを作成
PHPでページを作成して動かしてみる。
/var/www/html/test.php
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> </head> <body> <h1>テスト PHP</h1> <?php $value = apache_getenv("REMOTE_ADDR"); echo "<p>あなたのIPアドレスは $value です。</p>\n"; $value = gethostbyaddr($value); echo "<p>あなたのホスト名は、$value です。</p>\n"; ?> <p></p> <p><a href="/">トップページへ</a></p> </body> </html>
PHPのCGIを動かすのに、PHPと追加のモジュールが必要。
追加モジュールをインストールすると、MPM(マルチプロセッシングモジュールがeventからpreforkに変わる。
これによって、HTTP/2からHTTP/1.1に変わるので、注意。
もし、HTTP/2で通信したいなら、PHP-FPMで動作させる必要があるが、ちょっと手間は掛かる。
ここではpreforkで動作確認し、PHPを使わない場合には、eventに戻す手順を記しておく。
phpと追加のモジュールをインストールする。
root権限が必要なので、いつものユーザーに戻ってから、インストール。
$ exit $ sudo apt install php libapache2-mod-php
これで追加モジュールが有効になり、Apacheも再起動するので、テストページからCGIを起動する。
https://<IP アドレス>
テストができたので、MPMをeventに戻す。
$ sudo a2dismod mpm_prefork php8.1 $ sudo a2enmod mpm_event $ sudo systemctl restart apache2
この操作でHTTP/2の通信が復活し、PHPは動かなくなる。
PHPを使わないなら、アンインストールする。
$ sudo apt remove php libapache2-mod-php $ sudo apt autoremove
やっぱりPHPを使う!となったら、phpとlibapache2-mod-phpをインストールして、以下を実行。
$ sudo a2dismod mpm_event $ sudo a2enmod php8.1 $ sudo systemctl restart apache2
※php8.1を有効にすると、mpm_preforkが自動的に有効になる。
FTPの確認
FFFTPでファイルのアップロードも試してみよう。
バージョンは…1.99aとか、いつのだろう?
良い感じに見える。
ファイルをダウンロードしてみたところ、サイズもぴったり、問題はなさそうだ。
(ASCIIだと変換がうまくいかなかったのは、バージョンが古すぎるからだろう)
サーバーのファイルを消してみたところ、きれいに消えた。
アップロードして、パーミッションを設定してみたところ、こちらも問題なく実行できた。
(test.phpに実行権限は不要…ミスってます)
アップロードしたファイルが正しく動くか確認。
必要な権限さえ設定してあれば、問題なく動く。
これでリクエストには応えられるだろう。
FTPSの確認
基本的にはFTPと同じことをやってみる話。
FFFTPの1.99aで試している。
スクリーンショットを撮り忘れたのだけれど、最初にこのサーバーに接続して良いか聞かれる。
それにOKすると、そのサーバーを記憶してくれるようで、その後にその質問はされない。
ファイルの削除は問題なし。
ファイルのアップロードでは、ファイルがアップロードできているのに、2回目にトライしていて、以下のようなメッセージが表示された。
426 Failure reading network stream.
Skipを選択した状態でDo Allボタンを押せば、ファイルのアップロードは完了するが、動きがおかしい。
FFFTPの最新バージョン(この日は5.8だった)をダウンロードしてきて試したが、結果は同じだった。
この問題はvsftpdの設定で回避できたので、ここに記載した。
やったこと
改行コードを確認する
久しぶりに改行コードの問題が発生した。
ブラウザでスクリプトにアクセスしたところで、こんなエラーが発生。
/var/log/apache2/content-error.log
End of script output before headers: perl.cgi
原因は、改行コードがCR+LFだったからなんだけれど、改行コードを調べる方法が分からなかった。
以前は、vimで開くと改行コードが見えた気がするんだけれど。
fileコマンドで改行コードが確認できるというので実行してみたところ、そのような表示はされなかった。
$ file perl.cgi perl.cgi: Perl script text executable
そこで、dos2unixコマンドで確認してみることにした。
$ sudo apt install dos2unix
ファイルをチェックしてみる。
パラメーターが-iの場合、左から、DOSの行数、UNIXの行数、MACの行数、byte order mark、テキストorバイナリー、ファイル名の順で表示される。
パラメーターが-icの場合、変換が必要なファイル名だけが表示される。
$ dos2unix -i perl.cgi 19 0 0 no_bom text perl.cgi $ dos2unix -ic perl.cgi perl.cgi
改行コードがLFの場合には、以下の表示だった。
$ dos2unix -i perl.cgi 0 19 0 no_bom text perl.cgi $ dos2unix -ic perl.cgi
ファイルをバイナリー形式で表示して調べる方法もあるけれど、これがスマートなのかなと思われた。
プライマリグループを変更する
プライマリグループは以下で変更ができた。
$ sudo usermod -g www-data edituser $ groups edituser edituser : www-data edituser
この時、edituserのホームディレクトリは以下のように変化した。
edituser@hoge:~$ ll total 28 drwxr-x--- 3 edituser www-data 4096 Sep 23 04:29 ./ drwxr-xr-x 4 root root 4096 Sep 22 07:59 ../ -rw------- 1 edituser www-data 933 Sep 23 04:29 .bash_history -rw-r--r-- 1 edituser www-data 220 Sep 22 07:59 .bash_logout -rw-r--r-- 1 edituser www-data 3771 Sep 22 07:59 .bashrc drwx------ 2 edituser www-data 4096 Sep 23 04:02 .cache/ -rw-r--r-- 1 edituser www-data 807 Sep 22 07:59 .profile
ただ、umaskの問題があるので、プライマリグループはedituserに戻している。
本編では、そもそもプライマリグループを変更しない手順にした。
umaskが0002
ファイルやディレクトリを作ったとき、どんな権限が付くのか確認していて、グループに書き込み権限がないことに気付いた。
$ touch test.txt $ mkdir test.dir $ ll total 12 drwxr-xr-x 3 edituser www-data 4096 Sep 23 04:31 ./ drwxr-x--- 4 edituser www-data 4096 Sep 23 04:30 ../ drwxr-xr-x 2 edituser www-data 4096 Sep 23 04:31 test.dir/ -rw-r--r-- 1 edituser www-data 0 Sep 23 04:30 test.txt
このとき、edituserのプライマリグループをwww-dataにしていた。
$ groups www-data edituser
umaskを確認してみると、0002ではなく、0022だった。
$ umask 0022
この動きは、以下で決まっていた。
ask ubuntu / Why does a user's umask values differ between two systems?
/etc/login.defs
... # # Login configuration initializations: # # ERASECHAR Terminal ERASE character ('\010' = backspace). # KILLCHAR Terminal KILL character ('\025' = CTRL/U). # UMASK Default "umask" value. # # The ERASECHAR and KILLCHAR are used only on System V machines. # # UMASK is the default umask value for pam_umask and is used by # useradd and newusers to set the mode of the new home directories. # 022 is the "historical" value in Debian for UMASK # 027, or even 077, could be considered better for privacy # There is no One True Answer here : each sysadmin must make up his/her # mind. # # If USERGROUPS_ENAB is set to "yes", that will modify this UMASK default value # for private user groups, i. e. the uid is the same as gid, and username is # the same as the primary group name: for these, the user permissions will be # used as group permissions, e. g. 022 will become 002. # # Prefix these values with "0" to get octal, "0x" to get hexadecimal. # ERASECHAR 0177 KILLCHAR 025 UMASK 022 ... # # Enable setting of the umask group bits to be the same as owner bits # (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid is # the same as gid, and username is the same as the primary group name. # # If set to yes, userdel will remove the user's group if it contains no # more members, and useradd will create by default a group with the name # of the user. # USERGROUPS_ENAB yes ...
DeepL先生の翻訳によれば、USERGROUPS_ENABの説明はこうだった。
uid が gid と同じで、username がプライマリグループ名と同じ場合、非 root ユーザーの umask グループビットを所有者ビットと同じにする (例: 022 -> 002, 077 -> 007)。
yesに設定すると、userdelはそのユーザーのグループにメンバーがいなくなった場合に削除し、useraddはデフォルトでそのユーザーの名前のグループを作成します。
確かに、ユーザーを作ると、デフォルトでそのユーザーの名前のグループが作られるようになっている。
ログイン時にプライマリグループの名前がチェックされ、状態によりumaskは0022か0002になるが、それはこの仕組みによるものだった。
思想が統一されて今の状態になっているんだなと感じる。
これを自分ルールで変更しようと思うなら、/etc/profileに処理を書けば良いようだ。
さいごに
整理しながら雑なサーバー構築だなと思うけれど、バースト的なアクセスはなさそうなので、これくらいでだいたいうまくいくんじゃないかな。
後はどんなコンテンツが載るのかだけれど、必要なものがあったらチョイチョイ足していけばいい。
いままでSGIDとSUID、Sticky BITを理解せずに使っていて、なんかやばめの権限設定でSambaを運営していたりする。
家庭内でやってることだし、ファイルの消失がなければいいやとほったらかしだが、今回この辺りをちょっとかじったので、少し分かる範囲を広げたいところ。
何かが分かった気になっても、次から次へとナゾが襲いかかってくる感じで、全くクリアができないネトゲのようだ。
いつかどこかですっきりするのかな。
コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他