フォント帳アプリを作る → Linuxフォントの闇に触れる

はじまりはmonospace

フォント一覧として“monospaceフォントの一覧を作りたい”と考えたのだ。

アプリ自体は全フォント一覧も作れるようにしたが、monospace限定でも出力したい。 どのような方法があるかと考えた。

調べるとspacingの値を見ればわかるということはわかったのだが、spacingMonospaceProportionalのような文字列ではなく、90, 100といった値になっている。

とりあえず90100もおおよそmonospaceだということはわかったが、90を提示するがmonospaceでないフォントもある。

だいたい90100の違いはなんなのか…もしかしてQtのmonospaceフォント一覧に関係があるのか…

と調べていくといろいろわかってきた。

100FC_MONOの定数であり、Monospaceのことであるらしい。 90FC_DUALであり、幅を2種類持っているもののようだ。

確かに等幅和文フォントは “monospace” ではない。 “dualspace” である。

では等幅和文フォントのspacing100にした場合Qtウィジットもmonospaceとして認識してくれるのだろうか?

FontConfigの一覧はいじれない?

で、どうやらfontconfigの設定(fonts.conf)はフォントを “要求されたとき” には反映されるけれど、 “リストするとき” には無視されるようだ。

例えばこんな設定ファイルを作る。

Migu 1Mのspacing100(FC_MONO)になる。

% fc-match "Migu 1M" : spacing family
Migu 1M:spacing=100

けど、リストには入らない。

% fc-list :spacing=100 : family | grep -i migu

Foogothicもちゃんと認識はされているけど

% fc-match -a Foogothic : family | head
Anonymous Pro
Anonymous Pro
Anonymous Pro
Anonymous Pro
Migu 1M
Migu 1M
Bitstream Vera Sans
Bitstream Vera Sans
Bitstream Vera Sans
Bitstream Vera Sans

リストにはない。

% fc-list | grep -i foo

90(FC_DUAL)100(FC_MONO)として認識させると、フォントに設定されている幅を無視するアプリケーションが存在するため、リストに入れてくれるわけでもないのならばデメリットしかない。

欧文フォントを和文で使いたいという場合には和文フォントを加える形にしてあげれば良いのだが

問題はQtである。

QtはMonospaceフォントとしてFC_MONOの値よりもspacingが大きいフォントを提示する。

問題は、FC_MONO100であり、FC_DUAL90であるため、FC_DUALなフォントは提示されない、ということだ。

この問題はバグとして報告されているのだが、今以て解決していない。

現状、monospaceを和文フォントにするか、適当な(使わない)欧文monospaceフォントをインストールして、その名前を利用して置き換えるくらいしかなさそうだ。

和文プロポーショナルフォントの話

実は和文フォントは一般的には「ラテン文字はプロポーショナル、全角文字は等幅」となっており、 日本語部分までプロポーショナルになっている「和文プロポーショナルフォント」というのはかなり珍しい。

このためか、ラテン部分がプロポーショナルであるにもかかわらず、spacing=90にしてしまうフォントが結構あるようだ。例えばDynafontとか。

なので、今回のスクリプトは-s 90とすればdualspaceフォントを含めてリストしてくれるけれども、必ずしも等幅フォントであるとは限らない。

今回のスクリプト

GitHubにアップした。

基本的にはFontConfigから値をひらってeRubyで埋め込んだHTMLを作るプログラムである。

プログラム自体は至ってシンプルなものだ。 どちらかといえばFontConfigに関する知識のほうが求められる。

READMEにある通り、うまく動作しない部分がある。 family名を指定しても正しく表示されないもの、fullnameがBoldなどになっているもの、 spacing=90といいながら等幅フォントではないもの1kanahaniが実態と合っていないもの、 などなど「フォントの情報って結構ガバガバだなぁ」と感じてしまう。 もちろん、フォントの情報から取っているわけで、がフォントにウソ情報を書かれたりするとスクリプト側では処理しがたい。

ウェイトの違うフォントが別ファイルになっているものと同一ファイルのもの、PlainやNormalなどRegularではない標準フォントウェイト名であるためにBoldが標準になってしまうもの、 ビットマップとスケーラブルがスタイルの違いになっているもの(例えばTelegramaはRawがビットマップ、Renderがスケーラブル)と厄介なものは色々ある。

ついでなのでおすすめの欧文モノスペース(コーディング)フォント

日本語を交ぜ書きするため、一般的には日本で欧文モノスペースフォントが紹介される機会は少ないし、紹介されるとしてもだいたい定番のものになる (Source Code Pro, Anonymous Pro, Menlo, DejaVu Sans, Inconsolata, Droid Sans Mono, Ubuntu Mono あたり)。

そこで今回合成前提に欧文モノスペースフォントを探し回ったので、よさげなのを紹介しよう。

なお、日本語フォントだと

  • Migu 1M / VLGothic (アルファベットは同じ)
  • Migu 2M
  • MigMix

あたりが定番で、コーディングに適した欧文フォントと合成したものとしては

  • Osaka等幅 (Monaco)
  • Ricty / Myrica / Myricam (Inconsolata)
  • Source Han Code JP (Source Code Pro)
  • 更紗ゴシック (Iosevka)
  • Cica (Ubuntu Mono)

がある。

更紗ゴシックは中国語フォントだが、Jという日本語フォントファミリーを持っているので使いやすい。

monofur

monofur

まるっこくて可愛らしいフォントだけど、見やすく疲れにくい実用的なコーディングフォント。 識別性も大変高い。

ややコミックフォントっぽいけれど、実際はコードがびっしりでもかなり見やすい。

mononoki

mononoki

デフォルメ強くてシンプル。Ubuntu Monoなんかに近いフォント。

Ubuntu Monoほど文字密度がないので、記述量が多くても黒黒としなくて楽。 Ubuntu Monoほど特殊でもないし、より見やすいと思う。

やや幅なあるボクシーなスペースを持っている。

Agave

agave

monofurに近いまるっこい系。 もっとデフォルメが強くて非常に実用的。

似たグリフを持っている文字が全く違う形状になっているので識別は非常にしやすい。 グリフは小さめで、スペースの節約にもなる。

スペースの比率は昔ながらのターミナルフォント的。

印刷するときにも好まれる感じのフォントだと思う。

Monoid

Monoid

今風ターミナルフォント。

線が少なくシンプルだけど先別しやすい。

そして、めちゃめちゃキレイ。Retinaというスタイルがあるくらいだから自信あるんだろう。 すっきり見やすい。

フォントは縦長でグリフが大きめ。 ->, >=, !=, */ といった文字列が特殊な表示になる。

私のmonospaceはMonoid+Migu 1Mになっている。

Hermit

Hermit

ちょっとくどいけど、すごくかわいくて見やすい。

古のIBM系ターミナルフォントを今風にしたみたいな感じだ。 よく見るとブサイクなのだけれど、なかなかかわいらしい。

Atomで使うと細いウェイトが優先される。

Input Mono

Input Mono (Default Setting)

非常に人気の高いらしいフォント。

実際、きれいでかなり見やすい。 テイストとしては Source Code Pro に近い。わずかにタイプライター調。

密度あげたい人はCompressedというバージョンもある。 私はコーディングフォントは四角いスペースのほうが好きだけど、縦長が好きな人にはお勧め。 そこまでいかないCondensedやNarrowもある。

また、好みの分かれるグリフ(i, l, {}, *, a, g)についてはダウンロード時に調整可能。

Luculent

Luculent

lの見やすいフォント。

グリフはちょっと縦長だけど、左右に少しスペースがあるので割とみやすい。 縦長ウィンドウには割と合う。

Boldがわかりやすいのも特徴的。

Monofonto

Monofonto

あまり変哲のなさそうなコンソール系フォントだけど、グリフが小さくて見やすいので、ディスプレイの解像度が低かったりすると結構便利だと思う。

1366x768で作業している人はとりあえず入れておくといいかも。

昔からある、コーディングフォントとしてはお手本のような形状。

Sudo

Sudo

Monofontoよりさらに小さくてみやすいミニマル向けターミナルフォント。

小さいのもあるが、どちらかといえば縦長で横のスペースが少なくて済む。 逆に縦に関しては行間広めなので意外と節約できない。

フォントサイズが小さくなっても読みやすい。

Envy Code R

Envy Code R

シンプルで読みやすいフォント。

フォントが小さいとlがやや判別しにくい。グリフが細るという問題もあるので、フォント大きめだといい。

記号の識別性が大変よい。割とオーソドックスなコーディングフォントだ。

Fantasque Sans Mono

Fantasque

カーブがかってちょっとおしゃれなフォント。コード密度が高いときにはそれほど読みやすくないけど、識別はしやすい。

ちょっとタイプライターフォントと草書体フォントを混ぜたような感じ。 小文字のベースラインがちゃんと揃ってないのもおしゃれなんだろうか。

l, 1, Iの識別性は大変よい。その他見分けやすく読みやすい。 タイプライター系フォントが好きな人には結構いいかも。

CPMono

CPMono

グリフサイズがboxyなフォント。 おしゃれなterm系フォントといった趣。 ちょっとごついけど、結構見やすい。

欠点として、()[]が識別しにくい。あと、1lもだいぶ識別しづらい。 実用よりはややおしゃれよりのフォント。

Fira Code

Fira Code

Source Code Proと同じようなオーソドックスなフォント。 見間違いやすい文字の識別性が非常に良いよう設計されているほか、->, =>, >=, */, == != && といった文字列の結合・特殊化も行う。

すごく今風なコーディングフォントという感じで、欧文圏ではかなり人気もあるらしい。

おまけ。定番モノスペースフォント

Source Code Pro ( Source Han Code JP) / Noto Mono

Source Code Pro

Adobe と Google の共同開発フォント。 フリーフォントの品質の革命とも言えるもので、非常に良い。

グリフは割と幅があるのだが、スペースは行間が大きいため縦長。 表示スペースを多く取ることに好みが分かれる。

非常に見やすくモノスペースフォントの優等生。

DejaVu Sans Mono

DejaVu Sans Mono

Bitstream VesaのUnicode版として2004年から開発されている老舗フォント。 モノスペースフォントも歴史は長い。

グリフが揃っていることを重視したフォントプロジェクトなので見た目に美しくはないが、実用性はMonacoに負けず劣らず高い。

Droid Sans Mono

Droid Sans Mono

ハンドヘルドデバイスなどでの利用を想定し、アセンダーコーポレーションが開発するフォントファミリーのDroid。

その中のモノスペースフォントとしてDroid Sans Monoがある。

Android端末上で等幅フォントを表示する機会は少ないのだが、実は欧文圏では割と人気がある。 日本で人気がないのは、多分Droid Sans Japaneseの品質がだいぶひどいことにあるだろう。

オーソドックスだが、0とOの識別性が低いのが難点。

Inconsolata

Inconsolata

日本人でもある程度使っている人はいる、Rictyに含まれているので実はかなりいる細くて縦長のものスペースフォント。

飾り気はなく、美しくもないが、シンプルで見やすい。 また、特殊スペースが小さくても文字が小さくても読みやすい。

Ubuntu Mono

Ubuntu Mono

本家は別にかわいい路線じゃないのに、なぜかかわいいフォントで人気になってしまったUbuntuフォントファミリー。

Ubuntu MonoもちょっとかわいいフォントとしてSource Code Pro, DejaVu Sans Monoあたりと並んで割と席巻している。

日本でもユーザーは多く、最近はCicaという合成フォントも登場している。

Anonymous Pro

Anonymous Pro

細めのモノスペースフォントで、ややタイプライター風。

ブック向きで印刷映えする。

Iとlがやや判別しにくい。 広い画面で使うにはあまり向いていないフォントでもある。

Hack

Hack

Anonymous Proと並んで「欧文モノスペース」として紹介される定番フォント。

細めのタイプライター風のAnonymous Proに対してこちらは太めのターミナルフォントになっている。 ターミナルフォントとしてはオーソドックスな形状。識別性はかなり良い。

Meslo (Menlo)

Meslo

Mac OS Xのモノスペースフォントは10.6からついにMenloになった。

というわけで負けじとBitstream Vera Sans MonoをMenlo風のフォントに仕立てたのがMeslo。

本当にMacは世界的にファンが多い。

フォント自体はHackと非常によく似たターミナルフォント。 Monacoほどの識別性がないため、だいぶ地味になった。

Monaco

Monaco

Apple製のフォントはプロプライエタリだが、GitHubにクローンプロジェクトがある。

コンパクトで縦長のグリフを持つ。長くMac開発環境の定番として君臨しているだけあって非常に見やすい。非常にオーソドックスで望ましい形をしている。 book系のものスペースフォント。

今回の件で気づいたこと

欧文フォントフェイスはかなり似ている

「読みやすいアルファベット」という条件がついていて、あまり標準的でない形は読みづらさにつながる。

結局、こうした条件のためにだいたいのフォントが似ていて、「ターミナル」「タイプライター」「セリフ」「サンセリフ」の4種類で分類できるような形状しか生まれてこない。

だが、微妙な違いが読みやすさにつながる部分もある。 現在人気のフォントはどれも似たようなサンセリフまたはターミナル体なのだけど、探せばちょっと変わったフォントもあるにはある。 人気のあるフォントの中ではMonacoやUbuntu Monoがちょっと変わったグリフをしている。逆にHackやDroid Sans Monoはものすごく普通。

商用フォントはいいぞ

Dark Mono, Operator, Native, Range Mono, Vivala Codeなど見比べて「おっ、いいな」と思うフォントは軒並み商用フォントになっている。

ただし、プログラマ、コーダーなど「日がな一日プログラムを書いている」人に関しては、これぞと気に入ったフォントがあれば買ってみるのも手ではないだろうか。 一日中向き合っているものだし。

欧文フォントも売られているものは高いものもある

Operatorなんて$199もする。 1書体のお値段としては和文フォントに近い。

日本語フォントとは制作労力が雲泥の差なので、ちょっとえげつない値段だと言っていい。

Dark Monoも40ポンドとかなりお高め。

Atomはレンダリングが汚い

フォントのウェイトやサイズがばらつきやすく、autohintのようなガタツキも発生する。

うまく表示してくれるフォントを探すのにはずいぶん苦労した。

Atomでキレイに表示してくれるのはCPMonoなのだけど、CPMonoはコーディングフォントとして決して快適ではない。

日本語と混ぜるなら、agave, Fantasque, Fira Code, Luculent, Mesloあたりが快適。 私は結局Fira Code+Migu 1Mにした。

この組み合わせは非常に違和感がないが、後述のようにもう少し違和感があったほうが読みやすい。 その点、LuculentやMesloのほうが有力だった。

フォントの情報はいい加減

「漢字グリフはなくてひらがなだけなんだけど、haniは書いてあってkanaは書いてない」みたいなフォントもある。

商用フォントでも結構ザルである。 プログラムとしてはフォントの情報に基づいて処理するしかないわけで、「なんでここでそのフォントが?」と思うようなフォントが提示されたり、偽薬に提示されなかったりというのはここらへんに理由があるのだろう。

日本では紹介されていないモノスペースフォントはいろいろ

日本人としては日本語Glyphのないフォントは使えない、という感覚があるのだろう。 あまり紹介されることもない。

だが、実際にはプログラム中には日本語なんてほぼ使わないわけで、モノスペースフォントは日本語Glyphがなくてもあまり困らないし、 今はglyphがなければそれなりに処理してくれるのが普通なので別に好きに組み合わせても良いのではないだろうか。

日本で紹介されるものは人気があるというよりも無難なものが多く、実際にはもっと使ってみる価値のあるフォントは色々ある。

フォントレンダリングの微妙な差

KDEアプリケーションは割とにじみ強く太めに描画するため、キレイに見える。

Gnomeアプリケーションはもっとくっきり描画する。

どちらもFontConfigを使っているのだけど、描画ライブラリの微妙な差なのか、見え方が割と違う。 FirefoxとChromiumの描画差は気づきやすい。

そして前述のようにAtomはあまりよくない。

autohinterは諸悪の根源

以前は有効にするのが一般的だったように思うautohinterだけども、有効にするとガタガタになる。

フォントが寄る、ガタつく、崩れるなどのことがあったらautohinterを切るといいかも。

FontConfigはデフォルト設定がだいたいいい感じ

FontConfigの設定はディストリビューションによって作り込まれていることが多く、違いがあるけれども、 Arch設定だと色々いじってみても基本的にはデフォルトが良いようだ。

nobitmapと、サブピクセルレンダリングの配置くいらはいじったほうがいいけども。

日本語とアルファベットは差があるほうが見やすい

コーディング向きのglyphを持つ日本語フォント2の場合、統一感があるように調整されている。 「日本語とアルファベットの統一的デザインってなんだ」と思うかもしれないが、主にはグリフのボックスサイズと、線の太さである。

普通は「半角は全角の半分」となるように設計する。Source Code JPフォントの場合は全角は半角の1.5倍幅になっている。

だが、Linux環境ではこのような和文フォントのスペーシングがうまく適用されないことが多い。 Atomの場合和文フォントを指定すると縦には揃ってくれない。このことから、このようにメリットは少ない。

また、日本語にアルファベットを混ぜ書きするケース(スペースを持たないケース)においては、アルファベットは「検索(eyeboll-search)の対象」であることがあり、 区別しやすいほうが良いと考えられる。

このことを踏まえると、「日本語とアルファベットの統一感はなくても困らないし、むしろないほうが便利」だったりする。

おまけ: 今回のフォント画像に使ったコード

JavaScript組み込みHTMLのPandocテンプレートである。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
$for(author-meta)$
  <meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
  <meta name="dcterms.date" content="$date-meta$" />
$endif$
$if(keywords)$
  <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
  <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
  <style type="text/css">
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      body { padding: 50px; font-size: 25pt; }
$if(quotes)$
      q { quotes: "“" "”" "‘" "’"; }
$endif$
  </style>
$if(highlighting-css)$
  <style type="text/css">
$highlighting-css$
  </style>
$endif$
$for(css)$
  <link rel="stylesheet" href="$css$" />
$endfor$
$if(math)$
  $math$
$endif$
$for(header-includes)$
  $header-includes$
$endfor$
</head>
<body>

$for(include-before)$
$include-before$
$endfor$
$if(title)$
<header>
<h1 class="title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
<input type="text" size="20" id="FontForm" /><input type="button" value="Change" id="FS" />
</header>
$endif$
$if(toc)$
<nav id="$idprefix$TOC">
$table-of-contents$
</nav>
$endif$
<input type="text" size="40" id="FontName" style="border: none 0px; font-size: 26pt; background-color: #fff; padding: 5px; border: #39f double 3px; color: #333; margin-top: 2em; margin-bottom: 0.8em" />
$body$
$for(include-after)$
$include-after$
$endfor$
<script type="application/javascript">
  var fm = document.getElementById("FontForm")
  var nameBox = document.getElementById("FontName")
  document.getElementById("FS").addEventListener("click", function(e) {
    for (var code of document.getElementsByTagName("code")) {
      code.style.fontFamily = fm.value
      nameBox.value = fm.value
      nameBox.style.fontFamily = fm.value
    }
  }, false)
</script>
</body>
</html>

  1. Iosevkaはdualspace扱いになっている。

  2. そもそも合成でないフォントでそのようなフォントはかなり少ない。M+(VLゴシックとMiguもこのglyph)くらいではないだろうか。Source Han Code JPとOsaka等幅は専用に調整されてリリースされているものなので含めてもいいかもしれないが。

KDE Plasma5 * Conky

KDE Plasma5の場合、Conkyはwindow_typeによって

  • 表示領域をConkyに奪われる(ウィンドウが置けなくなる)
  • デスクトップをクリックすると裏に行く
  • 通常のウィンドウとして表示される

といった問題が発生する。 これらの問題は他のデスクトップよりシビアだ。

次のようにすることで、正常に表示・動作し、透過も機能する。

own_window = true,
own_window_transparent = true,
own_window_class = conky,
own_window_type = override,
own_window_hints = "undecorated,below,skip_taskbar,skip_pager",
own_window_argv_visual = true,
own_window_argv_value = 0,

新しい設定ファイルではyes, no ではなく、 true, false であることに注意してほしい。

KDE Plasmaとしてはウィンドウとして表示される形式が望ましいもので、own_window_hintsによって表示方法を制御する。

テキストをスクロール表示させる

プログラムについて

諸兄も一度はYouTubveで見たことがあるのではないだろうか。

文字がただ流れるだけの動画を。

自分のペースで読めない上に内容に乏しく、非常にイライラするものだが、 作業ウィンドウ以外で流しておくことでウィンドウフォーカスを切り替えなくて済む、というメリットもある。

特にmpvを使うと、右クリックで再生/停止ができるし、再生速度もコントロールできてまあまあ便利だ。

ただ、検索性がないのでやっぱりストレスである。

そこで、テキストリーダーに自動スクロール機能が欲しい、と思った。

ありそうだが、見当たらなかった。

もちろん、一行単位であればsleepreadでも組み合わせればいいのだが、これはパッと動いてしまうため目の追従が難しく、読みづらい。 1ピクセルずつ動かしたいのだ。

xdotoolを使う、という方法も考えたのだが、意図したようにはうまくいかなかった。

JavaScriptを使えばすごく簡単なのに…というわけで、JavaScriptを使う方向でがんばることにした。

すごーーーーく単純に一時ファイルでHTMLを作ってQtWebengineで表示すればいいと思って、Pythonで作ったのだが…

…Surfでよかった。

まぁ、配布するならSurfみたいなマイナーなものは入れたくないなんていう天邪鬼さん1や、Surfは入っていないなんていう弱小ディストリさんはいっぱいいると思うので良しとしよう。

そんなわけで作った。

珍しく日本語READMEもある。 BGM機能があるのは、YouTubeでよく見るものをリスペクトしたものであり、背景画像がほしければCSS編集でOKだ。

やっていることはごく単純で、Pandocで処理することを前提として一時ファイルを活用している。

プレーンテキストの場合はsedを使ってラインブロック化している。 この場合改行されなくなってしまうので、pre-wrapするようにデフォルトのスタイルシートで変更している。

スクロール制御はJavaScriptでものすごく入門的なコードだ。 右クリックでも再生・停止できるようにしているのは、コントロールをウィンドウフォーカスしてからでなくても行えるようにするためである。

ちなみに、今回のJavaScriptは互換性をあまり気にしない贅沢なコードでもある。 これはかなり新しいGtkWebkit、あるいはQtWebengineを使うという前提が成り立っているためだ。

解説

それでは恒例の初心者向けコード

JavaScript部分

せっかくなので、入門的なJavaScriptコードを解説しよう。

スクロール部分

まずスクロール自体はwindow.scroolByによって実現できる。

自動スクロールをするためにはこれを繰り返すようにしなくてはいけない。 もちろん、単純なループでもできるのだが、JavaScriptはシングルスレッドなので操作不能になってしまう。

このような反復はJavaScriptではタイマーイベントで行う。 タイマーにコールバック関数を登録することで、タイマー起動時にコールバック関数が実行される。 もし他の処理が実行中の場合は処理をキューに入れ、順番がきたら実行される。

反復の場合setIntervalのほうが簡単で、初心者向けの解説ではよくこちらが使われるが、 どちらかといえばタイマーイベントを都度登録するsetTimeoutのほうがコントロールしやすい。

関数オブジェクト、クロージャという考え方になれていないとそもそもコールバック関数が使えないので、ここはしっかりと理解しておく必要がある。

関数オブジェクトは実行可能なコード群をオブジェクト化したものである。 スイッチを押せば実行される物体になっているとでも思えばいい。 コールバック関数は、それを呼び出すタイミングでそのスイッチを押すように動作する。

setTimeout は指定した時間が経過したときにコールバックを行う。 コールバック関数の中で自身をコールバック関数とする setTimeout を呼ぶことでループさせることができる。

「スクロールの開始」はその関数を呼べば良い。 setTimeout で呼ばなくても一度呼べば setTimeout によってループする。

「スクロールの停止」は setTimeout をしなければ次回の実行がなされないため、停止する。 停止方法はタイマーをキャンセルするのではなく、次回のタイマーセットを行わないというものである。 このため、タイマー動作状態がonの場合のみ setTimeout を行う。

setInterval を使用した場合はタイマーをセット/キャンセルして制御する。

キーイベント部分

キーボードのキー入力は document あるいは window に対してイベントリスナーを設定する。

DOM Level1 のonKeydown を設定する場合の情報は割とあるのだが、 DOM Level3 の addEventListener を設定する情報はやや少ない。

コールバック関数の引数としては KeyboardEvent オブジェクトが渡される。 特定のキーをキャプチャするわけではなく、キーボード入力全てをキャプチャしてコールバック関数が呼び出される。 コールバック関数内でキーを判別する。

なお、コールバック関数が時間がかかるとフリーズしていると感じることになるため注意が必要。

キーの種別を取るための方法は色々あるのだが、 code が推奨される。 プリンタブルなキーに限定するのであれば key でも良い。 charcharCode はあまりうまく動作しないし、 keyCodewhich は廃止されている上に、プラットフォームに依存する。

それぞれどのような値になるのかは次のようなコードを書いて確認すれば良い。

元のキーの動作を無効にする場合は、第三引数を false として先にキーを捕捉してから Event.preventDefault によってバブリングを停止する。

右クリック禁止2、でお馴染み右クリックは contextmenu イベントになる3

スクロールスピード

スクロールは

  • スクロールする時間間隔が短くなると速くなる
  • 一度にスクロールする量が増えると速くなる

量が増えるとスムーズさが書けるため、時間を短くするほうが優先。 間隔が1(1ミリ秒)になった場合、加速はスクロール量によって行う。

逆に遅くするときはスクロール量が増えているならそちらを先に減らす。スクロール量が1であれば時間間隔を増やす。

1ミリ秒刻みでは使いにくかろうと思ったので、5ミリ秒刻み、ただし値が小さいほうが変化量は大きいため、5ミリ秒からは1ミリ秒で調整されるようにしている。 (50ミリ秒から+5された場合は10%遅くなるが、10ミリ秒から-5された場合は100%速くなる)

割合で増減させてもいいのだが、どちらかといえばキーリピートが効くため一定間隔にしたほうが良いUIであると考えられる。

今回は50ミリ秒をデフォルトとしたため、10%の5ミリ秒を増減単位とした。 なお、この速度感は画面のピクセル数によって異なり、特にピクセル数が多く高精細なディスプレイの場合は遅く、ディスプレイサイズが大きくピクセル数が少ない場合は速く感じることになる。

設計

基本的には「得意なことは得意な方法で」だ。

このようにスクロールやキーイベントなどはウェブブラウザとJavaScriptが簡単に書ける。 だから無理せずウェブブラウザとJavaScriptで書こうと考えたわけだ。

ただし余計な機能や情報があると使いにくい。 最低限のウェブブラウザが欲しいのだが、既にそのようなものはSurfがあるのでこれを使う。

もっとも、レンダリング部分はQtwebengineやGtkWebkitがあるのだから書くのは非常に簡単である。

もちろん、そのためにはテキストをHTMLにする必要がある。

テキストを正確にHTMLで表現するのはちょっと大変だが、CSSで white-space: pre-wrap にしてしまえばタグ要素と & をエスケープしてしまえば元のテキストを表現できる。 このようなことはSedでもできる。 ただし、 & を先にエスケープしなくてはならない。エスケープがエスケープされることを防ぐためだ。

テキスト処理するためのツールはLinux上に豊富にある。このようなことはシェルが得意とする部分だ。 文字列を埋め込むだけならば特別なツール(例えばeRuby)を使わなくても、ヒアドキュメントで十分だろう。 プログラムはシェルスクリプトで構築する、という方針は簡単に決められる。

中間的なファイルや生成に必要なファイルは、もちろん予め用意してリンクさせることもできるが、ディレクトリ設計に関する障害を増やすことになる。 今回はローカルディレクトリにインストールする、という前提を与えてはいるが、それでもできれば避けたいところだ。 それにバージョンアップの手間も考えれば、一時ファイルを作る方針にした。 これも簡単なものなのでヒアドキュメントで処理する。

ブラウザを作るにあたっては、私の得意なRubyではなく、割と苦手なPythonにした。 Rubyにもqml Rubyバインディングは存在するし、動作もするが、メンテナンスされておらず(3年間放置されている)、情報も非常に少ない。 また、ruby-qmlよりもpyqtのほうがqml自体もシンプルに書けるので、Pythonを採用した。

表示はウェブブラウザ(作ったもの的にはPython+qml+Webengine)で、スクロールはJavaScriptで、変換と橋渡しはシェルスクリプトで。 コンポーネントを分けて、それぞれが得意なことをシンプルに行う。 問題を簡単にし、ミスを減らすポイントでもある。


  1. もっとも、Unsurfを動かすためにはPyQt5とpython-openglが必要なので、ハードルの高さはどちらが上やら

  2. だいぶ懐かしい響きだが、今でもしているところはある。あまり意味はない。

  3. より正確にいえば、これはコンテキストメニューを表示させたときに発生するイベントで、右クリックと一対一ではない。キーボードのメニューキーを押した場合や、右クリック以外をコンテキストメニューキーにしている場合も同様にも発生するし、右クリックがコンテキストメニューでないのならば発生しない。

Linuxのターミナルエミュレータを比較してみた

ターミナルエミュレータにみんな不満を持っているようなのに、きちんと選んでない気がするので、比較。

メジャー編

挙動 Gnome Terminal Xfce4 Terminal MATE Terminal Konsole QTerminal Xterm
タブ Yes Yes Yes Yes Yes No
単一タブ時の表示 Hide Hide Hide Configuratable Configuratable -
カラーテーマ Scheme & Edit Scheme & Edit Scheme & Edit per Profile (Scheme) Scheme Configuratable
背景画像 廃止 Yes Yes Yes (+Blur) Yes No?
背景透過 廃止 (外部から可能) Yes Single Color only Yes Yes Yes?
アプリケーション透過 No No No No Yes No
タブの色分け プロファイルごと Yes Yes Yes Yes -
セル間隔調整 Yes Yes No No No No
ambiguous char Configuratable Configuratable FontConfig FontConfig FontConfig FontConfig
プロファイル Yes No Yes Yes No File
スクロール Yes Yes Yes Yes Yes Yes
バッファリミット Configuratable Configuratable Configuratable Configuratable Configuratable CLI Option
スクロールアップ時の新規出力 Configuratable Configuratable Keep Keep Scroll Scroll?
スクロールアップ時の入力 Configuratable Configuratable Scroll Scroll Scroll Scroll?
終了確認 No No No No Yes No
マルチタブでの終了確認 No Yes No Yes No -
稼働中プロセスでの終了警告 List No List List No No
フォント 自由 自由 自由 QT monospace QT monospace 自由
Backspace Configuratable Configuratable & Auto Configuratable Keyboard Type Emulation ASCII DEL
Del Configuratable Configuratable & Auto Configuratable Keyboard Type Emulation \e[3~
単語選択文字設定 No Yes Yes Yes No Yes
画面分割 No No No No Yes No
開くディレクトリ Terminal Process Default Tab Current / Configuratable Terminal Process Default Terminal Process Default Terminal Process Default / Tab Current -

Gnome TerminalとMATE Terminalはログイン端末にするかどうかの選択ができる。

XFce4 Terminalはダブルクリック時に選択する文字列や、ベルも設定可能でかなり細かい。 ただし、稼働プロセス警告やプロファイルはない。

KonsoleはURLヒントの表示、点滅テキストの制御などが可能。 マルチタブ終了もプロセス終了も警告される。

QTerminalは使いにくいポイントや配慮不足が色々ある割に、 ドロップダウン端末や、ショートカット設定、画面分割など独特な機能は持っている。 また、プロセス終了警告がなく、タブでプロセスが稼働している場合にタブを閉じると確認せずにタブを閉じる。

マイナー編

挙動 LXTerminal VTE3 Sakura Pantheon Terminal rxvt Unicode ST
タブ Yes No Yes Yes No No
単一タブ時の表示 Hide - Configuratable Show - -
カラーテーマ Scheme & Edit ? Sceheme (ウィンドウ色は編集可能) Edit Edit CLI Option / Build Config
背景画像 No ? No No No? No
背景透過 No ? No Yes Yes? No
アプリケーション透過 No No? No No No No
タブの色分け No - Yes No - -
セル間隔調整 No ? No No Yes No
ambiguous char FontConfig FontConfig FontConfig FontConfig FontConfig FontConfig
プロファイル No No? No No File / CLI Option No
スクロール Yes Yes Yes Yes Yes No
バッファリミット Configuratable ? Fixed Fixed Configuratable -
スクロールアップ時の新規出力 Keep Keep? Keep Keep Keep -
スクロールアップ時の入力 Scroll Scroll? Scroll Scroll Scroll -
終了確認 No No No No No No
マルチタブでの終了確認 Yes - No No - -
稼働中プロセスでの終了警告 No No No/Caution Only Caution Only No No
フォント 自由 ? 自由 自由 自由 CLI Option / Build Config
Backspace ASCII DEL ASCII DEL? ASCII DEL ASCII DEL ASCII DEL ASCII DEL
Del \e[3~ \e[3~? \e[3~ \e[3~ \e[3~ \e[3~
単語選択文字設定 Yes ? No No Yes No
画面分割 No ? No No No No
開くディレクトリ Tab Current - Tab Current Home Directory - -

LXTerminalとPantheon Terminalは機能自体がすごく限定的だ。

VTE3については情報がなくて設定方法がわからなかった。 色んな端末が内部的に使っているので入ってはいるけれど、直接使う人はいなさそう。

Sakuraは設定できる項目がだいぶ少ない。Pantheonも同様でここらへんはユーザーが触れる項目を減らす思想か。

Pantheon Terminalは終了時に開いていた数だけタブを開く。 多分全くうれしくないが、設定可能である。

urxvtは何気にフォーカスオフ時に暗くすることもできたりする。

STはスクロールできないと言っているが、Shift+Pg{Up,Down}でできる…が、かなり端末以外の設定に依存してしまうためできないと思ったほうがよい。 STは非常にシンプルで、日本文化にはなじまないかなぁ。

おまけ: grml-zsh-config のグラデーションプロンプトテーマ

端末 表示
Gnome Terminal グラデーション、ただし幅がおかしい
XFce4 Terminal グラデーション
MATE Terminal グラデーション
Konsole 文字として表示
QTerminal 文字として表示
XTerm 文字として表示
LXTerminal グラデーション
VTE3 グラデーション
Sakura グラデーション
Pantheon Terminal グラデーション
LXTerminal グラデーション
rxvt Unicode 文字として表示
ST 文字として表示

比較してみて

XFce4 Terminalが非常に優秀。

おことわり

とりあえず試してみたというところなので、各端末の設定に関して知悉しているわけではない。

誤りや不足がある可能性がある。

  • 誤りの指摘
  • 比較して欲しい点の追加
  • 設定方法のレクチャー

などどしどしお寄せいただきたい。

Let’s encryptとSSL/TLSに関する誤謬

全く以て意味不明な誤謬がはびこっていた上に、やたら上から目線だったので、消火しておこうと思う。

そもそもSSL, TLSとは何か

SSL/TLSは暗号化技術である。

SSL/TLSのデータ通信自体は対称暗号である。ただし、暗号化に利用する暗号鍵は使い捨てる。 Cipherはかなり色々使えるのだけど、だいたいはTriple DES (3DES)かAESが使われる。

その手順は

  1. <- HelloRequest
  2. -> ClientHello
  3. <- ServerHello
  4. <- ServerCertificate
  5. <- ServerKeyExchange
  6. <- ServerHelloDone
  7. -> ClientKeyExchange
  8. -> Finished
  9. -> ChangeCipherSpec
  10. <- Finished
  11. <- ChangeChiperSpec
  12. <-> Application Data

Key Exchangeというのは非対称暗号鍵の交換である。 これもその場で生成するもので、RSA/DHE/ECDHEのいずれかを使用する。

非対称暗号というのは暗号化する鍵(公開鍵)と復号化する鍵(秘密鍵)のペアからなる。 公開鍵では暗号化はできるが復号化はできないため、公開鍵は相手に渡してしまうことができる。 これを利用してKeyExchangeでそれぞれ公開鍵を送りつけるわけである。

RSAの場合はServer Certificateで送りつけたサーバー証明書に含まれるサーバー公開鍵を使って共有する秘密を暗号化して送りつけるだけだが(ServerKeyExchangeは省略される)、DHEの場合はちょっと複雑だ。

サーバーは共通値という値とキーペアを作成する。 そして、クライアントに対して共通値と公開鍵を送信する。

クライアントは共通値に基づいてキーペアを作成する。そして公開鍵を送信することで共有秘密値を算出できるようになる。

そしてお互いが共有秘密値に基づいて暗号鍵を生成する。同じ値から同じ方法で生成するのでサーバーとクライアント双方が同じ鍵を持っている状態となり、これにより対称暗号によるやりとりが行える。

証明書とはなにか

SSLに付属しているのが証明書、という概念である。

これは中間者攻撃を防ぐための仕組みで、サーバーの証明書に第三者が署名する。 サーバーの署名にはサーバーのドメインが含まれている。

これによってどうやって信頼を保つのか? これは、予め「信頼できる者」を決めて、その信頼できる者が署名したものは信頼できるという考え方だ。

悪意ある第三者が証明書を偽造しようとしても、サーバーのドメインを含んだ証明書を、「信頼できる者」は署名してくれない。 だから通信相手になりすまして情報を掠め取ろうとしても、信頼できる署名された証明書を送ることができず発覚する、というわけだ。

「信頼できる者」が実は信頼できない、ということはないのか?

答えは、ある。最近ではGoogleがセキュリティ企業大手のSymantec傘下の認証局が署名した証明書を信頼しない、と決めた。

ドメインの正当性の確認方法

サーバーの証明書に署名するには、署名を要求された証明書が間違いなくドメイン所有者のものであることを確認しなくてはいけない。

ではそれをどうやって確認するか?

方法は色々あるのだが、ドメイン所有の確認方法は現在はウェブを使う方法が一般的だ。 指定されたファイルを指定されたアドレスでアクセスできるように配置する。 本当にドメインの所有者であればそのドメインのアドレスでアクセスできる場所にファイルを配置することが可能だが、そうでなければそんなことはできない、というわけだ。

この方法は割と簡単で合理的だ。

DVとOV

さて、一般に使われている証明書はDVとOVがある。

DVというのはそのドメインの所有者であることを認めた署名だが、OVというのはそのドメインを所有する組織の実在を確認したものである。

これは登記簿を提出したりする。 まあ、ドメインの所有者情報と企業情報が一致することを確認するのだ。案外簡単に通る。

で、OVのほうが価値がある、と言われるのだが。

  • あなたはその証明書がDVであるかOVであるかを確認しているか?1
  • あなたはOVなら信頼するけどDVでは信頼しない、という対応をとっているか?
  • 「その企業は実在する」という情報は「その相手を信用する」に値する情報か?

私は意味がないと思っている。

SSLの良いところ

まず、計算量的安全性という意味からいえば、解読できるようになったとき解読できる量が少ないというのは計算価値を下げることになる。

さらに解読しなければならない鍵が増えると計算量自体が増えることになる。

SSLの悪いところ

技術に罪はないが仕組みが問題だった。

予め信頼できる者を決めてしまう。 もちろん、大手セキュリティ企業などだ。

するとどうなったか。

第一に、「ボロい商売」になった。

SSLはかなり重要な技術だ。 SSLの重要性は主には暗号化だ。盗聴の難易度と中間者攻撃の難易度はまるで違う。 盗聴はとても簡単だが、中間者攻撃はそうそうできない。 だから重要度としては中間者攻撃を防ぐことよりも盗聴を防ぐこと、暗号化することなのだ。

ところが、SSLの価値は証明書にあるように言われた。 当然だ。SSLの採用自体は別に「信頼できる者」の力は必要ない。 「信頼できる者」が証明書を売るためには「署名がないと危険です!!」というしかない。 実際、中間者攻撃を防ぐ仕組みがある以上、有効な証明書のない証明書は警告されることになる。

となるとSSLを使うためには署名してもらわなくてはならない、という図式ができあがる。 これを利用して高額で売りつけるわけだ。

ちなみに、現在署名の値段は年間8万円から22万円ほどである。 昔はもっとずっと高かった。

ビジネスでやっているのならできなくはないが、個人的にやっているだけのところでは使えるわけがない。

結果として社会的に不可欠な技術であるにもかかわらず、SSLを使える者を限定する、という行為に走ったわけだ。 「SSLを採用することが特別なことになってしまった」ということはより問題として深く、重大である。

SSLの構造悪とLet’s Encrypt

このような問題への対応として、Let’s Encryptが誕生した。

セキュリティ的見地から言えば、重要なのは「高額なSSL署名を購入すること」ではなく、「社会的にSSLを使用すること」が重要である。2 ウェブばかりが取り沙汰されるが、メールに至ってはSSL/TLSを使わなければパスワードも普通に平文で送りつけるのだ。34

そこでMozillaやGoogleなどが主導してHTTPSを当たり前のものにするべく、誰にでも使えるSSL認証を開始した。5 これがLet’s Encryptである。

Let’s Encryptは無料なだけではない。 簡便で、かつ透明性が確保されている、というのも特徴だ。

Let’s Encryptはルート認証局のIdenTrustによって署名されている。 これによりLet’s Encryptの署名は信頼できるものとして取り扱うことができるようになっている。

Let’s Encryptが信頼できない、という謎の誤謬

まぁ、根拠は全く不明だ。

Let’s Encryptではドメインが正当なものかどうかわからないだろ、と言ったりするのだが、 ACMEトランザクションは公開されているし、ウェブを使って自ドメイン上に指定ファイルを設置するのでドメイン所有は間違いなく確認できる。

むしろ証明書発行過程が不透明な他のDVよりもはるかに信用できると思う。6

OVには劣る、というのはわかるが、そもそもOVを使っているところなんてとても少ないし、「DVだ!!信用できない!!!」という発言ではないので意味がない。 Let’s EncryptがDVとしての機能を満たしていないというのであれば、なぜ満たしていないのかぜひ私に説明してほしい。 もちろん、Let’s Encryptでドメイン所有を偽る具体的な手法を、だ。

基本的には、「○○が署名したやつがいいんだ、Let’s Encryptなんて格が低い!!!」みたいな、SSL証明書にワインのような味わい深さを求めているか、あるいはたくさんお金を出すと偉いとか安心できるとか考えている人だと思う。 いずれにせよ、権威主義的なのではなかろうか。7

ところで、Let’s EncryptはIdenTrustが署名しているのだけど、IdenTrustは信用できない、という発言なのだろうか? そうなると、Let’s Encryptに限らず相当たくさん敵を作ることになると思うのだけど、アメリカのことだから別にいいや、ということだろうか。

署名と検証

動機となったもののうちに「証明書の確認でパフォーマンスが」という発言がある。

基本的には、証明書には証明あるいは署名の有効期限がある。 切れたら再取得する。新しいものがあれば更新するし、なければ失効したということで信頼できないとみなす。

実際には「署名したけど不正がみつかったので取り消す」ということがあるので、もうちょっと頻繁に取りに行く。

検証するためにサーバーに問い合わせる必要は全くない。 なぜならば証明書が正しいかどうかは手元にある公開鍵で検証できるからだ。

目的を思い出せ

DV証明書を用いたSSLの目的は

  • 安全な暗号化を行う8
  • サーバーがドメインの所有者であることを確認する

の2点である。

別にその相手が信用できるかとか、そんなことは関係ない。 というか、そんなことはむしろOVによって確認されるように企業が実在したところで信用できるかどうかとは関係ない。 実在するけど健全じゃない企業なんてたくさんあるだろう?

Let’s Encryptはこれを満たしているはずだし、これを採用しない、 つまり暗号化を行うことも相手を確認することができない状態よりもずっと良いはずだ。

Let’s Encryptが他のDVに劣る点は一体なんだろう? 私には全く想像もつかない。

残念ながらLet’s Encryptは駄目だと言っている人が、この2点を満たしていない具体的な根拠を示しているのは見たことがない。9

認証について指摘があったので重ねて

example.comの証明書に署名をもらおうとすると、Let’s Encryptは「http://example.com/abcdefgとしてこのファイルをダウンロードできるようにしてください。ダウンロードできたら署名します」と求めると同時に、配置するための一時ファイルを送ってくる。

Let’s Encryptからファイルをもらう方法はHTTPSである。

Let’s Encryptが署名で保証しているのは、これを通過できた、という点に尽きる。

「Let’s EncryptのDNSを偽れたらどうなんだ」

という点については、まぁ、ものすごくハードルが高いので、完璧ではないけれど標準的にはとりあえず良しとできると思う。 そもそもそんなことはすぐバレるし、バレたら即座にLet’s Encryptの証明書がBANされることになるので、まぁ、大丈夫だろう。

そもそもSSLは暗号化が本筋であって、証明書が本体ではないので、そこに完璧を求める気もない仕様だ。

これは「DV一般の中でLet’s Encryptが駄目だという誤謬」に言及しているのでDV自体に問題があるという点については述べていないのだが、その場合はどう判断するか、ということについて言うと、 DV一般に不十分だとしたら(私はむしろそう考えている)、「SSLって駄目だよね」という話なので、認証が十分かどうかを論じる必要もない。そうなったらむしろSSLで十分かを論じることになるからだ。

他のDVの確認方法がどうなのか…という点については、全く標準化されておらず、手続きも事業者によって違うので、 ザルなところもあれば厳しいところもある。ただ、ACMEはその中では割と偽るハードルが高い(ただし、現状ではその価値も高いけれど)ことと、手続きの透明性が確保されていること(不当なドメインでないかをどうやってちゃんと確認してるのかよくわからないところもある)を考えれば 相対的には Let’s Encryptは悪くないと思う。

個人的な意見だが、SSLの証明書はお墨付きを与えているように感じる。 ドメイン自体に問題がある(例えばフィッシングドメインなど)場合でも正当な証明書は取れるわけで、そこで安心感を持ってしまうリスクを考えれば、「証明書の署名を必須にしてDVなんていう仕組みを導入したこと自体が失敗だったのではないか」と感じる。 「クレジットカードをとるようなところは取得した者を明らかにする」みたいなOVを拡張したような方式をオプショナルに採用すればよかったのではないか。

SSLの証明書の方式に問題があるということは、私は15年は言い続けているけれど、その話は長くなるので略。

SSHはSSLじゃない

SSHはSSとついているせいか、SSLを使っていると思っている人が結構いるようだ。

SSHはSSLを使っていない。別物だ。

ただし、SSH2では鍵交換の方法としてDHEを使っている。 非対称暗号でセッションを開始し、対称暗号でやりとりを暗号化する方式も同じだ。だから、似てはいる。

なお、鍵ログインに使用するRSA/ECDSA/Ed22519の鍵は通信の暗号化で使うわけではなく、通信を暗号化したあとにホストの真正性を問うために使う。10

SSHでもPKIを使用した認証が行える、という話もあるのだけど(SSLと全く同じ方式)、これは私が見たことがないのでわからない。

参考文献

Qiitaでかなり詳しい記事を見かけたので参考にしてほしい。


  1. 「OVはブラウザで鍵マークの右に組織名が表示される」旨記載していたが、そのように表示されるのはEVのみであるという指摘があったため当該箇所は削除した。 どうもDVとOVで表示の違いはなさそうなので、区別している人などいるのだろうか。

  2. この意味では、「平文で通信するよりは自己署名証明書のSSLを使うほうがずっとマシ」なのである。

  3. もちろん、認証のたびに平文で送るだけでなく、受信だろうが送信だろうがメール本文も読み放題である。繰り返すが盗聴は非常に簡単だ。

  4. もっとも決済情報があるのにHTTPSでないサイトもたまに見るので、「ウェブは深刻じゃない」というのはものすごく間違っている。

  5. SSLを特別な (なんらかの格付け、あるいは権威付けに使うような) ものにして市場価値を高めるべきではなく、SSLを誰もが当たり前に使えるようにすることが社会的メリットだという主張である。これは、Let’s Encryptが無料だということに限らず、これに追随する他の無料認証局が出現したり、SSL署名を販売している認証局が価格を引き下げたりしてより普及に弾みがつけば大成功、というわけだ。

  6. 「証明書発行過程が不透明な他のDVより」であって、「他のDVより」ではない。

  7. この話はもともと「出、出ーwwwLet’s Encrypt使奴wwww」的な発言を受けての話なので、別にLet’s Encryptを使わないのが問題とは思っていない。むしろ権限集中するような事態はなんとしても避けるべきだと思う。

  8. 一応補足すると、安全な暗号化通信自体は自己署名証明書であっても可能である。安全性はCipherなどの問題で、証明書は暗号の安全性には影響を与えない。

  9. 言うまでもないとは思うが、Let’s Encryptの署名はDVとしての役割を十分に果たしている、という主張であって、Let’s Encryptは他のDVより優れているという主張でも、DVは十分に安全確実なものであるという主張でもない。

  10. サーバーが乱数を生成し、公開鍵で暗号化してクライアントに返す。クライアントは秘密鍵で解読し、乱数値からハッシュ値を算出、これをサーバーに送信する。サーバーで同欄数値から算出したハッシュ値と一致した場合は真正とみなしてログインを許可する、という仕組みである。

Linux: 特定のアドレスに到達しないことを明示する (route/ipコマンド)

open-iscsiを用いたiSCSIイニシエータの場合、iSCSIターゲットが複数のIPアドレスを持っているとMultipathによってその全てのアドレスで接続しようとする。

だが、例えば192.168.1.0/24に属するコンピュータで、192.168.1.0/24, 192.168.2.0/24の両方に属しているNASを使おうとすると、192.168.2.0/24に対する経路がなければタイムアウトするまでひどく待たされることになる。 デフォルトゲートウェイ経由で192.168.2.0/24に対するパケットを投げ続けるためだ。

そこで、このコンピュータは192.168.2.0/24には通じていないことを明示して、即座に失敗させたい。

これはネットワークマネージャの設定では行うことができない。 コマンドで行う必要がある。

routeコマンドに関してはManpageに書いてあるのでわかりやすい。

# route add 192.168.2.0 netmask 255.255.255.0 reject

だが、Arch Linux系列のディストリビューションで標準採用されるipコマンド (iproute2) のほうは1Manpageがものすごく読みにくい上に日本語訳もされていないのでちょっと大変だ。 ipコマンドの場合は次のようにする。

# ip route add prohibit 192.168.2.0/24

これにより192.168.2.0/24に対する接続はそもそも禁止されることになる。

% ping 192.168.2.64
Do you want to ping broadcast? Then -b. If not, check your local firewall rules.
% telnet 192.168.2.64
Trying 192.168.2.64...
telnet: Unable to connect to remote host: 許可がありません

  1. そもそもrouteやifconfigなどを使わず、iproute2(ip)を使うということをどれくらいの人が知っているのだろう? arp, ifconfig, iwconfig, nameif, netstat, route を兼ねる仕組みになっており、Manjaro Linuxではこれらのコマンドは標準では入っていない。 digやnslookupも含まれておらず、getentを使用する。

一連のサーバー&SSH技 応用・実用編

このブログは多くが検索によって解決法を探してたどり着いた読者に支えられている。

このような場合、その人が向いている方向としては二種類ある。

ひとつは、なにをしようとしているかが(適切かはどうかはともかく)決まっている場合だ。 ポートフォワーディングしたい、とか、SSHでなんとかならないか、とか、自分の中で絞り込みができている場合である。

もうひとつは自分の現状だけを認識している場合だ。 例えば「ストレージがいっぱいなった」とか、「家のデータに外からアクセスできなくて不便だ」とか。 どのような解決方法があるのか、あるいは解決可能なのかどうかから探している状態だ。

このふたつは明確に異なる。

前者であればポイントを絞った答が欲しい一方、自分の中にある前提が間違っていると解決まで回り道をすることになる。 後者であれば何をどう考えて調べればいいのか絞り込んでいくほうが難しい。

しかし前者の問題は解決できるのだが、後者の問題は解決できない、という人が割と多い。 問題を定義し、設計し、手法を構築することは地力が求められ、知識の体系化が進んでいないために発想力が求められると難しい、というケースだ。

そこで、今回はそのあたりに踏み込んで解説していく。

個別ケースや個々の人の理解に合わせた内容は仕事としてやっているので、興味があればのぞいてみてほしい

Overview

Definition

親と同居しているごく普通のLinuxer少女が、日々を便利にするために奮闘する姿でも描けばよい。

…と思ったのだが、VPSを用意するにあたりクレジットカードが必要になるあたり、やはりペット同居のごく普通の乙女、もちろんLinuxerである、ぐらいにとどめておくのが無難か。

phase 1

ストレージの不足を解消するためのNAS導入と、監視カメラの設置を行う。

スマートフォンはきっと愛猫の写真と動画でいっぱいなのだろう。 これに愛猫が勝手にツナ缶を空けて食べてしまわないかチェックしておく必要がある。

phase 2

NASに写真を保存するようにしたはいいが、このままでは仕事中に愛猫の写真を堪能できない。

24時間愛猫とたわむれるためには家にあるデータにもアクセスできなくてはならない。

phase 3

もし愛猫がトラに進化しそうなときには急いで家にかえりこれを阻止しなくてはならない。

また、愛猫がNASからハードディスクを取り出そうとしたときにはデータ保護のためにNASを緊急停止する必要があるはずだ。

構成と発展

予めお断り

コピペでできるようにすることを目標としているわけではないので、詳しい手順などは各自学んでほしい。

難しい部分は過去の記事で解説している。

家用コンピュータを導入する

この手順において一番最初に、そして独立して効果を発揮するのはNASなのだが、設定なども面倒なので、とりあえずコンピュータを用意しよう。

この段階でラップトップがあるのであれば、NASがさき、デスクトップは後でも構わないし、 この場合必ずしもデスクトップである必要はない。 だが、総合的に考えてよりよいコンピュータ環境を構築するのが目標なので、このような余剰部分も今回は入れていく。

さて、やはりコストパフォーマンスを考えても、デスクトップPCは性能が高い。 そこでデスクトップを導入することにしたいが、おしゃれに気を使う乙女としては部屋にあまりに無骨な機械を導入するのは気が引ける。 かといってせっかくコンピュータを導入するならいろいろしてみたい。 悩みに悩んだ彼女は神に問うた。天の声(はるか)はこう答えた

「Pavilion Waveあたりにしとけば?」

まじめに答えて、まじめに構築する話はそのうちMimir Yokohamaでやることにしよう。

NASを導入する

NASの導入と構築もそのうち向こうでやることにする。 今回はLinuxでのテクニック中心なので、簡単に。

ReadyNASあたりが鉄板だろうし、WebDAV(HTTP)で共有すればスマートフォンのデータを転送するのも簡単だ。

もう少しスマートな話をすると、まずはReadyNASでストレージ環境を作り、共有の作成まで進める。 共有をSMBにして、

# mount -t cifs //nas-aa-bb-cc/shared1 /srv/nas

のようにマウントする。

次にスマートフォンをLinuxデスクトップに接続し、MTP経由でデータをコピーする。 あとは

$ rsync -rv ~/Pictures/20180602 /srv/nas/

のようにして(/の有無に注意)コピーすればバックアップが確保された状態になる。

監視カメラを導入する

これはどちらかといえばSocks Proxyの説明のためなので詳しくは省くけれども、ウェブカムとmotionを使うのが定番。動体検知もできる。

これについては結構設定が色々ややこしい。 興味がある人は“Linux motion カメラ”で検索すれば色々でてくるので参考にすれば良い。 設定ファイルを眺めるのも悪くない。

ネットワークに接続する

NASを導入する時点で有線でのネットワークは構築されているはずだが、無線LANルーターを含めて、さらに 有線での インターネット接続環境を構築する。

VPSを契約する

私はConoHaのとServerMan@VPSのユーザーだが、さくらのVPSも結構定番。

今回はゲートウェイとして使うだけなので、最低限でも大丈夫でSSH接続だけできるようにすればよいが、発展的にはあまりおすすめできない。

VPSを契約したらサーバーを設定し、SSHログイン可能な状態にしておく。

ラップトップを買う

外からアクセスできるコンピュータが必要である。 ラップトップを買おう。ネットカフェのような環境からアクセスすることを考えるべきではない。

SSHを設定する

ラップトップで2つのSSH鍵を作り、それぞれデスクトップとサーバーのauthorized keyとして登録する。

制限をかけてもいいが、今回の場合鍵のパスワード保護が可能なため、特にかけないほうが良いだろう。

ラップトップ側の~/.ssh/configは設定が必要。

Host vps
  User jrh
  Port 22
  HostName vps.example.com
  IdentityFile ~/.ssh/vpslogin_rsa

Host target-proxy
  User jrh
  Port 10000
  HostName localhost
  IdentityFile ~/.ssh/proxylogin_rsa
  ProxyCommand ssh -CW %h:%p vps

この場合、VPSのログインしてからリバースプロキシを介してデスクトップにログインする。

localhostはVPSから見たlocalhost(VPSのlo)だが、デスクトップにログインするためのSSHを実行するのはラップトップである。

単純にログインしたあとログインする場合、デスクトップにログインするSSHを実行するのはVPSなので、これがProxyCommandの特徴となる。

単純にSSHを張るだけであればデスクトップからVPSにログインできるようにして、

$ ssh -R 22:localhost:10000 vps

のようにすればよいのだが、経験ではそれはうまくいかないのでそれは記事にした

外から家のデータにアクセスする

これでSSH経由でアクセスできるようになったので、デスクトップでNASをマウントしておけばデータは利用可能な状態になる。 Nemo, Thunarならばアドレス欄にsftp://target-proxy/と入力すればアクセス可能。NautilusならConnect to Serverから。 Dolphin/Konquerorにも同等の機能はあるのだが、うまく動作しない場合がある。

VPSのコンソールやmotionのストリーミングにアクセスする

VPSのコンソールなどWebインターフェイスをもつものについてはインターネットに公開するべきではなくインターネットに閉じておくべきだが、これでは外出中にコントロールできない。

だが、SSHでアクセスできる状態であれば、簡単に解決できる。

$ ssh target-proxy -NCD 8888

Chromiumが簡単。

$ chromium --proxy-server="socks://localhost:8888"

Vivladiでも良い。

$ vivaldi-stable --incognito --proxy-server="socks://localhost:8888"

Firefoxなら専用プロファイルか

$ firefox -P proxylogin

【注意】 Arch/Manjaro 最新 open-iscsi パッケージにバグ

Arch / Manjaro Linux の最新 open-iscsi パッケージをインストールすると、 libopeniscsiusr.so.0.1.0 というライブラリが欠如しているためにiSCSIが動作しなくなる。

システム構成の中核部に採用している場合はシステムが利用できなくなるため要注意。

有志がAURのgitバージョンからビルドした2.0.876-2をアップロードしており、このパッケージはManjaroでもそのまま動作する。

Linuxで プロセスごとにネットワークを禁止 / インターフェイスを限定

特定のプロセスをオフラインにする

稀にネットワークを禁止した状態で利用したいことがある。 例えば安全性に疑問があるケース、無用な通信が疑われるケース、ファイルをダウンロードできているかの確認などだ。

方法としては

  • cgroupsを使う
  • iptablesを使う (EUID/EGID単位なら制御可能)
  • SELinux + iptables を使う

の3種類。

単純にネットワークを利用できないようにするのはunshare

$ sudo unshare -n w3m /some/text/file

環境変数とかXAuthorityとか面倒なので、Firefoxなどを実行するならsudoでrootにしてsudoで戻すのが勧め。

特定のプロセスを特定のゲートウェイから流す

これだけだと「なんだよ」とか言われてしまうので、もうちょっとまともな話をしよう。

SSH+pppdを使ったVPNは比較的知られているが、VPNはプロキシを通すのに使うか、デフォルトゲートにしてしまうかというのが一般的で、プロセス単位でVPNにルーティングする方法というのはあまり知られていない。

しかしこれもcgroupsを使って実現可能だ。

ここではiptablesによるルーティングを目的とした仮想インターフェイスやブリッジを使うのではなく、あくまで既に割り当てられているインターフェイスを使用させることを目的とする。

まずはトンネルを通しておく。

# pppd updetach noauth silent nodeflate pty "/usr/bin/ssh root@gwhost /usr/sbin/pppd nodetach notty noauth" ipparam vpn 10.0.8.1:10.0.8.2

専用に名前空間を作り、pppデバイスを登録する。

# ip netns add vpnns
# ip link set ppp0 netns vpnns

pppデバイスにアドレスを与える。 pppは/32プレフィックスがつくのだけど、/32だと通信できないので、/24にしておく。

# ip netns exec vpnns ip addr add 10.0.8.1/32 dev ppp0

これで10.0.8.2とは通信できるようになった。 あとはこの状態でシェルを立ち上げ

# ip netns exec vpnns bash -l

お好きにどうぞ。

SSH+pppdのトンネルをデフォルトルートにするのはなかなか苦労がある。 もし単純に物理的なインターフェイスをデフォルトルートとして使わせたいのならDHCPが使えて楽。

# ip netns exec newns dhcpclient eth1
# ip netns exec newns bash -l

もっと簡単な方法

SOCKSを使えばもっと楽にできる。

$ ssh -NCD8080 proxyhost

単純にSOCKS5プロキシとしてlocalhost:8080を指定すればOK。SSH経由で流れてくれる。

SOCKSに対応していないプログラムでも、proxychainsやtsocksを使えばできる場合も多いだろう。

プロセスごとに帯域制限を

cgroupsとtcを使ってください。

現状では理想的に動作しないのでこの話は略。 情報はここらへんにある。

ThinkPad E440がスリープから復帰できない問題 (機種固有)

ThinkPad e440は基本的にLinuxではスリープ/サスペンドからの復帰が全くできない。 スリープに入る段階でフリーズしてしまうため、スリープはできるのだけれども、電源再投入ができなくなる。 ちなみに、Windowsでもやや不安定で復帰できないことがある。

Linuxのスリープというのは結構複雑な問題で難しく、調整が必要なことは珍しくないのだが、それでもなかなかうまくいかず、 しかしe440の出番がそれほど多くないことから放置していた。 そもそも私がスリープをほとんど使わないという理由も大きい。

ただ、最近不便を感じ始めていたので対応することにした。

ArchWikiには機種ごとの情報があったりするので調べてみたところ、e440のページもあった

のでこれに基づきBIOSのアップデートを試みる。 現在の最新バージョンは2018-03-06のv.2.27で、ArchWikiの記述と異なりISO配布されているため特別なツールは必要ない。

アップデートしたところ何の問題もなく復帰できるようになった。 なんのこっちゃ。