nginx

nginxでWebDAVとかを検索していると、よくWSGIってのが引っかかってくる。何これ? 雑魚かな。まてまて、ちゃんと調べてあげよう。

Web Server Gateway Interfaceが正式名称らしい。Pythonが発症、もとえ発祥の地とか。

WSGIとPythonでスマートなWebアプリケーション開発をとかを見ると、python2系では、すんなりと動く事が確認出来たよ。

ただ、3系になると、

sakae@usvr:/tmp$ python3 wsgi.py
127.0.0.1 - - [24/Oct/2018 07:07:06] "GET / HTTP/1.0" 200 0
Traceback (most recent call last):
  File "/usr/lib/python3.6/wsgiref/handlers.py", line 138, in run
    self.finish_response()
  File "/usr/lib/python3.6/wsgiref/handlers.py", line 180, in finish_response
    self.write(data)
  File "/usr/lib/python3.6/wsgiref/handlers.py", line 266, in write
    "write() argument must be a bytes instance"
AssertionError: write() argument must be a bytes instance
127.0.0.1 - - [24/Oct/2018 07:07:06] "GET / HTTP/1.0" 500 59

こんな具合に、盛大なエラーが発症する。2と3の偉大なる壁が有るんだな。どう回避する?

WSGI に対応するによって、簡単に回避出来るのかな?

# simple server using WSGI

from wsgiref.simple_server import make_server

def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return [ b'Hello, world' ]

if __name__ == '__main__':
    server = make_server('localhost', 8080, application)
    server.serve_forever()
sakae@usvr:/tmp$ w3m http://localhost:8080
sakae@usvr:/tmp$ python3 wsgi.py
127.0.0.1 - - [24/Oct/2018 07:33:29] "GET / HTTP/1.1" 200 12

動いた。2から3への肝は、body部の文字列をbにする事かな。

2から3への変換器、2to3では、下記のように対応してくれなかった。罪作りな変身だなあ。

sakae@usvr:/tmp$ /home/sakae/miniconda3/bin/2to3 -w p2.py
RefactoringTool: Skipping optional fixer: buffer
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: set_literal
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: No changes to p2.py
RefactoringTool: Files that need to be modified:
RefactoringTool: p2.py

ダイジェスト認証

ダイジェスト認証てどんなんだっけ? サーバー側の設定だよな。

Digest認証 思い出してきたわ。

でも、これと併用して、443番さんで、相手がちゃんとした所か検証しないと、盗まれるぞ。 盗まれるとMD5を逆演算して、秘密のキーを割り出される(かも)。

サーバー側が正しいかお墨付きを得ておくのが、これからのWeb生活で大事。

Apacheの認証の設定

Web serverとの付き合い

認証だとか言ってたら、昔からのWebサーバーとの付き合いが、走馬燈のように思い出された。 付き合いと言えば、CGIに決まってるじゃないですか。

現役時代は、今のように日記を書いてあげておくなんて言う、静的な事はさっぱりしていなかった。やってたのはCGI。

最初はPerl。なんたってこれしか無かったからね。作ったのは、スケジュール表。練習がてらに作った。部の全員のスケジュールが、見られたら、みんな便利じゃん。誰が、何処に出張に行ってるか一覧できる。

仕事の合間に、ちまちまと作っていた。人員の登録は、テキストファイルにした。個人のデータは、gdbmで持つ事にした。キーを日付、バリューは任意の文字列。全員を一つのgdbmに収めちゃうと、壊れた時に被害が甚大になるんで、個人単位、かつ1年限り。新しい年になると、gdbmを切り替え。

最初、自分の部内だけで使ってたけど、評判がよくて、あちこちに貰われていった。なんせ、設置とメンテナンスが楽だからね。

予定を入れるページに遷移した時、当月を含む3月分のカレンダーを表示。perlで書くのが筋だろうけど、unix流に、calの結果を合成した。その方法を考えるのが面白かったな。

matzさんがruby本を出した頃、perlとはさよならして、rubyに主軸を移した。確か、1.4の時代だったと思う。

社内の掲示板をrubyで作った。MySQLを使ったのを覚えている。Posgleに比べて速いと評判だったから。rubyとMySQLを繋ぐドライバーを日本人の方がメンテナンスされてて、随分と助けていただいた。

掲示板に、ファイルをアップロードしたいって言う、わがままを言い出すユーザーがいた。MySQLの中にファイルを取り込む訳にもいかないので、ファイル名だけを一意に決めてDBに入れ、ファイル本体は、適当な所に放り込んだ。

余り、多数のファイルを添付されても困るので、一投稿に3つまでって、最初から決めてた。 EXCELファイルに写真を何枚を張り付ける人がいて、往生したよ。EXCELの人達って、ファイル容量なんて、頓着しないからね。

懐かしいな、ruby。たまには触ってみるかな。

ruby

と言う事で、対面場所をCentOSに決めて、rubyをyumしようとしたら、ruby 2.0 ですって。これって古すぎないかい。久しぶりにtar玉から入れてみるか。

本家へ取りに行ったら、時代は2.5になってた。2.6系も有ったけど、枯れてるやつにした。 昔tar玉から入れた時、make install時に、rdocだかの作成に膨大な時間がかかっていたけど、 今はそんなの無いみたいで、すんなり終わった。

[sakae@cent ~]$ ruby -v
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux]

昔お世話になった、cgi.rbは健在? railsに押されて削除された? えと、/usr/local/lib/ ruby/2.5.0/ の下か。

cgi.rbは、ほとんどがコメントに覆われた、説明書になってた。実体はcgiの下に有るのね。 コメントがあって、コードが有るってスタイルが、コードが読みやすくていいな。

netの下に、http.rbなんてのが置いてある。開いてみたら、RHGの青木さんとか、GOTOU Yuuzouさんとか、懐かしい名前が出てきた。勿論、rubyの父の名前もあった。もう、20年近くたつのか。

x86_64-linux/cgi/escape.so escpapeはよく使う処理なんで、C語にしてるんだな。遅い々と 突き上げられていたんで、その対策か。

singleton method CGI.escape

素の Ruby で HTML エスケープするなら cgi/escapeが最強

たまに、rubyでcgiしてみたいけど、書けるかなあ。たまには、るびま も、見てあげましょう。

Ruby ビギナーのための CGI 入門

昔、同僚がアンケートCGIを書いていたなあ。探してみたら有った。

Linux rbox 2.4.18-0vl3 #1 Thu Mar 7 18:23:04 JST 2002 i686 unknown
Server version: Apache/1.3.27 (Unix)
Server built:   Apr 22 2003 14:59:00
mysql  Ver 12.22 Distrib 4.0.18, for pc-linux (i686)
ruby 1.8.1 (2003-12-25) [i686-linux]

cgi/tenji.cgi              ruby            cgi 本体
    tenji_basichtml.rb     ruby            HTML共通部
    tenji_basicmail.rb     ruby            メール処理共通部
    tenji_error.rb         ruby            エラーページ処理
    tenji_db.rb            ruby            データベースインターフェイス
     :

懐かしいな。読んでみるか。しっかりOOで書いてあるんで、関数型人間には辛いな。

nginx into CentOS7

前回は期待を込めて、OpenBSD httpdで、WebDAVが動くか調べていたんだけど、徒労に終わった。このままじゃ、オイラーの気持ちが収まらないよ。何としてもWebDAVが動く現場を見てみたい。何処に入れるか問題だ。色々考えた末、CentOS7に入れる事にした。

近頃では、apacheと人気を二分してるそうなので、コマンド一発で入るだろう。有るかどうか、検索してみた。

[sakae@cent ~]$ yum search nginx
  :
============================== N/S matched: nginx ==============================
pcp-pmda-nginx.x86_64 : Performance Co-Pilot (PCP) metrics for the Nginx
                      : Webserver

えっ、たったこれだけ? 嘘でしょう。ネットで調べてみると、本家でリポジトリィーを公開してるんで、その設定をしてから入れろらしい。デストリだと、動きが鈍いので、本家が腰を上げて、(多分)最新のやつが入るように配慮してるんだろうね。

Nginx を CentOS 7 にインストールする手順

CentOS7 に nginx導入

[sakae@cent ~]$ nginx -v
nginx version: nginx/1.15.5
[sakae@cent ~]$ sudo systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
[sakae@cent ~]$ curl -sv http://localhost/
 :
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost
> Accept: */*
>
< HTTP/1.1 200 OK                                                        [0/488]
< Server: nginx/1.15.5
< Date: Fri, 19 Oct 2018 21:31:53 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 02 Oct 2018 15:27:19 GMT
< Connection: keep-alive
< ETag: "5bb38e57-264"
< Accept-Ranges: bytes

<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
[sakae@cent ~]$ ps awx | grep nginx
 2257 ?        Ss     0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
 2258 ?        S      0:00 nginx: worker process

nginx -V で、config状況を確認。--with-http_dav_moduleは付いてたけど、拡張davは付いていないやつだった。

source

yumdownloader --source nginx-1.15.5.tar.gz しても無いと言われたので、 nginx source から、落としてきた。OpenBSDとどれぐらい似てるか確認すべ。

[sakae@cent nginx-1.15.5]$ cd src/http/modules/
[sakae@cent modules]$ ls *dav*
ngx_http_dav_module.c
static ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {
    { ngx_string("off"), NGX_HTTP_DAV_OFF },
    { ngx_string("put"), NGX_HTTP_PUT },
    { ngx_string("delete"), NGX_HTTP_DELETE },
    { ngx_string("mkcol"), NGX_HTTP_MKCOL },
    { ngx_string("copy"), NGX_HTTP_COPY },
    { ngx_string("move"), NGX_HTTP_MOVE },
    { ngx_null_string, 0 }
};

nginxの*.cを数えたら、204個、総容量は、17万行あった。 OpenBSDでは*.cが、たったの11個、容量は、9200行だった。個数も容量も桁が違うんで、OpenBSDに大きな期待を寄せる(寄せた)のは、無謀ってものです。

conf for nginx

OpenBSD httpdで、見よう見まねにconfファイルを作ったけど、ちゃんと勉強しておこう。 本格的にやるなら、 The Complete NGINX Cookbookを頂いてくればいいんだけど、人間かAIかの判定(画像の中から車が写っているのを、全て選べ)が行われるので、遠慮した。

nginx documentationの、Beginner's Guideに基本的な事が書いてあるので、取り合えずはそれで間に合うだろう。

http {
    server1 {
    }
    :
    serverN {
    }
}

serverNが、それぞれのサーバーの設定になる。fastcgi用のサーバーとか、仮想サーバーとか、WebDavサーバーとかだ。

/etc/nginx/ngins.confの最後の所のserverNに相当するのが、include /etc/nginx/conf.d/*.conf; なってるので、conf.d内に、適当な(サーバー名にすると、管理が楽)confを置けばよい。defalut.confが置いてあるんで、参考になる。

server {
    location / {
        fastcgi_pass  localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING    $query_string;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

location hoge の、hogeは、URIに使われる。最長のものにマッチする。最短は、上の例のように / である。ニョロマークは、お馴染みの正規表現によるマッチ。

root /data/images は、サーバー内の物理的な場所となる。http://example.com/hoge/fuga/abc.png とかのURIを与えられたら、/data/images/abc.png が、持って行かれる。

fastcgi_xxx とかは、それぞれのモジュールの設定による。これだけ分かれば何とかなるかな。

で、実技試験。 Module ngx_http_dav_moduleこれが分からないと、DAVしちゃだめよ。

location / {
    root                  /data/www;

    client_body_temp_path /data/client_temp;

    dav_methods PUT DELETE MKCOL COPY MOVE;

    create_full_put_path  on;
    dav_access            group:rw  all:r;

    limit_except GET {
        allow 192.168.1.0/32;
        deny  all;
    }
}

setup WebDAV

試しにWebDAVサーバーを作ってみる。まずは認証のための.htpasswdファイル作成。 CentOSには、そのためのコマンドが無い。以前作ったapacheは消しちゃったからなあ。 OpenBSDで作って持ってくる手もあるけど、あちらはBlow fishな暗号使ってるから、通用するのかな? そんな事で、bashの技を借りて、作成。

echo "sakae:$(openssl passwd -crypt himitu)" > /etc/nginx/conf.d/.htpasswd

次は、WebDAV用のエリアの作成と、コンテンツの作成(省略可)。

$ mkdir /usr/share/nginx/html/safe
$ chown nginx:nginx /usr/share/nginx/html/safe
$ echo "sakae himitu" > /usr/share/nginx/html/safe/key.txt

忘れないように認証データを、key.txtとして登録。金庫の鍵は、金庫の中状態です。

そして、WebDAVサーバーの設定ファイル。 /etc/nginx/conf.d/webdav.conf

server {
  listen       8282;
  location / {
    root /usr/share/nginx/html/safe;

    auth_basic "webdav safe-box";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;

    # upload limit
    client_max_body_size   1m;

    ## for firefox ....
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;

    client_body_temp_path  /usr/share/nginx/html/safe;
    create_full_put_path on;

    # WebDAV setup
    dav_access group:rw all:r;
    dav_methods  PUT DELETE MKCOL COPY MOVE;
  }
}

リスンポート番号は、たまたま聞いてたABBAのCDでバックコーラスが、ハニハニって謳ってたので、8282です。他意はありません。こうしておくと、デフォルトの80番さんと区別できますからね。

アップロードファイルの容量は、EXCELを台紙にしたアルバム拒否の願いを込めて、1Mにした。 昔CGIだと、マルチパート何とかで、必死にbase64変換したりして大変だったけど、もうそんな苦労は必要無い。

設定が完了したら、restartして、新しい生活を始める。なお、ここまでの所は、特権ユーザーで実施するのね。

$ service nginx restart
[sakae@cent ~]$ w3m http://localhost:8282/key.txt

これで、認証出来て、ファイルが閲覧出来れば、取り合えずOKかな。

Konfigurasi WebDav Nginx di CentOS 7 何語か知らないけど、手順は追える。curlの使い方も出てた。

なお、標準提供されてるnginxでは、WebDAVの拡張機能が入っていない。追加は、 nginx のモジュール追加リビルド (CentOS 7版)を参照。

セキュリティーの脅威

取り急ぎ、WebDAVサーバーを立ち上げたけど、他の(デフォルト)サーバーの事は、考慮していなかった。

[sakae@cent tmp]$ w3m http://localhost/safe/
Index of /safe/

----------------------------------------------------------------------------
../
key.txt                                            22-Oct-2018 21:27

WebDAVなサーバーは、貧弱ながら認証がかかっていて、パスワードを知らないと、アクセス 出来ない建前。でも、秘密の箱を知ってる人には、他のサーバーから中身が丸見え。

Netでよく話題になる、秘密情報が認証の無いエリアに放置されていて、盗まれてしまいましたってのの、再現ですよ。

default.conf

    listen       80;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        autoindex on;
    }

webdav.conf

     listen       8282;
     location / {
        root    /usr/share/nginx/html/safe;
        auth_basic "webdav safe-box";
          :

お分かりのように、webdavのエリアが、デフォルトのhtdocエリアに内包されてるからね。 autoindex onは、URLの最後が/で終わった時、前部をdirと見做して、ファイルリストを表示する機能。脅威を分かり易くするため、あえて追加した。

複数のサーバーを建てる時は、各種エリアを完全に独立させましょう。

cadaver

curlを使えば、WebDAVを操れそうだけど、お世辞にもftpクライアント並みの操作感を得られそうにもない。CUIで会話的にWebDAVとつるむやつってないの?

NetBSDのpkgsrc/wwwの下で、davって検索したら、下記を紹介された。OSSのカタログだな。

cadaver for CLIer

取り合えずtar玉を取り寄せてみた。docの下にmanが有ったので、

nb8$ mandoc ./cadaver.1 | lv

ftp風なのね。まずは、cadaverコマンドを使って、目的のサイトに接続。後はftpコマンドを 思い出しながら、各種コマンドで指示する。helpでどんなコマンドが使えるか出てくるのは、お約束だな。

       cadaver http://zope.example.com:8022/Users/fred/
              Connects to the server zope.example.com using port 8022, opening
              the collection "/Users/fred/".
	      
COMMAND REFERENCE
       ls [path]
              List contents of current [or other] collection
       put local [remote]
              Upload local file
       get remote [local]
              Download remote resource
       edit resource
              Edit given resource
        copy source... dest
              Copy resource(s) from source to dest
       lcd [directory]
              Change local working directory

FAQにアホなGUIerからの質問が出てたぞ。

-> Why doesn't the "edit" command work with gvim?

Because gvim forks and runs in the background by default.  Set the
EDITOR environment variable to "gvim -f", or put in ~/.cadaverrc

  set editor "gvim -f"

to make the edit command work properly.

DAV自身がアホな仕様と思うのは、オイラーだけ? DAVを提唱したのはMSだからしょうがない。 中でも、このリモートにあるリソースを、編集しましょなんてのは、思いあがった連中だ。 いつ回線が切れるか分からんじゃん。そんな環境で編集操作を許可するなんて、常軌を逸脱してるぞ(と思う)。 反論は何時でも受付ますよ。

おっと、待て々。いくらなんでも、そんな大胆な事はしないだろう。多分、これ複合コマンドだろう。

get target
vi  target at local
put target

ロック問題をどうしてるかは、ソースを見物する必要があるけど。

edit.c/execute_edit(*reomote)

    output(o_download, _("Downloading `%s' to %s"), real_remote, fname);

    else if (!run_editor(fname)) {

                output(o_upload, _("Uploading changes to `%s'"), real_remote);

run_editorの中では、systemcmdを使って、希望するeditorを起動してた。これ、Winな人への配慮だろうね。fork/execveはUNIX限定だもの。

なかなか、よさげなんで、下記を入れた。

cadaver-0.23.3-9.el7.x86_64.rpm

早速、試してみる。

[sakae@cent ~]$ cadaver http://localhost:8282/
Authentication required for webdav safe-box on server `localhost':
Username: sakae
Password:
Could not open collection:
405 Not Allowed
dav:/? help
Available commands:
 ls         cd         pwd        put        get        mget       mput
 edit       less       mkcol      cat        delete     rmcol      copy
 move       lock       unlock     discover   steal      showlocks  version
 checkin    checkout   uncheckout history    label      propnames  chexec
 propget    propdel    propset    search     set        open       close
 echo       quit       unset      lcd        lls        lpwd       logout
 help       describe   about
Aliases: rm=delete, mkdir=mkcol, mv=move, cp=copy, more=less, quit=exit=bye
dav:/? bye

Could no open collection って事は、サーバー側のdir情報が取れない。その原因は、発行したメソッドが(多分)未実装って事なんだろうね。そのメソッドは、PROPFIND,OPTIONSのどちらかが無いためだろう。

ソースをcadaverのソースをざっと見した所、cadaver.c/open_connection()の中に、該当のエラーを出してる所が有った。

    ret = ne_options(session.sess, session.uri.path, &caps);
    ret = NE_OK;          // quick hack
    switch (ret) {
      :
    default:
        printf(_("Could not open collection:\n%s\n"),
               ne_get_error(session.sess));
        break;
    }

んで、上記のように、誤魔化してみたけど、やはり同情のエラーが発生。

更に、helpを調べてみると、get/put等は、ちゃんとサーバーと接続されていないと使えないって出てた。これはもう、諦める鹿。

まあ、Webサーバーにftpの機能を突っ込むってのは、M$の筋の悪さが際立っているんで、真剣にやらんでも宜しい。

[sakae@cent cadaver-0.23.3]$ curl -u sakae:xxxxx http://localhost:8282/ --upload-file FAQ
[sakae@cent cadaver-0.23.3]$ curl -u sakae:xxxxx http://localhost:8282/FAQ
[sakae@cent cadaver-0.23.3]$ curl -u sakae:xxxxx http://localhost:8282/FAQ -X "DELETE"

FAQなファイルをアップロード、それを確認、それから削除。ちゃんと動いたから佳とするか。