Joplinの社内利用(Proxy経由での同期)

04/12

Use Joplin for Windows under HTTP Proxy

目次

Need to Write Anywhere

前回紹介した便利なJoplinだが、社内で使うときに困ったことが。
それはJoplinのインターネット接続。

社内からのインターネット接続はProxy経由でしか許していないのだがJoplinを使う場合HTTP Proxy経由でインターネットアクセスを行う必要がある。

What Shoul I Do for It?

Joplinの設定画面を見ても特にそれと思しきところは見当たらない。
macOSやLinuxの場合、環境変数http[s]_proxyというものがあるためそれを使うと良いようだ。
だがWindowsはこういう*nix由来の設計ではなく、コントロールパネルとかインターネットオプションというところでの設定を昔ながらに引き継いでいる。
ここがマルチプラットフォーム対応する上で配慮すべき点と言える。
ただ現時点で何をやっても上手くいないので、どうにかする必要がある。

Getting some helps for it

出来ないのであればできるようにするだけ。
では、どのようにしたらよいか。

Joplin on WSL2

Windows上にLinux環境を作るという選択肢。
X Window環境も作ってそこでLinux用Joplinを起動するって手法。
これはちょっとやりたくない。
Office製品も扱いたいのでLinux環境だとパッと見れないのでストレスでしかない。
ダメ却下

Indirectly Sync by Rclone

rcloneというツールを使うと特定のディレクトリとWebDAVとの同期が取れるようだ。
そこで、同期用ディレクトリをローカルドライブ上のどこか一つ決めて、あとはWSL2なりでDebian入れたりしてrcloneを入れると同期は取れそう。
ただコマンドのヘルプ見たらdiff取ってどうのこうのするってことは出来なさそう。
てことはそこらへんは自前で作るほかない、、、。

先に結論から書くとこれはダメだ、使えない。

graph TD
  a[[joplin]]
    c[WebDAV Server]
    d[(local resource)]
    e((rclone))
    b[proxy server]
  f[(WebDAV resource)]
    subgraph PC
      a-->|Archive|d
      subgraph Debian on WSL2
        e
      end
      e-.->d
    end
    e--->b
    b--->c
  c---f

    style a fill:#008eff,color:#eee
    style e fill:#e57,color:#eee
    style d fill:#991,color:#eee
    style b fill:#f41,color:#eee
    style c fill:#8aa,color:#eee
  style f fill:#991,color:#eee

ちょっとやってみる。
Joplinのリモートディレクトリ構造(WebDAVサーバ側)をみると以下のようになっている。

/.lock/
/.resource/{other files}
/.sync/readme.txt
/.sync/version.txt
/info.json
/{md files}
/locks/{json files}
/temp/

このファイル/ディレクトリたちをうまく同期できればよいと。
Joplinは同期前に.sync/version.txtを見ているようだが何に使っているかはあんまり調べてないが、参照のみのようなので今回は同期の対象外とする。
.lockって使ってるのか?
Apache2のアクセスログみたら、WebDAVクライアント使ってゴニョゴニョしてたときのものらしいから今回は対象外とする。
あと、tempはJoplinがtimeCheck000000.txtみたいなファイルをアップロードしては消してを繰り返している。
少なくとも同期には使わないからこれも対象外。

Setting up

$ sudo apt-get update
$ sudo apt-get install rclone
$ rclone config
yyyy/mm/dd HH:MM:SS NOTICE: Config file "/me/home/directory/.config/rclone/rclone.conf" not found - using defaults
No remotes found - make a new one
n) New remote
s) Set configuration password
q) Quit config
n/s/q> n
name> joplin
Type of storage to configure.
Enter a string value. Press Enter for the default ("").
Choose a number from below, or type in your own value
 1 / A stackable unification remote, which can appear to merge the contents of several remotes
   \ "union"
 2 / Alias for a existing remote
   \ "alias"
 3 / Amazon Drive
   \ "amazon cloud drive"
 4 / Amazon S3 Compliant Storage Providers (AWS, Ceph, Dreamhost, IBM COS, Minio)
   \ "s3"
 5 / Backblaze B2
   \ "b2"
 6 / Box
   \ "box"
 7 / Cache a remote
   \ "cache"
 8 / Dropbox
   \ "dropbox"
 9 / Encrypt/Decrypt a remote
   \ "crypt"
10 / FTP Connection
   \ "ftp"
11 / Google Cloud Storage (this is not Google Drive)
   \ "google cloud storage"
12 / Google Drive
   \ "drive"
13 / Hubic
   \ "hubic"
14 / JottaCloud
   \ "jottacloud"
15 / Local Disk
   \ "local"
16 / Microsoft Azure Blob Storage
   \ "azureblob"
17 / Microsoft OneDrive
   \ "onedrive"
18 / OpenDrive
   \ "opendrive"
19 / Openstack Swift (Rackspace Cloud Files, Memset Memstore, OVH)
   \ "swift"
20 / Pcloud
   \ "pcloud"
21 / SSH/SFTP Connection
   \ "sftp"
22 / Webdav
   \ "webdav"
23 / Yandex Disk
   \ "yandex"
24 / http Connection
   \ "http"
Storage> 22
** See help for webdav backend at: https://rclone.org/webdav/ **

URL of http host to connect to
Enter a string value. Press Enter for the default ("").
Choose a number from below, or type in your own value
 1 / Connect to example.com
   \ "https://example.com"
url> https://{WebDAV_SERVER}/{WebDAV_PATH}
Name of the Webdav site/service/software you are using
Enter a string value. Press Enter for the default ("").
Choose a number from below, or type in your own value
 1 / Nextcloud
   \ "nextcloud"
 2 / Owncloud
   \ "owncloud"
 3 / Sharepoint
   \ "sharepoint"
 4 / Other site/service or software
   \ "other"
vendor> 4
User name
Enter a string value. Press Enter for the default ("").
user> joplin
Password.
y) Yes type in my own password
g) Generate random password
n) No leave this optional password blank
y/g/n> y
Enter the password:
password: **********
Confirm the password:
password: **********
Bearer token instead of user/pass (eg a Macaroon)
Enter a string value. Press Enter for the default ("").
bearer_token> 
Remote config
--------------------
[joplin]
url = https://{WebDAV_SERVER}/{WebDAV_PATH}
vendor = other
user = joplin
pass = *** ENCRYPTED ***
--------------------
y) Yes this is OK
e) Edit this remote
d) Delete this remote
y/e/d> y
Current remotes:

Name                 Type
====                 ====
joplin               webdav

e) Edit existing remote
n) New remote
d) Delete remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config
e/n/d/r/c/s/q> q
$ rclone ls joplin:/
      100 0123456789abcdef0123456789abcdef.md
     1000 123456789abcdef0123456789abcdef0.md
    10000 23456789abcdef0123456789abcdef01.md
       10 3456789abcdef0123456789abcdef012.md
・
・
・
$ cd /mnt/c/Users/{me}/Documents/joplin
$ rclone check joplin:/ ./
yyyy/mm/dd HH:MM:SS ERROR : 0123456789abcdef0123456789abcdef.md: File not in Local file system at /mnt/c/Users/{me}/Documents/joplin
yyyy/mm/dd HH:MM:SS ERROR : 123456789abcdef0123456789abcdef0.md: File not in Local file system at /mnt/c/Users/{me}/Documents/joplin
yyyy/mm/dd HH:MM:SS ERROR : 23456789abcdef0123456789abcdef01.md: File not in Local file system at /mnt/c/Users/{me}/Documents/joplin
yyyy/mm/dd HH:MM:SS ERROR : 3456789abcdef0123456789abcdef012.md: File not in Local file system at /mnt/c/Users/{me}/Documents/joplin
・
・
・
yyyy/mm/dd HH:MM:SS Failed to check: NNN differences found
$ rclone copy joplin:/ ./
$ ls -l
-rw-r--r-- 1 {me} {me}   NNNN  mm月 dd HH:MM 0123456789abcdef0123456789abcdef.md
-rw-r--r-- 1 {me} {me}   NNNN  mm月 dd HH:MM 123456789abcdef0123456789abcdef0.md
-rw-r--r-- 1 {me} {me}   NNNN  mm月 dd HH:MM 23456789abcdef0123456789abcdef01.md
-rw-r--r-- 1 {me} {me}   NNNN  mm月 dd HH:MM 3456789abcdef0123456789abcdef012.md
・
・
・

設定と同期はできるようになった。
ここでは便宜上ユーザプロファイルの中のドキュメントフォルダにjoplinというフォルダを作っておいてそこを同期先に設定する。
あと先述しているが、Debianで使用するユーザの環境変数にはhttp[s]_proxyの設定は忘れずに。

Script

rclonesync

#!/bin/bash
#-
#-@(#) rclonesync ver.1.0.0 2022.04.16
#-
#-Usage:
#-  rclonesync
#-
#-Description:
#-  rclone sync for Joplin
#-
##########################################################################

SRCDIR=/mnt/c/Users/{me}/Documents/joplin
DSTDIR=joplin:/
PATH1=
PATH2=

RCLONE=$(which rclone)
resultfile=/tmp/rclonecheckresult.txt

http_proxy=http://{PROXY_SERVER_ADDRESS}:{PORT}
https_proxy=http://{PROXY_SERVER_ADDRESS}:{PORT}
no_proxy='{excluded_address_pattern}'

############
#-functions
############

#-initialize
function init () {
        ${RCLONE} check ${SRCDIR} ${DSTDIR} >$resultfile 2>&1
}

#-remote -> local
function notFoundInLocal () {
        cat $resultfile |\
					grep "File not in Local file system at" |\
					awk '{print $5}' |sed -e 's/:$//g' |while read i;
        do
                echo -n "${i} ... "
                PATH1=${DSTDIR}
                PATH2=${SRCDIR}
                ret=$(returnPathAfterCheckFile $i)
                ${RCLONE} copy ${PATH1}/$i ${PATH2}/$ret
        done
}

#-each sizes differ
function differFileSize () {
        ${RCLONE} check ${DSTDIR} ${SRCDIR}/ 2>&1 |\
					grep "Sizes differ" 2>&1 |\
					awk '{print $5}' |sed -e 's/:$//g' |while read i;
        do
                echo -n "${i} ... "
                ret=$(returnPathAfterCheckFile $i)
                diff1=$(${RCLONE} lsl ${DSTDIR}$i 2>&1 |awk '{print $2,$3}' |sed -e 's/\.[0]\{1,10\}$//' |xargs -Iddd date -d "ddd" '+%s')
                diff2=$(LANG=C ls -l --time-style="+%Y-%m-%d %H:%M:%S" $i |awk '{print $6,$7}' |xargs -Iddd date -d "ddd" '+%s')
                result=$(expr $diff1 - $diff2)
                if [ $result -ge 0 ];
                then
                        echo -n "remote file is newer than local"
                        PATH1=${DSTDIR}
                        PATH2=${SRCDIR}
                else
                        echo -n "local file is newer than remote"
                        PATH1=${SRCDIR}
                        PATH2=${DSTDIR}
                fi
                ${RCLONE} copy ${PATH1}/$i ${PATH2}/$ret
        done
}

#-local -> remote
function notFoundInRemote () {
        cat $resultfile |\
					grep "File not in webdav root" |\
					awk '{print $5}' |sed -e 's/:$//g' |while read i;
        do
                echo -n "${i}..."
                ret=$(returnPathAfterCheckFile $i)
                PATH1=${SRCDIR}
                PATH2=${DSTDIR}
                ${RCLONE} copy ${PATH1}/$i ${PATH2}/$ret
        done
}

#-Check File Type Then Return Path
function returnPathAfterCheckFile () {
        fileret1=$(echo -n $1 |grep "\.md" >/dev/null && echo "OK" || echo "NG")
        fileret2=$(echo -n $1 |grep "locks\/" >/dev/null && echo "OK" || echo "NG")
        fileret3=$(echo -n $1 |grep "\.resource\/" >/dev/null && echo "OK" || echo "NG")
        if [ "x${fileret1}" == "xOK" ];
        then
                echo ""
        fi
        if [ "x${fileret2}" == "xOK" ];
        then
                echo "locks/"
        fi
        if [ "x${fileret3}" == "xOK" ];
        then
                echo ".resource/"
        fi
}

#-check env
if [ "x${RCLONE}" == "x" ];
then
	echo "rclone command was not found"
	exit 1
fi

###########
#-main
###########
init
notFoundInLocal
notFoundInRemote
differFileSize

作ってみて思った。
これだとファイルを削除できないな。
ファイルを削除しても、ファイルがないと判断されたらどちらかから同期をとってしまうため、一生ファイル消せないな。
もう少し頑張ればできそうだけど。
あと致命的なのが排他処理ができていないため、恐らく古いファイルで上書きしてしまうだろう。
これでは駄目だな。ちゃんとJoplinの同期の仕様を完璧に実装しないと後々事故が起きる
基本一人で作業するので常に1on1の一方向だと思われるが、複数端末で作業することが多いから、排他処理は必須かと思う。
これもダメダッメ。

次だ次。

Cascading WebDAV

てことで次に考えたのが、こちら。
Debian on WSL2にApache + mod_dav + mod_sslでサーバ(ここではテンポラリサーバってことにする)を立ててしまい、Joplinはそこと同期を取る。
さらにテンポラリサーバの実ディレクトリは本当のWebDAVディレクトリをdavfs2なりでマウントしておくという多段WebDAV方式。
検証はしなくてはいけないが新たな作り込みは必要ないかもしれない。
こんな感じ。

graph TD
  a[[joplin]]
  b[proxy server]
    c[Apache2]
    d1[(dav-mounted resource by davfs2)]
    d2[("WebDAV Resource")]
    f["Apache2"]
    subgraph PC
      a-->|WebDAV|f
      subgraph Debian on WSL2
        f
          d1
      end
      f---|Remote Mount|d1
    end
    d1-->b
    b--->c
  subgraph WebDAV Server
      c---d2
    end
  style a fill:#008eff,color:#eee
    style d1 fill:#e57,color:#eee
    style d2 fill:#991,color:#eee
    style b fill:#4d1,color:#666
    style c fill:#8aa,color:#eee
    style f fill:#8aa,color:#eee

ただ、会社のPCがノートPCの場合、異なるネットワークに頻繁に移動するような用途には向いてないと思われる。
デスクトップPCのような据え置きタイプを想定している。

さて構築してみる。

Apache2 on Debian on WSL2

ここらへんは大して重要ではないのでちゃちゃっと行ってしまう。

$ sudo apt-get update 
・
・
・
$ sudo apt-get install apache2
・
・
・
$ sudo a2enmod dav*
Module dav...
Considering dependency dav for dav_fs:
・
・
・
$ sudo htpasswd -c /etc/apache2/.htpasswd joplin
New password: *********
Re-type new password: *********
$ sudo vi /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>

        ServerAdmin root@localhost
        DocumentRoot /var/web
        ServerName {debian-on-wsl2-address}

        Alias /joplin/resource /mnt/joplin/resource
        <Directory /mnt/joplin/resource>
                Options All -Indexes +SymLinksIfOwnerMatch +FollowSymLinks
                AllowOverride All
        </Directory>

        <Location /joplin/resource>
                DAV On
                Options None
                AuthType Basic
                AuthName WebDAV
                AuthUserFile /etc/apache2/.htpasswd
                <Limit PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
                        Require user joplin
                </Limit>
        </Location>

</VirtualHost>
$ 

これで準備OK。
joplinというBASIC認証のユーザを作成。
ServerNameは適当。
次にdavfs2。

davfs2 on Debian on WSL2

WebDAVをディレクトリとしてネットワークマウントできるのがdavfs2。
しかもproxy設定もできる優れもの。
早速仕掛けてみる。

$ sudo apt-get update
$ sudo apt-get install davfs2
(ここでroot以外のユーザもマウントできるように選択)
$ sudo mkdir -p /mnt/joplin/resource
$ sudo usermod -aG davfs2 {me}    #自分をdavfs2グループに設定
$ sudo usermod -aG davfs2 www-data    #apache2実行ユーザもdavfs2グループに設定
$ sudo vi /etc/davfs2/davfs2.conf
use_proxy       1      #proxy使う設定
proxy http://{PROXY_SERVER_ADDRESS}:{PROXY_PORT}    #proxyサーバのアドレス
$ mkdir ~/.davfs2 && chmod 700 ~/.davfs2
$ vi ~/.davfs2/secret
http://{PROXY_SERVER_ADDRESS}:{PROXY_PORT}	''	'' #proxyサーバのアドレスとユーザ名とパスワード
https://{WEBDAV_SERVER_ADDRESS}/{WEBDAVPASS}	{me}	{pass} #実サーバの認証情報
$ chmod 600 ~/.davfs2/secret
$ sudo vi /etc/fstab
https://{WEBDAV_SERVER_ADDRESS}/{WEBDAVPASS} davfs user,noauto,file_mode=0646,dir_mode=0757 0 0   #この行を追加
$ mount /mnt/joplin/resource
・
・ (ここでいろいろ聞かれるかもしれないけど適宜入力)
・
$ ls -la /mnt/joplin/resource
drwxr-xrwx 2 {me} {me}      0  mm月 dd HH:MM .resource
drwxr-xrwx 2 {me} {me}      0  mm月 dd HH:MM .sync
-rw-r--rw- 1 {me} {me}   NNNN  mm月 dd HH:MM 0123456789abcdef0123456789abcdef.md
-rw-r--rw- 1 {me} {me}   NNNN  mm月 dd HH:MM 123456789abcdef0123456789abcdef0.md
-rw-r--rw- 1 {me} {me}   NNNN  mm月 dd HH:MM 23456789abcdef0123456789abcdef01.md
-rw-r--rw- 1 {me} {me}   NNNN  mm月 dd HH:MM 3456789abcdef0123456789abcdef012.md
-rw-r--rw- 1 {me} {me}   NNNN  mm月 dd HH:MM info.json
drwxr-xrwx 2 {me} {me}   NNNN  mm月 dd HH:MM locks
drwx------ 2 {me} {me}   NNNN  mm月 dd HH:MM lost+found
drwxr-xrwx 2 {me} {me}   NNNN  mm月 dd HH:MM temp
$ 

裏で念のためパケットキャプチャをしてみて、ちゃんとproxyサーバ経由でアクセスしているかを確かめる

$ sudo tcpdump -i enp192 tcp dst port {PROXY_PORT}
HH:MM:SS.SSSSSS IP joplinjoplin.net.37334 > {PROXY_SERVER_ADDRESS}.{PROXY_PORT}: Flags [P.], seq 656:736, ack 2665, win 501, options [・・・], length 80
HH:MM:SS.SSSSSS IP joplinjoplin.net.37334 > {PROXY_SERVER_ADDRESS}.{PROXY_PORT}: Flags [P.], seq 736:1084, ack 2665, win 501, options [・・・], length 348
HH:MM:SS.SSSSSS IP joplinjoplin.net.37334 > {PROXY_SERVER_ADDRESS}.{PROXY_PORT}: Flags [P.], seq 1084:1271, ack 2665, win 501, options [・・・], length 187
・
・
・
$ 

おそらく大丈夫そうだ。
準備ができたところで、Apache2を起動する。

Starting Apache2

$ sudo systemctl restart apache2
$ 

ローカルWebDAVサーバのできあがり。
今回は簡易的に構築したのでtls通信はなし。
この時点では端末から外へパケット出ていかないし、外に出ていくときはdavfs2経由でproxyサーバにアクセスするときだけど、そこから先はtls通信を行うので安心。

Setting up Joplin

会社の自席PCのJoplinにて、[オプション] - [同期]から、 debian on WSL2のURLを設定する。

wevdav url
項目名
同期先 wevdav
WebDAV Url http://{debian-on-wsl2-address}/joplin/resource/
WebDAVユーザ名 joplin
WebDAVパスワード **

メイン画面に戻って[同期]ボタンをクリックする。
これで同期が取れていることを確認できている。
あとはdavfs2をDebian on WSL2を起動するたびにマウントするようにできたらOKかな。
これは使えるぞ。

Faced a problem

手元の環境以外で検証しているときにProxyサーバのポリシーによってはこれでうまく行かない場合があった。
原因まではつかめなかった。


コメント: