WebDAV by golang
11月である。霜月(しもつき)なんて名前がある通り、霜が降りる時期である。 更に別名として、かぐらづき(神楽月)とも言うそうな。
凛とした寒さが始まるこの月をオイラーは好きだ。12月になると慌ただしいし、逆に10月は、 ハロウィンがあったりして、街が汚れるから、渋谷区長さんも嫌いだろう。
11月は、天気も安定して神々しい感じがする。どこかで神楽をやってないかね? そんなの神楽坂に行って聞いてみろ。
search webdav server
前回は最後にncftp似のWebDAVクライアントであるcadaverを試して、あえなく撃沈した。 原因は分かっていて、nginxが標準で提供してるWebDAVにメソッドの機能不足があるからだ。
nginxを作り直す技が提供されてたけど、ちと大げさ過ぎる。もっとカジュアルに試せないかね?カジュアルと言えば、Python。きっとWebDAVサーバーの機能があるだろうと思って、探してみたよ。
オイラーの探し方が悪いかも知れないけど、クライアントは多数見つかるものの、サーバーになるような機能は見つからなかった。そんなのapacheに任せておけって態度。見捨てられているんですな。
探している時に、 Vim で引き籠る に出会った。WebDAVにも言及してた。そして、何やらgolangなんて語句が。。。。
そうか、Webの一分野だからgolangがサポートしてても不思議じゃないな。
そして、package webdav (doc)に行きついた。これって、部品の取り扱い説明書じゃん。何時もは、簡単な例が載ってるんだけど、こういう特殊な物に興味がある人には、例なんて不要だろって態度。オイラーみたいな初心者は困っちゃうぞ。
Simple Go WebDAV server.シンプルと言う割には、yamlとかjsonが登場してて、敷居値が高い。敬遠しましょ。
Simple WevDAV server
staaldraad/webdavserv.go そして、50行のシンプルなやつが見つかった。簡単だから、ソース嫁って態度で、説明も何も無し。
flagの4行分を理解するだけで良さそう。サーバーのエリアが設定出来るんだな。未指定なら、current-dirになるとな。 ポートは、80番なら普通のやつ。443番ならググル様ご推薦のサーバーになる。flg.Boolで、普通のサーバーにするか、安全なサーバーにするか選べとな。デフォは、ググル様に忖度しない普通のやつになるよ。
まてまて、80番って普通のポートじゃないぞ。ここは平民でも稼働させられるように、ハニハニ(8282)番にしておくか。
import で、golang.org/x/net/webdav してるので、本体をコンパイルする前に、go get golang.org/x/net/webdav すればいいんだな。
後は、go build dav.go する。オリジナルなファイル名は、webdavserv.goになってるけど、unix流に短縮名です。
cent:top$ curl -sv http://localhost:8282 -X OPTIONS > OPTIONS / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: localhost:8282 > Accept: */* > < HTTP/1.1 200 OK < Allow: OPTIONS, LOCK, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND < Dav: 1, 2 < Ms-Author-Via: DAV < Date: Tue, 30 Oct 2018 05:06:50 GMT
前回と違って、ちゃんとOPTIONSに反応して使えるメソッドを返してきた。Davのバージョン2もサポートしてるって言ってるけど、これで全部なのかな?
cadaverからアクセスするとdav君のログは、
2018/10/30 14:11:42 WEBDAV [OPTIONS]: / 2018/10/30 14:11:42 WEBDAV [PROPFIND]: / 2018/10/30 14:14:39 WEBDAV [PROPFIND]: / 2018/10/30 14:14:40 WEBDAV [GET]: /hello.txt
cent:~$ cadaver http://localhost:8282/ dav:/> ls Listing collection `/': succeeded. hello.txt 13 Oct 30 14:06 dav:/> cat hello.txt Displaying `/hello.txt': Hello WebDAV
それっぽく動いているな。次は、前回予測したeditの挙動。
dav:/> edit hello.txt Locking `hello.txt': succeeded. Downloading `/hello.txt' to /tmp/cadaver-edit-bmashP.txt Progress: [=============================>] 100.0% of 13 bytes succeeded. Running editor: `vi /tmp/cadaver-edit-bmashP.txt'... Changes were made. Uploading changes to `/hello.txt' Progress: [=============================>] 100.0% of 26 bytes succeeded. Unlocking `hello.txt': succeeded.
ロックをかけてからDLして編集してからUPそて、ロック解除って挙動をcadaverはしてる。 その痕跡が、davの画面にも出てた。
2018/10/30 14:17:02 WEBDAV [OPTIONS]: /hello.txt 2018/10/30 14:17:02 WEBDAV [LOCK]: /hello.txt 2018/10/30 14:17:02 WEBDAV [PROPFIND]: /hello.txt 2018/10/30 14:17:02 WEBDAV [GET]: /hello.txt 2018/10/30 14:17:21 WEBDAV [PUT]: /hello.txt 2018/10/30 14:17:21 WEBDAV [PROPFIND]: /hello.txt 2018/10/30 14:17:21 WEBDAV [UNLOCK]: /hello.txt
ロックがかかったまま、cadaverが亡くなったりすると、次の人は困っちゃうわけだな。 そういう時は、かまわずunlockすればいいのか。
dav:/> lock hello.txt Locking `hello.txt': succeeded. dav:/> edit hello.txt Locking `hello.txt': failed: 423 Locked dav:/> unlock hello.txt Unlocking `hello.txt': succeeded.
紳士協定、WebDAV使う人は善人ですっていうパラダイス理論に則っているんだな。これがまあ、インターネットの設計思想だった訳ですな。
後は、mkcolと言うWebDAV用語を嫌ってmkdirなんて言うaliasとかが定義されてる。普通にオペレーションしてる分には、NFSでリモート側をいじっているとの何ら代わりが無い。
https
443番さんの安全な接続をするには、証明書の類を用意しなきゃならん。巷では、Let's cryptoとかを使うようだけど、そんな大袈裟な事はしたくない。何とかならんかねと、 godoc -http :6060して、httpパッケージのListenAndServeTLS()を見てたら、答えが出てたぞ。地獄に仏である。
// One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem.
どうやらユーティリティが有るようだ。
cent:top$ go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost
これで、証明書ファイルと秘密キーファイルが作成される。内容はbase64だかの無味乾燥なファイルである。でも、人が見て理解し易い形で提示してくれるツールがある。
cent:top$ openssl x509 -text -fingerprint -noout -in cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 32:aa:ba:43:7b:1f:7d:27:c1:09:b1:9a:af:a0:3e:f4 Signature Algorithm: sha256WithRSAEncryption Issuer: O=Acme Co Validity Not Before: Oct 30 06:59:53 2018 GMT Not After : Oct 30 06:59:53 2019 GMT Subject: O=Acme Co Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:bb:54:68:cb:ec:55:5d:eb:05:dc:20:92:f7:0e: : Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: DNS:localhost Signature Algorithm: sha256WithRSAEncryption 5b:f7:b9:78:21:b6:0c:0c:e7:08:9d:3b:46:48:f5:93:7a:45: : e9:b2:56:f8 SHA1 Fingerprint=A8:EA:9C:52:CB:95:59:D7:00:24:4C:A7:56:7F:87:FD:8A:F7:0B:7B
これが証明書の内容。Acme Co(Automatic Certificate Management Environmentが正式名称)って会社の証明書を、同じ会社が署名して発行してる。(から、オレオレ証明書って言われるんだな。)公開鍵は勿論公開されてる。社名は別名でDNSに反映するんだな。今はlocalhostになってるけど、本来はfoo.bar.comとかってのが、ネットワーク上の社名になる。
近頃は、偽の社名を語る詐欺があるんで、DNSもきちんとしましょって方向で大変革が行わるはず。(あれ、行われたのかな。ISPの人なら詳しいはずだな。)
cent:top$ openssl rsa -text -noout -in key.pem Private-Key: (2048 bit) modulus: 00:bb:54:68:cb:ec:55:5d:eb:05:dc:20:92:f7:0e: : publicExponent: 65537 (0x10001) privateExponent: 00:a8:a5:b8:67:06:50:60:6a:2a:ef:5f:60:10:0d: :
こちらは秘密キーの内容。余り面白みが無いな。
cent:top$ ~/dav -s true 2018/10/30 16:18:55 WEBDAV [OPTIONS]: / 2018/10/30 16:18:55 WEBDAV [PROPFIND]: / 2018/10/30 16:18:59 WEBDAV [PROPFIND]: /
セキュリティーなサーバーを起動する。そして、httpsで接続。
cent:~$ cadaver https://localhost:8443/ WARNING: Untrusted server certificate presented for `localhost': Issued to: Acme Co Issued by: Acme Co Certificate is valid from Tue, 30 Oct 2018 06:59:53 GMT to Wed, 30 Oct 2019 06:59:53 GMT Do you wish to accept the certificate? (y/n) y dav:/> ls Listing collection `/': succeeded. cert.pem 1090 Oct 30 15:59 hello.txt 26 Oct 30 14:17 key.pem 1679 Oct 30 15:59
Acme Coの為にAcme Coが発行した証明書だよーーん。 その証明書を受け入れるかね? 勿論です。 1年間有効のやつである。
firefoxとかからアクセスすると、安全じゃないけどどうするって聞かれるぞ。身に覚えが有るんで、躊躇わずに接続。無事に接続出来たよ。
web server
一番簡単なWebサーバーの例を求めて、httpのパッケージを見てたら、安全安心なサーバーの例が出てた。
import ( "log" "net/http" ) func handler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Write([]byte("This is an example server.\n")) } func main() { http.HandleFunc("/", handler) log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) log.Fatal(err) }
この形式って、前回の枕にやったWSGIと同じじゃん。リクエストが有った時に動き出すハンドラーを書く。その内容は、ヘッダーとボディーを(stdoutかも知れない)を返す。
mainの中では、ハンドラーの登録か。第一引数は、URIを指定するんだな。そして、サーバーを起動して、接続を待つとな。
パッケージhttp これ、本家の日本語訳だ。ちゃんと読んで、土地勘を養っておこう。
Go 1.6以降、httpパッケージはHTTPSを使用する場合、HTTP / 2プロトコルを透過的に サポートしています。 HTTP / 2を無効にする必要があるプログラムでは、 Transport.TLSNextProto(クライアントの場合)またはServer.TLSNextProto (サーバーの場合)をNullでない空のマップに設定することで、そうすることができます。 あるいは、現在、次のGODEBUG環境変数がサポートされています。
知らないうちに陰謀にハマる所だったよ。こういうのを英語文の隅々まで読んで探すのは、苦労だから、日本語になってて良かったね。
net/httpパッケージでWebサーバー(handlerの書き方、静的ファイル配信、Basic認証、など)
golangのHTTPサーバを構成しているもの これ、良記事だなあ。おまじないの内容が良く分かる。
Goのhttp.Handlerやhttp.HandlerFuncをちゃんと理解する
Go Web Examples Middleware (Advanced)
neon
cadaverをgdbにかけようとして、自前でコンパイルしたんだ。
gcc -o cadaver ... -lreadline -lcurses -Llib/neon -lneon -lssl -lcrypto -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lexpat
上記は、コンパイルの最後フェーズから抜き出したもの。-lexpatって、 The Expat XML Parserの事なのね。初めて知ったよ。それはいいんだけど、-lneonって、あのネオンの事?(おじさんを惑わすやつって言う理解)そんな事は無いだろう。ちゃんと調べてみろよ。
Neon is an HTTP and WebDAV client library for Unix systems, with a C interface. Featuring: . High-level interface to HTTP and WebDAV methods (PUT, GET, HEAD etc) . Low-level interface to HTTP request handling, to allow implementing new methods easily. . HTTP/1.1 and HTTP/1.0 persistent connections . RFC2617 basic and digest authentication (including auth-int, md5-sess) . Proxy support (including basic/digest authentication) . Generic WebDAV 207 XML response handling mechanism . XML parsing using the expat or libxml parsers . Easy generation of error messages from 207 error responses . WebDAV resource manipulation: MOVE, COPY, DELETE, MKCOL . WebDAV metadata support: set and remove properties, query any set of properties (PROPPATCH/PROPFIND).
ごちゃごちゃと効能が書いてあるけど、まとめるとと表題な通りだな。これが無ければ始まらない。これ、FreeBSDのportsカタログからの受け売りなんだけど、URLが有ったので行ってみた。
WebDAV Resourcesにneonの詳しい説明があった。メインのページの更新情報が2009年の11月で止まってるよ。もう誰も注目してないって事なの?地方は注目されない過疎化の時代ですな。
cadaver on gdb
gdbで解剖実習です。これをやらないと生きてる気がしない。ってか、有難くOSSを利用させてもらってる人の勤めだろうに。ぼーっと使ってんじゃないよ。(チコちゃんの弟子)
CentOSでやろうとしたら、
dav:!> lls Breakpoint 1, execute_command (line=line@entry=0x644ac0 "lls") at src/cadaver.c:493 493 { Missing separate debuginfos, use: debuginfo-install expat-2.1.0-10.el7_3.x86_64 glibc-2.17-222.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.15.1-19.el7.x86_64 libcom_err-1.42.9-12.el7_5.x86_64 libselinux-2.5-12.el7.x86_64 ncurses-libs-5.9-14.20130511.el7_4.x86_64 pcre-8.32-17.el7.x86_64 readline-6.2-10.el7.x86_64
こんな事を言われた。これ全部入れないと駄目? いれたらちゃんと奥まで潜って行けるのかしらん? 河岸を変えて、ウブでやってみるかな。
dav:!> lls Breakpoint 1, execute_command (line=0x5555557c42a0 "lls") at src/cadaver.c:493 493 { (gdb) bt #0 execute_command (line=0x5555557c42a0 "lls") at src/cadaver.c:493 #1 0x000055555555b3e8 in main (argc=<optimized out>, argv=0x7fffffffe4a8) at src/cadaver.c:906
ああ、こちらは大丈夫だ。ディストリビューションによって性格違うな。赤帽さんはIBMに買収されちゃうけど、性格が変わってしまうのかな?
emacsとgdbを組み合わせて実行すると、クライアントが入力を要するものは不便。そんなの分離しよう。リナ系は普通にgdbserverが有るので便利。
gdbserver側で、クライアントを起動
sakae@usvr:/tmp/cadaver-0.23.3$ gdbserver :1234 ./a.out Process ./a.out created; pid = 6984 Listening on port 1234 Remote debugging from host 127.0.0.1 dav:!>
別画面のemacsでgdbを起動。そしてリモートのgdbserverに接続してから継続。
(gdb) target remote :1234 Remote debugging using :1234 Reading /lib64/ld-linux-x86-64.so.2 from remote target... warning: File transfers from remote targets can be slow. Use "set sysroot" to a\ ccess files locally instead. Reading /lib64/ld-linux-x86-64.so.2 from remote target... Reading symbols from target:/lib64/ld-linux-x86-64.so.2...Reading /lib64/ld-2.2\ 7.so from remote target... Reading /lib64/.debug/ld-2.27.so from remote target... (no debugging symbols found)...done. 0x00007ffff7dd6090 in ?? () from target:/lib64/ld-linux-x86-64.so.2 (gdb) c Continuing.
C-cでgdb画面を出して、breakコマンド実行。クライアント側から入力
(gdb) b execute_command Breakpoint 1 at 0x55555555c130: file src/cadaver.c, line 493. (gdb) c Continuing. Breakpoint 1, execute_command (line=0x5555557c4380 "lls") at src/cadaver.c:493 493 { (gdb) n 497 tokens = parse_command(line, &argcount);
後は、gdbコマンドを入力するだけ。
nginx on debian
debianにWebDAVのモジュールが有るかと思って、探してみたよ。そしたら有った。
debian:~$ apt-cache search nginx | grep dav libnginx-mod-http-dav-ext - WebDAV missing commands support for Nginx
これだけをinstall指示したら、本体もひっついてくるかと思ったのだけど、それはなかった。 しょうがないので、nginxも追加で入れた。普通は手順が逆だろうに。
追加で入れたものは、どういう扱いになるかと思ったら、/etc/nginx/module-enabledに、リンクが出来ていて、追跡してくと、
debian:nginx$ lv modules-enabled/50-mod-http-dav-ext.conf load_module modules/ngx_http_dav_ext_module.so;
soですか。so来ましたか。apacheだとDSOとか言う表現だったかな。 sites-available/defaultが、CentOSのnginxのconf.d/defaultに相当するんだな。
前回やったwebdav.confを持ってきて、/etc/nginx/conf.d/webdav.confに
dav_ext_methods PROPFIND OPTIONS;
を追加。どんなオプションが使えるか確認。
debian:~$ curl -sv http://localhost:8282/ -X OPTIONS : < DAV: 1 < Allow: GET,HEAD,PUT,DELETE,MKCOL,COPY,MOVE,PROPFIND,OPTIONS
DAVのバージョン1しかサポートしませんって、冷たい扱いでしたよ。
そしてnginxを入れたんなら、/var/www/html内に、色々なコンテンツを突っ込んで、一人geocitiesをやってみた。根暗だな。
明るく、WordPressでも入れて、世間一般並みになってみる? php+MySQLが必須となると、ちょいと面倒かな?
debian:~$ apt-cache search wordpress blogofile-converters - blog converter collection for Blogofile gnome-blog - GNOME application to post to weblog entries hashcat - World's fastest and most advanced password recovery utility blogliterately - Tool for posting Haskelly articles to blogs libghc-blogliterately-dev - Tool for posting articles to blogs (internals) libghc-blogliterately-doc - Tool for posting articles to blogs (internals); documentation libghc-blogliterately-prof - Tool for posting articles to blogs (internals); profiling libraries blogilo - graphical blogging client libmarkdown-php - PHP library for rendering Markdown data qtm - Web-log interface program ruby-omniauth-wordpress - Wordpress strategy for OmniAuth wordpress - weblog manager wordpress-l10n - weblog manager - language files wordpress-theme-twentyfifteen - weblog manager - twentytfifteen theme files wordpress-theme-twentyseventeen - weblog manager - twentyseventeen theme files wordpress-theme-twentysixteen - weblog manager - twentysixteen theme files wordpress-shibboleth - Shibboleth plugin for WordPress wordpress-xrds-simple - XRDS-Simple plugin for WordPress
debian:~$ sudo apt install wordpress Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: apache2 apache2-bin apache2-data apache2-utils default-mysql-client libapache2-mod-php libapache2-mod-php7.0 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libdbd-mysql-perl libdbi-perl libjemalloc1 libjs-cropper libjs-prototype libjs-scriptaculous libphp-phpmailer libreadline5 libterm-readkey-perl mariadb-client-10.1 mariadb-client-core-10.1 mariadb-common php-common php-gd php-getid3 php-mysql php7.0-cli php7.0-common php7.0-gd php7.0-json php7.0-mysql php7.0-opcache php7.0-readline ssl-cert vorbis-tools wordpress-l10n wordpress-theme-twentyseventeen Suggested packages: apache2-doc apache2-suexec-pristine | apache2-suexec-custom php-pear libclone-perl libmldbm-perl libnet-daemon-perl libsql-statement-perl mail-transport-agent php-league-oauth2-client php-league-oauth2-google openssl-blacklist default-mysql-server | virtual-mysql-server php-ssh2 The following NEW packages will be installed: apache2 apache2-bin apache2-data apache2-utils default-mysql-client libapache2-mod-php libapache2-mod-php7.0 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libdbd-mysql-perl libdbi-perl libjemalloc1 libjs-cropper libjs-prototype libjs-scriptaculous libphp-phpmailer libreadline5 libterm-readkey-perl mariadb-client-10.1 mariadb-client-core-10.1 mariadb-common php-common php-gd php-getid3 php-mysql php7.0-cli php7.0-common php7.0-gd php7.0-json php7.0-mysql php7.0-opcache php7.0-readline ssl-cert vorbis-tools wordpress wordpress-l10n wordpress-theme-twentyseventeen 0 upgraded, 39 newly installed, 0 to remove and 68 not upgraded. Need to get 28.2 MB of archives. After this operation, 147 MB of additional disk space will be used.
なんか、ruby railsの二の舞になりそうな雰囲気。二の舞ってのはブラックボックスって意味ね。もしやるなら、こつこつと組み上げてみる事だな。