自宅サーバーが出来たらFTP、WWWサーバーも立ち上げたくなりますね。
それではFTPサーバーを立ち上げましょう!
インストールにはportsを利用します初めての場合はオプション選択画面になります
# cd /usr/ports/ftp/proftpd
# make install clean
あえて設定し直したい時は「make config」で設定しましょう
proftpdの起動方法には2種類有ります。
■デーモンで常駐するタイプ
■inetdでアクセスが有る度に起動するタイプ
それぞれの利点は以下のとおりです
■inetdで立ち上げる場合
■デーモンで立ち上げる場合
・confファイルを書き換えた場合、立ち上げ直さなくても次に接続された時から反映される
・接続のない時にはRAMを消費しない
・IPが動的の場合PASVで帰すアドレスが変っても対処できる
・立ち上がりが早い
・複数接続された場合おそらくRAMの消費量が少ないと思う
・VirtualHostが使える
だと思います。
そこで、個人で立ち上げる場合は上記の内容からinetdの方が都合がいいのではと思い
私はinetdで起動させています。
inetdの場合を基本に解説します。
まずはinetdで起動するのでinetdのファイルに記述します。
/etc/inetd.conf を編集するデフォルトで上記1行目が有る場合はコメントアウトして下さい。
#ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l ftp stream tcp nowait root /usr/local/sbin/proftpd proftpd
2行目がproftpdを立ち上げる為の行です。
次にproftpdの本体とも言えるproftpd.confを設定します。
サンプルが置いてあると思うのでそれを参照するのもいいでしょう!
/usr/local/etc/proftpd.conf を編集する気をつけなければならないのは<Anonymous></Anonymous>のあいだは
# ログイン時に表示されるメッセージ ServerIdent on "Personal FTP Server" # サーバーの名前 ServerName "server.hogehoge.org" # 立ち上げ方法(デーモンの場合はstandalone) ServerType inetd # バーチャルホストで一致しない場合はデフォルトで受けるか、とりあえずON DefaultServer on # 制御用ポート番号 Port 21 # 書き込まれた場合のパーミッション、002なら書かれたファイルは775になる Umask 002 # 時計はGMTではないのでFALSE TimesGMT FALSE # 最大接続数(スタンドアローンだけかも) MaxInstances 30 # デーモンを走らせるユーザー名 User nobody # 同じくグループ名 Group nogroup # デフォルトのルートディレクトリー(~はhomeを示す) DefaultRoot ~/public_html # LOGの場所 systemlog /var/log/proftpd.log # アイドルの許す時間(秒) TimeoutIdle 300 # 接続からログイン終了までの許す時間(秒) TimeoutLogin 60 # データ転送しない時間がどれくらい許すか(秒) TimeoutNoTransfer 600 # LOGのフォーマット LogFormat default "%t %a %l %u %r %f" # IDENTを使うか(自分が閉じるくらいなのでoffしておきましょう) IdentLookups off # サーバードメイン名(PASV接続先指示に使います) MasqueradeAddress server.hogehoge.org # PASVモードで使用可能なポート番号範囲 PassivePorts 7030 7050 # とりあえず今はOFF、TLS出来るようになったらONかな? AuthPAM off #なぜか1.210からscoreboardが無いとエラーしますので指定しておきます ScoreboardFile /var/run/proftpd.scoreboard #ftps用 LoadModule mod_tls.c #パスワードファイルを設定する AuthUserFile /usr/local/etc/proftpd/ftpd.passwd #ftpsの設定 <IfModule mod_tls.c> TLSEngine on TLSLog /var/log/proftpd-tls.log TLSProtocol SSLv23 TLSCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP # Are clients required to use FTP over TLS when talking to this server? TLSRequired off # Server's certificate TLSRSACertificateFile /usr/local/etc/letsencrypt/live/server.hogehoge.org/cert.pem TLSRSACertificateKeyFile /usr/local/etc/letsencrypt/live/server.hogehoge.org/privkey.pem # Authenticate clients that want to use FTP over TLS? TLSVerifyClient off </IfModule> # デフォルトはローカルからしかログインできないようにする <Limit LOGIN> Order Allow,Deny Allow from 192.168.1. Deny from all </Limit> <Global> <Directory /*> AllowOverwrite on AllowStoreRestart on AllowRetrieveRestart on </Directory> </Global> # ここからは基本とは別の動作をします # /data/tempがルート <Anonymous /data/temp> # その場合のUNIXユーザー User ユーザー名(UNIX) # FTPログイン名をUNIXに変換 UserAlias ログインユーザー名 ユーザー名(UNIX) # FTPログインパスワード(暗号化された物を書きます) UserPassword ユーザー名(UNIX) 暗号化PASS # その時のグループ Group グループ名(UNIX) AnonRequirePassword on AuthAliasOnly on RequireValidShell off HideNoAccess on LsDefaultOptions "-o" # 読み込み速度制限(バイト) RateReadBPS 92160 # 書き込み速度制限(バイト) RateWriteBPS 91260 # 最大接続数 MaxClients 2 "Sorry, already %m users login now." # 1クライアントあたりの最大接続数 MaxClientsPerHost 1 "Sorry, 1 connect 1 Client MAX." # ファイルの持ち主をrootに見せかける DirFakeUser on root # 同じくグループもwheelに見せかける DirFakeGroup on wheel # パーミッションも全て644に見せかける DirFakeMode 644 AllowRetrieveRestart on <Directory *> <Limit ALL> IgnoreHidden on </Limit> <Limit WRITE> DenyAll </Limit> </Directory> # incomingのディレクトリーは見えない <Directory incoming> <Limit ALL> DenyAll </Limit> </Directory> # tempのディレクトリー設定 <Directory temp> # 送信レジューム可 AllowStoreRestart on # 上書き可 AllowOverwrite on # 書き込み削除可 <Limit WRITE DELE> AllowAll </Limit> </Directory> # ログイン制限 # 怪しい外国から接続を試みた物は弾きましょう! <Limit LOGIN> Order Deny,Allow Deny from .fr,.nl,.ca,.kr,.it,.hk Allow from all </Limit> </Anonymous>
グローバルで設定していてもその内容が全てに効くわけではなく、それぞれ設定しなければならない項目も
ありますので、各自設定して試して見てください。
設定ファイルの文法は上記でいいかと思いますが、それぞれの意味が数が多くく
日本語の解説はこちらの「Directive 設定のオンラインマニュアル」にありますので
あとは見ながら設定して下さい。
また、上記リンク先が無くなる可能性もあるので無断でひかえを取らさせていただいております。
それと先程のscoreboardファイルを生成しておきます
# touch /var/run/proftpd.scoreboard
そしてユーザーを作成する
# ftpasswd --passwd --file=/usr/local/etc/proftpd/ftpd.passwd --name=ログインユーザー名 --uid=UNIXユーザー番号 --gid=UNIXグループ番号 --home=/usr/home/ftpdata --shell=/sbin/nologin
設定が完了したら起動して見ましょう。
inetdの場合はinetdを再起動する事でアクセスすれば自動で立ち上がります。
(91はプロセス番号ですので各自プロセス番号に合わせてHUPして下さい)デーモンで走らせる場合は
# ps -ax | grep inetd 91 ?? Is 0:00.52 /usr/sbin/inetd -wW # kill -HUP 91
/etc/rc.confを追記して
proftpd_enable="YES"
で走ります
# /usr/local/etc/rc.d/proftpd start
もし
error opening scoreboard: No such file or directory
このようなエラーが出た場合は
/var/run/proftpd.scoreboard
というファイルを作成して下さい(パーミッションは644)
とあるバージョンからなぜかこのようなエラーが出ることがあります
それにしても、自動で作成してくれてもいいと思うのは私だけでしょうか?
またinetdエラーする事があります。
この場合は
inetd[23031]: unknown rpc/udp6 or rpc/tcp6
/etc/netconfig
を有効にすることで起動する。
udp6 tpi_clts v inet6 udp - -
tcp6 tpi_cots_ord v inet6 tcp - -
・暗号化パスワードの生成方法
以下の内容をファイルにしてCGI実行可能なディレクトリーにセーブし、ブラウザからアクセスすれば
暗号化パスワードを生成できます。
crypt.cgi を作成する
#!/usr/bin/perl # # パスワード生成ツール "crypt.cgi" is Free. # by www.rescue.ne.jp # $buffer = $ENV{'QUERY_STRING'}; @pairs = split(/&/,$buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value; } if ($buffer eq '') { print "Content-type: text/html\n\n"; print "<html><head><title>暗号文字化</title></head>\n"; print "<body>\n"; print "<h1>暗号文字化</h1>\n"; print "<form action=\"crypt.cgi\">\n"; print "パスワード <input type=text name=\"plain\" size=30> <input type=submit value=\"暗号化\"><input type=reset value=\"RESET\">\n"; print "</form><p>\n"; print "<ul>\n"; print "<li>半角英数字(_を含む)のみ使えます.\n"; print "</ul><p>\n"; print "</body></html>\n"; exit; } if ($FORM{'plain'} eq '' || $FORM{'plain'} =~ /\W/) { &error('パスワードの入力が無いか、文字列に半角英数字以外の文字が含まれています.'); } @char = ('a'..'z','A'..'Z','0'..'9'); srand(time|$$); foreach (0..7) { { local(@temp); push(@temp,splice(@char,rand(@char),1)) while @char; @char = @temp; } $keisu = $char[($_)] . $keisu; } $now = time; ($p1, $p2) = unpack("C2",$keisu); $wk = $now / (60*60*24*7) + $p1 + $p2 - 8; @saltset = ('a'..'z','A'..'Z','0'..'9','.','/'); $nsalt = $saltset[$wk % 64] . $saltset[$now % 64]; $pwd = crypt($FORM{'plain'}, $nsalt); if ($pwd =~ /^\$1\$/) { $salt = 3; } else { $salt = 0; } if (crypt($FORM{'plain'}, substr($pwd,$salt,2)) eq $pwd) { $verify = 'OK'; } else { $verify = 'NG'; } print "Content-type: text/html\n\n"; print "<html><head><title>暗号文字化</title></head>\n"; print "<BODY>\n"; print "<h2>実行結果</h2>\n"; print "<blockquote>\n"; print "<form>\n"; print "パスワード <input size=30 value=\"$FORM{'plain'}\"><br>\n"; print "暗号化されたパスワード <input size=30 value=\"$pwd\"><p>\n"; print "[照合試験] $pwd → $FORM{'plain'} = $verify<p>\n"; if ($verify ne 'OK') { print "照合に失敗しました。このサーバではこのツールはご利用できないと思われます。<p>\n"; } print "</form></blockquote>\n"; print "</body></html>\n"; exit; sub error { print "Content-type: text/html\n\n"; print "<html><head><title>暗号文字化</title></head>\n"; print "<body>\n"; print "<h3>$_[0]</h3>\n"; print "</body></html>\n"; exit; }
サンプルはこちら
ですが、なぜかMD5が上手く照合できない…
友人がrubyで作ったので、そちらなら照合OKでした。
crypt.rb
#!/usr/bin/env ruby # -*- mode:ruby; coding:shift_jis -*- require "cgi" cgi = CGI.new("html3") passwd1 = cgi["passwd1"].to_s passwd2 = cgi["passwd2"].to_s cgi.out() do cgi.head{ "\n<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=Shift_JIS\">\n" } + cgi.body() do if (passwd1 == "") or (passwd2 == "") then "<h1>暗号文字化</h1>\n" + cgi.form() do "<table border=0>\n" + "<tr><td>パスワード</td><td>" + cgi.password_field("passwd1") + "</td></tr>\n" + "<tr><td>もう一回</td><td>" + cgi.password_field("passwd2") + "</td></tr>\n" + "</table>\n" + cgi.submit("暗号化") + cgi.reset("リセット") end + "<li>半角英数字(_を含む)のみ使えます.\n" elsif (passwd1 != passwd2) or (passwd1 =~ /\W/) then "<h1>パスワードが異なっているか、半角英数字以外の文字が使われています!</h1>" + cgi.form() do cgi.hidden("passwd1", "") + cgi.hidden("passwd2", "") + cgi.submit("元に戻る") end else srand(Time.now.to_i) salt = [] (0..7).each { salt << rand(64) } salt = salt.pack("C*").tr("\x00-\x3f","A-Za-z0-9./") des = passwd1.crypt(salt[0,2]) md5 = passwd1.crypt("$1$#{salt}$") deschk = ((passwd1.crypt(des[0,2]) == des) and (des[0,2] == salt[0,2]) ? "OK" : "NG") md5chk = ((passwd1.crypt(md5[0,12]) == md5) and (md5[0,12] == "$1$#{salt}$") ? "OK" : "NG") "<h1>実行結果</h1>\n" + "<table border=0>\n" + "<tr><td>暗号化されたパスワード(DES)</td>" + "<td>" + cgi.text_field("des", des, 50) + "</td>" + "<td>照合結果:#{deschk}</td></tr>\n" + "<tr><td>暗号化されたパスワード(MD5)</td>" + "<td>" + cgi.text_field("md5", md5, 50) + "</td>" + "<td>照合結果:#{md5chk}</td></tr>\n" + "</table>\n" + if (deschk == "NG") or (md5chk == "NG") then "<h3><font color=red>照合に失敗した項目があります。" + "このサーバではこのツールはご利用できないと思われます。</font></h3>\n" + cgi.br else "<h3><font color=blue>いずれも照合に成功しました!</font></h3>\n" + cgi.br end + cgi.form() do cgi.hidden("passwd1", "") + cgi.hidden("passwd2", "") + cgi.submit("元に戻る") end end end end