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環境変数がサポートされています。

知らないうちに陰謀にハマる所だったよ。こういうのを英語文の隅々まで読んで探すのは、苦労だから、日本語になってて良かったね。

Welcome to a tour of Go

net/httpパッケージでWebサーバー(handlerの書き方、静的ファイル配信、Basic認証、など)

Goのhttp serverの雰囲気を理解する

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の二の舞になりそうな雰囲気。二の舞ってのはブラックボックスって意味ね。もしやるなら、こつこつと組み上げてみる事だな。