highlight.xcode

ラベル 未解決 の投稿を表示しています。 すべての投稿を表示
ラベル 未解決 の投稿を表示しています。 すべての投稿を表示

2018年11月11日日曜日

Debian stretch もしかして腐ってる? 代理マシン編 その2 (x11vnc)



以前TOSHIBA dynabook EX/35LWHKS」というラップトップ PC に、Debian stretch をインストールしたんですが、ラップトップ PC を広げっぱなしにしておくのも邪魔なので、VNC サーバーをインストールし、メイン PC から遠隔操作する事にしました。

普通に Linux に VNC サーバーをインストールすると、VNC クライアントから接続する度に新たなセッションが作られる様になります。それはそれで Linux らしい仕様で良いんですが、今回は既存のセッションを利用する形で運用できないかと思い、色々調べたてみました。どうやら可能みたいです。

x11vnc という VNC サーバーを使用すると、既存のセッションに対して接続出来る様です。x11vnc の基本的な情報は「DebianでX11VNCをインストールする – OSS情報システム研究会」を参考にさせてもらいました。


x11vnc をインストール、パスワードの設定、起動してみます。
# aptitude install x11vnc
(省略)

# x11vnc -storepasswd

Enter VNC password:[パスワード]
Verify password:[パスワード]
Write password to /root/.vnc/passwd? [y]/n
Password written to: /root/.vnc/passwd

# x11vnc -rfbauth /root/.vnc/passwd
VNC クライアントとしては Windows 版 UltraVNC Viewer を使用し、色々試してみました。

が…、またしても腐っていた訳です…。
Debian stretch の x11vnc が…。

イントールされたバージョンは "0.9.13-2" でした。主な問題は次の様なものです。
  1. たまにクラッシュする。
  2. 一部のキーが無限にリピートする。
  3. Shift + TAB入力時に Shift が無視され、TAB のみ入力される。
他にも問題は有るかもしれません…。


取り合えずネットで検索した所「新しいバージョンを使え」という事の様で…。 ご丁寧にスクリプトも公開されていたので、それを参考に、ダウンロード、ビルドを行いました。インストールしたバージョンは "0.9.14" です。

Debian の場合、libxtst-dev、libssl-dev、libjpeg-dev が必要みたいですが、私の環境では libxtst-dev はインストール済みだったので下記では省略しています。
# aptitude install libssl-dev
(省略)
# aptitude install libjpeg-dev
(省略)
# cd /usr/src

# wget http://x11vnc.sourceforge.net/dev/x11vnc-0.9.14-dev.tar.gz
(省略)
# gzip -dc x11vnc-0.9.14-dev.tar.gz | tar -xvf -
(省略)
# cd x11vnc-0.9.14

# ./configure --prefix=/usr/local CFLAGS='-g -O2 -fno-stack-protector -Wall'
(省略)
# make
(省略)
# make install
(省略)
# /usr/local/bin/x11vnc -auth /var/run/lightdm/root/:0 -display :0 -xkb -forever -shared -rfbport 5900 -repeat
しばらく使ってみたんですが、少なくともクラッシュする事はなくなりました。ただし、「キーの無限リピート」や「Shift + TAB が使えない」といった症状はそのままでした。


APT でのパッケージの更新を止めて置かないと、自ビルドしたバイナリを上書きされてしまいますので、このまま使い続ける場合は、x11vnc の更新を停止しておきます。

パッケージの更新停止。
# aptitude hold x11vnc
# aptitude hold x11vnc-dat
パッケージの更新停止を解除する場合。
# aptitude unhold x11vnc
# aptitude unhold x11vnc-dat


「Shift + TAB が使えない」については、解決方法が見つかったので、対処しました。
# vi /usr/share/X11/xkb/symbols/pc
変更前。
key   {  [ Tab,  ISO_Left_Tab    ]   };
変更後。
key   {  [ Tab                   ]   };
これで X の再起動、または PC の再起動で改善されるはずです。


次に、サービス化です。VNC サーバーをサービス化する事により、グラフィカルログイン画面に対しても接続可能とする方法が「debian - Creating x11vnc system service - Unix & Linux Stack Exchange」に、そのままズバリ載っていたので、これも設定します。

vnc.service ファイルを編集(作成)。
# vi /lib/systemd/system/vnc.service
vnc.service ファイルの内容。
[Unit]
Description=Start x11vnc at startup.
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/local/bin/x11vnc -auth guess -forever -loop -noxdamage -repeat -rfbauth /root/.vnc/passwd -rfbport 5900 -shared -remap Zenkaku-Zenkaku_Hankaku,Hankaku-Zenkaku_Hankaku

[Install]
WantedBy=multi-user.target
サービスの有効化、およびリロード。
sudo systemctl enable vnc.service
sudo systemctl daemon-reload
これで、PC を起動直後のグラフィカルログイン画面の状態に対し、VNC ビューワーから接続出来る様になります。

注意が必要なのは、vnc.service ファイルの内容のうち、x11vnc のオプションについては環境により調整が必要だと思います。特にキー関連のイベントの扱いが、使用する VNC ビューワーにより異なるので、思わぬ挙動をする場合もあるようです。


最後に「キーの無限リピート」の対処方、というより誤魔化し方です。

無限リピートが始まったら他のキーを押す事で、リピート自体は止められます。今の所、解決策が見つかっていないので、こんな使い方で誤魔化すしかないでしょう。

私の環境では「半角/全角」キーが 100% リピートするので、「半角/全角」キーを押したら、すぐに「Esc」を押すことでリピートを止めています。

2018年10月26日金曜日

Java 6 の TLS 1.2 対応



いまだに Java 6 使ってます、はい…。
移行しなければならないのは重々承知しています、はい…。

そんな中、HTTPS のセキュリティプロトコルを TLS 1.2 に移行するサイトが増え、TLS 1.2 に未対応の Java 6 ではスクレイピング出来なくなって困っていました。色々と検索した所、暗号化ライブラリを導入すると Java 6 でも TLS 1.2 に対応する事が可能な様です。

また、Let's Encrypt といった新しめの認証局のサーバー証明書も、 Java 6 では対応出来ていないので、それらも導入します。

お品書き。
  1. Bouncy Castle という暗号化ライブラリを導入します。
    ライセンスは Apache Software License, Version 1.1. の様です。
  2. Bouncy Castle を利用して SSL 通信を行う SSLSocketFactory の実装クラス TLSSocketFactory、および関連クラスを導入します。
    ライセンスは MIT X11 License の様です。
  3. 有志作成のスクリプトを参考にし、Let's Encrypt のサーバー証明書を導入します。
    Windows 版 bat ファイル(証明書ファイル付き)、Linux 版 sh ファイル(証明書ファイルダウンロード)を参考にしました。
  4. Let's Encrypt の中間認証局のサーバー証明書を導入します。
    IdenTrust DST Root および、ISRG Root です。
「2.」の Bouncy Castle を利用するクラスについては、別のクラスについて日本語で発信している方が何人かいらっしゃったのですが、私の環境ではことごとく機能しませんでした。細かくは検証していないので原因は不明ですが…。

「4.」の中間認証局のサーバー証明書については、ネットの情報ではどうしてもうまく機能せず、最終手段として Java 8 のキーストアからサーバー証明書を抜いてきて、java 6 のキーストアに突っ込む、とい強引な方法をとっています。

まあ、自己責任ですね…。


Bouncy Castle をプロジェクトへ導入します。
  1. Bouncy Castle の jar ファイルをダウンロードします。
    Latest Releases からダウンロードできます。
    今回私が使用したのは bcprov-jdk15on-160.jar です。
  2. 上記 jar ファイルをプロジェクトのライブラリとして追加します。
  3. また、実行環境のクラスパスにも上記 jar ファイルを追加します。


SSLSocketFactory の実装クラス TLSSocketFactory、および関連クラスを導入します。

TLSSocketFactory からダウンロードするなり、クローンするなり、コピペするなりし、プロジェクトへ 5つのクラスを追加します。
  1. メインとなるクラス TLSSocketFactory
    javax.net.ssl.SSLSocketFactory 抽象クラスの実装クラスです。
  2. 関連クラスTLSAuthentication
    org.bouncycastle.crypto.tls.TlsAuthentication インターフェイスの実装クラスです。
  3. 関連クラス TLSClient
    org.bouncycastle.crypto.tls.DefaultTlsClient 抽象クラスの実装クラスです。
  4. 関連クラス TLSSession
    javax.net.ssl.SSLSession インターフェイスの実装クラスです。
  5. こっちの方がメインとなるクラスとも言える TLSSocket
    javax.net.ssl.SSLSocket 抽象クラスの実装クラスです。
基本的にコードの質は良くないです…。インデントがタブ、スペース混じりだったり、コメントが英語だったり、日本語だったり、無かったり…。API コメントはほぼ無い…。

とりあえずインデントが崩れてるのは、スペース 4つをタブ 1つに置換するか、逆にタブ 1つをスペース 4つに置換すれば揃います…。

あと、1 箇所問題が有って、仮対応として修正しました。

TLSSession クラスのオーバーライドメソッド getLocalPrincipal() が未実装なんですが、このメソッドが呼ばれた場合に、未実装であることを表す UnsupportedOperationException をスローしているので、例外でコケてしまいます。

これを null を返す様にすると、一応は動作する様になります。ただし、これは仮対応なので、本来は正しく実装しないと問題が起きる場合が有ると思います。調査が追いついていませんが、Google が化けるのはこれが原因かなぁ…。

TLSSession # getLocalPrincipal() メソッド。
 @Override
 public Principal getLocalPrincipal()
 {
//	throw new UnsupportedOperationException();
	return null;
 }


Let's Encrypt のサーバー証明書を導入します。

サーバー証明書へのリンクや、サーバー証明書についての説明は Let's Encrypt Chain of Trust に有るんですが、Java のキーストアへインポート出来る der ファイルへのリンクが無いっていうね…。

私は有志作成のスクリプト import-letsencrypt-java.sh を参考にし、Let's Encrypt のサーバー証明書 4つのみインポートするスクリプトを書きました。

私の場合は Java 開発環境をインストールしない派(置いておくだけ)、環境変数 JAVA_HOME を使わない派(パスはスクリプト等で自己管理)なので、実際には、ディレクトリ等を直に指定したり、ダウンロードしたファイルを残しておいたり、ログファイルを出力する等、大幅に書き直しました。が、そんなスクリプトは参考にならないと思うので、オリジナルの一部をコメントアウトしたコードを参考までに載せておきます。

オリジナル からの変更点は、letsencryptauthorityx1.der および、letsencryptauthorityx2.der をダウンロード、インポート、削除しない様にコメントアウトしただけです。

Let's Encrypt のサーバー証明書をインポートする参考コード。
#!/bin/bash -e
# JAVA_HOME can be passed as argument if not set
if [ ! -d $JAVA_HOME ]; then
 JAVA_HOME=${1}
fi

KEYSTORE=$JAVA_HOME/jre/lib/security/cacerts
if [ ! -f "$KEYSTORE" ]; then
 echo "Keystore not found in '$KEYSTORE'"
 exit 1
fi
cp $KEYSTORE $KEYSTORE.`date +"%Y%m%d%H%m%S"`

#wget https://letsencrypt.org/certs/letsencryptauthorityx1.der
#wget https://letsencrypt.org/certs/letsencryptauthorityx2.der
wget https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.der
wget https://letsencrypt.org/certs/lets-encrypt-x2-cross-signed.der
wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.der
wget https://letsencrypt.org/certs/lets-encrypt-x4-cross-signed.der

# to be idempotent
#keytool -delete -alias isrgrootx1 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true
#keytool -delete -alias isrgrootx2 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true
keytool -delete -alias letsencryptauthorityx1 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true
keytool -delete -alias letsencryptauthorityx2 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true
keytool -delete -alias letsencryptauthorityx3 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true
keytool -delete -alias letsencryptauthorityx4 -keystore $KEYSTORE -storepass changeit 2> /dev/null || true

#keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias isrgrootx1 -file letsencryptauthorityx1.der
#keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias isrgrootx2 -file letsencryptauthorityx2.der
keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias letsencryptauthorityx1 -file lets-encrypt-x1-cross-signed.der
keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias letsencryptauthorityx2 -file lets-encrypt-x2-cross-signed.der
keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias letsencryptauthorityx3 -file lets-encrypt-x3-cross-signed.der
keytool -trustcacerts -keystore $KEYSTORE -storepass changeit -noprompt -importcert -alias letsencryptauthorityx4 -file lets-encrypt-x4-cross-signed.der

#rm -f letsencryptauthorityx1.der letsencryptauthorityx2.der lets-encrypt-x1-cross-signed.der lets-encrypt-x2-cross-signed.der lets-encrypt-x3-cross-signed.der lets-encrypt-x4-cross-signed.der
rm -f lets-encrypt-x1-cross-signed.der lets-encrypt-x2-cross-signed.der lets-encrypt-x3-cross-signed.der lets-encrypt-x4-cross-signed.der

キーストア内の一覧を表示する参考コード。
keytool -list -keystore $KEYSTORE -storepass changeit

キーストア内の詳細を表示する参考コード。
keytool -v -list -keystore $KEYSTORE -storepass changeit


Let's Encrypt の中間認証局のサーバー証明書を導入します。

IdenTrust DST Root および、ISRG Root です。

これについてネットで検索すると、特に英語のサイトでは色々なやり方が紹介されているんですが、ことごとく機能しませんでした…。得に IdenTrust DST Root のサーバー証明書には手こずって手こずって、諦めてしまいました…。

で、Java 8 のキーストアからサーバー証明書をぶっこ抜いて、java6 のキーストアへ突っ込む、という強引な方法にたどり着いた訳です…。邪道です…。

これも参考コードを載せておきます。が、あくまで自己責任で…。

あと、パスを直接指定しているので、全てのパスを変更しないと動きません。

Java 8 のキーストアから、Java 6 のキーストアへサーバー証明書をコピーする参考コード。
#! /bin/sh

# Java 8 のキーツールのパス
J8KEYTOOL=/hogehoge/jdk1.8.0/jre/bin/keytool

# Java 8 のキーストアのパス
J8KEYSTORE=/hogehoge/jdk1.8.0/jre/lib/security/cacerts

# Java 6 のキーツールのパス
J6KEYTOOL=/hogehoge/jdk1.6.0/jre/bin/keytool

# Java 6 のキーストアのパス
J6KEYSTORE=/hogehoge/jdk1.6.0/jre/lib/security/cacerts

# 作業ディレクトリ
WORKDIR=/hogehoge
cd ${WORKDIR}

# Java 8 から証明書をエクスポート
${J8KEYTOOL} -exportcert -alias letsencryptisrgx1\ [jdk] -keystore ${J8KEYSTORE} -storepass changeit -file ${WORKDIR}/jdk8_letsencryptisrgx1.cer
${J8KEYTOOL} -exportcert -alias identrustdstx3\ [jdk] -keystore ${J8KEYSTORE} -storepass changeit -file ${WORKDIR}/jsk8_identrustdstx3.cer

# Java 6 のキーストアから既存を削除
${J6KEYTOOL} -delete -alias letsencryptisrgx1 -keystore ${J6KEYSTORE} -storepass changeit
${J6KEYTOOL} -delete -alias identrustdstx3 -keystore ${J6KEYSTORE} -storepass changeit

# Java 6 のキーストアへ追加
${J6KEYTOOL} -trustcacerts -keystore ${J6KEYSTORE} -storepass changeit -noprompt -importcert -alias letsencryptisrgx1 -file ${WORKDIR}/jdk8_letsencryptisrgx1.cer
${J6KEYTOOL} -trustcacerts -keystore ${J6KEYSTORE} -storepass changeit -noprompt -importcert -alias identrustdstx3 -file ${WORKDIR}/jsk8_identrustdstx3.cer

# 証明書ファイルの削除
rm -f ${WORKDIR}/jdk8_letsencryptisrgx1.cer ${WORKDIR}/jsk8_identrustdstx3.cer


おまけ。

Apache HTTPClient で HttpClientBuilder を使用して、素の HttpClient のインスタンスを生成する場合は簡単なんですが、ConnectionManager を使用する場合は工夫が必要なので、参考コードを載せておきます。

素の HttpClient の参考コード。
SSLConnectionSocketFactory sockFctr = new SSLConnectionSocketFactory
(
	new TLSSocketFactory()
	, new String[]{"TLSv1.2"}
	, null
	, new DefaultHostnameVerifier()
);

HttpClient client = HttpClientBuilder
	.create()
	.setSSLSocketFactory(sockFctr)
	.build();

ConnectionManager を指定する HttpClient のダメな参考コード。
PoolingHttpClientConnectionManager connMngr = new PoolingHttpClientConnectionManager();

SSLConnectionSocketFactory sockFctr = new SSLConnectionSocketFactory
(
	new TLSSocketFactory()
	, new String[]{"TLSv1.2"}
	, null
	, new DefaultHostnameVerifier()
);

HttpClient client = HttpClientBuilder
	.create()
	.setConnectionManager(connMngr)
	.setSSLSocketFactory(sockFctr)
	.build();

上記コードの場合、setConnectionManager メソッドで指定した ConnectionManager の SocketFactory でオーバーライドされてしまい、setSSLSocketFactory メソッドで指定した SocketFactory は使用されません。

ConnectionManager を指定する HttpClient の参考コード。
Registry sockFctrReg = RegistryBuilder
	.create()
	.register
	(
		"https"
		, new SSLConnectionSocketFactory
		(
			new TLSSocketFactory()
			, new String[]{"TLSv1.2"}
			, null
			, new DefaultHostnameVerifier()
		)
	)
	.register
	(
		"http"
		, PlainConnectionSocketFactory.getSocketFactory()
	)
	.build();

PoolingHttpClientConnectionManager connMngr = new PoolingHttpClientConnectionManager
(
	sockFctrReg
);

HttpClient client = HttpClientBuilder
	.create()
	.setConnectionManager(connMngr)
	.build();

org.apache.http.config.Registry へ HTTPS 用の SocketFactory と、HTTP 用の SocketFactory を登録し、その Registry を ConnectionManager のコンストラクタへ渡すという、かなり回りくどい方法です。他の方法も有るかとは思いますが、まあ参考まで。

2016年4月9日土曜日

VNC で option キーが送れない

Mac でウィンドウのスクリーンショットを撮ると、ウィンドウの周りに影を表示する枠が付く。これが邪魔になってきたので調べた。

ウィンドウのスクリーンショットを撮るには、「command + shift + 4」を押下し、マウスカーソルが範囲選択用の十字型になったらスペースキーを押下。これでマウスカーソルがカメラ型になるので、撮りたいウィンドウをクリックする。
これが通常の、枠が付いてしまう撮り方。

カメラ型カーソルになった後、撮りたいウィンドウをクリックする時に、option キーを押しながらクリックすると枠無しで撮れる。

が、ここで今まで見ないふりをしてきた問題が顕在化する。

現在の環境は、Mac の画面共有に対し、Windows 機から VCN ビューワで接続し、キーボード、およびマウスの操作内容を送信している状態だ。
Windows キーボードを直接 Mac に接続した場合は、Windows キーボードの Ctrl キー、Windows キー、Alt キーのそれぞれが、Mac の Contorl キー、optionキー、commandキーへと割り当てられる。
ところが、VNC 越しだと Windows キー、Alt キーのどちらも、command キーに割り当てられてしまい、option キーが押せないのである。

もちろん、VNC クライアントも疑い、何種類か試したが改善せず。
Windows キーボードが繋がった Linux 機から接続しても同じ症状なので、これはサーバー側の問題である可能性が高い。

Mac のキー割り当てを変更出来るアプリ Seil(旧 PCKeyboardHack)も試してみたが、ダメだった。
これらのアプリは、物理キーが押されたイベントと、その時に入力されるキャラクタの対応付けを変更するモノの様で、VNC 接続の様にキャラクタを送信しているであろうソフトには効かないのだ。

ググッてみても、日本語、英語ともにこの問題の報告は散見するが、解決策に辿り着く事は無かった。
VNC クライアント側のキーマッピングが変更出来れば、解決しそうではある…。