<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[てくにかるむ]]></title><description><![CDATA[「エラーをなくすことは非常に有益で時には新しい真実や事実を作り上げるよりも勝る」
ー チャールズ・ダーウィン]]></description><link>http://multix.jp/</link><generator>Ghost 0.6</generator><lastBuildDate>Tue, 10 Mar 2026 19:08:13 GMT</lastBuildDate><atom:link href="http://multix.jp/tag/memorandom/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[SOAP::Transport::HTTP がおかしい]]></title><description><![CDATA[<p>Perl 5.20.3 + SOAP::Lite ver1.20 で XQuery 投げるとなぜか落ちる。調査すると STDIN に対して Bareward エラーが。それでこういう場当たり的なパッチが必要になった。おいおい？</p>

<pre><code class="language-brush:perl gutter:true">--- C:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2016-12-17 10:45:40.000000000 +0900
+++ D:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2017-01-23 12:42:33.000000000 +0900
@@ -586,6 +586,</code></pre>]]></description><link>http://multix.jp/soap-lite-failed/</link><guid isPermaLink="false">59c25a8e-8ac7-4a28-bce5-3b3bf7ed3827</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Fri, 24 Feb 2017 01:24:43 GMT</pubDate><content:encoded><![CDATA[<p>Perl 5.20.3 + SOAP::Lite ver1.20 で XQuery 投げるとなぜか落ちる。調査すると STDIN に対して Bareward エラーが。それでこういう場当たり的なパッチが必要になった。おいおい？</p>

<pre><code class="language-brush:perl gutter:true">--- C:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2016-12-17 10:45:40.000000000 +0900
+++ D:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2017-01-23 12:42:33.000000000 +0900
@@ -586,6 +586,7 @@
         #my $content = q{};
         if ( !$chunked ) {
             my $buffer;
+            no strict 'subs';
             binmode(STDIN);
             if ( defined $ENV{'MOD_PERL'} ) {
                 while ( read( STDIN, $buffer, $length ) ) {
</code></pre>

<p>直接原因はよくわからんがとにかくこうすれば正常動作はした。STDIN が <code>strict subs</code> に引っかかるって通常ではちょっと考えにくいところだが？</p>

<hr>]]></content:encoded></item><item><title><![CDATA[textareaでTAB入力]]></title><description><![CDATA[<p><code>&lt;textarea/&gt;</code> のフォーカス内でTABキーを押すと普通は次のform部品へフォーカスが移ってしまうが、これをTAB入力に変換する。フォーカス動作はキャンセルしなければならないので keydown イベントを使う。</p>

<hr>

<pre><code class="language-brush:javascript gutter:true">// textareaでのTABキー入力
$('textarea').on('keydown', function(e){
    if (e.keyCode === 9) {
        e.preventDefault();
        var elem = e.target;
        var val = elem.value;
        var pos = elem.selectionStart;
        elem.value = val.substr(0, pos) + '\t' + val.substr(pos, val.length);
        elem.setSelectionRange(pos + 1, pos</code></pre>]]></description><link>http://multix.jp/tab-input-textarea/</link><guid isPermaLink="false">f0beb3a4-5add-4f20-85b1-c2f21b70f871</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 20 Feb 2017 09:31:33 GMT</pubDate><content:encoded><![CDATA[<p><code>&lt;textarea/&gt;</code> のフォーカス内でTABキーを押すと普通は次のform部品へフォーカスが移ってしまうが、これをTAB入力に変換する。フォーカス動作はキャンセルしなければならないので keydown イベントを使う。</p>

<hr>

<pre><code class="language-brush:javascript gutter:true">// textareaでのTABキー入力
$('textarea').on('keydown', function(e){
    if (e.keyCode === 9) {
        e.preventDefault();
        var elem = e.target;
        var val = elem.value;
        var pos = elem.selectionStart;
        elem.value = val.substr(0, pos) + '\t' + val.substr(pos, val.length);
        elem.setSelectionRange(pos + 1, pos + 1);
    }
});
</code></pre>

<ul>
<li><code>e.keyCode === 9</code> <br>
TABキー押し下げを検知する。</li>
<li><code>e.preventDefault()</code> <br>
ブラウザのデフォルト動作を停止する。つまりフォーカス移動をキャンセルする。</li>
<li><code>e.target</code> <br>
イベント対象のDOMエレメントを取得する。すなわち <code>&lt;textarea/&gt;</code> 本体。jQueryの <code>$(this)[0]</code> におなじ。</li>
<li><code>elem.value</code> <br>
入力されている <code>&lt;textarea/&gt;</code> の内容。</li>
<li><code>elem.selectionStart</code> <br>
キャレットカーソルの位置。次の行でこの位置に<code>\t</code>を挿入して書き戻している。</li>
<li><code>elem.setSelectionRange(Start, End, [Direction])</code> <br>
キャレットを<code>\t</code>の後ろへ進める。選択範囲は無いので <em>Start</em> と <em>End</em> にはおなじ値を指定する。</li>
</ul>

<p>なお <em>selectionStart/selectionEnd</em> プロパティは直接書き換えることもできる。またキャレット選択範囲の後方は <em>elem.selectionEnd</em> で得られる。なので選択範囲があるなら丸ごとインデントするといったコードも書けなくはない。選択方向を指示する Direction はプラットフォームによって挙動が若干異なるので<a href="http://www.html5.jp/tag/elements/forms-textFieldSelection.html">こちら</a><sup id="fnref:1"><a href="http://multix.jp/tab-input-textarea/#fn:1" rel="footnote">1</a></sup>も参照のこと。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><a href="http://www.html5.jp/tag/elements/forms-textFieldSelection.html">http://www.html5.jp/tag/elements/forms-textFieldSelection.html</a> <a href="http://multix.jp/tab-input-textarea/#fnref:1" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Allied Telesis CentreCOM の小技]]></title><description><![CDATA[<p>最近のアラテレは SDNへの注力もあって ATxシリーズへの世代移行が加速しており、昔ながらの CentreCOMシリーズは青息吐息な感があるけれども、かえってかつての高級機が手頃な値段でオークション等に出回るようになった。ここでは CentreCOM系では最後にして現役の AR5xxルータおよびそれと同族の 8xxxスイッチ関係の小技をいくつか取り上げる。<sup id="fnref:1"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:1" rel="footnote">1</a></sup></p>

<ul class="index"></ul>  

<hr>

<h4 id="">コンソールへのアラートを黙らせる</h4>

<p>AR系や87xx系でファイアウォールを有効にすると、デフォルトではルールにないパケットを受信する度にマネージャ権限以上でログインしているコンソール（async/telnet/ssh）にアラート通知が流れてしまう。</p>

<pre><code class="language-brush:plain">XXX.XXX.XXX.XXX attempting to use non-policy interface

Denial of service attack from XXX.XXX.XXX.XXX is finished  
</code></pre>

<p>設定作業中にこれらが次々に流れてゆくと作業の邪魔も甚だしい（電源を切りたくなる）ので、次のコマンドでコンソールへのアラートは止めてしまおう。</p>

<pre><code class="language-brush:plain">disable firewall notify=manager  
</code></pre>

<p>UnTagポートにTagパケットが入ってきた時も <code>attempting to use</code></p>]]></description><link>http://multix.jp/allied-telesis-centrecom-memorandom/</link><guid isPermaLink="false">ca593ede-78b8-4ffc-9364-73cbc0bf2005</guid><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 27 Jul 2015 08:39:00 GMT</pubDate><content:encoded><![CDATA[<p>最近のアラテレは SDNへの注力もあって ATxシリーズへの世代移行が加速しており、昔ながらの CentreCOMシリーズは青息吐息な感があるけれども、かえってかつての高級機が手頃な値段でオークション等に出回るようになった。ここでは CentreCOM系では最後にして現役の AR5xxルータおよびそれと同族の 8xxxスイッチ関係の小技をいくつか取り上げる。<sup id="fnref:1"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:1" rel="footnote">1</a></sup></p>

<ul class="index"></ul>  

<hr>

<h4 id="">コンソールへのアラートを黙らせる</h4>

<p>AR系や87xx系でファイアウォールを有効にすると、デフォルトではルールにないパケットを受信する度にマネージャ権限以上でログインしているコンソール（async/telnet/ssh）にアラート通知が流れてしまう。</p>

<pre><code class="language-brush:plain">XXX.XXX.XXX.XXX attempting to use non-policy interface

Denial of service attack from XXX.XXX.XXX.XXX is finished  
</code></pre>

<p>設定作業中にこれらが次々に流れてゆくと作業の邪魔も甚だしい（電源を切りたくなる）ので、次のコマンドでコンソールへのアラートは止めてしまおう。</p>

<pre><code class="language-brush:plain">disable firewall notify=manager  
</code></pre>

<p>UnTagポートにTagパケットが入ってきた時も <code>attempting to use non-policy</code>と怒られるので、VLAN変更をしている最中にも、これをしておくと結構助かる。</p>

<p>なお<code>notify=all</code>にするとすべてのアラートが止まるが、ログにも残らなくなる。ファイアウォール設定が充分固まって逆に <code>FIRE ATACK</code> 通知で <code>sh log</code> が埋まるようになったら、全部止めたほうがむしろ都合が良いだろう。</p>

<hr>

<h3 id="">時系列逆順ログ</h3>

<p>コマンドラインヘルプ（?キーのあれ）では表示されないので気づきにくいが <code>show log reverse (sh log rev)</code> でログを時系列の逆順に見ることが出来る。最新のログだけ確認して Quitで抜けるのに便利だ。</p>

<pre><code class="language-brush:plain">&gt; sh log rev

Date/Time   S Mod  Type  SType Message  
-------------------------------------------------------------------------------
27 17:58:21 3 IPG  IPFIL DUMP  Received invalid DA 8.240.213.66&gt;22.103.89.96  
                               Prot=17 Int=ppp5
27 17:57:57 3 IPG  IPFIL FRAG  DirBcast Fail 6.102.80.241&gt;22.103.89.103  
                               Prot=6 Int=vlan12
27 17:57:43 3 IPG  IPFIL FRAG  DirBcast Fail 19.6.240.45&gt;22.103.89.103 Prot=6  
                               Int=vlan12
</code></pre>

<p>もちろん moduleと組み合わせて特定カテゴリのログだけ表示するときにも使える。</p>

<pre><code class="language-brush:plain">&gt; sh log reverse module=ssh

Date/Time   S Mod  Type  SType Message  
-------------------------------------------------------------------------------
27 17:51:39 3 SSH  SSH   RJCT  Rejecting SSH connection from 43.29.53.43  
                               incompatible versions
27 17:43:23 3 SSH  SSH   RJCT  Rejecting SSH connection from 45.14.11.37  
                               incompatible versions
27 17:37:53 3 SSH  SSH   RJCT  Rejecting SSH connection from 43.29.53.51  
                               incompatible versions
</code></pre>

<p>この機能は AR5xxS系や8xxxXL系で使えるが、9424T/SP等では使えない。</p>

<hr>

<h4 id="securitymode">Security Mode の有効時間を伸ばす</h4>

<p>IPSec/PKI/SSH を有効化して SECURITYOFFICERユーザに切り替えると、デフォルトでは1分で再ログインを求められるようになる。いくらなんでもこれは短いと感じた時は、次のコマンドを打ち込む。</p>

<pre><code class="language-brush:plain">set user securedelay=3600  
</code></pre>

<p>設定可能な範囲は 10〜3600秒である。最短の10秒にすると相当の苦行を味わえるが、タイプ速度が上達する効能もある。</p>

<hr>

<h4 id="">非同期ポートのボーレートを変更する</h4>

<p>アラテレ製品では、管理用シリアルコンソールポートを<mark>非同期ポート</mark>と呼称する。初期値は baud=9600なのでこれを変更するには、次のようなコマンドを打ち込む。</p>

<pre><code class="language-brush:plain">set asyn=0 speed=115200

# 以下でも同じ（show conf dynの表示はこちら）
set asyn=asyn0 speed=115200  
</code></pre>

<p>打ち込んだ瞬間に設定が反映されるので、現在正に非同期ポートでログインしている場合は、対向端末側も設定を合わせるまで制御不能になることに注意しよう。</p>

<p>だがこの設定にかかわらず、機器ブート（起動時診断）メッセージは baud=9600固定である。従って動的にボーレートを変更できる端末ソフトを使っていないと思ったより難儀してしまう。結局できるだけ 9600固定で運用するほうがいざというとき慌てずに済むかもしれない。</p>

<p>なお設定内容は <code>show asyn=0</code> で確認できる。</p>

<hr>

<h4 id="ntp">NTPモジュール</h4>

<p>NTP時刻合わせのテンプレを以下に示す。ログ記録に関わるので、機器に IPを付したら真っ先に行う作業だ。</p>

<pre><code class="language-brush:plain"># NTPモジュールを活かして時差を指定する
enable ntp  
set ntp utc=+09:00:00

# peerアドレスは一つだけ書ける
# DNSモジュールを活かしていても FQDNでは指定できない。
add ntp peer=133.243.238.163

# 強制同期
# "ACTIVATE NTP" コマンドではない（そういうものはない）
reset ntp

# ステータス表示
show ntp

# peerアドレスの変更
# "SET NTP PEER"コマンドが存在しないので "DEL"して "ADD"する
# 当然DNSラウンドロビンも出来ないので機器毎にバラけた設定をしたりする
del ntp peer=133.243.238.163  
add ntp peer=133.243.238.164  
</code></pre>

<hr>

<h4 id="l3ppp">L3スイッチでの PPP接続</h4>

<p>L3スイッチでのLAN型PPP接続の例を挙げる。下記は 8948XL、8724SL/8748SLでの場合だ。<sup id="fnref:2"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:2" rel="footnote">2</a></sup></p>

<pre><code class="language-brush:plain"># Port1をアップリンクポートとして ONU接続に使う
create vlan="PPPoE-FLET" vid=4  
add vlan="4" port=1

# そのポートの中に PPPセッションを張って PPPインタフェースを作る
create ppp=4 over=vlan4-any  
set ppp=4 bap=off iprequest=on username="&lt;ISPアカウント&gt;" password="&lt;パスワード&gt;"  
set ppp=4 over=vlan4-any lqr=off echo=10 

# PPPインタフェースの IPは ISPから貰う
enable ip  
enable ip remote  
add ip int=ppp4 ip=0.0.0.0

# ・・・ことにしつつ、内側のVLANにそのIPを付けて NETMASKを切る
# これがL3スイッチ配下から見たゲートウェイIPになる
add ip int=vlan1 ip=&lt;付与されたグローバルIPのひとつ&gt; mask=255.255.255.248

# デフォルトルートは PPPインタフェースに丸投げする
add ip rou=0.0.0.0 mask=0.0.0.0 int=ppp4 next=0.0.0.0  
</code></pre>

<p>上位への接続は L3ルーティングとなる。設定のミソは本来 PPPインタフェースに付くべき IPを、内側の VLANにつけるところだ。</p>

<p>言うまでもなかろうが、IPが一つしか付与されない端末型接続の PPPでは、NATが使えなければおよそ意味が無い。L3スイッチ系では通常は NAT接続ができないので（ファイアウォールライセンスの購入が別途必須）、その場合はおとなしく ARシリーズを使おう。AR系と 89xx/87xx系の使い分けは、装備された物理ポートの数とNAT機能の有無で、PPPインタフェースの設定については概ね互換性がある。</p>

<hr>

<h4 id="">工場出荷時初期化</h4>

<p>Security Mode 状態から工場出荷時設定へ一気に戻すには次のようにする。AR550S v2.9.2 で確認した。</p>

<pre><code class="language-brush:plain"># 非同期ポート速度を初期値に変更
set asyn=0 speed=9600

# 設定ファイルを読み込まずにコールドスタート
restart router config=none

Warning: Config file MUST add a user with SECURITY OFFICER privilege.  
Do you wish to proceed with restart using specified config?(y/n)

INFO: Initialising Flash File System.  
INFO: Router startup complete

# 規定値ユーザの manager/friend でログイン
login: manager  
Password:

# SECURITY_MODE を切る
disable system security_mode

Warning: This command will disable security mode and delete all security files.  
Are you sure you wish to proceed?(y/n)  y

Info (1034003): Operation successful.

# 起動設定を消して、元の起動設定ファイルを rename あるいは delete fileする
set conf=none  
rename boot.cfg boot-old.cfg  
</code></pre>

<p>見れば解るが、デフォルトの managerユーザは Security Mode を切ることができる。起動設定ファイル（規定値では boot.cfg）を削除して再起動しても同じことだが、Security Mode 状態は設定ファイルと関係なく EEPROM内に保存されているため、これを切る方法を知らないと show confも create confも出来ない。</p>

<p>ところで起動診断時に以下のメッセージの途中で数秒間止まる。そこで "y" を押すと何が起きるだろう？</p>

<pre><code class="language-brush:plain">INFO: Self tests beginning.  
INFO: RAM test beginning.  
PASS: RAM test, 65536k bytes found.  
INFO: Self tests complete.  
INFO: Downloading router software.  
Force EPROM download (Y) ? 

INFO: Initialising Flash File System.  

INFO: Executing configuration script &lt;flash:boot-1.cfg&gt;  
INFO: Router startup complete

AR550S login:  
</code></pre>

<p>答えは「起動設定ファイルは読み込むが、すべてのEnableコマンドを無視する（組み込みモジュールを有効化しない）」である。このため Security Mode は切れて起動するものの、SET USERは通常通り実行されているために SECURITY OFFICERでログインすることが出来ず、かつ managerユーザが設定ファイルから削除もしくはパスワード変更されていたならば、結局は再ログイン手段が得られずに行き詰まる。</p>

<p>ところがここで "y" ではなく "s" を押すとどうなるだろう？<sup id="fnref:3"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:3" rel="footnote">3</a></sup></p>

<pre><code class="language-brush:plain">Force EPROM download (Y) ?  
INFO: Initial download successful.

INFO: Initialising Flash File System.  


INFO: Router startup complete

 login: 
</code></pre>

<p>ログインプロンプトに "SET SYS NAME" が反映されていなければ成功である。つまり起動設定ファイルを読み込まないで起動できる。こうなれば自動的に managerユーザが復活するので、それでログインして Security Modeを切り、工場出荷時状態に戻してしまえる。</p>

<p>この Undocumentedな挙動は、対応しているいくつかの機種・ファームウェアでのみ可能<sup id="fnref:4"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:4" rel="footnote">4</a></sup>ですべての機種で可能なわけではない。同じ機種でもファームウェアによっては（Undocumentedなのだから当然だが）そもそも対応していなかったり、GS916M/GS924M に至っては managerユーザは復活するもののパスワードはリセットされない<sup id="fnref:5"><a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fn:5" rel="footnote">5</a></sup>ので、パスワード忘れ事案には対応できない。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>CentreCOMは開発担当部署によっていくつかの製品グループがある。AR5xxSルータと89xxXL/83xxXLは同族だが、87xxSLはそれより1世代古くて多少性格が異なり、94xxTSと GS9xxMはいずれも全く異なる出自と設計なので似て非なる挙動を示したりする。これらの見分け方というか個性の違いは、見たとおり型番ルールの類似性や（アンドキュメントな）隠し機能の有無、コンソールエラーメッセージの違いに強く現れる。 <a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>8948XLは正式には PPPに対応しておらず Undocumentedなのだが、実際には 8724SL/8748SLと同様の設定と運用ができる。両者の決定的な違いはファイアウォール（NAT機能）追加ライセンスが設定されているか否かだ。だが NAT設定が不要＝LAN型接続であれば、8948XLでも不都合なく設定して運用できる。 <a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>恐らく SKIP の頭文字だ。必ずしもすべてのファームウェアで対応している保証はない。別の一部機種では起動メッセージ中で明示的に "S"と表示しているものもあるが、AR5xxS系の場合はノーヒントである。 <a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>アラテレ製品の場合、パスワード忘れによる工場出荷時初期化は、有償修理対応が原則である。エンドユーザレベルでこれが出来るのは一部機種の隠し機能に限られる。 <a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>GS916M/GS924Mは、パスワードが EEPROMに独立して記録されるため設定ファイルを読み飛ばしても再ログイン可能にはならない。だがこの機種はブートローダー起動中に特定の操作をすると、ブートローダーシェルに入ることができる。そこで EEPROM内容のダンプ＆エディットが不可能でもない。 <a href="http://multix.jp/allied-telesis-centrecom-memorandom/#fnref:5" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[LinuxヘッドレスLiveBoot（CentOS編）]]></title><description><![CDATA[<p>本稿は<a href="http://multix.jp/headless-liveboot-ubuntu/">前稿</a>の CentOS版だ。要件定義はおよそ同じだが、LiveBootの実現方法の違いにより、構築手順は大きく異なる。</p>

<ul class="index"></ul>

<hr>

<h4 id="">要件定義</h4>

<ol>
<li>対象機は x86_64（amd64）とする。  </li>
<li>VGAデバイスは物理的に搭載されていない。GUIも使用しない。よって起動する CentOSはコンソールベースとする。  </li>
<li>BIOSコンソールリダイレクト機能は対象機材でサポートされているものとする。  </li>
<li>シリアルコンソールは baud=115.2kで接続する。<sup id="fnref:1"><a href="http://multix.jp/headless-liveboot-centos/#fn:1" rel="footnote">1</a></sup>  </li>
<li>成果物は ISOイメージとし、CD-R/DVD-Rに焼いて USB-ODDブート（USB光学ドライブ起動）できるものとする。通常は、再起動毎にあらゆる設定や痕跡は完全に忘却される。  </li>
<li>他の Linuxマシンを必要とすることなく、一般的な Windowsマシンから既成の USBインストーラを用いて、USBフラッシュドライブに変換できるものとする。この際 persistent領域を設定できなければならない。  </li>
<li>yum一式を備えている。追加したパッケージや設定は、persistent有効時は維持される。  </li>
<li>既定のログインユーザ名は <em>liveuser</em> とし、パスワードも無しとする。ゆえにパスワード無しで sudoできるものとする。（一般的な</li></ol>]]></description><link>http://multix.jp/headless-liveboot-centos/</link><guid isPermaLink="false">cdc0b430-1ee4-4d3b-b6b8-e9407ad9c019</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Wed, 24 Jun 2015 02:31:27 GMT</pubDate><content:encoded><![CDATA[<p>本稿は<a href="http://multix.jp/headless-liveboot-ubuntu/">前稿</a>の CentOS版だ。要件定義はおよそ同じだが、LiveBootの実現方法の違いにより、構築手順は大きく異なる。</p>

<ul class="index"></ul>

<hr>

<h4 id="">要件定義</h4>

<ol>
<li>対象機は x86_64（amd64）とする。  </li>
<li>VGAデバイスは物理的に搭載されていない。GUIも使用しない。よって起動する CentOSはコンソールベースとする。  </li>
<li>BIOSコンソールリダイレクト機能は対象機材でサポートされているものとする。  </li>
<li>シリアルコンソールは baud=115.2kで接続する。<sup id="fnref:1"><a href="http://multix.jp/headless-liveboot-centos/#fn:1" rel="footnote">1</a></sup>  </li>
<li>成果物は ISOイメージとし、CD-R/DVD-Rに焼いて USB-ODDブート（USB光学ドライブ起動）できるものとする。通常は、再起動毎にあらゆる設定や痕跡は完全に忘却される。  </li>
<li>他の Linuxマシンを必要とすることなく、一般的な Windowsマシンから既成の USBインストーラを用いて、USBフラッシュドライブに変換できるものとする。この際 persistent領域を設定できなければならない。  </li>
<li>yum一式を備えている。追加したパッケージや設定は、persistent有効時は維持される。  </li>
<li>既定のログインユーザ名は <em>liveuser</em> とし、パスワードも無しとする。ゆえにパスワード無しで sudoできるものとする。（一般的な LiveBootの流儀）  </li>
<li>既定のネットワーク設定・接続はオフとする。  </li>
<li>これらのパスワードやネットワーク設定は persistent有効時には自由に設定変更して永続化することができ、再起動しても設定内容が失われてはならない。</li>
</ol>

<p>以後は CentOS-7-x86_64-LiveCD-1503.iso をベースにして話を進める。32bit版や CentOS-6でも同様の手順で構築できる。<sup id="fnref:2"><a href="http://multix.jp/headless-liveboot-centos/#fn:2" rel="footnote">2</a></sup></p>

<p>Ubuntuでは最終的な rootfsを作るのにオンメモリ（2GiB程度）で作業できたが、CentOS用の rootfsを作成するには作業領域が足りないため、起動用 USBメモリの他に Ext4でフォーマットできるフラッシュドライブや HDDを別途用意しなければならない。なお最終的に出来上がるブータブルイメージは CD-Rに充分格納できるサイズ（640MiB以下）になる。</p>

<p>なお製作時のリファレンス機材には Riava RS670A を使用した。<sup id="fnref:3"><a href="http://multix.jp/headless-liveboot-centos/#fn:3" rel="footnote">3</a></sup></p>

<hr>

<h4 id="usb">シリアルコンソール対応のインストーラUSBメモリを作る</h4>

<p>まずヘッドレス環境で使用できる CentOSインストーラを LiveCD ISOイメージから作成する。Windows上でこれを行うには Fedora LiveUSB Creator<sup id="fnref:4"><a href="http://multix.jp/headless-liveboot-centos/#fn:4" rel="footnote">4</a></sup>を使用する。</p>

<p><img src="http://multix.jp/content/images/2015/06/2015-06-24-11-56-53.png" alt=""></p>

<p>次に起動メニューを書き換える。LiveUSB Creatorが作成したベースファイルからの相違点はシリアルコンソール設定の追加が主だ。</p>

<pre><code class="language-brush:plain gutter:true title:/syslinux/syslinux.cfg （フラッシュドライブ起動用）">#serial 0 115200
#console 0

default vesamenu.c32  
timeout 100  
menu background  
menu autoboot Starting CentOS Linux 7 in # second{,s}. Press any key to interrupt.

menu clear  
menu title CentOS Linux 7  
menu vshift 4  
menu rows 12  
menu margin 8  
menu helpmsgrow 15  
menu tabmsgrow 13

menu tabmsg Press Tab for full configuration options on menu items.  
menu separator  
menu separator  
label linux0  
  menu label ^Start CentOS Linux 7 Live
  kernel vmlinuz0
  append initrd=initrd0.img root=live:CDLABEL=LIVE rootfstype=vfat ro rd.live.image quiet rd.luks=0 rd.md=0 rd.dm=0 console=tty0 console=ttyS0,115200n8
  menu default
menu separator  
menu begin ^Troubleshooting  
  menu title Troubleshooting
label check0  
  menu label ^Test this media &amp; start CentOS Linux 7 Live
  kernel vmlinuz0
  append initrd=initrd0.img root=live:CDLABEL=LIVE rootfstype=vfat ro rd.live.image quiet rd.luks=0 rd.md=0 rd.dm=0 rd.live.check console=tty0 console=ttyS0,115200n8
menu separator  
label local  
  menu label Boot from ^local drive
  localboot 0xffff
menu separator  
label returntomain  
  menu label Return to ^main menu.
  menu exit
menu end  
</code></pre>

<p>このフラッシュドライブで、Troubleshootingサブメニュー内の <strong>Test this media &amp; start CentOS Linux 7 Live</strong> を選んで起動しよう。しばらく待っているとログインプロンプトが出現する。ここからは <code>root[ENTER]</code>で rootシェルに入ることが出来る。</p>

<hr>

<h4 id="rootfs">rootfsを作成する</h4>

<p>この時点で NetworkManager は起動しているので DHCPでのIP取得は既に済んでいるだろう。作業に必要なパッケージを yumで追加する。</p>

<pre><code class="language-brush:bash">LANG=C  
yum install -y squashfs-tools  
</code></pre>

<p>作業用の追加ドライブを接続してパーテションをフォーマットし、<code>/mnt</code>にマウントしてそこに移動する。ここでは <code>/dev/sdb1</code>とする。</p>

<pre><code class="language-brush:bash">mkfs.ext4 /dev/sdb1  
mount /dev/sdb1 /mnt  
cd /mnt  
</code></pre>

<p>起動時のフラッシュドライブは <code>/run/initramfs/live</code>に ReadOnlyでマウントされているので、これを rwオプションでリマウントして書き換え可能にする。</p>

<pre><code class="language-brush:bash">mount -o rw,remount /run/initramfs/live  
cd /run/initramfs/live  
</code></pre>

<p>現在起動している rootfsの実体は <code>/run/initramfs/live/LiveOS/squashfs.img</code>だ。このファイルを差し替えるのが最終目的だが、これは <code>LiveOS/ext3fs.img</code>という loopbackファイルを squashfsで圧縮した構造になっている。そこでこれを以下のようにして再現する。rootfs容量はここでは8GiBとするが、適宜任意のサイズに変えても良い。</p>

<pre><code class="language-brush:bash">mkdir -p squashfs-root/LiveOS  
truncate -s $((2**33)) squashfs-root/LiveOS/ext3fs.img  
mkfs.ext4 -L _CentOS-7-livecd squashfs-root/LiveOS/ext3fs.img  
</code></pre>

<p>このファイルを loopbackマウントして /mnt/chrootでアクセスできるようにする。</p>

<pre><code class="language-brush:bash">mkdir /mnt/chroot  
mount -o loop squashfs-root/LiveOS/ext3fs.img /mnt/chroot/  
</code></pre>

<p>後は yumのグループインストールコマンド一発で、任意の新しい rootfs環境が構築できる。</p>

<pre><code class="language-brush:bash">yum --installroot=/mnt/chroot --releasever=7 -y groupinstall Core  
</code></pre>

<p>releaseverオプションに指定する CentOSのバージョンは、<code>/etc/os-release</code>ファイルを参照すると良い。</p>

<hr>

<h4 id="rootfs">rootfsの環境設定</h4>

<p>まず各システム領域を chroot内に bindマウントする。そしてダミーの <code>/etc/fstab</code>（中身は空でも良い）を作成しておく。</p>

<pre><code class="language-brush:bash">mount -o bind /dev/ chroot/dev/  
mount -o bind /proc/ chroot/proc/  
mount -o bind /tmp/ chroot/tmp/  
mount -o bind /run/ chroot/run/  
touch chroot/etc/fstab  
</code></pre>

<p>initramfsの構築設定ファイルを追加する。これは LiveBoot可能な initramfsを作成するのに必須の設定だ。</p>

<pre><code class="language-brush:plain title:chroot/etc/dracut.conf.d/01-liveos.conf">hostonly="no"  
add_dracutmodules+="dmsquash-live"  
compress="xz"  
</code></pre>

<p>現在起動している LiveCDシステムの、livesysサービスファイルを chroot内にコピーしてサービス登録する。このサービスは初回起動時に <em>liveuser</em>アカウントの作成や rootパスワードの削除処理を行うようになっている。<sup id="fnref:5"><a href="http://multix.jp/headless-liveboot-centos/#fn:5" rel="footnote">5</a></sup></p>

<pre><code class="language-brush:bash">cp -p /etc/init.d/livesys* chroot/etc/init.d/  
chroot chroot/ chkconfig --add livesys  
chroot chroot/ chkconfig --add livesys-late  
</code></pre>

<p>タイムゾーンとロケール情報を設定し、NetworkManagerが初回起動時は起動しないように修正する。</p>

<pre><code class="language-brush:bash">ln -sf ../usr/share/zoneinfo/Asia/Tokyo chroot/etc/localtime

echo "LANG=en_US.utf8" &gt; chroot/etc/locale.conf

chroot chroot /usr/bin/systemctl disable NetworkManager  
</code></pre>

<hr>

<h4 id="linuxkernel">Linux kernelモジュールの組み込み</h4>

<p>kernelと、その他の追加パッケージをインストールする。</p>

<pre><code class="language-brush:bash">yum --installroot=/mnt/chroot -y install kernel squashfs-tools dump isomd5sum epel-release  
</code></pre>

<p>kernelと作成された initramfsを、フラッシュドライブに移動する。rescue関係のファイルは必要ないので削除する。</p>

<pre><code class="language-brush:bash">rm -f chroot/boot/*rescue*  
mv chroot/boot/vmlinuz-* /run/initramfs/live/syslinux/vmlinuz0.new  
mv chroot/boot/initramfs-* /run/initramfs/live/syslinux/initrd0.img.new  
</code></pre>

<p>最後にクリーンアップを行ってアンマウントする。rootfsの umount後は fsckで損傷がないかチェックする。</p>

<pre><code class="language-brush:bash highlight:[9]">yum --installroot=/mnt/chroot clean all  
find chroot/var/log -type f -exec /bin/truncate -s0 {} \;  
umount chroot/proc/  
umount chroot/dev/  
umount chroot/run/  
umount chroot/tmp/

du -sh chroot/  
720M    chroot/

umount chroot/  
fsck.ext4 squashfs-root/LiveOS/ext3fs.img  
</code></pre>

<hr>

<h4 id="squashfsimg">squashfs.img ファイルの作成</h4>

<p>こうして出来た rootfsを mksquashfsで圧縮すれば、LiveBoot可能な新しいシステムファイルになる。ただし Ext4フォーマットした loopbackファイルをそのまま圧縮するため、インストール過程で生じた削除ファイルの痕跡などもそのまま残っていて圧縮率が低下してしまう。</p>

<pre><code class="language-brush:bash highlight:[4]">mksquashfs squashfs-root squashfs.img.new

ls -lh squashfs.img.new  
-rw-r--r--. 1 root root 474M Jun 24 08:21 squashfs.img.new
</code></pre>

<p>これを可能な限り取り除いて圧縮するには、dump/restoreコマンドを経由してファイルシステムを完全に作り直すとよい。ただし dumpコマンドはブロックデバイスしか扱えないので、losetupコマンドを用いて /dev/loopX に loopbackファイルをバインドして指定する。手順は面倒だが、比較するとその効果は絶大だ。</p>

<pre><code class="language-brush:bash highlight:[11,26,27]">mv squashfs.img.new squashfs.img.nev

yum install -y dump

loopdev=$(losetup -f)  
losetup $loopdev squashfs-root/LiveOS/ext3fs.img  
dump -0z9 -f rootdump $loopdev  
losetup -d $loopdev

ls -lh rootdump  
-rw-r--r--. 1 root root 277M Jun 24 08:30 rootdump

rm -f squashfs-root/LiveOS/ext3fs.img  
truncate -s $((2**33)) squashfs-root/LiveOS/ext3fs.img  
mkfs.ext4 -L _CentOS-7-livecd squashfs-root/LiveOS/ext3fs.img

mount -o loop squashfs-root/LiveOS/ext3fs.img chroot  
(cd chroot/; restore -r -f ../rootdump)
rm -fr chroot/lost+found restoresymtable  
umount chroot

fsck.ext4 squashfs-root/LiveOS/ext3fs.img  
mksquashfs squashfs-root squashfs.img.new

ls -lh squashfs.img.ne?  
-rw-r--r--. 1 root root 474M Jun 24 08:21 squashfs.img.nev
-rw-r--r--. 1 root root 250M Jun 24 08:38 squashfs.img.new

cp squashfs.img.new /run/initramfs/live/LiveOS/  
</code></pre>

<hr>

<h4 id="rootfs">新 rootfsでの起動テスト</h4>

<p>こうして作成した rootfsファイルをブートUSBメモリにコピーして shutdownし、Windows（あるいは他のLinux機）で以下の起動用ファイルを差し替える。</p>

<ul>
<li>vmlinuz0.new を syslinux/vmlinuz0 へ</li>
<li>initrd0.img.new を syslinux/initrd0.img へ</li>
<li>squashfs.img.new を LiveOS/squashfs.img へ</li>
</ul>

<p>syslinux/syslinux.cfg は前述のそのままでよいが、今度の起動は通常の <strong>Start CentOS Linux</strong>ラベルを選択する。ログイン・プロンプトは <em>root</em>で直接、あるいは <em>liveuser</em>（いずれもパスワード無し）でシェルに入れる。<em>liveuser</em>アカウントは sudoで <em>root</em>アカウントになれる。</p>

<p><img src="http://multix.jp/content/images/2015/06/2015-06-24-16-47-35.png" alt=""></p>

<p>NetworkManagerは停止状態なので、ネットワークに接続するには dhclientで Link Upするか、nmcli/nmtuiでインタフェース設定を行って NetworkManagerを起動する。</p>

<pre><code class="language-brush:bash">sudo -s

systemctl enable NetworkManager  
systemctl start NetworkManager

nmcli connection modify enp2s0 ipv4.method manual ipv4.addresses 192.168.240.101/24 ipv4.gateway 192.168.240.1 ipv4.dns 8.8.8.8

systemctl restart network.service

# cat /etc/sysconfig/network-scripts/ifcfg-enp2s0
</code></pre>

<hr>

<h4 id="iso">ISOイメージを作成する</h4>

<p>元の ISOの squashfs.img等を差し替えた、新しいISOイメージを作成する。まず作業に必要なパッケージをインストールし、作業領域に ISO内のファイルを展開して ReadOnlyを外す。</p>

<pre><code class="language-brush:bash">mount /dev/sdb1 /mnt  
cd /mnt

# install mkisofs tools
yum install -y mkisofs isomd5sum

# mount image: CentOS-7-x86_64-LiveCD-1503.iso
mkdir loop  
mount /dev/cdrom loop

# copy iso files and modify attributes
cp -rf loop files  
chmod -R +w files/  
umount loop  
</code></pre>

<p>フラッシュドライブから先に作成した新しいシステムファイルをコピーする。このときフラッシュドライブ上では <code>/syslinux</code>に vmlinuz0と initrd0.img ファイルが置かれているが、ISOイメージでは <code>/isolinux</code>ディレクトリになる。<sup id="fnref:6"><a href="http://multix.jp/headless-liveboot-centos/#fn:6" rel="footnote">6</a></sup></p>

<pre><code class="language-brush:bash"># copy kernel and filesystem
cp -f /run/initramfs/live/syslinux/vmlinuz0 files/isolinux/  
cp -f /run/initramfs/live/syslinux/initrd0.img files/isolinux/  
cp -f /run/initramfs/live/LiveOS/squashfs.img files/LiveOS/  
</code></pre>

<p>files/isolinux/isolinux.cfg をシリアルコンソール対応に修正する。最初のシリアルポート設定は BIOSコンソールリダイレクトの有無による。また先に作成した rootfsは SELinuxをサポートするように構築してはいないので、これを無効にする。<code>isolinux/mt86plus</code>については標準では存在しないが、これについては<a href="http://multix.jp/buildup-memtest-headless/">別稿を参照</a>されたい。</p>

<pre><code class="language-brush:plain gutter:true title:isolinux/isolinux.cfg （ODD起動用）">#serial 0 115200
#console 0

default vesamenu.c32  
timeout 100  
menu background  
menu autoboot Starting CentOS Linux 7 in # second{,s}. Press any key to interrupt.

menu clear  
menu title CentOS Linux 7  
menu vshift 4  
menu rows 12  
menu margin 8  
#menu hidden
menu helpmsgrow 15  
menu tabmsgrow 13

menu tabmsg Press Tab for full configuration options on menu items.  
menu separator  
menu separator  
label linux0  
  menu label ^Start CentOS Linux 7 Live
  kernel vmlinuz0
  append initrd=initrd0.img root=live:CDLABEL=CentOS7-liveboot rootfstype=auto ro rd.live.image quiet rd.luks=0 rd.md=0 rd.dm=0 selinux=0 console=tty0 console=ttyS0,115200n8
  menu default
menu separator  
menu begin ^Troubleshooting  
  menu title Troubleshooting
label check0  
  menu label ^Test this media &amp; start CentOS Linux 7 1503 Live
  kernel vmlinuz0
  append initrd=initrd0.img root=live:CDLABEL=CentOS7-liveboot rootfstype=auto ro rd.live.image quiet rd.luks=0 rd.md=0 rd.dm=0 rd.live.check selinux=0 console=tty0 console=ttyS0,115200n8 
label memtest  
  menu label Test ^memory
  kernel mt86plus
  append console=ttyS0,115200n8
menu separator  
label local  
  menu label Boot from ^local drive
  localboot 0xffff
menu separator  
label returntomain  
  menu label Return to ^main menu.
  menu exit
</code></pre>

<p>mkisofsコマンドで ISOイメージを作成する。ここではもっともシンプルなオプション構成として GRUB（gfxboot）や EFI対応、Macintosh対応は行ってはいない。注意が必要なのは -Vオプションに渡すCDラベルで、これは kernel起動オプションに記述したのと同じものでなければならない。またこのラベルは最大16文字なのでこれを超えないようにしよう。</p>

<pre><code class="language-brush:bash">find ./files -print | xargs touch -m {} \;  
LANG=C mkisofs -r -J -l \  
  -V CentOS7-liveboot \
  -cache-inodes \
  -b isolinux/isolinux.bin \n
  -c isolinux/boot.cat \
  -no-emul-boot \
  -boot-load-size 4 \
  -boot-info-table \
  -o CentOS7-liveboot.iso \
  ./files
</code></pre>

<p>こうして出来た ISOイメージに、それ自体の改竄検出 MD5チェックサムを implantisomd5コマンドで埋め込む。Ubuntuではファイル単位で MD5をチェックしていたが、CentOSではメディアそのものをチェックする。従ってフラッシュドライブではこの仕組は動かない。一方で CD-R等に焼いた際に Write Errorが有っても、メディアの完全性を確認することが出来る。<sup id="fnref:7"><a href="http://multix.jp/headless-liveboot-centos/#fn:7" rel="footnote">7</a></sup></p>

<pre><code class="language-brush:bash">LANG=C implantisomd5 CentOS7-liveboot.iso  
</code></pre>

<p>改竄チェックは checkisomd5コマンドで行える。引数には調べたい ISOファイルか、メディアを挿れた ODDのデバイスファイルを指定すればよい。なお implantisomd5と checkisomd5コマンドは、isomd5sumパッケージに含まれている。<sup id="fnref:8"><a href="http://multix.jp/headless-liveboot-centos/#fn:8" rel="footnote">8</a></sup></p>

<pre><code class="language-brush:bash">checkisomd5 CentOS7-liveboot.iso  
checkisomd5 /dev/cdrom  
</code></pre>

<hr>

<h4 id="cdrwdvdrwiso">CD-RW/DVD-RWに ISOイメージを焼く</h4>

<p>光学メディアへのライティングは、Ubuntuとおなじく wodimコマンドで行える。</p>

<pre><code class="language-brush:bash">yum install -y wodim

# erase CD/DVD-RW media
wodim dev=/dev/sr0 blank=fast

# writing image
wodim -sao -eject dev=/dev/sr0 CentOS7-liveboot.iso

# check image
checkisomd5 /dev/sr0  
</code></pre>

<p>こうして作成した光学メディアで ODDブートが正常に行えたら、<em>Test this media &amp; start CentOS</em>ラベルで起動してみよう。検査が正常に終了するとログイン・プロンプトに進み、失敗した場合は System Haltするはずだ。<sup id="fnref:9"><a href="http://multix.jp/headless-liveboot-centos/#fn:9" rel="footnote">9</a></sup></p>

<hr>

<h4 id="persistent">persistentモードを設定する</h4>

<p>前述の ISOイメージ/光学メディアで ODDブートが正常に行えたなら、次はその ISOイメージを Live USB Creatorでもって、今度は persistent領域を設定したフラッシュドライブを作成しよう。</p>

<p>persistentモードは kernelオプションの <code>rd.live.overlay=LABEL=LIVE</code>によって有効になる。initramfsは指定された LABELを blkidコマンドで探し、これを <code>/run/initramfs/live/</code>にマウントする。persistent領域ファイルは <code>LiveOS</code>ディレクトリ内の <code>overlay-${LABEL}-${UUID}</code>というファイル名で探して、Device Mapperによって snapshotとして <code>/</code>に結合される。これ自体は初期状態では（ファイルシステムではないので）単なる Zero Fillファイルである。</p>

<pre><code class="language-brush:bash highlight:[2,5,8,9,10,11]">blkid -L LIVE  
/dev/sda1

blkid /dev/sda1  
/dev/sda1: LABEL="LIVE" UUID="0621-6D9B" TYPE="vfat" 

ls -l /run/initramfs/live/LiveOS/  
total 1299100  
-rwxr-xr-x 1 root root      20480 Jun 26 05:26 osmin.img
-rwxr-xr-x 1 root root 1068498944 Jun 30 12:32 overlay-LIVE-0621-6D9B
-rwxr-xr-x 1 root root  261758976 Jun 30 08:26 squashfs.img
</code></pre>

<p>Ubuntuと異なり、Device Mapperを使用しているためもあって実際にあとどの程度データが書き込めるか（保存できるか）は dfコマンドでは判断することができない。dmsetupコマンドの statusサブコマンドで、live-rwボリュームの snapshot消費ブロック数（512byte単位）を知ることはできるから、これを利用するしかない。</p>

<pre><code class="language-brush:bash highlight:[2,5]">dmsetup status live-rw  
0 16777216 snapshot 14960/2086912 72

echo | awk '{print 14960/2086912}'  
0.00716849  
</code></pre>

<p>また Ubuntuの場合は、persistentファイルは overlayfsで重ねられた独立したファイルシステムであったから、非起動状態のフラッシュドライブから persistent領域内のファイルを抜き出すことは容易だったが、CentOSではこれは容易なことではない。</p>

<p>（現在起動しているのとは別の）フラッシュドライブ内の persistent領域にアクセスするには、以下のようにする。</p>

<pre><code class="language-brush:bash highlight:[15,23,28]">## 作業領域の準備

cd /mnt  
mkdir media squash rootfs

## フラッシュドライブを作業領域にマウントし、その中の squashfs.imgをマウントする

mount /dev/sdc1 media  
mount -o loop media/LiveOS/squashfs.img squash

## ext3fs.img（readonly）と overlayファイルに loopbackデバイスを割り当てる
## 空きデバイス名は losetup -fで得られる

losetup -f  
/dev/loop6

losetup /dev/loop6 squash/LiveOS/ext3fs.img -r  
losetup /dev/loop7 edia/LiveOS/overlay-LIVE-0621-6D9B

## ふたつのデバイスを束ねて rootfsを再生する

blockdev -q --getsz /dev/loop6  
16777216

dmsetup create live-rootfs --table "0 16777216 snapshot /dev/loop6 /dev/loop7 P 8"

dmsetup status live-rootfs  
0 16777216 snapshot 17312/2086912 80

## あとはfsckしたり・・・

fsck -f -y /dev/mapper/live-rootfs

## dumpしたり・・・
## （squashfs.img作成以後分だけを抜き出すのに /etc/dumpdatesを利用する例）

echo "/dev/mapper/live-rootfs 0 $(LANG=C \  
  date +"%a %b %d %X %z" \
    -r media/syslinux/ldlinux.sys)" &gt;&gt; /etc/dumpdates
dump -u1z9 /dev/mapper/live-rootfs -f rootfsdump

## mountしたり・・・

mount /dev/mapper/live-rootfs rootfs  
ls -a rootfs/home/liveuser

## 作業を終えたら後始末してフラッシュドライブを切り離す

umount rootfs  
dmsetup remove live-rootfs  
losetup -d /dev/loop6  
losetup -d /dev/loop7  
umount squash  
umount media  
rmdir media squash rootfs  
</code></pre>

<hr>

<h4 id="homeswap">/homeや swapパーテションを設定する</h4>

<p><code>/etc/init.d/livesys</code>を覗いてみると解る<sup id="fnref:10"><a href="http://multix.jp/headless-liveboot-centos/#fn:10" rel="footnote">10</a></sup>が、フラッシュドライブの <code>/LiveOS</code>ディレクトリに、<code>home.img</code>ファイルがあれば <code>/home</code>に、swap.imgファイルが有れば <code>swap</code>に、それぞれ自動的にマウントするようになっている。これらのボリュームファイルは以下のようにセットアップする。</p>

<pre><code class="language-brush:bash highlight:[22,23,24,25,26,27,28,32,33,34]">## persistentオフの場合はフラッシュドライブを rwリマウントする 
mount -o rw,remount /run/initramfs/live

## 保存領域へcd
cd /run/initramfs/live/LiveOS

## home.img ボリュームの作成（サイズは適宜）
truncate -s $((2**31)) home.img  
mkfs.ext4 -L "home-vol" home.img

## swap.img ボリュームの作成（サイズは適宜）
truncate -s $((2**31)) swap.img  
mkswap -L "swap-vol" swap.img

## あとは特に設定変更もなく再起動するだけ
sync;sync;sync  
reboot

## homeボリュームの有無は losetupで確認できる
## 容量は dfコマンドで把握可能
losetup  
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE  
/dev/loop0         0      0         0  1 /osmin.img (deleted)
/dev/loop1         0      0         0  1 /osmin
/dev/loop2         0      0         0  1 /run/initramfs/live/LiveOS/squashfs.img
/dev/loop3         0      0         0  1 /LiveOS/ext3fs.img
/dev/loop4         0      0         0  0 /LiveOS/overlay-LIVE-748B-70C6
/dev/loop5         0      0         0  0 /run/initramfs/live/LiveOS/home.img

## swapは freeコマンドで容量が確認できる
free  
              total        used        free      shared  buff/cache   available
Mem:        8162140      178452     7610852       16720      372836     7767880  
Swap:        262136           0      262136
</code></pre>

<hr>

<h4 id="">ダウンロード</h4>

<p><a href="https://secure.multix.jp/download/SerialLive/CentOS7-liveboot-20150703.iso">CentOS7-liveboot-20150703.iso</a> 313MiB (<a href="https://secure.multix.jp/download/SerialLive/CentOS7-liveboot-20150703.iso.md5">md5sum</a>)</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>USBシリアル変換は FTDI系を前提にしている。PL2303系（特にノーブランドのコピー品）は baud=115.2kではFIFOバッファが足りずに通信不具合が多発してトラブルのもとになる。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p><a href="https://www.centos.org">centos.org</a> <a href="http://multix.jp/headless-liveboot-centos/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p><a href="http://store.shopping.yahoo.co.jp/riava/rs670-s16a.html">Riava RS670A</a> 基本スペックは Intel Gigabit NICx6を持つ Intel/Atom C2758 でメモリ8GiB。基本OSは CentOSまたはUbuntu。SSD内蔵だがこれはここでは使用しない（壊さない）。なおフロントパネルのシリアルコンソールポート（COM0）はCisco互換配線（<a href="http://yost.com/computers/RJ45-serial">Yost Serial Device Wiring Standard</a>）になっている。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p><a href="https://fedorahosted.org/liveusb-creator/">Fedora LiveUSB Creator</a> <a href="http://multix.jp/headless-liveboot-centos/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>persistentモードで初期化済フラグファイルが書けるようになると、これらの初期化処理はスキップされるようになる。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:5" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:6"><p>Fedora LiveUSB Creatorが、ISOイメージ中の /isolinux を /syslinux へコンバートしている。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:6" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:7"><p>Ubuntuでは USBメモリでも一応改竄チェックが可能だが、ISOイメージ中のブートセクタといった非ファイル領域に生じたライティングエラーを検出できない。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:7" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:8"><p>Dracutは、isomd5sumパッケージがインストール済なら initramfsを作成する際にこれを組み込む。initramfsに組み込まれていない場合、rd.live.checkオプションによるメディア検査は正しく実行できない。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:8" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:9"><p><em>Test this media &amp; start CentOS</em>は光学メディア起動時は chkisomd5が走り、検査に成功しなければログイン・プロンプトに達しないが、USBメモリ起動時はメディア自体が検査不能なので、そのまま進んでログイン・プロンプトに達することができる。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:9" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:10"><p>物理パーテションを /homeにしたり、暗号化したりする起動オプションもある。 <a href="http://multix.jp/headless-liveboot-centos/#fnref:10" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[LinuxヘッドレスLiveBoot（Ubuntu編）]]></title><description><![CDATA[<p>VGAのない産業用ヘッドレスPC類に Linux系OSを組み込むのは割とよく行う。もっぱら CentOSをメインにしているのだが、時には諸事情やら比較検証用途やらで急遽 Ubuntuが必要になったりもする。そこでまあ Ubuntuベースのヘッドレス LiveBootを作っておくことにした。  </p>

<ul class="index"></ul>  

<hr>

<h4 id="">要件定義</h4>

<ol>
<li>対象機は x86_64（amd64）とする。  </li>
<li>VGAデバイスは物理的に搭載されていない。GUIも使用しない。よって起動する Ubuntuはコンソールベースとする。  </li>
<li>BIOSコンソールリダイレクト機能は対象機材でサポートされている。  </li>
<li>シリアルコンソールは baud=115.2kで接続する。<sup id="fnref:1"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:1" rel="footnote">1</a></sup>  </li>
<li>成果物はISOイメージとし、CD-R/DVD-Rに焼いてUSB-ODDブート（USB光学ドライブ起動）できるものとする。通常は、再起動毎にあらゆる設定や痕跡は完全に忘却される。  </li>
<li>他の Linuxマシンを必要とすることなく、一般的な Windowsマシンから既成のUSBインストーラを用いて、USBフラッシュドライブに変換できるものとする。この際 persistent領域を設定できなければならない。  </li>
<li>apt-get一式を備えている。追加したパッケージや設定は、persistent有効時は維持される。  </li>
<li>既定のログインユーザ名は <em>ubuntu</em> とし、パスワードも無しとする。ゆえにパスワード無しで sudoできるものとする。（一般的なLiveBootの流儀）  </li>
<li>既定のネットワーク設定・</li></ol>]]></description><link>http://multix.jp/headless-liveboot-ubuntu/</link><guid isPermaLink="false">2367878c-5496-47cb-962a-44089346dca7</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Tue, 09 Jun 2015 02:27:30 GMT</pubDate><content:encoded><![CDATA[<p>VGAのない産業用ヘッドレスPC類に Linux系OSを組み込むのは割とよく行う。もっぱら CentOSをメインにしているのだが、時には諸事情やら比較検証用途やらで急遽 Ubuntuが必要になったりもする。そこでまあ Ubuntuベースのヘッドレス LiveBootを作っておくことにした。  </p>

<ul class="index"></ul>  

<hr>

<h4 id="">要件定義</h4>

<ol>
<li>対象機は x86_64（amd64）とする。  </li>
<li>VGAデバイスは物理的に搭載されていない。GUIも使用しない。よって起動する Ubuntuはコンソールベースとする。  </li>
<li>BIOSコンソールリダイレクト機能は対象機材でサポートされている。  </li>
<li>シリアルコンソールは baud=115.2kで接続する。<sup id="fnref:1"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:1" rel="footnote">1</a></sup>  </li>
<li>成果物はISOイメージとし、CD-R/DVD-Rに焼いてUSB-ODDブート（USB光学ドライブ起動）できるものとする。通常は、再起動毎にあらゆる設定や痕跡は完全に忘却される。  </li>
<li>他の Linuxマシンを必要とすることなく、一般的な Windowsマシンから既成のUSBインストーラを用いて、USBフラッシュドライブに変換できるものとする。この際 persistent領域を設定できなければならない。  </li>
<li>apt-get一式を備えている。追加したパッケージや設定は、persistent有効時は維持される。  </li>
<li>既定のログインユーザ名は <em>ubuntu</em> とし、パスワードも無しとする。ゆえにパスワード無しで sudoできるものとする。（一般的なLiveBootの流儀）  </li>
<li>既定のネットワーク設定・接続はオフとする。  </li>
<li>これらのパスワードやネットワーク設定は persistent有効時には自由に設定変更して永続化することができ、再起動しても設定内容が失われてはならない。</li>
</ol>

<p>要するに、KVMやDocker等の仮想マシン環境のそれと同様の感覚で、気軽に使用できる使い捨て環境の物理版<sup id="fnref:2"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:2" rel="footnote">2</a></sup>である。その仕様上基本的には RAMシステムとなり Swapは初期設定しない。従ってメインメモリは充分潤沢に搭載されているものとする。<sup id="fnref:3"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:3" rel="footnote">3</a></sup></p>

<p>Ubuntu LiveCD Customization については <strong><a href="https://help.ubuntu.com/community/LiveCDCustomization">こちらを参照</a></strong> のこと。</p>

<p>以後は ubuntu-15.04-desktop-amd64.iso をベースにして話を進める。32bit版でも同様だが、14.xx LTS では本稿で解説している手法は使えない。<sup id="fnref:4"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:4" rel="footnote">4</a></sup></p>

<p>なお製作時のリファレンス機材には Riava RS670A を使用した。<sup id="fnref:5"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:5" rel="footnote">5</a></sup></p>

<hr>

<h4 id="usb">シリアルコンソール対応のインストーラUSBフラッシュドライブを作る</h4>

<p>まずはヘッドレス環境で使用できるUbuntuインストーラを作らなければならない。UbuntuのインストーラISOには Desktop版と Server版があり、後者には CLI（テキスト）インストール機能があるが、本稿では Desktop版のISOイメージをベースにする。</p>

<p>端的には、内蔵ストレージにテキストベースの Ubuntuをクリーンインストールして GRUBで起動するのであれば、Server版をベースにするのが筋だ。だが Server版のISOイメージは LiveBoot機能を持たないため、今回の目的では Desktop版ISOイメージから作り出すほうが楽なのである。</p>

<p>用意するのは Ubuntu Desktop 64-bit Install ISO ファイルと、FAT32でフォーマットした1GiB以上のUSBフラッシュドライブと、Universal USB Installer<sup id="fnref:6"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:6" rel="footnote">6</a></sup>だ。</p>

<p>Windowsマシンで USBインストーラを立ち上げ、ダウンロードしたISOイメージと書き込み先フラッシュドライブを指定して起動可能メディアを作成する。この時 persistent領域は指定しない（永続化しない）。</p>

<p><img src="http://multix.jp/content/images/2015/06/2015-06-09-11-13-58.png" alt=""></p>

<p>次にブートメニューを書き換えてシリアルコンソールが使えるようにする。この作業は原則として TeraPadや MIFESのようなテキストエディタで行うが、Windows Notepadでもできなくはない。フラッシュドライブに書き込まれた元ファイルは LF改行なので Notepadでは全部が1行に繋がってしまうが、ブートローダー（SYSLINUX）は CR+LF改行でも動作するので、Notepadでも以下のテンプレートにそのまま入れ替えてしまってもよい。</p>

<p>isolinux.cfg冒頭のシリアルポート設定<sup id="fnref:7"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:7" rel="footnote">7</a></sup>は、BIOSにコンソールリダイレクト機能が備わっている場合には必要ない。BIOSとブートローダーの両方で設定をしてしまうと、入出力がバッティングして表示が崩れたりキー入力に不具合が生じる。従って今回の場合はコメントアウトで記述しておくだけとする。</p>

<pre><code class="language-brush:plain gutter:true highlight:[1,2] title:/isolinux/isolinux.cfg">#serial 0 115200
#console 0
path  
include menu.cfg  
default vesamenu.c32  
prompt 0  
timeout 50  
</code></pre>

<p>stdmenu.cfg からはスプラッシュ画像や表示色の設定をごっそり削る。またブートメニューが 80x24程度に収まるよう、vshift等を調整する。</p>

<pre><code class="language-brush:plain gutter:true title:/isolinux/stdmenu.cfg">menu vshift 4  
menu rows 10  
menu helpmsgrow 15  
menu cmdlinerow 16  
menu timeoutrow 16  
menu tabmsgrow 18  
menu tabmsg Press ENTER to boot or TAB to edit a menu entry  
</code></pre>

<p>txt.cfgのカーネルオプション行には、BIOSからカーネルに制御が渡った後のシリアルコンソール設定 <code>console=tty0 console=ttyS0,115200n8</code>を追記する。またLiveCD機能による自動ネットワーク構成が勝手に動かないよう <code>ip=frommedia</code>も必ず設定する。そしてこの段階では GUIと Ubuntuインストーラの起動をスキップしてシェルに落ちるための <mark>single</mark>を指定しておく。</p>

<pre><code class="language-brush:plain gutter:true highlight:[6] title:/isolinux/txt.cfg （USBメモリブート用）">menu title Ubuntu Console boot menu  
default live  
label live  
  menu label ^Try Ubuntu Console
  kernel /casper/vmlinuz.efi
  append  file=/cdrom/preseed/ubuntu.seed boot=casper cdrom-detect/try-usb=true noprompt floppy.allowed_drive_mask=0 ignore_uuid initrd=/casper/initrd.lz ip=frommedia console=tty0 console=ttyS0,115200n8 single ---
label memtest  
  menu label Test ^memory
  kernel /install/mt86plus
  append console=ttyS0,115200n8
label hd  
  menu label ^Boot from first hard disk
  localboot 0x80
</code></pre>

<p>追加された kernel起動パラメータは以下の意味を持つ。</p>

<ul>
<li><em>boot=casper</em> <br>
casperによるブートシーケンス（LiveBoot）を実行する。  </li>
<li><em>cdrom-detect/try-usb=true</em> <br>
USBメモリブートデバイスを CDROMと同等に扱う。  </li>
<li><em>floppy.allowed_drive_mask=0</em> <br>
フロッピーデバイスを無視する。</li>
<li><em>ignore_uuid</em> <br>
ブートメディアのUUIDをチェックしない。</li>
<li><em>ip=frommedia</em> <br>
ブートメディアの /etc/network/interfaces 設定を有効にする。</li>
<li><em>console=tty0</em> <br>
tty0をコンソール入出力に使用する。tty0は一般に VGA表示デバイスやフレームバッファである。  </li>
<li><em>console=ttyS0,115200n8</em> <br>
ttyS0をコンソール入出力に使用する。ttyS0は一般に第一シリアルポート。通信要件は baud=115200、パリティなし、8bit幅とする。このふたつのconsole指定は、両方書く場合はこの順番（tty0、ttyS0）で書かなければならない。<sup id="fnref:8"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:8" rel="footnote">8</a></sup>  </li>
<li><em>single</em> <br>
シングルモードで起動する。ここでは Ubuntuインストーラを起動させないために指定する。</li>
</ul>

<p>なおインストールイメージに含まれる memtest86+（mt86plus）は、シリアルコンソールに対応していない。通常のVGA表示に加えてBIOSコンソール表示もするようにリビルドしたバイナリと差し替えておくと便利だ。<sup id="fnref:9"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:9" rel="footnote">9</a></sup></p>

<hr>

<h4 id="rootfs">rootfsを作成する</h4>

<p>上記のUSBメモリで起動し <strong>Try Ubuntu Console</strong>を実行するとシングルユーザモードのbashシェルが起動する。まずは dhclientを起動したり、あるいは ifconfigと routeを叩くなりしてインターネットへ接続できるようにしよう。またロケール変数もセットする。</p>

<pre><code class="language-brush:bash">dhclient eth0

LANG=C  
</code></pre>

<p>現在起動しているこの環境は RAMシステムのため、8GiBの物理メモリを積んでいるなら 4GiB容量の RAMディスクが使える。これから作る rootfsは 2GiBも余裕があれば充分なので、/root以下を作業領域とする。なお /tmpは tmpfsマウント時のオプションのせいでそのままでは使用できない<sup id="fnref:10"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:10" rel="footnote">10</a></sup>。RAM容量が足りない場合には Ext4フォーマットした別の USBメモリや外付けHDD等を作業領域に使おう。</p>

<p>新しい rootfsは debootstrapパッケージを利用して作成する。さらにそれを LiveBootで使えるようにするために squashfs-toolsパッケージを用いるので、これらをインストールする。</p>

<pre><code class="language-brush:bash">apt-get install debootstrap squashfs-tools  
</code></pre>

<p>構築する Ubuntuのコードネームと CPUアーキテクチャは USBメモリから起動したそれと同じものとするが、これらは以下のコマンドで確認できる。</p>

<pre><code class="language-brush:plain highlight:[2,3,4,5,6,9]">lsb_release -a  
No LSB modules are available.  
Distributor ID: Ubuntu  
Description:    Ubuntu 15.04  
Release:        15.04  
Codename:       vivid

dpkg --print-architecture  
amd64  
</code></pre>

<p><em>lsb_release</em>コマンドが参照している実体は<code>/etc/lsb-release</code>なので、これをsourceコマンドで読めば環境変数で参照できるようになる。</p>

<pre><code class="language-brush:bash highlight:[2,3,4,5,10]">cat /etc/lsb-release  
DISTRIB_ID=Ubuntu  
DISTRIB_RELEASE=15.04  
DISTRIB_CODENAME=vivid  
DISTRIB_DESCRIPTION="Ubuntu 15.04"

source /etc/lsb-release

echo $DISTRIB_CODENAME  
vivid  
</code></pre>

<p>これらを debootstrapコマンドの引数に記述してミニマムな新規 rootfsを構築する。</p>

<pre><code class="language-brush:bash">source /etc/lsb-release  
mkdir newroot  
debootstrap --arch $(dpkg --print-architecture) \  
   $DISTRIB_CODENAME newroot http://archive.ubuntu.com/ubuntu
</code></pre>

<hr>

<h4 id="rootfs">rootfsの環境設定</h4>

<p>構築された rootfsは上記のコマンドだけで、すでに chrootして一応 apt-getも実行できる状態になっているが、kernelはまだインストールされていない。そこでこれらを行う前の準備として /proc、/dev、/run を rootfs内にマウントする。またデフォルトロケールとタイムゾーンをセットする。</p>

<pre><code class="language-brush:bash">mount -o bind /proc/ newroot/proc/  
mount -o bind /dev/ newroot/dev/  
mount -o bind /run/ newroot/run/

chroot newroot/ /usr/sbin/locale-gen en_US.UTF-8  
chroot newroot/ /usr/sbin/update-locale LANG=en_US.UTF-8  
chroot newroot/ /usr/sbin/dpkg-reconfigure locales  
chroot newroot/ /usr/bin/timedatectl set-timezone Asia/Tokyo  
</code></pre>

<p>さらに aptが参照する <code>newroot/etc/apt/sources.list</code>には必要最小限の1行しか書かれていないので、これを以下の内容に書き換える。<sup id="fnref:11"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:11" rel="footnote">11</a></sup></p>

<pre><code class="language-brush:plain title:newroot/etc/apt/sources.list for 15.04">deb http://jp.archive.ubuntu.com/ubuntu/ vivid main restricted  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid main restricted  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid-updates main restricted  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid-updates main restricted  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid universe  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid universe  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid-updates universe  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid-updates universe  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid multiverse  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid multiverse  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid-updates multiverse  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid-updates multiverse  
deb http://jp.archive.ubuntu.com/ubuntu/ vivid-backports main restricted universe multiverse  
deb-src http://jp.archive.ubuntu.com/ubuntu/ vivid-backports main restricted universe multiverse  
deb http://security.ubuntu.com/ubuntu vivid-security main restricted  
deb-src http://security.ubuntu.com/ubuntu vivid-security main restricted  
deb http://security.ubuntu.com/ubuntu vivid-security universe  
deb-src http://security.ubuntu.com/ubuntu vivid-security universe  
deb http://security.ubuntu.com/ubuntu vivid-security multiverse  
deb-src http://security.ubuntu.com/ubuntu vivid-security multiverse  
</code></pre>

<p>次に LiveBoot専用の管理者ユーザ <em>ubuntu</em>およびグループを作成し、パスワードなしでログイン出来るようにする。さらにrootアカウントでは直接ログイン出来ないようにロックしておく。このユーザ名は LiveBoot機能を提供している casperモジュールの設定に合わせたものだ。むろんそれぞれに任意のパスワードを設定しても構わない。（それを忘れたりしなければ）</p>

<pre><code class="language-brush:bash">chroot newroot/ /usr/sbin/addgroup --system --gid=999 ubuntu  
chroot newroot/ /usr/sbin/useradd -s /bin/bash -g ubuntu --uid=999 -m -k /dev/null ubuntu  
chroot newroot/ /usr/bin/passwd -d ubuntu  
chroot newroot/ /usr/sbin/usermod -L root  
</code></pre>

<p>内蔵ストレージに直接起動可能な rootfsをインストールする場合は、この時点で<code>/etc/fstab</code>を作成しなければならないが、LiveBootではこれが起動中に動的に作成・設定されるため、必要ない。</p>

<p>以上の環境設定が済んだら、apt-getで update/upgradeを行って chroot環境を更新する。</p>

<pre><code class="language-brush:bash">chroot newroot/ /usr/bin/apt-get update  
chroot newroot/ /usr/bin/apt-get upgrade  
</code></pre>

<hr>

<h4 id="linuxkernel">Linux kernelモジュールの組み込み</h4>

<p>いよいよ rootfs内に kernelモジュールをインストールするのだが、その前に現在起動中の USBメモリ（この例では/dev/sda）の MBRセクタを一応バックアップしておく。kernelインストール中に grub-pcセットアップが走るのだが、このとき MBRが上書きされてリブート不能<sup id="fnref:12"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:12" rel="footnote">12</a></sup>になるのを避けるためだ。なおインストールする kernelパッケージ名は 64bitと 32bitでは異なる。</p>

<pre><code class="language-brush:bash">dd if=/dev/sda of=mbr count=1  
</code></pre>

<pre><code class="language-brush:bash title:64bit kernel for EFI Support">chroot newroot/ /usr/bin/apt-get install linux-signed-image-$(uname -r)  
</code></pre>

<pre><code class="language-brush:bash title:32bit kernel">chroot newroot/ /usr/bin/apt-get install linux-image-$(uname -r)  
</code></pre>

<p>grub-pcセットアップは <em>Continue without installing GRUB?</em>を選んで何もせずに抜ける。そして忘れないうちにバックアップしておいた MBRを書き戻す。</p>

<pre><code class="language-brush:bash">dd if=mbr of=/dev/sda  
</code></pre>

<p>続いてその他のパッケージを追加インストールし、整合性を確認したのち、パッケージキャッシュを削除する。少なくとも <code>plymouth</code>はインストールが必要である。</p>

<pre><code class="language-brush:bash">chroot newroot/ /usr/bin/apt-get install plymouth squashfs-tools openssh-client  
chroot newroot/ /usr/bin/apt-get -f install  
chroot newroot/ /usr/bin/apt-get upgrade  
chroot newroot/ /usr/bin/apt-get autoremove  
chroot newroot/ /usr/bin/apt-get clean  
</code></pre>

<p>最後に newroot/var以下のクリーニング<sup id="fnref:13"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:13" rel="footnote">13</a></sup>と、newroot/bootから <strong>kernelとinitrdの削除</strong>する。LiveBootではブートデバイスの <code>casper</code>ディレクトリにある kernelとinitrdをそのまま使用するのでこれらの容量を節約する。</p>

<pre><code class="language-brush:bash">find newroot/var/log -type f -exec truncate -s 0 {} \;  
find newroot/var/cache -name \*-old -delete  
rm -fr newroot/var/lib/apt/lists/*

rm -f newroot/boot/vmlinuz-* newroot/boot/initrd.img-*  
</code></pre>

<p>以上で rootfsの構築は完了したのだが、作業時にマウントした /proc、/dev、/run がアンマウントできない場合がある。その際は lsofコマンドで掴んでいるプロセス探して killすればよい。正しくアンマウントできていれば duコマンドで使用容量が把握できる。</p>

<pre><code class="language-brush:bash highlight:[10]">lsof | grep newroot

kill &lt;PID&gt;

unmount newroot/run  
unmount newroot/dev  
unmount newroot/proc

du -sh newroot/  
420M    newroot/  
</code></pre>

<hr>

<h4 id="filesystemsquashfs">filesystem.squashfs ファイルの作成</h4>

<p>構築した rootfsを mksquashfsコマンドで圧縮する。このファイルはUSBメモリの /casperディレクトリに格納するが、これは <code>/cdrom/casper</code>として見ることが出来る。ただし <code>/cdrom</code>（USBメモリ）は roマウントされていてそのままでは書き込めないため、rwオプションでリマウントする。またそこにある <code>filesystem.squashfs</code>はいままさに <code>/rofs</code>にマウントして使われているため、直接上書きしてはならない。そこで別のファイル名で保存しておく。またこの rootfs全体のブロックサイズも保存しておく。</p>

<pre><code class="language-brush:bash">mount -o rw,remount /cdrom  
mksquashfs newroot /cdrom/casper/filesystem.squashfs.newroot  
printf $(du -s --block-size=1 newroot | cut -f1) &gt; /cdrom/casper/filesystem.size.newroot  
</code></pre>

<p>ちなみに <code>mksquashfs</code>で作成したファイルは <code>unsquashfs</code>コマンドで展開することが出来る。</p>

<pre><code class="language-brush:bash highlight:[4,5]">unsquashfs -d expand-root /cdrom/casper/filesystem.squashfs

ls expand-root/  
bin   dev  home        lib    media  opt   root  sbin  sys  usr  vmlinuz  
boot  etc  initrd.img  lib64  mnt    proc  run   srv   tmp  var  
</code></pre>

<hr>

<h4 id="rootfs">新 rootfs での起動テスト</h4>

<p>これらの作業を終えたら poweroff（shutdown -h）して、USBメモリを Windowsマシンに戻して以下の修正を行う。</p>

<ul>
<li>既存の /casper/filesystem.squashfs を filesystem.squashfs.org にリネーム</li>
<li>/casper/filesystem.squashfs.newroot を filesystem.squashfs にリネーム</li>
<li>同様に filesystem.size も差し替える。</li>
<li>/isolinux/txt.cfg の append行からキーワード single を削除する。</li>
</ul>

<p>この新たな環境での LiveBoot起動に失敗した場合は、上記の逆の手順でインストーライメージのシングルモードに戻す。そして unsquashfsコマンドで rootfsを展開して修正することを繰り返す。</p>

<p>新たな LiveBootが正しく起動してログイン・プロンプトが現れたら、ユーザ名 <em>ubuntu</em>（パスワード無し）でコンソールに入れるはずだ。また <code>sudo -s</code>で rootシェルになれる。</p>

<p><img src="http://multix.jp/content/images/2015/06/2015-06-22-18-14-35.png" alt=""></p>

<hr>

<h4 id="iso">ISOイメージを作成する</h4>

<p>こうして作成した filesystem.squashfsと、ブートメニューファイルを差し替えた ISOイメージを作成する。</p>

<p>まずベースになる Desktop版ISOイメージをローカルに展開する。これはODD（光学ドライブ）から読みだしても良いし、フラッシュドライブにISOイメージをコピーしておいて loopbackマウントで読みだしても良い。コピーしたファイルは書き込み不可となっているので <code>chmod</code>コマンドでパーミッションを変更する。</p>

<pre><code class="language-brush:bash highlight:[6,13,14]">cd /tmp

mkdir loop

sudo mount -o loop ubuntu-15.04-desktop-amd64.iso loop/  
mount: /dev/loop2 is write-protected, mounting read-only

cp -r loop files  
sudo umount loop  
chmod -R +w ./files

ls files/  
autorun.inf  casper  EFI      isolinux    pics  preseed             ubuntu  
boot         dists   install  md5sum.txt  pool  README.diskdefines  wubi.exe  
</code></pre>

<p>現在起動している filesystem.squashfsは <code>/cdrom/casper/</code>以下に見えているのでこれをコピーする。</p>

<pre><code class="language-brush:bash">cat /cdrom/casper/filesystem.squashfs &gt; files/casper/filesystem.squashfs  
</code></pre>

<p>isolinux/isolinux.cfg<sup id="fnref:14"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:14" rel="footnote">14</a></sup>や stdmenu.cfgについては前に上げたのと同じなので割愛する。新しい ODDブート用の isolinux/txt.cg は以下のように記述する。</p>

<pre><code class="language-brush:plain gutter:true title:isolinux/txt.cfg （ODDブート用）">menu title Ubuntu Console boot menu  
default live  
label live  
  menu label ^Try Ubuntu Console
  kernel /casper/vmlinuz.efi
  append  file=/cdrom/preseed/ubuntu.seed boot=casper initrd=/casper/initrd.lz quiet ip=frommedia console=tty0 console=ttyS0,115200n8 ---
label check  
  menu label ^Check disc for defects
  kernel /casper/vmlinuz.efi
  append  boot=casper integrity-check initrd=/casper/initrd.lz quiet console=tty0 console=ttyS0,115200n8 ---
label memtest  
  menu label Test ^memory
  kernel /install/mt86plus
  append console=ttyS0,115200n8
label hd  
  menu label ^Boot from first hard disk
  localboot 0x80
</code></pre>

<p>md5sum.txtは2番目の起動選択 <strong>Check disc for defects</strong>を選んだ際に参照される改竄チェックファイルだ。単純にこのファイルをゼロバイトに切り詰めれば何もチェックされない。一方この機能を活かしたい場合、チェックファイルを作成した後に md5sumが変わってしまうファイル、すなわち md5sum.txt自身と mkisofsコマンドが作成・更新する boot.cat、isolinux.bin はチェックリストから除外しておかなければならない。</p>

<pre><code class="language-brush:bash">pushd ./files  
rm -f md5sum.txt isolinux/boot.cat  
find . -type f -print | xargs md5sum | grep -v isolinux.bin &gt; ../md5sum.txt  
mv ../md5sum.txt .  
popd  
</code></pre>

<p>ISOイメージを作成する mkisofsコマンドは、同名のパッケージでインストールできる。</p>

<pre><code class="language-brush:bash">sudo apt-get install mkisofs  
</code></pre>

<p>mkisofsコマンドの引数<sup id="fnref:15"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:15" rel="footnote">15</a></sup>のうち -b（ブートローダーファイル）と-c（カタログファイル）は構築対象ディレクトリを基準とした場合のパス指定になるので注意しよう。また出力する ISOファイルは、先に上げた USBインストーラを使用する場合はその制限によりファイル名中に <em>desktop</em>の文字列が入っていなければならない。ともかく指定するオプションが多いのでシェルスクリプトにしておいたほうがコードの再利用と修正はしやすい。</p>

<pre><code class="language-brush:bash highlight:[14,15]">find ./files -print | xargs touch -m {} ¥;  
LANG=C mkisofs -r -J -l \  
  -V UBUNTU64 \
  -cache-inodes \
  -b isolinux/isolinux.bin \
  -c isolinux/boot.cat \
  -no-emul-boot \
  -boot-load-size 4 \
  -boot-info-table \
  -o ubuntu-15.04-desktopless-amd64.iso \
  ./files

ls -la ubuntu-15.04-desktop*  
-rwxr--r-- 1 ubuntu ubuntu 1150844928 Jun 17 16:59 ubuntu-15.04-desktop-amd64.iso
-rw-rw-r-- 1 ubuntu ubuntu  238929920 Jun 17 17:25 ubuntu-15.04-desktopless-amd64.iso
</code></pre>

<p>出来上がった ISOイメージはフラッシュドライブや NASなどにコピーして保存しておこう。</p>

<hr>

<h4 id="cdrwdvdrwiso">CD-RW/DVD-RWに ISOイメージを焼く</h4>

<p>前項で作成したISOイメージを Linuxで CD-RW/DVD-RWメディアに焼くには以下のようにする。</p>

<p>まずライティングソフトとして wodimパッケージをインストールする。</p>

<pre><code class="language-brush:bash">sudo apt-get install wodim  
</code></pre>

<p>USB-ODDは、おそらく /dev/sr0 等に見えるだろう。wodimコマンドの <code>-scanbus</code>オプションは SCSIバス以外には機能しないので、dmesgでデバイスノードを確認する。</p>

<pre><code class="language-brush:bash highlight:[2,3,4,5]">sudo wodim dev=/dev/sr0 --devices  
wodim: Overview of accessible drives (1 found) :  
-------------------------------------------------------------------------
 0  dev='/dev/sr0'      rwrw-- : 'Slimtype' 'eTAU108   1'
-------------------------------------------------------------------------
</code></pre>

<p>CD-RW/DVD-RWメディアをブランク消去するには wodimコマンドの blankオプションを使う。</p>

<pre><code class="language-brush:bash">sudo wodim dev=/dev/sr0 blank=fast  
</code></pre>

<p>wodimコマンドにISOイメージファイルを与えると、それを指定のドライブに焼きこむ。</p>

<pre><code class="language-brush:bash">sudo wodim -sao -eject dev=/dev/sr0 ubuntu-15.04-desktopless-amd64.iso  
</code></pre>

<hr>

<h4 id="persistent">persistentモードを設定する</h4>

<p>前述の ISOイメージ/光学メディアで ODDブートが正常に行えたなら、次はその ISOイメージを USBインストーラでもって、今度は persistentモードを設定した USBメモリを作成しよう。</p>

<p>persistentモードが有効な USBメモリは、kernel起動オプションに <em>persistent</em>が加わり、USBメモリのルートに <code>casper-rw</code>というファイルが作成される。このファイルの実体は overlayfsを用いて <code>/</code>に重ねられた ext4フォーマットの loopbackマウントファイルだ。<code>/rofs</code>にマウントされた filesystem.squashfsとの変更差分は、この <code>casper-rw</code>に記録されるようになる。</p>

<pre><code class="language-brush:plain highlight:[2,3,4,5,8,9,10,11,12]">df -Th  
Filesystem     Type      Size  Used Avail Use% Mounted on  
udev           devtmpfs  3.9G     0  3.9G   0% /dev  
tmpfs          tmpfs     798M  8.6M  789M   2% /run  
/dev/sda       vfat      2.0G  1.5G  468M  76% /cdrom
/dev/loop0     squashfs  182M  182M     0 100% /rofs
/cow           overlay   976M   14M  895M   2% /
tmpfs          tmpfs     3.9G     0  3.9G   0% /dev/shm  
tmpfs          tmpfs     5.0M     0  5.0M   0% /run/lock  
tmpfs          tmpfs     3.9G     0  3.9G   0% /sys/fs/cgroup  
tmpfs          tmpfs     3.9G     0  3.9G   0% /tmp  
tmpfs          tmpfs     798M     0  798M   0% /run/user/999  
</code></pre>

<p>persistentモードが正しく効いているかどうかは、更新したログインパスワードやネットワーク設定が再起動しても正しく機能するか否かで容易にわかる。例えばrootアカウントでログイン可能にし、ubuntuアカウントを無効にして新たにadminユーザを作成し、rootとadminにパスワードを設定してみよう。</p>

<pre><code class="language-brush:plain highlight:[6,7,8,13,14,19,20,21]">sudo -s

usermod -U root

passwd root  
Enter new UNIX password:  
Retype new UNIX password:  
passwd: password updated successfully

usermod -L ubuntu

addgroup --system admin  
Adding group `admin' (GID 112) ...  
Done.

useradd -s /bin/bash -g admin -m -k /dev/null admin

passwd admin  
Enter new UNIX password:  
Retype new UNIX password:  
passwd: password updated successfully  
</code></pre>

<p>/etc/network/interfaces に記述したネットワーク設定も persistentモードでなら起動時に反映される。<sup id="fnref:16"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:16" rel="footnote">16</a></sup></p>

<pre><code class="language-brush:plain gutter:true title:/etc/network/interfaces sample">source-directory /etc/network/interfaces.d

auto lo  
iface lo inet loopback

auto eth0  
iface eth0 inet static  
    address 192.168.240.135
    network 192.168.240.0
    netmask 255.255.255.0
    broadcast 192.168.240.255
    gateway 192.168.240.1
    dns-nameservers 192.168.240.1 8.8.8.8 8.8.4.4

auto eth4  
iface eth4 inet dhcp  
</code></pre>

<p>persistentモードで記録された内容を無視してデフォルト状態で起動するには、ブートメニューの起動ラベル選択時に <code>persistent</code>ラベルを取り除けば良い。あるいは casper-rwファイルをリネームして再起動してしまう。<sup id="fnref:17"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:17" rel="footnote">17</a></sup></p>

<pre><code class="language-brush:bash">cd /cdrom  
mv casper-rw casper-rw.bak  
reboot  
</code></pre>

<p>新たな（まっさらの）1GiBの casper-rwファイルは Linux上では以下の手順で作成できる。また事のついでなので、Windows上からもこのファイルを容易に置き換えられるように、フォーマットしたばかりの casper-rwファイルを zipコマンドで圧縮バックアップしておくと、USBインストーラを使わなくても最初期化できるので、いざというとき何かと便利だ。<sup id="fnref:18"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:18" rel="footnote">18</a></sup></p>

<pre><code class="language-brush:bash highlight:[8]">cd /cdrom

rm -f casper-rw.bak

truncate -s $((2**30)) casper-rw

ls -l casper-rw  
-rwxr-xr-x 1 root root 1073741824 Jun 18 13:11 casper-rw

mkfs.ext4 -L casper-rw casper-rw


apt-get install zip

zip casper-rw.zip casper-rw  
</code></pre>

<p>なお FAT32での最大のファイルサイズは 4GiB-1バイトなので、truncateコマンドでの容量指定は<code>$((2**32-1))</code>となる。<sup id="fnref:19"><a href="http://multix.jp/headless-liveboot-ubuntu/#fn:19" rel="footnote">19</a></sup></p>

<hr>

<h4 id="">ダウンロード</h4>

<p><a href="https://secure.multix.jp/download/SerialLive/ubuntu-15.04-desktopless-amd64-20150703.iso">ubuntu-15.04-desktopless-amd64-20150703.iso</a> 174MiB (<a href="https://secure.multix.jp/download/SerialLive/ubuntu-15.04-desktopless-amd64-20150703.iso.md5">md5sum</a>)</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>USBシリアル変換は FTDI系を前提にしている。PL2303系（特にノーブランドのコピー品）は baud=115.2kでは通信異常が多発してトラブルのもとになる。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>ヘッドレスにしてディスクレスでもある。PXEBOOTなら USBメモリすら使わない完全ディスクレスだが、ネットワーク等の周辺環境規模が大きくなるので気軽にお試し出来るものではない。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>メインメモリは最低でも2GiB、標準的には8GiBは乗っているものとする。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p><a href="http://www.ubuntu.com/download/desktop">Ubuntu.com</a> 14.xx以前はシリアルコンソール自体に対応していない。ローカルストレージインストールの場合は /etc/init/ttyS0.confを作成すれば一応使えるようになるが、LiveBootはこの設定を壊してしまうため、15.04とおなじ挙動をさせるにはかなりの修正が必要になるため、本稿では 14.xx対応については割愛した。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p><a href="http://store.shopping.yahoo.co.jp/riava/rs670-s16a.html">Riava RS670A</a> 基本スペックは Intel Gigabit NICx6を持つ Intel/Atom C2758 でメモリ8GiB。基本OSは CentOSまたは Ubuntu。SSD内蔵だがこれはここでは使用しない（壊さない）。なおフロントパネルのシリアルコンソールポート（COM0）は Cisco互換配線（<a href="http://yost.com/computers/RJ45-serial">Yost Serial Device Wiring Standard</a>）になっている。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:5" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:6"><p><a href="http://www.pendrivelinux.com/universal-usb-installer-easy-as-1-2-3/">Universal USB Installer</a> <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:6" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:7"><p><a href="http://www.tldp.org/HOWTO/Remote-Serial-Console-HOWTO/configure-boot-loader-syslinux.html">Remote Serial Console HOWTO</a> <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:7" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:8"><p>最初に書いたほうが主コンソール <em>/dev/console</em>になり、そのデバイスは常に存在することが期待される。物理的にVGAを持たない機器では tty0なしの ttyS0だけでも実は良いのだが、シリアルポート未接続であったりシリアルポートドライバに問題がある場合は起動不能になる事態もありうる。そこで plymothを使うことでフレームバッファを用意し、tty0が必ず存在する状態をとすることで不具合を回避している。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:8" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:9"><p>ただし memtest86+のフル機能がシリアルコンソールで使えるわけではない。configメニュー表示等に不具合があるため、設定変更が難しい。このためECC関係などのオプションはビルド時に修正しておかなければならない。シリアルコンソール対応memtest86+の作成については<a href="http://multix.jp/buildup-memtest-headless/">別項を参照</a>のこと。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:9" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:10"><p>規定値でnosuid,nodev,noexecが指定されている。これらをremountで外しても良いが、最初から制限のかかっていない /rootや /homeを使うほうが手間がない。ただし後述の persistentモードが有効な場合、これらはRAMディスクではないので、<em>mount -t tmpfs none /mnt</em> といった具合に明示的に RAMディスクをマウントして使ったほうが良い。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:10" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:11"><p>デフォルトの1行だけでも必要最小限のことはできるが、docker等いまどきのアプリケーションをフルに動かせるようにするには、これだけの行数が必要になる。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:11" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:12"><p>MBRが壊れるのは、起動したUSBメモリ以外の内蔵ストレージがある場合でかつ /dev、/run等のマウントを忘れていた場合に発生する。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:12" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:13"><p>ログファイルは単純な削除でも良いが、wtmpやlastlog等も区別なく初期化するために、ここではtruncateでゼロバイトに切り詰めている。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:13" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:14"><p>ISOからコピーしたオリジナルのisolinux.cfgには最後に<strong>gfxboot</strong>にチェインする行があるが本稿では使わない（ISOLINUXをブートローダーとする）ためこれは必ず削除しなければ起動不能になる。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:14" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:15"><p>gfxboot関係については、GRUB設定ごと割愛した。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:15" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:16"><p>ただし kernel起動時に <em>ip=frommedia</em> をつけておかないと、ネットワーク設定は綺麗サッパリ無視されてしまうので気をつけよう。知らないと確実にハマる。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:16" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:17"><p>persistentモードで起動しているときは当然マウントされているので削除したりdd初期化したりするのは論外だが、FAT32フォーマットの USBメモリで起動している場合はマウントしたまま mvコマンドでリネームすることはできてしまう。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:17" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:18"><p>ISOイメージを生成する前に準備しておくとより効果的だろう。 <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:18" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:19"><p>ブランクファイルを用意するのに ddコマンドがよく使われるが、バイト単位でのファイルサイズ指定は不得手なので truncateコマンドを使うほうが楽だし、そもそも動作が早い。あえて ddコマンドのオプションで 4GiB-1を表現するなら <em>bs=$((2**16-1)) count=$((2**16+1))</em> となる。  <a href="http://multix.jp/headless-liveboot-ubuntu/#fnref:19" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[気付いたらrootメールが溢れてる]]></title><description><![CDATA[<p>そんなことになる前にやれるべきことはやっておこう。</p>

<ul class="index"></ul>  

<hr>

<h4 id="">犯人はだれ</h4>

<p>Linuxサーバをテキトーに作ってテキトーに運用してるとやらかしがちなのがrootメールの始末だろう。ターミナルログインしてrootにsuしたら毎回<em>You have mail</em>と言われるアレである。mailコマンドを打ってもよくわからんメールが大量に溜まっておりいちいち読むのも削除するのも面倒だからと放置しがちだが、気を抜くと何年かして忘れた頃に突如ディスクが溢れて、システム丸ごと制御不能ということも稀にあるからあなどれない。それはまあ自業自得なのだが、ミニマルな普通のRHEL/CentOS（Linux）ならrootメールの生成元は基本的に以下のふたつだけだ。</p>

<ul>
<li>crond</li>
<li>logwatch</li>
</ul>

<p>たったふたつしかないのだからサーバセットアップ時に忘れずに対処しておくに越したことはない。あとからnagiosやらchkrootkitやらを動かしだすと当然メール生成元が増えてゆくわけだしその前に、である。とりあえず手堅いのは<code>/etc/aliases</code>を編集してrootメールを外部のまともなメールアドレスに丸ごと転送してしまうことだ。</p>

<hr>

<h4 id="aliases">aliasesで転送する</h4>

<p><code>/etc/aliases</code>の末尾を見ると次のようになっている。</p>

<pre><code class="language-brush:plain title:/etc/aliases"># trap decode to catch security attacks
decode:         root

# Person who should get root's mail
#root:          marc
</code></pre>

<p>見れば分かる通り <code>root: USERID</code>を追記すれば、 root宛のメールは指定のローカルアカウントに転送され、</p>]]></description><link>http://multix.jp/rootmail-overflow/</link><guid isPermaLink="false">fbdfc202-3271-4b0a-9b0b-41a7aecc7b13</guid><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 08 Jun 2015 09:17:00 GMT</pubDate><content:encoded><![CDATA[<p>そんなことになる前にやれるべきことはやっておこう。</p>

<ul class="index"></ul>  

<hr>

<h4 id="">犯人はだれ</h4>

<p>Linuxサーバをテキトーに作ってテキトーに運用してるとやらかしがちなのがrootメールの始末だろう。ターミナルログインしてrootにsuしたら毎回<em>You have mail</em>と言われるアレである。mailコマンドを打ってもよくわからんメールが大量に溜まっておりいちいち読むのも削除するのも面倒だからと放置しがちだが、気を抜くと何年かして忘れた頃に突如ディスクが溢れて、システム丸ごと制御不能ということも稀にあるからあなどれない。それはまあ自業自得なのだが、ミニマルな普通のRHEL/CentOS（Linux）ならrootメールの生成元は基本的に以下のふたつだけだ。</p>

<ul>
<li>crond</li>
<li>logwatch</li>
</ul>

<p>たったふたつしかないのだからサーバセットアップ時に忘れずに対処しておくに越したことはない。あとからnagiosやらchkrootkitやらを動かしだすと当然メール生成元が増えてゆくわけだしその前に、である。とりあえず手堅いのは<code>/etc/aliases</code>を編集してrootメールを外部のまともなメールアドレスに丸ごと転送してしまうことだ。</p>

<hr>

<h4 id="aliases">aliasesで転送する</h4>

<p><code>/etc/aliases</code>の末尾を見ると次のようになっている。</p>

<pre><code class="language-brush:plain title:/etc/aliases"># trap decode to catch security attacks
decode:         root

# Person who should get root's mail
#root:          marc
</code></pre>

<p>見れば分かる通り <code>root: USERID</code>を追記すれば、 root宛のメールは指定のローカルアカウントに転送され、 rootのスプールには残らなくなる。従って普段コンソールログインに使用するアカウントを指定してやれば、 rootにならなければメールが来ていることに気付かない、という事態は避けられる。</p>

<p><code>/etc/aliases</code>を編集したら忘れずに <code>newaliases</code>を実行してMTAを更新しよう。</p>

<p>当然、USERIDの代わりに外部メールアドレスを記述すれば、 rootメールはすべてそこに送られる・・・はずなのだが、システムによってはうまく動かない。その秘密が直前にある  <code>decode: root</code>だ。コメントにある通りセキュリティ上の理由で rootメールが外部に送信されないよう制限をかけている。故にこの制限をコメントアウトすれば意図通りあらゆる rootメールが外部メールアドレスへ全転送されるようになる。</p>

<pre><code class="language-brush:plain highlight:[2,5] title:ほんとうにそれでよいのか"># trap decode to catch security attacks
#decode:        root

# Person who should get root's mail
root:           log@example.com  
</code></pre>

<p>言うまでもなくこの設定で万一あれやこれやをされるとたいへん面白くない事態になる。セキュリティ設定は無闇に外すべきではないのが筋であるから、いったん普段使いのログインアカウントに転送し、そちらで外部メールアドレスへ再転送するように設定するのが正しい。</p>

<pre><code class="language-brush:plain highlight:[5,7] title:メール送信にログインアカウントを使う"># trap decode to catch security attacks
decode:         root

# Person who should get root's mail
root:           charlie

charlie:        log@outer.example.com  
</code></pre>

<p>ログイン管理者が複数いるなら、カンマで区切って転送先を列挙すればめいめいに転送される。これは簡易的なメーリングリストの作成方法と同じだ。</p>

<pre><code class="language-brush:plain title:カンマ区切りで複数ユーザ指定">root:           charlie, dennis  
</code></pre>

<p>当たり前だが自分自身に転送すればループメールになってしまう。外部転送しつつローカルにもメールを残したい場合は、バックスラッシュをつけたエイリアス名を記述する。</p>

<pre><code class="language-brush:plain title:エイリアスバックスラッシュを使う">X charlie:        charlie, log@outer.example.com

O charlie:        \charlie, log@outer.example.com  
</code></pre>

<hr>

<h4 id="aliasesmailfro">aliasesで MailFroｍを書き換える</h4>

<p>ところで、当然ながら外部メールアドレスへの転送を行う場合は、リジェクトやらドロップやらでエラーメールが戻ってくる<sup id="fnref:1"><a href="http://multix.jp/rootmail-overflow/#fn:1" rel="footnote">1</a></sup>ことになる。これをまた転送してしまえば当然無限ループになるので、回避するために転送メールの Fromをカスタマイズする手がある。各メール生成元で個別に MailFromを設定するのが正道だが、上記のエイリアスの仕組みを使えば該当アカウントを通過するすべてのメールのFromをもれなく上書きしてしまえる。</p>

<pre><code class="language-brush:plain title:aliases設定">charlie:          \charlie, "|/var/local/fw-charlie-to-log.sh"  
owner-fw-charlie: \charlie  
</code></pre>

<pre><code class="language-brush:bash gutter:true title:/var/local/fw-charlie-to-log.sh">#!/bin/sh -
/usr/lib/sendmail -f owner-fw-charlie@mydomain log@outer.example.com
</code></pre>

<p>これは、<em>charlie</em>に届いたメールは自分自身のローカルに保存されるとともに、指定スクリプト<sup id="fnref:2"><a href="http://multix.jp/rootmail-overflow/#fn:2" rel="footnote">2</a></sup>の標準入力に送る。スクリプトの方は Fromを <em>charlie</em>のエイリアス <em>owner-fw-charlie</em><sup id="fnref:3"><a href="http://multix.jp/rootmail-overflow/#fn:3" rel="footnote">3</a></sup>に書き換えつつ外部の <em>log@outer.example.com</em>に送信する。こうしておいていざリジェクトメールが帰ってくると、それは <em>owner-fw-charlie</em>宛に着信することになる。 <em>owner-fw-charlie</em>のエイリアス設定行では <em>charlie</em>へのローカル保存のみを指示しているので、再度の外部転送は発生せず、ループメールもここで止めつつリジェクトを捨てずに保存させることができる。</p>

<p>この話はクラウドVPS等の運用でグローバルIPを持っておりかつ SMTPポートを開けてメール受信もする正規のサーバでのことだが、送信元サーバ自身でエラーメールを受け取らないような運用でも、同じように MailFromを書き換える価値はある。とは言えその場合は From偽装とやっていることは同じなので正しく着発信できループにもならないよう充分に気を払う必要がある。</p>

<hr>

<h4 id="crond">crondのメール送信を止める</h4>

<p>ここまでは各種ステータス＆ジョブメールを捨てずに外部転送して受け取り＆閲覧する前提の話<sup id="fnref:4"><a href="http://multix.jp/rootmail-overflow/#fn:4" rel="footnote">4</a></sup>だったが、ここからはメール生成そのものを元から止めてしまう方法である。</p>

<p>cronの出すジョブメールを止めるには、まず第一に <strong>cron実行されるジョブがstdoutもstderrも吐かないこと</strong>が最重要だ。出力が何もなければ cronメールは生成されない。本来cronジョブのバックグラウンド終了結果を実行ユーザに返す仕組みなので、その中身は個々のジョブが責任を持っている・・・が、そうも言ってられない事態も時にはあるもので、そういう時は <code>/etc/sysconfig/crond</code> の<code>CRONDARGS</code>を次のように修正し、 <code>service crond restart</code>する。</p>

<pre><code class="language-brush:plain gutter:true highlight:[3] title:/etc/sysconfig/crond"># Settings for the CRON daemon.
# CRONDARGS= :  any extra command-line startup arguments for crond
CRONDARGS="-m off"  
</code></pre>

<p>もう少し一般的な（汎用的な）メールの止め方としては、 <code>/etc/crontab</code>に <code>MAILTO=""</code>を書く方法だろう。この方法は RHEL/CentOSに限らず Linux全般や *BSDにも通用する。そして編集後には crondを再起動（あるいは<code>kill -HUP</code>）しなければならないのだが、どういうわけか誰もが頻繁に<strong>プロセス再起動を忘れる</strong>のは定番のお約束である。<sup id="fnref:5"><a href="http://multix.jp/rootmail-overflow/#fn:5" rel="footnote">5</a></sup></p>

<pre><code class="language-brush:plain gutter:true highlight:[5] title:/etc/crontab">SHELL=/bin/bash  
PATH=/sbin:/bin:/usr/sbin:/usr/bin  
MAILTO=root  
MAILTO="log@outer.example.com"  
MAILTO=""  
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
</code></pre>

<p>ところがこの方法には落とし穴がある。crontabファイルは <code>/etc</code>の下にあるものばかりではない。</p>

<pre><code class="language-brush:plain gutter:true title:/var/spool/cron/root"># /usr/bin/crontab -l
MAILTO="root"  
#* * * * * /etc/pound/poundchk.sh 1&gt;/dev/null 2&gt;&amp;1
* * * * * /etc/rc.d/secure_cleaning.pl
*/3 * * * * /etc/rc.d/dns_listen_flush.sh
</code></pre>

<p><code>/var/spool/cron/</code> 以下には各ユーザ別の crontabファイルを作ることが出来る。 <code>/etc/crontab</code>に比べると6カラム目のユーザ指定がない<sup id="fnref:6"><a href="http://multix.jp/rootmail-overflow/#fn:6" rel="footnote">6</a></sup>ことを除けば実質同じものだ。当然MAILTO環境変数も同様に指定できるので、こちらでもメール送信が設定されていないか確認したほうがよい。</p>

<p>言うまでもなく先に上げた <code>/etc/sysconfig/crond</code>でメールを止める方法はメール送信が完全に止まるので、MAILTO環境変数を改めて指定してもジョブメールが出たりすることはない。</p>

<hr>

<h4 id="logwatch">logwatchのレポートメールを止める</h4>

<p>logwatchについては、もっと高級なシステム監視（ZABBIXやNagios）を採用しているなら不必要なことがほとんどなので、logwatchパッケージ自体を削除して最初からなかったことにしてしまうのが一番シンプルで確実だ。</p>

<pre><code class="language-brush:plain">sudo yum remove logwatch  
</code></pre>

<p>logwatchはperlやsendmail（postfix）くらいにしか依存していないはずなので、依存性により本パッケージ削除できないという事態は滅多にない。だが事情により既存パッケージを削除したくない・できない<sup id="fnref:7"><a href="http://multix.jp/rootmail-overflow/#fn:7" rel="footnote">7</a></sup>等という運営上の理由があるならば、以下の設定で動作を止めることができる。</p>

<pre><code class="language-brush:bash">echo "DailyReport=No" &gt;&gt; /usr/share/logwatch/default.conf/logwatch.conf  
</code></pre>

<p>これは<code>/etc/cron.daily/0logwatch</code>が次のように書かれているためだ。</p>

<pre><code class="language-brush:bash gutter:true title:/etc/cron.daily/0logwatch">#!/bin/bash

DailyReport=`grep -e "^[[:space:]]*DailyReport[[:space:]]*=[[:space:]]*" /usr/share/logwatch/default.conf/logwatch.conf | head -n1 | sed -e "s|^\s*DailyReport\s*=\s*||"`

if [ "$DailyReport" != "No" ] &amp;&amp; [ "$DailyReport" != "no" ]  
then  
    logwatch
fi  
</code></pre>

<p><code>/etc/cron.daily/0logwatch</code>を削除したり実行パーミッションを落とせば良くないか？という発想は甘い。<code>yum update</code>の際に欠損ファイルが復旧されたり、パーミッションが元通りに修復されてしまう事態がおこるため、こういうものは設定ファイルで動作を止めるのが正義である。</p>

<hr>

<h4 id="root">溢れたrootメールを消す・証拠を残す</h4>

<p>以上の予防処置を講じるまでもなく rootメールが溢れたあとでは、まあだいたいシステム（のディスク残量）が終わっているのでこうするしかない。</p>

<pre><code class="language-brush:plain title:ゼロバイト消去"># &gt; /var/spool/mail/root 
</code></pre>

<p>ゼロバイト消去をするか、<code>rm -f</code>で削除するかは好みではあるが、いずれにせよ早急にディスク容量を回復する必要がある状況のはずだ。悠長に問題ファイルのバックアップを保存して・・・などという余裕はないだろう。そもそもファイルコピーするだけのリソース余地なぞ残っていまい。</p>

<p>だが障害報告書に貼り付けるだけの最小限の状況証拠は残しておきたい場合はどうするか。かろうじてリモートターミナル<sup id="fnref:8"><a href="http://multix.jp/rootmail-overflow/#fn:8" rel="footnote">8</a></sup>が繋がっているなら、ターミナルアプリのログ機能を利用して記録を取ることができる。要するにスプールファイルを catでコンソールに流して保存したりスクリーンショットを撮影したりする。</p>

<p>もっとも真正直に catで全部流すと何時間掛かるか分かったものではないので、 head と tailで最初と最後の数メールぶん程度を記録すれば充分だろう。少なくとも何時からメールが溜まり始め、何時までシステムが動いていたかの問題期間を特定することは出来るはずだ。</p>

<hr>

<h4 id="postfix">Postfixのドロップメールを消す</h4>

<p>rootメールを外部送信するようにしていながら送信先がなかったり間違っていた場合、maildropやbounceディレクトリが溢れることになる。「それ」が溢れていることが自明<sup id="fnref:9"><a href="http://multix.jp/rootmail-overflow/#fn:9" rel="footnote">9</a></sup>であるならば、敢えてディレクトリの中を lsしようとしたり、rm -fしようとしたりしてはいけない。<sup id="fnref:10"><a href="http://multix.jp/rootmail-overflow/#fn:10" rel="footnote">10</a></sup></p>

<pre><code class="language-brush:plain"># rm -f /var/spool/postfix/maildrop/*
Too many arguments  
</code></pre>

<p>この場合は該当ディレクトリごと <code>rm -fr</code>して改めてディレクトリを作りなおすか、 <code>find|xargs</code>で消すか、今時の findなら <code>-delete</code>オプションが備わっているので、それを使って消す。</p>

<pre><code class="language-brush:plain title:丸ごと作り直す方法（手数が多くて面倒かつミスもしやすい）"># rm -fr /var/spool/postfix/maildrop
# mkdir /var/spool/postfix/maildrop
# chown postfix:postdrop /var/spool/postfix/maildrop
# chmod 730 /var/spool/postfix/maildrop
</code></pre>

<pre><code class="language-brush:plain title:割と何処でも通用する消し方"># find /var/spool/postfix/maildrop/ -type f -print | xargs rm -f
</code></pre>

<pre><code class="language-brush:plain title:スマートかつ高速だが対応していないfindもある"># find /var/spool/postfix/maildrop/ -type f -delete
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>rootで外部送信しない理由は、これが攻撃手段に使えるからだ。 <a href="http://multix.jp/rootmail-overflow/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>スクリプトには当然Sendmail/Postfixから実行できるオーナーとパーミッションが付いていること。 <a href="http://multix.jp/rootmail-overflow/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>ここで使うエイリアス名は、ローカルに実在するアカウント名であってはならないし、<em>/etc/aliases</em>で他用途に使っているエイリアス名であってもならない。 <a href="http://multix.jp/rootmail-overflow/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>万一悪意ある攻撃を受けたり乗っ取られて勝手にあれこれされたりした場合、各種メールやsyslogを外部転送してあると事後調査が捗る。 <a href="http://multix.jp/rootmail-overflow/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>crondは起動/再起動直後の1分間は実際には動作しないので、テストジョブは少なくとも2〜3分先に仕掛ける必要がある。この3分というのが結構長いので、虫取りを何回か繰り返しているとたいがい再起動してたかどうかも忘れてしまう。（SEの頭の方に虫が湧く） <a href="http://multix.jp/rootmail-overflow/#fnref:5" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:6"><p>この文法の違いを忘れて cronが正しく動かなかった話はよくある。文脈によってはどちらの crontabを扱っているつもりなのか再確認しないと危ない。通常 crontabコマンドはユーザレベルの定期ジョブ用で、システムタスク用には使わないだろう。だがこれを用いる最大の利点は crondの再起動が不要なことである。 <a href="http://multix.jp/rootmail-overflow/#fnref:6" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:7"><p>設計仕様書に何故か含まれていて、要らないのに消すのは面倒という案件とか。 <a href="http://multix.jp/rootmail-overflow/#fnref:7" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:8"><p>sshdはとうに死んでいて手に負えないことが大多数だろうが、その状況でもシリアルコンソールには何の影響もなく使えるのが普通である。 <a href="http://multix.jp/rootmail-overflow/#fnref:8" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:9"><p>だいたい dfと duコマンドで何処がディスクを食い潰しているかを絞り込む過程で見つけることになる。 <a href="http://multix.jp/rootmail-overflow/#fnref:9" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:10"><p>どうしても lsしたい場合は <strong>-U</strong>（ソートしない）オプションを使わなければならない。これを忘れてswapフリーズに陥ると手に負えなくなる。そうなったプロセスは <strong>kill -KILL でも止められない</strong>のだ。 <a href="http://multix.jp/rootmail-overflow/#fnref:10" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[VMware ESXi をシリアルコンソールで動かす]]></title><description><![CDATA[<p>VMware ESXi はデフォルトではハイパーバイザがホストPCの物理VGAコンソール（GPU）を専有してしまうが、正直これはハードウェア資源が勿体無い。そこでハイパーバイザをヘッドレス化しようという話。  </p>

<ul class="index"></ul>  

<hr>

<h4 id="bootbankbootcfg">/bootbank/boot.cfg の修正</h4>

<p>通常の手順で既にインストール＆稼働済のESXiホストが相手の場合、シリアルコンソール化するには起動設定ファイルに修正を加えるだけで済む。ESXiのバージョンによって多少異なるが、5.x系は<code>kernelopt</code>の1行にシリアルコンソール設定を加えるだけで良い。</p>

<pre><code class="language-brush:plain gutter:true highlight:[3] title:/bootbank/boot.cfg">bootstate=0  
kernel=tboot.b00  
kernelopt=no-auto-partition text nofb com1_baud=115200 com1_Port=0x3f8 tty2Port=com1 gdbPort=none logPort=none  
modules=b.b00 --- useropts.gz --- k.b00 ---</code></pre>]]></description><link>http://multix.jp/vmware-esxi-over-serialconsole/</link><guid isPermaLink="false">45fd0e5a-313d-49ca-b7f7-e53f223392e5</guid><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Tue, 19 May 2015 03:29:00 GMT</pubDate><content:encoded><![CDATA[<p>VMware ESXi はデフォルトではハイパーバイザがホストPCの物理VGAコンソール（GPU）を専有してしまうが、正直これはハードウェア資源が勿体無い。そこでハイパーバイザをヘッドレス化しようという話。  </p>

<ul class="index"></ul>  

<hr>

<h4 id="bootbankbootcfg">/bootbank/boot.cfg の修正</h4>

<p>通常の手順で既にインストール＆稼働済のESXiホストが相手の場合、シリアルコンソール化するには起動設定ファイルに修正を加えるだけで済む。ESXiのバージョンによって多少異なるが、5.x系は<code>kernelopt</code>の1行にシリアルコンソール設定を加えるだけで良い。</p>

<pre><code class="language-brush:plain gutter:true highlight:[3] title:/bootbank/boot.cfg">bootstate=0  
kernel=tboot.b00  
kernelopt=no-auto-partition text nofb com1_baud=115200 com1_Port=0x3f8 tty2Port=com1 gdbPort=none logPort=none  
modules=b.b00 --- useropts.gz --- k.b00 --- a.b00 --- net-ixgb.v00 --- ata-pata.v00 --- ata-pata.v01 --- ata-pata.v02 --- ata-pata.v03 --- ata-pata.v04 --- ata-pata.v05 --- ata-pata.v06 --- ata-pata.v07 --- block-cc.v00 --- ehci-ehc.v00 --- s.v00 --- ima-qla4.v00 --- ipmi-ipm.v00 --- ipmi-ipm.v01 --- ipmi-ipm.v02 --- misc-cni.v00 --- misc-dri.v00 --- net-be2n.v00 --- net-bnx2.v00 --- net-bnx2.v01 --- net-cnic.v00 --- net-e100.v00 --- net-e100.v01 --- net-enic.v00 --- net-forc.v00 --- net-igb.v00 --- net-nx-n.v00 --- net-r816.v00 --- net-r816.v01 --- net-s2io.v00 --- net-sky2.v00 --- net-tg3.v00 --- ohci-usb.v00 --- sata-ahc.v00 --- sata-ata.v00 --- sata-sat.v00 --- sata-sat.v01 --- sata-sat.v02 --- sata-sat.v03 --- scsi-aac.v00 --- scsi-adp.v00 --- scsi-aic.v00 --- scsi-bnx.v00 --- scsi-fni.v00 --- scsi-hps.v00 --- scsi-ips.v00 --- scsi-lpf.v00 --- scsi-meg.v00 --- scsi-meg.v01 --- scsi-meg.v02 --- scsi-mpt.v00 --- scsi-mpt.v01 --- scsi-mpt.v02 --- scsi-qla.v00 --- scsi-qla.v01 --- scsi-rst.v00 --- uhci-usb.v00 --- imgdb.tgz --- state.tgz  
build=5.0.0-3.48.1851670  
updated=3  
</code></pre>

<p>このファイルは起動中なら上記のパスにある。そうでなければ起動ディスク中のVFATフォーマットされたパーテションの一つにある。<sup id="fnref:1"><a href="http://multix.jp/vmware-esxi-over-serialconsole/#fn:1" rel="footnote">1</a></sup></p>

<hr>

<h4 id="">ヘッドレスインストール</h4>

<p>ヘッドレスで起動できることが確認できたから、これを応用すればヘッドレスインストールだってできる。インストールディスクはブートローダーにISOLINUXを使用しているので、これを丸ごとtftpサーバの<code>/tftpboot/images/esx</code>にコピーして、要所をPXELINUX用に修正すればよい。この時の<code>pxelinux.cfg</code>は以下のようになる。</p>

<pre><code class="language-brush:plain gutter:true title:/tftpboot/pxelinux.cfg/default">serial 0 115200  
console 0

DEFAULT menu.c32  
MENU TITLE ESXi-5.x.x-XXXXXX-full Boot Menu  
NOHALT 1  
PROMPT 0  
TIMEOUT 80  
LABEL install  
  KERNEL images/esx/mboot.c32
  APPEND -c images/esx/boot.cfg text nofb com1_baud=115200 com1_Port=0x3f8 tty2Port=com1 gdbPort=none logPort=none
  ipappend 2
  MENU LABEL ESXi-5.x.x-XXXXXX-full ^Installer

LABEL hddboot  
 LOCALBOOT 0x80
 MENU LABEL ^Boot from local disk
</code></pre>

<p>1,2行目がSYSLINUXの表示をシリアルポート（0=最初のシリアルポート）にもリダイレクトするための設定<sup id="fnref:2"><a href="http://multix.jp/vmware-esxi-over-serialconsole/#fn:2" rel="footnote">2</a></sup>だが、<strong>PCのBIOS自体にリダイレクトコンソールサポートがある場合は必要ない</strong>。これは存外見落としがちなのだが、BIOSでそれが有効な場合、SYSLINUX（やGRUB）のほうでも有効にしてしまうと入出力が二重化してしまい、特にキーボード入力で不具合が生じる。従ってこれはBIOSリダイレクトコンソールを使えない環境・設定においてのみ必須となる。</p>

<p>一方10行目のカーネルパラメータは、コンソール制御がBIOSやSYSLINUXから離れてカーネルに移ってから有効になるものなので、BIOSリダイレクトコンソールの有無に関わらず必要となる。</p>

<pre><code class="language-brush:plain gutter:true highlight:[5] title:/tftpboot/images/esx/boot.cfg (インストーラ起動用)">bootstate=0  
title=Loading ESXi installer  
prefix=images/esx/  
kernel=tboot.b00  
kernelopt=runweasel text nofb com1_baud=115200 com1_Port=0x3f8 tty2Port=com1 gdbPort=none logPort=none  
modules=b.b00 --- useropts.gz --- k.b00 --- chardevs.b00 --- a.b00 --- user.b00 --- s.v00 --- ata_pata.v00 --- ata_pata.v01 --- ata_pata.v02 --- ata_pata.v03 --- ata_pata.v04 --- ata_pata.v05 --- ata_pata.v06 --- ata_pata.v07 --- block_cc.v00 --- ehci_ehc.v00 --- weaselin.t00 --- esx_dvfi.v00 --- xlibs.v00 --- ima_qla4.v00 --- ipmi_ipm.v00 --- ipmi_ipm.v01 --- ipmi_ipm.v02 --- misc_cni.v00 --- misc_dri.v00 --- net_be2n.v00 --- net_bnx2.v00 --- net_bnx2.v01 --- net_cnic.v00 --- net_e100.v00 --- net_e100.v01 --- net_enic.v00 --- net_forc.v00 --- net_igb.v00 --- net_ixgb.v00 --- net_nx_n.v00 --- net_r816.v00 --- net_r816.v01 --- net_s2io.v00 --- net_sky2.v00 --- net_tg3.v00 --- net_vmxn.v00 --- ohci_usb.v00 --- sata_ahc.v00 --- sata_ata.v00 --- sata_sat.v00 --- sata_sat.v01 --- sata_sat.v02 --- sata_sat.v03 --- sata_sat.v04 --- scsi_aac.v00 --- scsi_adp.v00 --- scsi_aic.v00 --- scsi_bnx.v00 --- scsi_fni.v00 --- scsi_hps.v00 --- scsi_ips.v00 --- scsi_lpf.v00 --- scsi_meg.v00 --- scsi_meg.v01 --- scsi_meg.v02 --- scsi_mpt.v00 --- scsi_mpt.v01 --- scsi_mpt.v02 --- scsi_qla.v00 --- scsi_qla.v01 --- scsi_rst.v00 --- uhci_usb.v00 --- tools.t00 --- xorg.v00 --- imgdb.tgz --- imgpayld.tgz  
build=  
updated=0s  
</code></pre>

<p>インストーラ起動用の<code>boot.cfg</code>にもシリアルポート用の設定を加える。またネットワークブートカーネルのパスも修正が必要だ。</p>

<p>以上でESXiインストーラを動かせるはずだがインストール終了後に、最初に述べた<code>/bootbank/boot.cfg</code>の修正が必要だ。ここを自動化したい場合にはインストーライメージ内からkickstartファイルを見つけて、postスクリプトセクションに以下の1行を加えておく。</p>

<pre><code class="language- brush:plain">sed -i '/no-auto-partition/ s/$/ text nofb com1_baud=115200 com1_Port=0x3f8 tty2Port=com1 gdbPort=none logPort=none/' /bootbank/boot.cfg  
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>全く同じ内容の予備パーテションもあるが、通常はディスク先頭に近いほうがプライマリとして使われている。なお4行目のmodules読込設定は実ハード環境によって異なるので、このままコピペしないこと。 <a href="http://multix.jp/vmware-esxi-over-serialconsole/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p><a href="http://www.tldp.org/HOWTO/Remote-Serial-Console-HOWTO/configure-boot-loader-syslinux.html">Remote Serial Console HOWTO</a> <a href="http://multix.jp/vmware-esxi-over-serialconsole/#fnref:2" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[ホーム画面/Webクリップアイコン]]></title><description><![CDATA[<p>スマートフォンでWebページをホーム画面に登録したり、Windows 8.xでタイル画面にWebクリップをピン留めするときに使われるアイコンの設定方法に関するメモ。</p>

<ul class="index"></ul>

<hr>

<h4 id="ios">iOSの場合</h4>

<p>iOSの場合、特に何もしていなければホーム画面登録時に以下のファイルをサーバのドキュメントルートへ探しに行く。</p>

<pre><code class="language-table">|ファイル名|表示サイズ|クライアント|
|apple-touch-icon-152x152.png|152px|Retina版のiPad、iPad mini用、iOS 7以降|
|apple-touch-icon-144x144.png|144px|Retina版のiPad、iPad mini用、iOS 6以前|
|apple-touch-icon-120x120.png|120px|Retina版のiPhone、iPod touch用、iOS 7以降|
|apple-touch-icon-114x114.png|114px|Retina版のiPhone、iPod touch用、iOS 6以前|
|apple-touch-icon-76x76.png|76px|非Retina版のiPad 2、iPad miniの用、iOS</code></pre>]]></description><link>http://multix.jp/webclip-icon/</link><guid isPermaLink="false">f37cc7e2-ced3-427d-946a-b4987a71b3b8</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 11 May 2015 02:34:37 GMT</pubDate><content:encoded><![CDATA[<p>スマートフォンでWebページをホーム画面に登録したり、Windows 8.xでタイル画面にWebクリップをピン留めするときに使われるアイコンの設定方法に関するメモ。</p>

<ul class="index"></ul>

<hr>

<h4 id="ios">iOSの場合</h4>

<p>iOSの場合、特に何もしていなければホーム画面登録時に以下のファイルをサーバのドキュメントルートへ探しに行く。</p>

<pre><code class="language-table">|ファイル名|表示サイズ|クライアント|
|apple-touch-icon-152x152.png|152px|Retina版のiPad、iPad mini用、iOS 7以降|
|apple-touch-icon-144x144.png|144px|Retina版のiPad、iPad mini用、iOS 6以前|
|apple-touch-icon-120x120.png|120px|Retina版のiPhone、iPod touch用、iOS 7以降|
|apple-touch-icon-114x114.png|114px|Retina版のiPhone、iPod touch用、iOS 6以前|
|apple-touch-icon-76x76.png|76px|非Retina版のiPad 2、iPad miniの用、iOS 7以降|
|apple-touch-icon-72x72.png|72px|非Retina版のiPad 2、iPad miniの用、iOS 6以前|
|apple-touch-icon.png|57px/60px|デフォルト|
</code></pre>

<p>iOS7未満ではグローエフェクトをバイパスする<code>-precomposed</code>付きの<code>apple-touch-icon-precomposed.png</code>等も探しに行くようになっていたが、iOS7以降は廃止された。<sup id="fnref:1"><a href="http://multix.jp/webclip-icon/#fn:1" rel="footnote">1</a></sup></p>

<p>真面目にアイコンを用意しようとするとこれらを全部用意しなければならないが、サーバに<mark>404 File not found</mark>エラーログが残ることを気にしないならば<code>apple-touch-icon.png</code>さえ存在していれば実用上の問題はない。</p>

<p>もともとの画像サイズの規定値は上記の通りだが、サイズが合ってなければ自動的にリサイズされるし、また使用されている大多数のiOSデバイスは既にRetinaディスプレイ化されているため、生真面目に最小公倍数で作成する必要も取り立ててない。ざっくり512pxや600pxあたりで作画してしまってもよいだろう。</p>

<p>その他気を付けるべきこととして以下の制約がある。</p>

<ul>
<li>画像ファイルは標準的な24bit PNG形式とする。</li>
<li>True Color 形式とし、Indexカラーにしてはならない。</li>
<li>インタレース化してはいけない。リサイズによって表示が乱れる。</li>
<li>SSLは問題ないが、クライアント認証に対応していない。</li>
<li>Expires等で有効期限が指示されている場合、期限が切れると自動的に再アクセス・再生成される。ファイルサイズが大きいと処理の重さが気になるかもしれない。</li>
</ul>

<hr>

<h4 id="android">Androidの場合</h4>

<p>Android系の場合、Webクリップ画像についてはiOSに準じたものでよい。それを<code>&lt;head&gt;</code>セクション内の<code>&lt;link&gt;</code>要素で指定することができる。</p>

<pre><code class="language-brush:html">&lt;link rel="apple-touch-icon" href="apple-touch-icon.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png"/&gt;  
</code></pre>

<p><code>&lt;sizes&gt;</code>属性で優先するアイコンサイズを指定することができるが、これは機種やAndroidバージョンによって千差万別<sup id="fnref:2"><a href="http://multix.jp/webclip-icon/#fn:2" rel="footnote">2</a></sup>となるため「これは必須」と明言することができない。従ってこれもアバウトに大きめのサイズをひとつ用意するのが実利的だろう。</p>

<hr>

<h4 id="windows">Windowsタイル画面へのピン留め</h4>

<p>Windows8.x では（正確にはInternet Explorer 11の機能）スタートタイル画面にWebクリップを貼ろうとすると、デフォルトではまずドキュメントルートの<code>browserconfig.xml</code>を探しに行く。このブラウザ構成ファイルのフォーマットは次のようになっている。</p>

<pre><code class="language-brush:xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;  
&lt;browserconfig&gt;  
  &lt;msapplication&gt;
    &lt;tile&gt;
      &lt;square70x70logo src="images/smalltile.png"/&gt;
      &lt;square150x150logo src="images/mediumtile.png"/&gt;
      &lt;wide310x150logo src="images/widetile.png"/&gt;
      &lt;square310x310logo src="images/largetile.png"/&gt;
      &lt;TileColor&gt;#009900&lt;/TileColor&gt;
    &lt;/tile&gt;
  &lt;/msapplication&gt;
&lt;/browserconfig&gt;  
</code></pre>

<p>このカスタム構成ファイルは<code>&lt;head&gt;</code>中の<code>&lt;meta&gt;</code>要素で明示的に指定することができる。</p>

<pre><code class="language-brush:html">&lt;meta name="msapplication-config" content="ieconfig.xml" /&gt;  
</code></pre>

<p>このカスタム構成ファイルが見つからない場合は、通常のFaviconがタイル画像として採用される。</p>

<p>使用される画像のサイズは下表のとおりだが、ワイドサイズのみ長方形でそれ以外は正方形として規定されている。</p>

<pre><code class="language-table">|タイルのサイズ|標準のタイルのサイズ|最小画像サイズ|推奨される画像のサイズ|
|:-|:-:|:-:|:-:|
|小サイズ|70 x 70|56 x 56|128 x 128|
|普通サイズ|150 x 150|120 x 120|270 x 270|
|ワイドサイズ|310 x 150|248 x 120|558 x 270|
|大サイズ|310 x 310|248 x 248|558 x 558|
</code></pre>

<p>カスタム構成ファイルには画像更新間隔や通知テキストを追記することもでき、ウェザーサイトのように一定時間間隔で画像を書き換えさせることもできる。詳しくはMSDNの該当ページを参照されたい。<sup id="fnref:3"><a href="http://multix.jp/webclip-icon/#fn:3" rel="footnote">3</a></sup></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>iOS7以降はフラットデザインとなったため、角丸処理の他は行う必要がなくなったことで廃止された。 <a href="http://multix.jp/webclip-icon/#fnref:1" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:2"><p>環境によっては画像URLは絶対パスで指定しないと正常認識されないケースもあるようだ。 <a href="http://multix.jp/webclip-icon/#fnref:2" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:3"><p><a href="https://msdn.microsoft.com/ja-jp/library/dn455106(v=vs.85).aspx">https://msdn.microsoft.com/ja-jp/library/dn455106(v=vs.85).aspx</a> <a href="http://multix.jp/webclip-icon/#fnref:3" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Node.js環境を作る]]></title><description><![CDATA[<p>プラットフォーム別のNode.js環境構築メモ</p>

<ul class="index"></ul>

<hr>

<h4 id="centos6linux">CentOS6/Linux</h4>

<p>CentOSの場合、nodejsとnpmの各パッケージはEPELレポジトリに収録されている。EPELを使用可能にするには<a href="https://ghost.multix.jp/centos-addon-repos/">こちら</a>を参照のこと。これが済んでいればyumだけで基本環境のインストールが出来る。</p>

<pre><code class="language-brush:plain">sudo yum install nodejs npm  
</code></pre>

<p>ただしこれでOSにインストールされるのはやや古いバージョンである。</p>

<pre><code class="language-brush:plain">$ node -v
v0.10.33  
$ npm -v
1.3.6  
</code></pre>

<p>異なるバージョンのNode.jsが必要な場合はnvmを使用して切り替える。まずnvmコマンドをgitでダウンロードし、添付のスクリプトを実行して導入する。<sup id="fnref:1"><a href="http://multix.jp/install-nodejs/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain">$ git clone https://github.com/creationix/nvm.git ~/.nvm
Initialized empty Git repository in ~/.nvm/.git/  
remote: Counting objects:</code></pre>]]></description><link>http://multix.jp/install-nodejs/</link><guid isPermaLink="false">f570ed62-aba5-4a83-81be-788b686230cb</guid><category><![CDATA[Windows]]></category><category><![CDATA[Machintosh]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 07 May 2015 08:14:34 GMT</pubDate><content:encoded><![CDATA[<p>プラットフォーム別のNode.js環境構築メモ</p>

<ul class="index"></ul>

<hr>

<h4 id="centos6linux">CentOS6/Linux</h4>

<p>CentOSの場合、nodejsとnpmの各パッケージはEPELレポジトリに収録されている。EPELを使用可能にするには<a href="https://ghost.multix.jp/centos-addon-repos/">こちら</a>を参照のこと。これが済んでいればyumだけで基本環境のインストールが出来る。</p>

<pre><code class="language-brush:plain">sudo yum install nodejs npm  
</code></pre>

<p>ただしこれでOSにインストールされるのはやや古いバージョンである。</p>

<pre><code class="language-brush:plain">$ node -v
v0.10.33  
$ npm -v
1.3.6  
</code></pre>

<p>異なるバージョンのNode.jsが必要な場合はnvmを使用して切り替える。まずnvmコマンドをgitでダウンロードし、添付のスクリプトを実行して導入する。<sup id="fnref:1"><a href="http://multix.jp/install-nodejs/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain">$ git clone https://github.com/creationix/nvm.git ~/.nvm
Initialized empty Git repository in ~/.nvm/.git/  
remote: Counting objects: 3595, done.  
remote: Compressing objects: 100% (20/20), done.  
remote: Total 3595 (delta 7), reused 0 (delta 0), pack-reused 3574  
Receiving objects: 100% (3595/3595), 765.49 KiB | 304 KiB/s, done.  
Resolving deltas: 100% (2060/2060), done.

$ source ~/.nvm/nvm.sh
</code></pre>

<p>nvmコマンドはエイリアスとしてシェル環境変数に組み込まれるため、新たな端末を開く度にnvm.shを再実行する必要がある。毎回使用するなら<code>~/.bashrc</code>等に追記しておくと良い。</p>

<p>初期状態ではsystemインストールされたNode.jsが使われているので<code>nvm current</code>を実行すると<em>system</em>が返る。</p>

<pre><code class="language-brush:plain">$ nvm current
system

$ which node
/usr/bin/node
</code></pre>

<p>取り敢えず最新のNode.jsを使えるようにするには次のようにするだけで良い</p>

<pre><code class="language-brush:plain">$ nvm install 0
######################################################################## 100.0%
Now using node v0.12.2 (npm v2.7.4)

$ nvm current
v0.12.2

$ node -v
v0.12.2

$ which node
~/.nvm/versions/node/v0.12.2/bin/node
</code></pre>

<p>上記のようにnvmでインストールされるnodeコマンドは、ユーザ環境毎に独立している。環境切替はシェル環境変数に依るので、systemサービスレベルでNode.jsを使用する場合は注意しなければならない。</p>

<p>なおsystemおよびnvmで導入したnodeコマンドと各グローバルnode_modulesパスは次のようになる。これはつまり<code>npm install -g</code>で書き換えられる先がsudo不要になるということでもある。</p>

<pre><code class="language-table">|＼|system|nvm|
|-:|:-|:-|
|node|/usr/bin/node|~/.nvm/versions/node/[VER]/bin/node|
|npm|/usr/bin/npm|~/.nvm/versions/node/[VER]/bin/npm|
|modeules|/usr/lib/node_modules|~/.nvm/versions/node/[VER]/lib/node_modules|
</code></pre>

<hr>

<h4 id="macosx">Mac OSX</h4>

<p>Mac OSXの場合、<a href="http://nodejs.org/">公式サイト</a>からインストーラパッケージをダウンロードしてきて実行するのがもっとも手軽だろう。だがnpmコマンドで各モジュールをインストールする段階でネイティブビルド環境（Xcode）を要求されたりすると途端に面倒なことになり、最終的な手間は以下のどれをとっても結局は大差なくなる。</p>

<ol>
<li><a href="http://nodejs.org/">公式サイト</a>からインストーラを得て導入（/usr/local/bin/node）  </li>
<li><a href="http://nodejs.org/">公式サイト</a>またはGitHubからソースを得て./configure &amp;&amp; make  </li>
<li>Mac Ports環境で port install nodejs（/opt/local/bin/node）</li>
</ol>

<p>1番以外は結局Xcodeがなければ話にならない。なおXcode（それ自体はAppStoreから入手できる）のコマンドライン・デベロッパ・ツールの追加インストールは以下のコマンドをターミナルで実行する。<sup id="fnref:2"><a href="http://multix.jp/install-nodejs/#fn:2" rel="footnote">2</a></sup></p>

<pre><code class="language-brush:plain">$ xcode-select --install
</code></pre>

<p>nvmでのバージョン切替はLinuxと同じなので前項を参照のこと。</p>

<hr>

<h4 id="windows">Windows</h4>

<p>Windowsの場合はだいたい以下のシナリオがある。</p>

<ol>
<li><a href="http://nodejs.org/">公式サイト</a>からインストーラを得て導入  </li>
<li><a href="https://www.visualstudio.com/downloads/download-visual-studio-vs">Visual Studio</a>（Expressの場合はfor Web）に<a href="https://nodejstools.codeplex.com">NTVSアドオン</a>を追加する  </li>
<li>CygwinあるいはMinGW環境でGitHunソースからmakeする  </li>
<li><a href="https://github.com/marcelklehr/nodist">nodist</a>で導入する</li>
</ol>

<p>基本は1番だが、おすすめはWindowsらしい強力なIDEと一体化できる2番を更に追加した環境だ。開発PCでは1+2番、サービスサーバでは1番だけという使い分けが良いだろう。3番は余程の物好きでない限りおすすめしかねる。4番はもはやふるいやり方だがZIP配布なのでレジストリを汚すこともなくコンパクトな環境を作れるが環境変数設定等は全て手動なのでGUIインストーラを使えない場合の代替手段と見たほうが良い。<sup id="fnref:3"><a href="http://multix.jp/install-nodejs/#fn:3" rel="footnote">3</a></sup> <sup id="fnref:4"><a href="http://multix.jp/install-nodejs/#fn:4" rel="footnote">4</a></sup></p>

<p>nvmでのnodeバージョン管理は<a href="https://github.com/hakobera/nvmw">nvmw</a>と<a href="https://github.com/coreybutler/nvm-windows">nvm-windows</a>の2通りの実装がある。 <br>
前者はPython2.7+が更に必要なので、ActivePython等のインストールが要求される。後者ならスタンドアロンインストーラ配布なので手軽だ。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>git自体はCentOSの場合は標準レポジトリに属しているので、もしなくても<strong>yum install git</strong>で導入できる。 <a href="http://multix.jp/install-nodejs/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>実行するにはかつてはApple Developer Member登録が必須だったが、いまはそんなことはない？ <a href="http://multix.jp/install-nodejs/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>自分の場合最初はnodistで環境構築したため、便宜上追記した。 <a href="http://multix.jp/install-nodejs/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>その後マルチプラットフォーム対応の Visual Studio Codeが公開されたため、環境構築の選択肢は更に増えた。 <a href="http://multix.jp/install-nodejs/#fnref:4" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Finder/Explorerのアイコン表示]]></title><description><![CDATA[<p>MacOSの.icnsファイルもWindowsの.icoファイルも復数のアイコンリソースを含むマルチ解像度対応画像アーカイブだといえる。ではどんな状況でどの解像度のアイコン画像が選択されるのかを調べてみた。</p>

<ul class="index"></ul>

<hr>

<h4 id="osxmarvericksyosemite">OSX(Marvericks/Yosemite)の場合</h4>

<p>まずはOSXでの話。テスト用には前掲<a href="http://multix.jp/create-osx-icns-for-windows/">【OSX用アイコンファイルをWindows上で作る】</a>の<code>mkosxicns.pl</code>を使って<strong><a href="https://secure.multix.jp/download/multisize.icns">multisize.icns</a></strong>という.icnsファイルを作成した。これには以下のアイコンが含まれている。</p>

<p><img src="http://multix.jp/content/images/2015/03/2015-03-16-15-38-39.png" alt=""></p>

<p>これをファインダーで表示したりアプリケーションアイコンに設定したりしてみる。OSXはMarvericksとYosemiteを使用したがどちらも結果は変わらなかった。</p>

<pre><code class="language-table title:.icnsファイルのファインダー表示">|表示サイズ|状況|
|:-:|:-|
|16x16|リスト表示&lt;br&gt;カラム表示&lt;br&gt;デスクトップ/アイコン表示(最小)|
|32x32|デスクトップ/アイコン表示(17〜32)|
|32x32@2x&lt;br&gt;(64x64)|デスクトップ/</code></pre>]]></description><link>http://multix.jp/icon-view-size/</link><guid isPermaLink="false">101287b5-ce16-4cfa-bd70-8079bc18dc5f</guid><category><![CDATA[Windows]]></category><category><![CDATA[Machintosh]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 07 May 2015 08:13:31 GMT</pubDate><content:encoded><![CDATA[<p>MacOSの.icnsファイルもWindowsの.icoファイルも復数のアイコンリソースを含むマルチ解像度対応画像アーカイブだといえる。ではどんな状況でどの解像度のアイコン画像が選択されるのかを調べてみた。</p>

<ul class="index"></ul>

<hr>

<h4 id="osxmarvericksyosemite">OSX(Marvericks/Yosemite)の場合</h4>

<p>まずはOSXでの話。テスト用には前掲<a href="http://multix.jp/create-osx-icns-for-windows/">【OSX用アイコンファイルをWindows上で作る】</a>の<code>mkosxicns.pl</code>を使って<strong><a href="https://secure.multix.jp/download/multisize.icns">multisize.icns</a></strong>という.icnsファイルを作成した。これには以下のアイコンが含まれている。</p>

<p><img src="http://multix.jp/content/images/2015/03/2015-03-16-15-38-39.png" alt=""></p>

<p>これをファインダーで表示したりアプリケーションアイコンに設定したりしてみる。OSXはMarvericksとYosemiteを使用したがどちらも結果は変わらなかった。</p>

<pre><code class="language-table title:.icnsファイルのファインダー表示">|表示サイズ|状況|
|:-:|:-|
|16x16|リスト表示&lt;br&gt;カラム表示&lt;br&gt;デスクトップ/アイコン表示(最小)|
|32x32|デスクトップ/アイコン表示(17〜32)|
|32x32@2x&lt;br&gt;(64x64)|デスクトップ/アイコン表示(サイズ33〜64)|
|128x128|デスクトップ(最大)&lt;br&gt;アイコン表示(65〜128)|
|256x256|アイコン表示(129〜256)&lt;br&gt;Cover Flow(最小〜256)|
|512x512|アイコン表示(257〜512)&lt;br&gt;Cover Flow(257〜512)|
|512x512@2x&lt;br&gt;(1024x1024)|プレビュー&lt;br&gt;Cover Flow(513〜最大)|
</code></pre>

<p>.icnsファイルはマルチ解像度なので必要な場面でもっとも適切な解像度のアイコンが自動的に選択される。その結果7種類の画像サイズすべてが何らかの形で出現した。Retina用@2xについても特別扱いしている様子はない。またプレビューについては常に使用可能な最大解像度が選択されるがこれはプレビューの性質からして妥当だろう。</p>

<hr>

<h4 id="osx">アプリアイコン(OSX)</h4>

<pre><code class="language-table title:アプリアイコンのファインダー表示">|表示サイズ|Retina(HiDPI)|状況|
|:-:|:-:|:-|
|16x16|16x16@2x&lt;br&gt;(32x32)|リスト表示&lt;br&gt;カラム表示&lt;br&gt;デスクトップ/アイコン表示(最小)|
|32x32|32x32@2x&lt;br&gt;(64x64)|デスクトップ/アイコン表示(17〜32)|
|128x128|128x128@2x&lt;br&gt;(256x256)|ドックアイコン&lt;br&gt;デスクトップ(33〜最大)&lt;br&gt;アイコン表示(33〜128)|
|256x256|256x256@2x&lt;br&gt;(512x512)|アイコン表示(129〜256)&lt;br&gt;Cover Flow(最小〜256)&lt;br&gt;プレビュー|
|512x512|512x512@2x&lt;br&gt;(1024x1024)|アイコン表示(257〜512)&lt;br&gt;Cover Flow(257〜最大)|
</code></pre>

<p>一方この.icnsファイルをアプリアイコンに設定した場合、同一リソースなのに使用されるファイルが5種類になる。つまり標準解像度ではRetina用アイコンが、Retina環境では標準解像度アイコンが選択されない。そしてこの5種類の中に64x64の等倍は含まれていないので、その上の128x128が使用されるようになるのだ。このため.icnsファイルと並べると、ファインダー設定のアイコン表示サイズによっては見映えが変わったり両者が混在して表示されたりする。</p>

<p><img src="http://multix.jp/content/images/2015/03/2015-03-17-14-37-05.png" alt=""></p>

<hr>

<h4 id="windows81update">Windows 8.1 Updateの場合</h4>

<p>次はWindowsでの話。こちらもOSX版と同様にマルチ解像度のマルチサイズ.icoファイルを用意した。これは以下の35種類のアイコンサイズに対応する。<sup id="fnref:1"><a href="http://multix.jp/icon-view-size/#fn:1" rel="footnote">1</a></sup></p>

<p><img src="http://multix.jp/content/images/2015/03/2015-03-17-14-56-42.png" alt=""></p>

<p>.icoファイルの場合、収録可能な最大のアイコンサイズは256x256に制限される。一方で縦横サイズは任意に指定でき、実は正方形ではない長方形画像<sup id="fnref:2"><a href="http://multix.jp/icon-view-size/#fn:2" rel="footnote">2</a></sup>も登録できる。また画像形式もVista以降はαチャンネル付透過True Color PNGフォーマットが使用できるようになった。ただしPNG形式アイコンでは表示に不具合がでる場面もあるため、旧来のDIB形式の.icoファイル <strong><a href="https://secure.multix.jp/download/multisizedib.ico">multisizedib.ico</a></strong>と、PNG形式だけを集めた <strong><a href="https://secure.multix.jp/download/multisizepng.ico">multisizepng.ico</a></strong>の2種類を用意した。</p>

<p>Windowsのアイコンの扱いについてはMSDNの該当ページ<sup id="fnref:3"><a href="http://multix.jp/icon-view-size/#fn:3" rel="footnote">3</a></sup>が一次資料となるが、Explorerでは基本的には以下のようになる。</p>

<pre><code class="language-table title:Windows Exploerでのアイコンサイズ">|96dpi&lt;br&gt;100%|120dpi&lt;br&gt;125%|144dpi&lt;br&gt;150%|192dpi&lt;br&gt;200%|状況|
|16x16|←|←|←|タスクバー（小・実行アプリ）&lt;br&gt;ウィンドウタイトル|
|16x16|20x20|24x24|32x32|タスクバー（小・ピン留め・ショートカット）&lt;br&gt;リストアイテム&lt;br&gt;Explorer（小）|
|32x32|←|←|←|タスクバー（中・実行アプリ）&lt;br&gt;ダイアログアイコン|
|32x32|40x40|48x48|64x64|タスクバー（中・ピン留め・ショートカット）&lt;br&gt;Explorer(コンテンツ)|
|48x48|60x60|72x72|96x96|Explorer（中・並べて表示）|
|256x256|←|←|←|Explorer（大・特大）|
</code></pre>

<p>このうちアプリケーションを直接起動した場合のウィンドウタイトル・タスクバー・ダイアログアイコンについてはコントロールパネルの<strong>テキストとその他の項目の大きさ</strong>設定にかかわりなく常に16x16と32x32に固定<sup id="fnref:4"><a href="http://multix.jp/icon-view-size/#fn:4" rel="footnote">4</a></sup>される。また<strong>Explorer（大・特大）</strong>表示については原則として.icoファイルに収録された最大サイズのものがアイコンプレビューに選ばれる。</p>

<p>アプリケーションでもショートカットやピン留めから起動した場合、タスクバーアイコンについてはショートカットに登録されたアイコンが使用され、その場合はマルチ解像度が有効になる。</p>

<hr>

<h4 id="windows10tecnicalpreview">Windows 10 Tecnical Preview の場合</h4>

<p>ところで、Windows10はまだ開発中ということもあるが、TP版にはタスクバーのアイコン表示に問題が有る。少なくとも以下の2点が判明している。</p>

<ul>
<li>タスクバー設定が標準の場合、アイコン表示サイズが実サイズの75%に縮小される。（32x32->24x24、48x48->36x36） <br>
ただしタスクバー表示が小アイコン設定の場合の16x16表示には問題がない。</li>
<li>ショートカット・ピン留めでPNG形式アイコンを使用した場合、マスクプレーン（透過色切り抜き）が2ピクセル右にずれる。 <br>
<img src="http://multix.jp/content/images/2015/03/SS-2015-03-19-14-23-16.png" alt="96dpi" title=""> <br>
<img src="http://multix.jp/content/images/2015/03/SS-2015-03-19-14-20-53.png" alt="120dpi" title=""> <br>
左がアプリケーション直接、右がショートカットから起動した場合でいずれもPNG形式アイコンだが、右のものには黒いゴミが付いている。これはアイコンを角丸にするためのマスクプレーン（PNG画像のαチャンネル）がずれて重ねられているためだ。DBI形式の場合はこのようにはならない。<sup id="fnref:5"><a href="http://multix.jp/icon-view-size/#fn:5" rel="footnote">5</a></sup></li>
</ul>

<hr>

<h4 id="ios">iOSアプリ用アイコンの場合</h4>

<p>iOSの場合、使われる場面によってアイコンサイズが決められているため、用意する面倒を無視すれば悩む要素はない。ただしその規定サイズはiOSのバージョンによって異なり、マルチバージョン対応とする場合は当然準備しなければならない数が増える。以下はその対応一覧表だ。概ねホーム画面・設定画面・Spotlight検索<sup id="fnref:6"><a href="http://multix.jp/icon-view-size/#fn:6" rel="footnote">6</a></sup>の3場面各々に標準解像度とRetina解像度が必要になる。iOS8の場合さらに大画面iPhone用の3倍精度が追加された。</p>

<pre><code class="language-table title:iOSアプリアイコン一覧">|サイズ|iOS6|iOS7|iOS8|主な用途|
|:-|:-:|:-:|:-:|:-|
|29x29|◯|◯|◯|iPhone Spotlight(6)&lt;br&gt;iPhone設定(6)&lt;br&gt;iPad設定(78)|
|29x29@2x|◯|◯|◯|iPhone設定(67)&lt;br&gt;iPad設定(78)|
|29x29@3x|✕|✕|◯|iPhone設定(8)|
|40x40@2x|✕|◯|✕|iPhone Spotlight(7)|
|40x40@3x|✕|✕|◯|iPhone Spotlight(8)|
|50x50|◯|✕|✕|iPad Spotlight(6)&lt;br&gt;iPad設定(6)|
|50x50@2x|◯|✕|✕|iPad Spotlight(6)&lt;br&gt;iPad設定(6)|
|57x57|◯|✕|✕|iPhoneホーム(6)|
|57x57@2x|◯|✕|✕|iPhoneホーム(6)|
|60x60@2x|✕|◯|✕|iPhoneホーム(7)|
|60x60@3x|✕|✕|◯|iPhoneホーム(8)|
|72x72|◯|✕|✕|iPadホーム(6)|
|72x72@2x|◯|✕|✕|iPadホーム(6)|
|76x76|✕|◯|◯|iPadホーム(78)|
|76x76@2|✕|◯|◯|iPadホーム(78)|
|512x512@2|-|-|-|iTunes Store&lt;br&gt;App Store|
</code></pre>

<hr>

<h4 id="">ダウンロード</h4>

<ul>
<li><a href="https://secure.multix.jp/download/multisize.icns">multisize.icns</a> MacOS用</li>
<li><a href="https://secure.multix.jp/download/multisizedib.ico">multisizedib.ico</a> Windows用(DIB)</li>
<li><a href="https://secure.multix.jp/download/multisizepng.ico">multisizepng.ico</a> Windows用(PNG)</li>
</ul>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>妙に半端なサイズがやたら沢山入っているのはFavicon調査用である。ほとんどOperaのせい。 <a href="http://multix.jp/icon-view-size/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>もっとも正方形以外のアイコンリソースを想定・対応している表示環境があるとは考え難いのだが。 <a href="http://multix.jp/icon-view-size/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>Icons <a href="https://msdn.microsoft.com/en-us/library/dn742485.aspx">https://msdn.microsoft.com/en-us/library/dn742485.aspx</a> <a href="http://multix.jp/icon-view-size/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>この性質から16x16と32x32についてはPNG形式アイコンよりはDIB形式アイコンのほうが推奨される。 <a href="http://multix.jp/icon-view-size/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>カスタム表示500%拡大までの全てでゴミが付くので160pxまではDBI形式アイコンにしないと見映えが落ちてしまう。当然DBI形式では透過マスクのアンチエイリアス効果を得られないため別の方向で高品位は得られない。 <a href="http://multix.jp/icon-view-size/#fnref:5" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:6"><p>40x40と60x60の等倍スケールは使用されていないようだ。なおこの他にもUINavigationBar等で22x22の1〜3倍スケールがアプリ制作現場では必要になる。 <a href="http://multix.jp/icon-view-size/#fnref:6" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[OSX用アイコンファイルをWindows上で作る]]></title><description><![CDATA[<p>Macintosh OSX用アイコンセットをWindows上で作ってしまう方法へ至る道。OSXの.icnsファイルフォーマットについて解説する。</p>

<ul class="index"></ul>

<hr>

<h4 id="">動機</h4>

<p>艦板-KanPan- を開発するとき、最初からMacintoshとWindows両方に対応すると決めていたが、その際両者でフォーマットが異なるアイコンリソースをどうするかという問題があった。OSXでは<mark>.icns</mark>、Windowsでは<mark>.ico</mark>というファイル形式が使われる。それぞれを他方のプラットフォーム上のコマンドラインから作成できれば、make作業を一方の上で完結できる。とりあえず資料が豊富な.icoは放っておいて.icnsについて調べてみたところ意外と簡単な方法で作成できることが分かった。</p>

<hr>

<h4 id="iconutil">iconutil ユーティリティ</h4>

<p>OSX添付の純正アイコンリソース作成ユーティリティは<code>iconutil</code>という。このCLIコマンドは以下のように使用する。</p>

<ul>
<li>以下の10個のフルカラーPNG画像(RGBA)を格納した<code>foo.iconset</code>というフォルダを作成する。アイコンファイル名は厳密にこの通りであり、フォルダ名の<mark>.iconset</mark>という拡張子もこの通りでなければならない。</li>
</ul>

<pre><code class="language-table title:iconutilが要求する画像ファイル">|ファイル名|画像サイズ|
|:-|:-:|
|icon_16x16.png|16x16|
|icon_16x16@2x.png|32x32|
|icon_32x32.</code></pre>]]></description><link>http://multix.jp/create-osx-icns-for-windows/</link><guid isPermaLink="false">f88729c6-221d-4514-bf83-8a8ffb519052</guid><category><![CDATA[Windows]]></category><category><![CDATA[Machintosh]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Perl]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 07 May 2015 08:11:59 GMT</pubDate><content:encoded><![CDATA[<p>Macintosh OSX用アイコンセットをWindows上で作ってしまう方法へ至る道。OSXの.icnsファイルフォーマットについて解説する。</p>

<ul class="index"></ul>

<hr>

<h4 id="">動機</h4>

<p>艦板-KanPan- を開発するとき、最初からMacintoshとWindows両方に対応すると決めていたが、その際両者でフォーマットが異なるアイコンリソースをどうするかという問題があった。OSXでは<mark>.icns</mark>、Windowsでは<mark>.ico</mark>というファイル形式が使われる。それぞれを他方のプラットフォーム上のコマンドラインから作成できれば、make作業を一方の上で完結できる。とりあえず資料が豊富な.icoは放っておいて.icnsについて調べてみたところ意外と簡単な方法で作成できることが分かった。</p>

<hr>

<h4 id="iconutil">iconutil ユーティリティ</h4>

<p>OSX添付の純正アイコンリソース作成ユーティリティは<code>iconutil</code>という。このCLIコマンドは以下のように使用する。</p>

<ul>
<li>以下の10個のフルカラーPNG画像(RGBA)を格納した<code>foo.iconset</code>というフォルダを作成する。アイコンファイル名は厳密にこの通りであり、フォルダ名の<mark>.iconset</mark>という拡張子もこの通りでなければならない。</li>
</ul>

<pre><code class="language-table title:iconutilが要求する画像ファイル">|ファイル名|画像サイズ|
|:-|:-:|
|icon_16x16.png|16x16|
|icon_16x16@2x.png|32x32|
|icon_32x32.png|32x32|
|icon_32x32@2x.png|64x64|
|icon_128x128.png|128x128|
|icon_128x128@2x.png|256x256|
|icon_256x256.png|256x256|
|icon_256x256@2x.png|512x512|
|icon_512x512.png|512x512|
|icon_512x512@2x.png|1024x1024|
</code></pre>

<ul>
<li>ターミナルで以下のように実行するとカレントディレクトリに<code>foo.icns</code>というアイコンファイルが作成される。</li>
</ul>

<pre><code class="language-brush:plain">iconutil -c icns foo.iconset  
</code></pre>

<p>10個のファイル（画像サイズ自体は7種類）を用意しろというのは御無体だが、これはまあ原画をSVG形式で作成して所定サイズとファイル名のPNG画像にコンバートすればなんとかなる。ともかく出来上がった.icnsファイルを調べてみると次のようになっていた。</p>

<hr>

<h4 id="icns">ICNSファイルフォーマット</h4>

<p>ICNSファイルは次のような構造になっている</p>

<pre><code class="language-table table:ICNSファイル構造">|:-:|
|ICNSファイルヘッダ|
|アイコンヘッダ|
|アイコンデータ|
|アイコンヘッダ|
|アイコンデータ|
|:|
</code></pre>

<p>まずファイル先頭にICNSファイルヘッダがあり、その後ろにアイコンヘッダとアイコンデータの組（ブロック）が任意個数続く。</p>

<hr>

<h4 id="icns">ICNSファイルヘッダ</h4>

<p>ICNSファイルヘッダの大きさは8バイトで、以下の2フィールドを持つ。</p>

<pre><code class="language-table table:ICNSファイルヘッダ">|Offset|Length|内容|
|-:|-:|:-|
|0|4|ファイル識別子`ICNS` (リテラルバイト文字列 0x69 0x63 0x6e 0x73)|
|4|4|ICNSファイル全体のバイナリサイズ (MSB First/Big Endian)|
</code></pre>

<p>先頭の4バイトがファイル識別子なのはMachintosh系データフォークの伝統だ。</p>

<hr>

<h4 id="">アイコンヘッダ</h4>

<p>アイコンヘッダの大きさは8バイトで、以下の2フィールドを持つ。</p>

<pre><code class="language-table title:アイコンブロックヘッダ">|Offset|Length|内容|
|-:|-:|:-|
|0|4|アイコンタイプ識別子（リテラルバイト文字列）|
|4|4|ブロック長(アイコンヘッダ＋アイコンデータの合計バイト長) (MSB First/Big Endian)|
</code></pre>

<p>アイコンタイプ識別子もまた４バイトのリテラル文字列で、これがアイコンデータの画像表示サイズと画像フォーマットとを示している。続くフィールドは後続するアイコンデータのバイナリサイズ+8に一致する。</p>

<hr>

<h4 id="">アイコンデータ</h4>

<p>これのデータフォーマットはアイコンタイプ識別子によって規定されるが、iconutilが作成するファイルの場合は<strong>PNG画像ファイルデータ</strong>そのままか<strong>RGB生データ</strong>か<strong>マスクデータ</strong>である。とにかくPNG形式のアイコン識別子が付いていたならPNGファイルがそのまま埋め込まれているので、何も難しいことはない。</p>

<hr>

<h4 id="">アイコンタイプ識別子一覧</h4>

<p>iconutilが作成するアイコンファイルに含まれるアイコン識別子の一覧を次に示す。なお識別子のレターケースは区別される。</p>

<pre><code class="language-table title:iconutilが生成するアイコンタイプ識別子一覧">|Type|Length|Size|MacOS|詳細|
|:-:|:-:|:-:|:-:|:-|
|ic07|可変|128x128|10.7+|128x128 PNG/JPEG2000|
|ic08|可変|256x256|10.5+|256x256 PNG/JPEG2000|
|ic09|可変|512x512|10.5+|512x512 PNG/JPEG2000|
|ic10|可変|1024x1024|10.7+|1024x1024 PNG/JPEG2000 (1024x1024, 512x512@2x)|
|ic11|可変|32x32|10.8+|32x32 PNG/JPEG2000 (16x16@2x)|
|ic12|可変|64x64|10.8+|64x64 PNG/JPEG2000 (32x32@2x)|
|ic13|可変|256x256|10.8+|256x256 PNG/JPEG2000 (128x128@2x)|
|ic14|可変|512x512|10.8+|512x512 PNG/JPEG2000 (256x256@2x)|
|il32|可変|32x32|8.5+|32x32 24bit TrueColor RGB|
|is32|可変|16x16|8.5+|16x16 24bit TrueColor RGB|
|l8mk|1,024|32x32|8.5+|32x32 8bit MASK|
|s8mk|256|16x16|8.5+|16x16 8bit MASK|
</code></pre>

<p>ic11からic14はRetinaディスプレイ用に追加されたもので、それぞれその半分の画像サイズのアイコンと対応している。ic10はRetina対応であると同時に<strong>OSX10.7以降のデフォルトアイコン</strong>サイズである。極端な話、このic10さえあれば、OSX10.7以降のファインダー＆ドック表示はすべて可能になる。</p>

<p>一方で以下のアイコンタイプも10.7で登場したのだが iconutil では生成されないようだ。代わりに非Retina用等倍の16x16と32x32についてはOSX10.7のものではなくMaxOS8.5のアイコンフォーマットにコンバートして収録されている。これは後方互換性を維持するためだろう。</p>

<pre><code class="language-table title:その他のアイコン識別子一覧">|Type|Length|Size|MacOS|詳細|
|:-:|:-:|:-:|:-:|:-|
|icp4|可変|16x16|10.7+|16x16 PNG/JPEG2000|
|icp5|可変|32x32|10.7+|32x32 PNG/JPEG2000|
|icp6|可変|64x64|10.7+|64x64 PNG/JPEG2000|
</code></pre>

<hr>

<h4 id="macos85">MacOS8.5形式アイコンフォーマット</h4>

<p>il32とl8mk、is32とs8mkはそれぞれアイコンカラーデータとアイコンマスクデータのペアとして使用される。常にペアであるため個々に単独の画像ファイルとしては現れず、少なくとも2ブロックから成る.icnsファイルとして存在する。</p>

<p>アイコンカラーデータは1画素あたりRGB24bitだが、RGBそれぞれ別の(8bpp)プレーン毎に、連続する左上原点ビットマップデータ列をPackBitsしてまとめられている。</p>

<pre><code class="language-table title:il32アイコンブロック (32x32画素カラー)">|Offset|Length|詳細|
|-:|-:|:-|
|0|4|アイコンタイプ識別子`il32` (0x69 0x6c 0x33 0x32)|
|4|4|ブロック長 (MSB First)|
|8|可変|Rプレーン1,024バイトのPackBitsデータ|
|?|可変|Gプレーン1,024バイトのPackBitsデータ|
|?|可変|Bプレーン1,024バイトのPackBitsデータ|
</code></pre>

<pre><code class="language-table title:is32アイコンブロック (16x16画素カラー)">|Offset|Length|詳細|
|-:|-:|:-|
|0|4|アイコンタイプ識別子`is32` (0x69 0x73 0x33 0x32)|
|4|4|ブロック長 (MSB First)|
|8|可変|Rプレーン256バイトのPackBitsデータ|
|?|可変|Gプレーン256バイトのPackBitsデータ|
|?|可変|Bプレーン256バイトのPackBitsデータ|
</code></pre>

<p>アイコンマスクデータも同様に1画素あたり8bit(8bpp)の連続する左上原点ビットマップデータ列だが、無圧縮のベタデータとして定義されている。</p>

<p>これはPNG画像フォーマットのアルファチャンネルと実質同じもので、アイコン画像の背景デスクトップからの切り抜きとアンチエイリアス表現を実現する。 なお画像縦横サイズについてはアイコンタイプ識別子で一意に決定されるためデータブロック内には現れない。</p>

<pre><code class="language-table title:l8mkマスクブロック (32x32画素マスク)">|Offset|Length|詳細|
|-:|-:|:-|
|0|4|アイコンタイプ識別子`l8mk` (0x6c 0x38 0x6d 0x6b)|
|4|4|ブロック長 (MSB First)|
|8|1,024|MASKプレーンの8bppビットマップデータ|
</code></pre>

<pre><code class="language-table title:s8mkマスクブロック  (16x16画素マスク)">|Offset|Length|詳細|
|-:|-:|:-|
|0|4|アイコンタイプ識別子`s8mk` (0x73 0x38 0x6d 0x6b)|
|4|4|ブロック長 (MSB First)|
|8|256|MASKプレーンの8bppビットマップデータ|
</code></pre>

<hr>

<h4 id="packbits">PackBits連長圧縮アルゴリズム</h4>

<p>カラープレーンのビットマップは生データではなく、PackBitsと呼ばれる連長圧縮アルゴリズムでコンバートされている。これは古き良きMacintosh時代の、PICT (QuickDraw PICTure)ファイルフォーマットで用いられていたものだからとうぜん当時の資料を調べれば詳細を知ることが出来る。そのPackBitsデータ列は次のように解釈すれば元のビットマップデータへ復元・展開できる。</p>

<ul>
<li>ポインタ上の1バイトが128未満（MSBが0）であるならこのバイトが示す0〜127に1を加え、次のそのバイト数（1〜128バイト）を取り出してそのまま出力し、ポインタを進める。</li>
<li>ポインタ上の1バイトが128以上
（MSBが1）であるならこのバイトが示す128〜255から125を引き、取り出した次の1バイトをその回数（3〜130回）繰り返して出力し、ポインタを進める。</li>
</ul>

<p>このPackBitsの圧縮アルゴリズムを真面目に実装しようとするとそれなりに面倒だが抜け道はある。無圧縮でよしと妥協すれば、128バイト毎にバイトストリームを切り刻み、0x7Fを前置した129バイトブロックに整えて横流しすればすむのである。32x32画素プレーンと16x16画素プレーンのいずれも128バイトの整数倍バイト長なのだから全く不都合はない。32x32画素プレーンなら計8ブロック1,032バイト、16x16画素プレーンなら計2ブロック258バイトの固定長データとして処理してしまえる。</p>

<hr>

<h4 id="unosxicnspl">unosxicns.pl</h4>

<p>ここまで解ってしまえばあとはもう悩むことはない。<code>unosxicns.pl</code>は以上の情報をまとめて .icnsファイルから .png<sup id="fnref:1"><a href="http://multix.jp/create-osx-icns-for-windows/#fn:1" rel="footnote">1</a></sup>ファイルを抽出するように記述したものだ。ActivePerl(5.10+)<sup id="fnref:2"><a href="http://multix.jp/create-osx-icns-for-windows/#fn:2" rel="footnote">2</a></sup>と ImageMagick(PerlMagick)<sup id="fnref:3"><a href="http://multix.jp/create-osx-icns-for-windows/#fn:3" rel="footnote">3</a></sup>で動作する。OSX他でも MacPortsで同等の環境を揃えれば動作する。</p>

<p>使い方は引数に .icnsファイルを指定すると同名の .iconsetディレクトリを作成してその中に抽出した<code>icon_NNNxNNN.png</code>を生成、あるいは<code>-t</code>オプションを付加した場合は<code>アイコンタイプ.png</code>を生成する。前掲のアイコンタイプ以外は対応外として無視する。</p>

<pre><code class="language-brush:plain">unosxicns.pl -t foo.icns

ls foo.iconset  
ic07.png              ic08.png              ic09.png  
ic10.png              ic11.png              ic12.png  
ic13.png              ic14.png              il32.png  
is32.png  
</code></pre>

<pre><code class="language-brush:perl collapse:true gutter:true title:unosxicns.pl">#!/usr/bin/perl
# $Id: unosxicns.pl 122 2015-03-16 04:06:57Z askn $ ＵＴＦ８

use 5.010;  
use strict;  
use warnings;  
use File::Basename;  
use Image::Magick;  
use Getopt::Std;

my %iconset = (  
    ic07 =&gt; [128,  1],
    ic08 =&gt; [256,  1],
    ic09 =&gt; [512,  1],
    ic10 =&gt; [1024, 2],
    ic11 =&gt; [32,   2],
    ic12 =&gt; [64,   2],
    ic13 =&gt; [256,  2],
    ic14 =&gt; [512,  2],
    icp4 =&gt; [16,   1],
    icp5 =&gt; [32,   1],
    icp6 =&gt; [64,   1],
    is32 =&gt; [16,   0],
    il32 =&gt; [32,   0],
    s8mk =&gt; [16,   0],
    l8mk =&gt; [32,   0],
);

our($opt_t);  
getopts("t");

unless (scalar @ARGV) {  
    die "Usage: unosxicns.pl [-t] icon.icns [...]\n";
}

my %cache;  
my $dirname = dirname($0);  
foreach my $argv (@ARGV) {  
    foreach my $glob (glob $argv) {
        my $filename = $glob;
        my $basename = basename $filename, qw{.svg .png .ico .icns .icowin .iconset};
        my $folder = $basename . ".iconset";
        $filename = $basename . ".icns";
        next if $cache{$filename};
        die "file not found $filename\n" unless -f $filename;
        $cache{$filename} = 1;
        &amp;mkpict($filename, $folder);
    }
}
exit;

# アイコンブロックの抽出
sub mkpict {  
    my $filename = shift;
    my $folder = shift;
    my %maskset;
    mkdir $folder, 0777;
    my($FH, $GH, $buff);
    unless (open $FH, "&lt;", $filename) {
        die "Cant read input\n";
    }
    binmode $FH;

    # 先頭のアイコンヘッダ8バイトの取得
    die "Read error\n" unless sysread($FH, $buff, 8);
    my($filetype, $filesize) = unpack "A4N", $buff;
    die "Not ICNS format\n" unless $filetype eq "icns";
    die "File size unmatch\n" unless $filesize == -s $filename;
    say "$filename filesize $filesize";

    # 各アイコンブロックについて
    while (sysread $FH, $buff, 8) {
        my($set, $ext, $outname, $scale);

        # アイコンブロックヘッダ8バイト
        my($icontype, $blocksize) = unpack "A4N", $buff;
        my $readsize = $blocksize - 8;
        die "Format broken\n"
            if $readsize &lt; 1
                or $readsize != sysread $FH, $buff, $readsize;
        say "  type $icontype length $blocksize";

        # 未知のアイコンタイプ
        unless ($set = $iconset{$icontype}) {
            say "    undefined icon type";
            next;
        }

        # PNGまたはJPEG2000形式
        if ($scale = $set-&gt;[1]) {

            # PNGヘッダ確認
            $ext = ("\x89\x50\x4E\x47" eq substr $buff, 0, 4) ? "png" : "jp2";

            # -t付きの場合はアイコンタイプをファイル名にする
            if ($opt_t) {
                $outname = sprintf "%s.%s", $icontype, $ext;
            }

            # 倍密解像度の場合のファイル名
            elsif ($scale == 2) {
                $outname = sprintf "icon_%ux%u\@x2.%s",
                    $set-&gt;[0] &gt;&gt; 1, $set-&gt;[0] &gt;&gt; 1, $ext;
            }

            # 標準解像度のファイル名
            else  {
                $outname = sprintf "icon_%ux%u.%s",
                    $set-&gt;[0], $set-&gt;[0], $ext;
            }
            say "\t$outname size $readsize";

            # 画像データ書出
            unless (open $GH, "&gt;", "$folder/$outname") {
                die "Cant write output\n";
            }
            binmode $GH;
            print $GH $buff;
            close $GH;
            next;
        }

        # MacOS8.5 mask raw format
        if ($icontype =~ /(s8mk|l8mk)/) {
            die "Mask broken,  length missmatch\n"
                unless $set-&gt;[0] ** 2 == length $buff;
            $maskset{$icontype} = [unpack "C*", $buff];
        }

        # MacOS8.5 Color PackBits format
        if ($icontype =~ /(is32|il32)/) {
            my @data;
            my @input = unpack "C*", $buff;

            # PackBits unpack
            while (scalar @input) {
                my $code = shift @input;
                if ($code &lt; 128) {
                    push @data, splice @input, 0, $code + 1;
                }
                else {
                    my $next = shift @input;
                    push @data, ($next) x ($code - 125);
                }
            }
            die "PackBits broken,  length missmatch\n"
                unless $set-&gt;[0] ** 2 * 3 == scalar @data;
            $maskset{$icontype} = \@data;
        }
    }
    close $FH;
    &amp;mkpngicon(\%maskset, "il32", "l8mk", $folder);
    &amp;mkpngicon(\%maskset, "is32", "s8mk", $folder);
}

# RGBAからPNGファイルへの変換
sub mkpngicon {  
    my $maskset = shift;
    my $type = shift;
    my $color;
    return unless $color = $maskset-&gt;{$type};
    my $mask = shift;
    my $folder = shift;
    $mask = $maskset-&gt;{$mask};
    my $size = $iconset{$type}-&gt;[0];
    my $sqrt = $size ** 2;

    my $image = Image::Magick-&gt;new(magick=&gt;'png', size=&gt;"${size}x${size}", matte=&gt;"True");
    $image-&gt;Read("NULL:");

    for (my $y = 0; $y &lt; $size; $y++) {
        for (my $x = 0; $x &lt; $size; $x++) {
            my @pixcels;
            $pixcels[0] = $color-&gt;[$y * $size + $x]             / 255; # R
            $pixcels[1] = $color-&gt;[$y * $size + $x + $sqrt]     / 255;    # G
            $pixcels[2] = $color-&gt;[$y * $size + $x + $sqrt * 2] / 255;    # B
            $pixcels[3] = $mask ? 1.0 - $mask-&gt;[$y * $size + $x] / 255 : 0.0; # A (Opacity)
            my $err = $image-&gt;SetPixel(x=&gt;$x, y=&gt;$y, color=&gt;\@pixcels, channel=&gt;'RGBA', normalize=&gt;'True');
            say $err if $err;
        }
    }
    my $outname = sprintf "icon_%ux%u.png", $size, $size;

    # -t付きの場合はアイコンタイプをファイル名にする
    if ($opt_t) {
        $outname = sprintf "%s.png", $type;
    }

    say "\t$outname";
    my $err = $image-&gt;Write("$folder/$outname");
    die $err if $err;
}

1;  
__END__  
</code></pre>

<hr>

<h4 id="mkosxicnspl">mkosxicns.pl</h4>

<p>これでようやく本題に辿り着いた。<code>mkosxicns.pl</code> は<code>unosxicns.pl</code>とは逆に  .pngファイルから .icnsファイルを生成する。ActivePerl(5.10+)と ImageMagick(PerlMagick)の他、パスの通った場所に <code>PhantomJS</code><sup id="fnref:4"><a href="http://multix.jp/create-osx-icns-for-windows/#fn:4" rel="footnote">4</a></sup>がインストールされている必要がある。<code>iconrast.js</code>は <code>PhantomJS</code>スクリプトで <code>mkosxicns.pl</code>と同じ場所に置く。OSX他でもMacPortsで同等の環境を揃えれば動作する。</p>

<p>使い方は引数に<code>-b</code>オプションと .iconsetディレクトリを指定すると、iconutilと同等の動作を行って同名の .icnsファイルを生成する。</p>

<pre><code class="language-brush:plain">mkosxicns.pl -b foo.iconset  
</code></pre>

<p>もうひとつの使い方は<code>-m</code>オプションとSVGファイル<sup id="fnref:5"><a href="http://multix.jp/create-osx-icns-for-windows/#fn:5" rel="footnote">5</a></sup>を指定することで、iconutilが必要とする10個のPNGファイルを含む .iconsetディレクトリを自動的に生成する動作だ。</p>

<pre><code class="language-brush:plain">mkosxicns.pl -m foo.svg  
</code></pre>

<p>両者は<code>-mb</code>オプションを指定して実行することで同時に行うことが出来る。すなわち以下の場合 foo.svgファイルから foo.iconsetディレクトリを作成し foo.icnsファイルを生成する。</p>

<pre><code class="language-brush:plain">mkosxicns.pl -mb foo.svg  
</code></pre>

<pre><code class="language-brush:perl collapse:true gutter:true title:mkosxicns.pl">#!/usr/bin/perl
# $Id: mkosxicns.pl 122 2015-03-16 04:06:57Z askn $ ＵＴＦ８

use 5.010;  
use strict;  
use warnings;  
use File::Basename;  
use Image::Magick;  
use Getopt::Std;

my @fileset = (  
    [128,  "icon_128x128.png",     "ic07"],
    [256,  "icon_256x256.png",     "ic08"],
    [512,  "icon_512x512.png",     "ic09"],
    [1024, "icon_512x512\@2x.png", "ic10"],
    [32,   "icon_16x16\@2x.png",   "ic11"],
    [64,   "icon_32x32\@2x.png",   "ic12"],
    [256,  "icon_128x128\@2x.png", "ic13"],
    [512,  "icon_256x256\@2x.png", "ic14"],
    [32,   "icon_32x32.png",       "icp5"],
    [16,   "icon_16x16.png",       "icp4"],
);

our($opt_m, $opt_b);  
getopts("mb");  
unless (($opt_m || $opt_b) &amp;&amp; 1 == scalar @ARGV) {  
    die "Usage: mkosxicns.pl -mb icon[.svg] [...]\n";
}

my %cache;  
my $dirname = dirname($0);  
my $phantomjs = "phantomjs";  
my $rasterize = $dirname . "/iconrast.js";

foreach my $argv (@ARGV) {  
    foreach my $glob (glob $argv) {
        my $filename = $glob;
        my $basename = basename $filename, qw{.svg .png .ico .icns .icowin .iconset};
        my $folder = $basename . ".iconset";
        if ($opt_m) {
            $filename = $basename . ".svg";
            die "file not found $filename\n" unless -f $filename;
        }
        next if $cache{$filename};
        say $filename;
        $cache{$filename} = 1;
        &amp;mkpng($filename, $folder) if $opt_m;
        &amp;mkico($basename, $folder) if $opt_b;
    }
}
exit;

# SVGからPNGへの変換
sub mkpng {  
    my $filename = shift;
    my $folder = shift;
    mkdir $folder, 0777;
    foreach my $set (@fileset) {
        printf "%s/%s\n", $folder, $set-&gt;[1];
        system $phantomjs , $rasterize, $filename, $set-&gt;[0] , $folder . "/" . $set-&gt;[1];
    }
    say "complete";
}

# ICNSファイル作成
my @maskset;  
sub mkico {  
    my $basename = shift;
    my $folder = shift;
    my $FH;
    unless (open $FH, "&gt;", $basename . ".icns") {
        die "Cant write output\n";
    }
    binmode $FH;

    my $total = 0;
    my $data = "";
    my @iconset;
    foreach my $set (@fileset) {

        # PNGファイル読込
        my $path = $folder . "/" . $set-&gt;[1];
        my $blob;
        if (open my $FI, "&lt;", $path) {
            binmode $FI;
            $blob = join '', &lt;$FI&gt;;
            close $FI;
        }
        unless (scalar $blob) {
            die "Cant read $path\n";
        }

        # MacOS8.5形式への変換
        if ($set-&gt;[2] eq "icp4") {
            &amp;buildmaskset($path, "is32", "s8mk", 16, 1, 3);
            next;
        }
        elsif ($set-&gt;[2] eq "icp5") {
            &amp;buildmaskset($path, "il32", "l8mk", 32, 0, 2);
            next;
        }

        # アイコンタイプヘッダ8バイトの付加
        my $length = length $blob;
        $data .= pack("A4N", $set-&gt;[2], $length + 8);
        $data .= $blob;
        printf "%u %s\n", length($data), $path;
    }

    # 全アイコンブロック連結
    $data .= join "", @maskset if scalar @maskset;

    # アイコンヘッダ8バイトの付加
    my $output = pack("A4N", "icns", length($data) + 8);
       $output .= $data;
    print $FH $output;
    close $FH;
    printf "%u complete", length($output);
}

# 24bitカラーと8bitマスク作成
sub buildmaskset {  
    my $path = shift;
    my $name = shift;
    my $mask = shift;
    my $size = shift;
    my $idxc = shift;
    my $idxm = shift;

    # PNGファイルを読み込んで全ピクセルを取得
    my $image = Image::Magick-&gt;new(magick=&gt;'png');
       $image-&gt;Read($path);
       $image-&gt;Set(magick=&gt;'png');
    my @input = $image-&gt;GetPixels(x=&gt;0, y=&gt;0,
        width=&gt;$size, height=&gt;$size, map=&gt;"RGBA", normalize=&gt;"true");

    # カラープレーン・マスクプレーンに分割
    my @plane = ([], [], [], []);
    for (my $y = 0; $y &lt; $size; $y++) {
        for (my $x = 0; $x &lt; $size; $x++) {
            for (my $i = 0; $i &lt; 4; $i++) {
                push @{$plane[$i]}, int(255 * shift(@input));
            }
        }
    }

    # カラープレーンをPackBits
    my @encode = ("", "", "", pack("C*", @{$plane[3]}));
    for (my $i = 0; $i &lt; 3; $i++) {
        my $buff = pack "C*", @{$plane[$i]};
        while (length $buff) {
            $encode[$i] .= chr(127) . substr($buff, 0, 128, "");
        }
    }

    # 各データを連結してヘッダ8バイトを付加
    my $buff = join "", @encode[0..2];
    $maskset[$idxc] = pack("A4N", $name, length($buff) + 8) . $buff;
    $maskset[$idxm] = pack("A4N", $mask, length($encode[3]) + 8) . $encode[3];
}

1;  
__END__  
</code></pre>

<pre><code class="language-brush:js collapse:true gutter:true title:iconrast.js">// $Id: iconrast.js 121 2015-03-13 10:01:44Z askn $

var page = require('webpage').create(),  
    system = require('system'),
    address, output, size;

if (system.args.length != 4) {  
    console.log('Usage: rasterize.js URL size filename');
    phantom.exit(1);
}
else {  
    address = system.args[1];
    size = system.args[2];
    output = system.args[3];
    page.viewportSize = { width: size, height: size };
    page.open(address, function (status) {
        if (status !== 'success') {
            console.log('Unable to load the address!');
            phantom.exit();
        } else {
            window.setTimeout(function () {
                page.render(output);
                phantom.exit();
            }, 200);
        }
    });
}
// End of script
</code></pre>

<hr>

<h4 id="">ダウンロード</h4>

<p>本項で述べた mkosxicns.pl unosxicns.pl iconrast.js の3点セット。</p>

<p><a href="https://secure.multix.jp/download/mkosxicns.zip">mkosxicns.zip</a></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>もしPNGヘッダではないアイコンブロックであった場合は JPEG2000と仮定して .jp2ファイルで出力する。 <a href="http://multix.jp/create-osx-icns-for-windows/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>ActivePerl <a href="http://www.activestate.com/activeperl">http://www.activestate.com/activeperl</a> <a href="http://multix.jp/create-osx-icns-for-windows/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>ImageMagick <a href="http://www.imagemagick.org">http://www.imagemagick.org</a><br>・・・正直使用している機能に対してオーバースペックなのだがMachintosh/Windows両方で動作する画像操作ライブラリは希少なので困ってしまう。 <a href="http://multix.jp/create-osx-icns-for-windows/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>PhantomJS <a href="http://phantomjs.org">http://phantomjs.org</a><br>・・・CLIで動作するWebkitヘッドレスブラウザ。ここではSVGファイルから任意画素数の透過PNG画像を生成するのに使用している。 <a href="http://multix.jp/create-osx-icns-for-windows/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>SVGファイルはWidth値とHeight値が共に無指定（つまり規定値の'100%'）である必要がある。固定値が指定されていると意図した大きさのアイコン画像とはならない。 <a href="http://multix.jp/create-osx-icns-for-windows/#fnref:5" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[本番用のサーバ証明書を取得する]]></title><description><![CDATA[<p>ここまで述べた手順でのSSL/TLSサーバ証明書は、自己認証なのでプライベートLAN内でしか事実上運用できない。グローバルIPを持ったサーバにインストールしてインターネットを跨いで運用するには、正規の認証局によって認証・署名された正当なSSL/TLSサーバ証明書が必要になる。</p>

<ul class="index"></ul>

<hr>

<h4 id="ssltls">本番用のSSL/TLSサーバ証明書を請求するには</h4>

<p>SSL/TLSサーバ証明書を得るまでには概ね以下の手順を踏む。</p>

<ol>
<li>サーバを運用するための独自ドメインを取得し自身の管理下に置く。そもそもこれがなければ始まらない。  </li>
<li>認証局を選択して会員登録を行い、会員の実在性承認を受ける。個人の場合は少なくとも住所氏名電話番号、会員登録したメールアドレスなどが実在するかの確認がなされる。  </li>
<li>認証局の証明書発行要件を確認し、必要なら独自ドメインの管理者情報をWHOISに登録し公開する。<sup id="fnref:1"><a href="http://multix.jp/getting-ssl-certificate/#fn:1" rel="footnote">1</a></sup>  </li>
<li>証明書申請者は証明書に埋め込まれる管理者メールアドレスを受信できるように準備する。これは一般に<code>webmaster</code>、<code>postmaster</code>、<code>hostmaster</code>といった認証局が指定する定型メールアドレスである。無論その独自ドメイン名は合致していなければならず、他のドメインのメールアドレスにすることは出来ない。  </li>
<li>独自ドメインの承認を受ける。すなわちWHOIS情報などと照らしあわせてその独自ドメインが正しく請求者に所有権があるか、管理者メールアドレスにセキュリティトークンを送信して請求者が正しくその運用権限を有しているかの確認がなされる。<sup id="fnref:2"><a href="http://multix.jp/getting-ssl-certificate/#fn:2" rel="footnote">2</a></sup>  </li>
<li>証明書申請者はSSL/TLSサーバ証明書を運用するサブドメインをDNSに登録・公開し、そのサブドメインをコモンネーム(CN)と管理者メールアドレスその他を記入した証明書請求（CSR）ファイルを作成し、認証局に送付する。  </li>
<li>認証局はCSR記載の内容と会員情報とを照らし合わせて監査し、不備なく承認となれば自身の持つCA証明書と秘密鍵でCSRに署名を行い、証明書（CRT）ファイルとして証明書申請者の会員登録あるいは管理者メールアドレスに返送する。</li>
</ol>

<p>証明書請求(CSR)ファイルの作成では、先の自己証明書と同様に必要な項目を記入しなければならない。</p>]]></description><link>http://multix.jp/getting-ssl-certificate/</link><guid isPermaLink="false">cf6e198d-b0e1-4890-8c73-d8aee1192e53</guid><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[SSL/TLS]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 19 Feb 2015 09:47:00 GMT</pubDate><content:encoded><![CDATA[<p>ここまで述べた手順でのSSL/TLSサーバ証明書は、自己認証なのでプライベートLAN内でしか事実上運用できない。グローバルIPを持ったサーバにインストールしてインターネットを跨いで運用するには、正規の認証局によって認証・署名された正当なSSL/TLSサーバ証明書が必要になる。</p>

<ul class="index"></ul>

<hr>

<h4 id="ssltls">本番用のSSL/TLSサーバ証明書を請求するには</h4>

<p>SSL/TLSサーバ証明書を得るまでには概ね以下の手順を踏む。</p>

<ol>
<li>サーバを運用するための独自ドメインを取得し自身の管理下に置く。そもそもこれがなければ始まらない。  </li>
<li>認証局を選択して会員登録を行い、会員の実在性承認を受ける。個人の場合は少なくとも住所氏名電話番号、会員登録したメールアドレスなどが実在するかの確認がなされる。  </li>
<li>認証局の証明書発行要件を確認し、必要なら独自ドメインの管理者情報をWHOISに登録し公開する。<sup id="fnref:1"><a href="http://multix.jp/getting-ssl-certificate/#fn:1" rel="footnote">1</a></sup>  </li>
<li>証明書申請者は証明書に埋め込まれる管理者メールアドレスを受信できるように準備する。これは一般に<code>webmaster</code>、<code>postmaster</code>、<code>hostmaster</code>といった認証局が指定する定型メールアドレスである。無論その独自ドメイン名は合致していなければならず、他のドメインのメールアドレスにすることは出来ない。  </li>
<li>独自ドメインの承認を受ける。すなわちWHOIS情報などと照らしあわせてその独自ドメインが正しく請求者に所有権があるか、管理者メールアドレスにセキュリティトークンを送信して請求者が正しくその運用権限を有しているかの確認がなされる。<sup id="fnref:2"><a href="http://multix.jp/getting-ssl-certificate/#fn:2" rel="footnote">2</a></sup>  </li>
<li>証明書申請者はSSL/TLSサーバ証明書を運用するサブドメインをDNSに登録・公開し、そのサブドメインをコモンネーム(CN)と管理者メールアドレスその他を記入した証明書請求（CSR）ファイルを作成し、認証局に送付する。  </li>
<li>認証局はCSR記載の内容と会員情報とを照らし合わせて監査し、不備なく承認となれば自身の持つCA証明書と秘密鍵でCSRに署名を行い、証明書（CRT）ファイルとして証明書申請者の会員登録あるいは管理者メールアドレスに返送する。</li>
</ol>

<p>証明書請求(CSR)ファイルの作成では、先の自己証明書と同様に必要な項目を記入しなければならない。これらはサーバ証明書のSubjectフィールドに埋め込まれるが、コモンネーム(CN)は必須である。</p>

<ul>
<li><strong>Country Name</strong> : 組織所在地の2文字の国コードを記名する。一般に省略不可。日本ならJP。</li>
<li><strong>State or Province Name</strong> : 組織所在地の都道府県名。一般に省略不可。東京都なら Tokyo。  </li>
<li><strong>Locality Name</strong> : 組織所在地の市町村名。一般に省略不可。千代田区なら Chiyoda。</li>
<li><strong>Organization Name (O)</strong>: 組織名。中間CA証明書や法人格EV認証を取る場合は原則必須で、正式な英語名称を記入する必要がある。このフィールドが必須の場合は証明書運用管理上重要なため、当然ながら無関係な他組織名を偽証することは許されない。非EV認証や個人登録申請では省略できるが時に認証局側で適宜削除・修正を受けることもある。この場合は他組織と区別のつくオリジナルな組織名を名乗ってさえいれば問題なく、個人登録では屋号や同人サークル名を記名してもよい。</li>
<li><strong>Organizational Unit Name (OU)</strong>: 部署名。省略可能。同一のONで複数のサーバ証明書が必要になる場合、その区別のために入力する。申請者側で自由に記名できるコメント欄とも言える。</li>
<li><strong>Common Name (CN)</strong>: サーバホスト名。サーバ証明書においては必須。ブラウザのアドレスバー表示とこれが一致しない場合は認証エラー<sup id="fnref:3"><a href="http://multix.jp/getting-ssl-certificate/#fn:3" rel="footnote">3</a></sup>となる。故に独自ドメイン管理と一体でなければならない。原則として一つのサーバホスト名にはただ一つの証明書が一対一で対応するべきなので、有効期間切れ以前に新しい証明書の再発行を行う際は、同時に古い証明書を破棄申請（Revoke）して公開CRTリストに記載しなければならない。<sup id="fnref:4"><a href="http://multix.jp/getting-ssl-certificate/#fn:4" rel="footnote">4</a></sup></li>
<li><strong>Email Address</strong> : 管理者連絡用メールアドレス。多くの場合必須。WebサイトやメールベースでCSR申請・CRT発行を行う場合はここに記載したメールアドレスに署名された証明書が返送されたりする。なかには自動で正しい記載に差し替えてくれる認証局もあり、その場合は省略しても誤入力しても関係がない。むしろ気にすべきは、このフィールドは無条件に世界中に公開される<sup id="fnref:5"><a href="http://multix.jp/getting-ssl-certificate/#fn:5" rel="footnote">5</a></sup>ということである。ゆえに普段使いのメールアドレスを記載すると大変な目に合うこともあるので、専用の管理者用メールアドレスや、エイリアスにしておくとよい。<sup id="fnref:6"><a href="http://multix.jp/getting-ssl-certificate/#fn:6" rel="footnote">6</a></sup></li>
</ul>

<hr>

<h3 id="csr">証明書申請(CSR)ファイルの作成</h3>

<p>証明書申請(CSR)ファイルの作成手順は、自己証明書ファイルの時とほぼ同じである。すなわち<code>/etc/pki/tls/certs/</code>に移動して<code>make server.csr</code>を実行するだけだ。CSRファイルと同時に同名の秘密鍵も作成される。</p>

<pre><code class="language-brush:plain">[root@secure ~]# cd /etc/pki/tls/certs/
[root@secure certs]# make server.csr
umask 77 ; \  
    /usr/bin/openssl genrsa -aes128 2048 &gt; server.key
Generating RSA private key, 2048 bit long modulus  
............................+++
.............+++
e is 65537 (0x10001)  
Enter pass phrase:  
Verifying - Enter pass phrase:  
umask 77 ; \  
    /usr/bin/openssl req  -new -key server.key -out server.csr
Enter pass phrase for server.key:  
You are about to be asked to enter information that will be incorporated  
into your certificate request.  
What you are about to enter is what is called a Distinguished Name or a DN.  
There are quite a few fields but you can leave some blank  
For some fields there will be a default value,  
If you enter '.', the field will be left blank.  
-----
Country Name (2 letter code) [XX]:JP↩  
State or Province Name (full name) []:Tokyo↩  
Locality Name (eg, city) [Default City]:Chiyoda↩  
Organization Name (eg, company) [Default Company Ltd]:MULTIX↩  Organizational Unit Name (eg, section) []:↩  
Common Name (eg, your name or your server's hostname) []:secure.multix.jp↩  
Email Address []:hostmaster&amp;#64;multix&amp;#46;jp↩

Please enter the following 'extra' attributes  
to be sent with your certificate request  
A challenge password []:↩  
An optional company name []:↩  
[root@secure certs]# ls -l server.*
-rw------- 1 root root 1037 Jan 29 12:58 server.csr
-rw------- 1 root root 1766 Jan 29 12:49 server.key
[root@secure certs]# 
</code></pre>

<p>作成したserver.crtファイルを認証局に送付して署名を依頼する。また新しい秘密鍵からもパスフレーズを削除した版を生成してパーミッションを落としておく。</p>

<pre><code class="language-brush:plain">[root@secure certs]# umask 77; openssl rsa -in server.key -out ../private/server.key
Enter pass phrase for server.key:********↩  
writing RSA key  
[root@secure certs]# ls -l ../private/server.key 
-rw------- 1 root root 1679 Jan 29 12:59 ../private/server.key
[root@secure certs]#
</code></pre>

<p>発行されるSSL/TLSサーバ証明書が深さ2以上となる場合は、認証局のサイトから対応する中間CA証明書もダウンロードしておこう。これは<code>/etc/pki/tls/certs/</code>に<code>server-chain.crt</code>のファイル名で保存しておく。</p>

<pre><code class="language-brush:plain">[root@secure certs]# ls -l server-chain.crt 
-rw-------. 1 root root 2212 Jan 29 14:07 server-chain.crt
[root@secure certs]# 
</code></pre>

<hr>

<h4 id="ssltlsweb">本番用SSL/TLSサーバ証明書でWebサーバを起動する</h4>

<p>無事に認証局から署名されたSSL/TLSサーバ証明書ファイルが返送されてきたら、これを<code>server.crt</code>のファイル名でcertsディレクトリに保存する。これでサーバ運用に必要な秘密鍵と証明書の用意が終わったので、ふたたびhttpsサーバ設定<code>/etc/httpd/conf.d/ssl.conf</code>の該当項目2箇所を書き換える。また中間CA証明書<code>server-chain.crt</code>の設定<sup id="fnref:7"><a href="http://multix.jp/getting-ssl-certificate/#fn:7" rel="footnote">7</a></sup>も行頭コメントを削除して有効にする。そしてこのファイル中の<code>ServerName</code>を正式なサーバホスト名（証明書のコモンネーム）に変更する。</p>

<pre><code class="language-brush:plain">#ServerName www.example.com:443
ServerName secure.multix.jp

#SSLCertificateFile /etc/pki/tls/certs/localhost.crt
#SSLCertificateFile /etc/pki/tls/certs/develop.crt
SSLCertificateFile /etc/pki/tls/certs/server.crt

#SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
#SSLCertificateKeyFile /etc/pki/tls/private/develop.key
SSLCertificateKeyFile /etc/pki/tls/private/server.key

SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt  
</code></pre>

<p>また必要ならブラウザを開くPCの<code>/etc/hosts</code>ファイルにサーバホスト名を追加しよう。これはNATやDMZの内側にサーバがありまだテスト段階で外部公開していない場合の代替手段となる。</p>

<pre><code class="language-brush:plain">192.168.0.252 secure secure.multix.jp  
</code></pre>

<p>あとはconfigtestで書き換えエラーがないのを確認してサービスを再起動すればよい。またサーバ再起動時にWebサーバも起動するようにchkconfigも設定しておこう。</p>

<pre><code class="language-brush:plain">[root@secure ~]# service httpd configtest
Syntax OK  
[root@secure ~]# service httpd restart
httpd を停止中:                                            [  OK  ]  
httpd を起動中:                                            [  OK  ]  
[root@secure ~]# chkconfig httpd on
[root@secure ~]# chkconfig httpd --list
httpd              0:off   1:off   2:on    3:on    4:on    5:on    6:off  
[root@secure ~]# 
</code></pre>

<p>以上ですべての準備が整ったので、ブラウザでサーバホスト名を参照<sup id="fnref:8"><a href="http://multix.jp/getting-ssl-certificate/#fn:8" rel="footnote">8</a></sup>してみよう。今度は【頼できない接続】の警告なしでサイトが閲覧できるはずだ。証明書を確認して署名を依頼した認証局名が表示されれば正常である。<sup id="fnref:9"><a href="http://multix.jp/getting-ssl-certificate/#fn:9" rel="footnote">9</a></sup></p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-22-19-24-11.png" alt=""></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>独自ドメイン管理業者が証明書認証業務を行っており、それを利用して証明書認証を受ける場合はおおむね身分証明プロセスの多くが簡略化されるので必要ないこともある。WHOIS情報の開示が要求されるのは両者が別組織であったり管理業者の国が異なるような場合だろう。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>WHOIS以外に一意の指定トークンを含むCNAMEやTXTフィールドをDNS登録する承認方法もある。こうすることで証明書申請者が正当な管理者権限を有しているかを見定めている。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>ワイルドカード証明書はこれに限らない。また証明書によってはサブドメインと主ドメインの両方でエラー無く扱えるようになっていることもある。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>サーバ証明書の有効期間は一般に1〜2年なので、サーバ証明書用のCRTが厳密に運用されている様子はあまり見る機会がない。それでも時々DNS乗っ取りなどで不正発行された証明書が出回る騒ぎになると、これを取り消すためにCRTが更新されることになる。有効期間の長い証明書ほど事態は重く、騒ぎも大きくなるため、一見煩雑な証明書の毎年更新にも（認証局側は特に）利がある。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>opensslコマンドの-subjectオプションで表示できるし、どんなブラウザでもOU、CNと並んでこのフィールドは必ず簡単に参照できる。ほぼ間違いなくSPAMの標的になるので事前に防御策は講じておいたほうが良い。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:5" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:6"><p>また多数の独自ドメインを並行運用する場合個別にメールサーバを運用管理するのは不経済なのでドメイン管理業者の提供するメール転送サービスを利用したりする。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:6" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:7"><p>サーバでの中間CA証明書の設定を怠ると正しい証明書を使用していても【信頼できない接続】警告の対象となる。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:7" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:8"><p>このアドレス参照をlocalhostやIPアドレスにすると証明書のコモンネームと異なるのでブラウザは警告を出す。ブラウザを開くPCの/etc/hostsにIPアドレスとサーバホスト名を追加することでこの警告が出なくなることを確認できる。テストが終わって正規の運用状態に移行しおえたらこの記述は除去するのが望ましい。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:8" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:9"><p>「運営者: 検証され信頼できる運営者情報はありません」の表示はEV認証証明書を使っていない限り必ず表示されるものなので、通常は気に病む必要はない。ここに運営者情報が表示される場合は同じものがアドレスバーに緑色のラベルとして表示される。これを取得するには法人資格を持ち信頼性監査に合格する必要がある。無論かかる経費も一般に高額となる。 <a href="http://multix.jp/getting-ssl-certificate/#fnref:9" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[自己サーバ証明書を自作する]]></title><description><![CDATA[<p>前項<a href="http://multix.jp/starting-apache-ssl/">Apache/SSLサーバ導入手順</a>に続き、自己サーバ証明書を導入して起動するまでの手順を示す。通常はCentOSデフォルトで用意されているlocalhost証明書で充分用が足りるはずなのでこの項は必ずしも必要ではない。</p>

<ul class="index"></ul>

<hr>

<h4 id="ssltls">SSL/TLSサーバ証明書の配置場所と作成コマンド</h4>

<p>https通信を必要とするWebサイト/アプリの開発では以上の作業を行えば充分用が足りる。次は自己サーバ証明書を自作しそれを用いてWebサーバを設定する手続きを練習してみよう。</p>

<p>SSL/TLS証明書を得るには openssl コマンドで秘密鍵と証明書のセットを作成する必要があるが、この作業を簡略化する Makefile が openssl パッケージとともにインストールされているので、ここではこれを使用する。これは <code>/etc/pki/tls/certs</code> に配置されている。</p>

<pre><code class="language-brush:plain">[root@localhost ~]# cd /etc/pki/tls/certs/
[root@localhost certs]# ls -la
合計 1780
drwxr-xr-x. 2 root root    4096  2月 20 12:41</code></pre>]]></description><link>http://multix.jp/making-ssl-self-certificate/</link><guid isPermaLink="false">cfc7197a-3433-4ad1-9628-431fc260b24e</guid><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[SSL/TLS]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 19 Feb 2015 09:46:00 GMT</pubDate><content:encoded><![CDATA[<p>前項<a href="http://multix.jp/starting-apache-ssl/">Apache/SSLサーバ導入手順</a>に続き、自己サーバ証明書を導入して起動するまでの手順を示す。通常はCentOSデフォルトで用意されているlocalhost証明書で充分用が足りるはずなのでこの項は必ずしも必要ではない。</p>

<ul class="index"></ul>

<hr>

<h4 id="ssltls">SSL/TLSサーバ証明書の配置場所と作成コマンド</h4>

<p>https通信を必要とするWebサイト/アプリの開発では以上の作業を行えば充分用が足りる。次は自己サーバ証明書を自作しそれを用いてWebサーバを設定する手続きを練習してみよう。</p>

<p>SSL/TLS証明書を得るには openssl コマンドで秘密鍵と証明書のセットを作成する必要があるが、この作業を簡略化する Makefile が openssl パッケージとともにインストールされているので、ここではこれを使用する。これは <code>/etc/pki/tls/certs</code> に配置されている。</p>

<pre><code class="language-brush:plain">[root@localhost ~]# cd /etc/pki/tls/certs/
[root@localhost certs]# ls -la
合計 1780
drwxr-xr-x. 2 root root    4096  2月 20 12:41 2015 .  
drwxr-xr-x. 5 root root    4096  2月 20 11:03 2015 ..  
-rw-r--r--. 1 root root    2242  1月 21 02:32 2015 Makefile
-rw-r--r--. 1 root root  786601  7月 14 23:55 2014 ca-bundle.crt
-rw-r--r--. 1 root root 1005005  7月 14 23:55 2014 ca-bundle.trust.crt
-rw-------. 1 root root    1541  2月 20 11:29 2015 localhost.crt
-rwxr-xr-x. 1 root root     610  1月 21 02:32 2015 make-dummy-cert
-rwxr-xr-x. 1 root root     829  1月 21 02:32 2015 renew-dummy-cert
</code></pre>

<p>ここで例えば<code>make develop.crt</code>を実行すると、秘密鍵<code>develop.key</code>と証明書<code>develop.crt</code>のセットを作成することが出来る。質問事項のうち秘密鍵を作成・参照するためのパスフレーズは必須、<code>Organization Name</code>（ここではDEVELOP）と<code>Common Name</code>の入力（ここではlocalhost）は必要だが、その他は規定値で構わないので空ENTERでもよい。</p>

<pre><code class="language-brush:plain">[root@localhost certs]# make develop.crt
umask 77 ; \  
    /usr/bin/openssl genrsa -aes128 2048 &gt; develop.key
Generating RSA private key, 2048 bit long modulus  
.............................+++
...........................................+++
e is 65537 (0x10001)  
Enter pass phrase:********↩  
Verifying - Enter pass phrase:********↩  
umask 77 ; \  
    /usr/bin/openssl req -utf8 -new -key develop.key -x509 -days 365 -out develop.crt -set_serial 0
Enter pass phrase for develop.key:********↩  
You are about to be asked to enter information that will be incorporated  
into your certificate request.  
What you are about to enter is what is called a Distinguished Name or a DN.  
There are quite a few fields but you can leave some blank  
For some fields there will be a default value,  
If you enter '.', the field will be left blank.  
-----
Country Name (2 letter code) [XX]:JP↩  
State or Province Name (full name) []:Tokyo↩  
Locality Name (eg, city) [Default City]:Chiyoda↩  
Organization Name (eg, company) [Default Company Ltd]:DEVELOP↩  
Organizational Unit Name (eg, section) []:↩  
Common Name (eg, your name or your server's hostname) []:localhost↩  
Email Address []:↩  
[root@localhost certs]# ls -l develop.*
-rw-------. 1 root root 1261  2月 20 13:18 2015 develop.crt
-rw-------. 1 root root 1766  2月 20 13:18 2015 develop.key
</code></pre>

<p>出来上がったdevelop.crtの署名情報を確認してみると<code>-subject</code>と<code>-issuer</code>の中身が同一なので自己証明書である事がわかる。</p>

<pre><code class="language-brush:plain">[root@localhost certs]# openssl x509 -in develop.crt -noout -subject -issuer -dates
subject= /C=JP/ST=Tokyo/L=Chiyoda/O=DEVELOP/CN=localhost  
issuer= /C=JP/ST=Tokyo/L=Chiyoda/O=DEVELOP/CN=localhost  
notBefore=Feb 20 04:18:44 2015 GMT  
notAfter=Feb 20 04:18:44 2016 GMT  
</code></pre>

<p>次にパスフレーズを削除した秘密鍵を<code>/etc/pki/tls/private/</code>に作成し、パーミッションを落としておく。<sup id="fnref:1"><a href="http://multix.jp/making-ssl-self-certificate/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain">[root@localhost certs]# umask 77; openssl rsa -in develop.key -out ../private/develop.key
Enter pass phrase for develop.key:********↩  
writing RSA key  
[root@localhost certs]# ls -l ../private/
合計 8
-rw-------. 1 root root 1679  2月 20 13:21 2015 develop.key
-rw-------. 1 root root 1679  2月 20 11:29 2015 localhost.key
[root@localhost certs]#
</code></pre>

<hr>

<h4 id="ssltlsweb">自作したSSL/TLSサーバ証明書でWebサーバを起動する</h4>

<p>これで秘密鍵と証明書が用意出来たので、httpsサーバ設定<code>/etc/httpd/conf.d/ssl.conf</code>の該当項目2箇所を書き換える。またこのファイルの中にも<code>ServerName</code>を指定する箇所があるが、いまは何もしなくても良い。</p>

<pre><code class="language-brush:plain">#ServerName www.example.com:443

#SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateFile /etc/pki/tls/certs/develop.crt

#SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
SSLCertificateKeyFile /etc/pki/tls/private/develop.key  
</code></pre>

<p>あとはconfigtestで書き換えエラーがないのを確認してサービスを再起動するだけだ。</p>

<pre><code class="language-brush:plain">[root@localhost conf.d]# service httpd configtest
Syntax OK  
[root@localhost conf.d]# service httpd restart
httpd を停止中:                                            [  OK  ]  
httpd を起動中:                                            [  OK  ]  
[root@localhost conf.d]# 
</code></pre>

<p>ブラウザでふたたび<code>https://localhost</code>を閲覧して証明書の内容を確認してみると、認証局の位置に先ほど<code>Organization Name</code>に入力した文字列 DEVELOP が表示されているだろう。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-15-03-01.png" alt=""></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>ここではumaskコマンドを前置してopensslコマンドの出力段階で不要なパーミッションを落としている。 <a href="http://multix.jp/making-ssl-self-certificate/#fnref:1" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Apache/SSLサーバ導入手順]]></title><description><![CDATA[<p>CentOS6のApache+mod_sslでHTTPS/SSL/TLSサーバを立ち上げるシンプルな手順を示す。ここではLANに接続されたCentOSデスクトップPCでの構築練習を仮定している。前項<a href="http://multix.jp/openssl-command/">opensslコマンド例文集</a>も参照のこと。</p>

<ul class="index"></ul>

<hr>

<h4 id="apache">Apache導入</h4>

<p>必要なupdateを行った上で、Apache、mod_sslを導入する。もしopensslが入っていなければ自動的に追加される。</p>

<pre><code class="language-brush:plain">yum update -y  
yum install httpd mod_ssl  
</code></pre>

<hr>

<h4 id="web">Webサーバを起動する</h4>

<p>さっそくこのWebサーバを起動してみよう。/etc/httpd/conf/httpd.conf を編集して<code>ServerName</code>を所定の位置に追記する。既定では記入例がコメントで書かれているからその直下に<code>ServerName localhost</code>を追記して保存する。設定編集が必要なのはこれだけだ。<sup id="fnref:1"><a href="http://multix.jp/starting-apache-ssl/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain gutter:true title:/etc/httpd/conf/httpd.conf">#ServerName www.example.com:80
ServerName localhost  
</code></pre>

<p>設定に間違いがないか確認してhttpdサービスを起動する。</p>

<pre><code class="language-brush:plain">[root@localhost ~]# service</code></pre>]]></description><link>http://multix.jp/starting-apache-ssl/</link><guid isPermaLink="false">e81f94f9-3965-4d97-a026-f369935a1745</guid><category><![CDATA[ユーティリティ]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[SSL/TLS]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 19 Feb 2015 09:45:00 GMT</pubDate><content:encoded><![CDATA[<p>CentOS6のApache+mod_sslでHTTPS/SSL/TLSサーバを立ち上げるシンプルな手順を示す。ここではLANに接続されたCentOSデスクトップPCでの構築練習を仮定している。前項<a href="http://multix.jp/openssl-command/">opensslコマンド例文集</a>も参照のこと。</p>

<ul class="index"></ul>

<hr>

<h4 id="apache">Apache導入</h4>

<p>必要なupdateを行った上で、Apache、mod_sslを導入する。もしopensslが入っていなければ自動的に追加される。</p>

<pre><code class="language-brush:plain">yum update -y  
yum install httpd mod_ssl  
</code></pre>

<hr>

<h4 id="web">Webサーバを起動する</h4>

<p>さっそくこのWebサーバを起動してみよう。/etc/httpd/conf/httpd.conf を編集して<code>ServerName</code>を所定の位置に追記する。既定では記入例がコメントで書かれているからその直下に<code>ServerName localhost</code>を追記して保存する。設定編集が必要なのはこれだけだ。<sup id="fnref:1"><a href="http://multix.jp/starting-apache-ssl/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain gutter:true title:/etc/httpd/conf/httpd.conf">#ServerName www.example.com:80
ServerName localhost  
</code></pre>

<p>設定に間違いがないか確認してhttpdサービスを起動する。</p>

<pre><code class="language-brush:plain">[root@localhost ~]# service httpd configtest
Syntax OK  
[root@localhost ~]# service httpd start
httpd を起動中:                                            [  OK  ]  
[root@localhost ~]#
</code></pre>

<p>Linuxデスクトップ上でWebブラウザを起動し<code>http://localhost</code>を参照すると以下の画面を得ることが出来る。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-11-57-32.png" alt=""></p>

<p>自分自身（localhost）以外から参照するにはファイアウォールでHTTPポート接続を許可する必要<sup id="fnref:2"><a href="http://multix.jp/starting-apache-ssl/#fn:2" rel="footnote">2</a></sup>がある。これには<code>WWW(HTTP)</code>と<code>安全なWWW(HTTPS)</code>のふたつを許可しよう。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-11-57-21.png" alt=""></p>

<hr>

<h4 id="https">HTTPSで接続する</h4>

<p>実はこの段階で、プリインストールされた自己CA証明書を用いたHTTPSサーバがすでに起動している。ブラウザから<code>https://localhost</code>を参照すると以下【信頼できない接続】の画面を得ることが出来る。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-12-21-46.png" alt=""></p>

<p>FireFoxの場合は【危険性を理解した上で接続するには】の【例外を追加】からこの自己CA証明書を【承認】する。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-12-25-30.png" alt=""></p>

<p>すると先のHTTP接続と同じ画面が表示されるが、アドレス欄には鍵マークが付いている。これをクリックするとHTTPSで確かに通信されていることが判る。</p>

<p><img src="http://multix.jp/content/images/2015/02/2015-02-20-12-28-23.png" alt=""></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><strong>ServerName</strong>がコメントアウトされたまま起動しようとしてもエラーが出て立ち上がらない。 <a href="http://multix.jp/starting-apache-ssl/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p><strong>system-config-firewall-base</strong>パッケージの<strong>system-config-firewall-base-tui</strong>コマンドでファイアウォールを制御できる。 <a href="http://multix.jp/starting-apache-ssl/#fnref:2" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[opensslコマンド例文集]]></title><description><![CDATA[<p>opensslコマンド関係の備忘録。</p>

<ul class="index"></ul>

<hr>

<h4 id="">サーバに設定された証明書を確認する</h4>

<p>正しくSSL通信が為されているならば証明書の署名と発行者、またSSLネゴシエーションのパラメータや暗号方式が得られる。</p>

<pre><code class="language-brush:plain">openssl s_client -connect HOSTNAME_OR_ADDR:443 -showcerts  
</code></pre>

<p>中間証明書が正しく設定されているならばそれらを通じてルートCAまで一貫して辿れることが確認できる。
なお、ポート番号にはデフォルトでは以下を指定する。</p>

<ul>
<li>443 HTTPS</li>
<li>995 POP over SSL</li>
<li>465 SMTP over SSL</li>
<li>993 IMAPS</li>
</ul>

<p>このコマンドは「---」で表示が止まるが、TCPセッションは接続したままになっている。HTTPSの場合は<code>get /</code>ENTERを打てばトップページの内容が得られるし、SMTPなら<code>HELO</code>等を打つことでSMTPサーバと直接会話することができる。</p>

<hr>

<h4 id="crt">証明書ファイル(CRT)の内容を確認する</h4>

<p><code>-inform</code>を省略した場合は<code>-in</code>ファイルは（BASE64エンコードされた）PEMフォーマットであると仮定する。解読できない場合はDERを指定してみると良い。これは他のコマンドでも同様である。</p>

<pre><code class="language-brush:plain">openssl</code></pre>]]></description><link>http://multix.jp/openssl-command/</link><guid isPermaLink="false">93e16083-30d4-46bb-95e2-1109c0e4ce72</guid><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><category><![CDATA[SSL/TLS]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 19 Feb 2015 09:01:00 GMT</pubDate><content:encoded><![CDATA[<p>opensslコマンド関係の備忘録。</p>

<ul class="index"></ul>

<hr>

<h4 id="">サーバに設定された証明書を確認する</h4>

<p>正しくSSL通信が為されているならば証明書の署名と発行者、またSSLネゴシエーションのパラメータや暗号方式が得られる。</p>

<pre><code class="language-brush:plain">openssl s_client -connect HOSTNAME_OR_ADDR:443 -showcerts  
</code></pre>

<p>中間証明書が正しく設定されているならばそれらを通じてルートCAまで一貫して辿れることが確認できる。
なお、ポート番号にはデフォルトでは以下を指定する。</p>

<ul>
<li>443 HTTPS</li>
<li>995 POP over SSL</li>
<li>465 SMTP over SSL</li>
<li>993 IMAPS</li>
</ul>

<p>このコマンドは「---」で表示が止まるが、TCPセッションは接続したままになっている。HTTPSの場合は<code>get /</code>ENTERを打てばトップページの内容が得られるし、SMTPなら<code>HELO</code>等を打つことでSMTPサーバと直接会話することができる。</p>

<hr>

<h4 id="crt">証明書ファイル(CRT)の内容を確認する</h4>

<p><code>-inform</code>を省略した場合は<code>-in</code>ファイルは（BASE64エンコードされた）PEMフォーマットであると仮定する。解読できない場合はDERを指定してみると良い。これは他のコマンドでも同様である。</p>

<pre><code class="language-brush:plain">openssl x509 -in "CERTFILE.PEM" -noout -text  
</code></pre>

<pre><code class="language-brush:plain">openssl x509 -in "CERTFILE.CRT" -inform der -noout -text  
</code></pre>

<hr>

<h4 id="">証明書ファイルの有効期間を確認する</h4>

<p><code>-text</code>での表示内容から有効期間(Validity)フィールドだけを表示する。</p>

<pre><code class="language-brush:plain">openssl x509 -in "CERTFILE.PEM" -noout -dates

notBefore=Jan 28 17:19:49 2015 GMT  
notAfter=Jan 29 17:58:54 2016 GMT  
</code></pre>

<hr>

<h4 id="">証明書ファイルの要求使用者名を確認する</h4>

<p><code>-text</code>での表示内容から要求使用者(Subject)フィールドだけを表示する。</p>

<pre><code class="language-brush:plain">openssl x509 -in "CERTFILE.PEM" -noout -subject

subject= /C=JP/CN=secure.example.jp/emailAddress=hostmaster@example.jp  
</code></pre>

<hr>

<h4 id="">証明書ファイルの発行署名者を確認する</h4>

<p><code>-text</code>での表示内容から発行署名者(Issuer)フィールドだけを表示する。</p>

<pre><code class="language-brush:plain">openssl x509 -in "CERTFILE.PEM" -noout -issuer

issuer= /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 1 Primary Intermediate Server CA  
</code></pre>

<hr>

<h4 id="csr">証明書申請ファイル(CSR)の内容を確認する</h4>

<pre><code class="language-brush:plain">openssl req -in "CERTRESIGNQUEST.PEM" -noout -text  
</code></pre>

<hr>

<h4 id="">秘密鍵ファイルの内容を確認する</h4>

<pre><code class="language-brush:plain">openssl rsa -in "PRIVATEKEY.PEM" -noout -text  
</code></pre>

<hr>

<h4 id="pub">証明書と秘密鍵の公開鍵(PUB)が一致するかを確認する</h4>

<p>これは証明書と秘密鍵それぞれから公開鍵を抽出して比較することで実現できる。</p>

<pre><code class="language-brush:plain">openssl rsa -in "PRIVATEKEY.PEM" -pubout -out "PUBKEY.PEM"  
openssl x509 -in "CERTIFICATE.PEM" -pubkey -noout &gt; "CERTPUB.PEM"  
diff "PUBKEY.PEM" "CERTPUB.PEM" &amp;&amp; echo OK  
</code></pre>

<hr>

<h4 id="">秘密鍵のパスフレーズを削除する</h4>

<p>サービスデーモンで使用する秘密鍵は、サービスを起動する度に毎回パスフレーズ入力を求められることになるのを避けるため、セキュリティ的に弱くなるのを承知しつつパスフレーズを削除しておくのが一般的<sup id="fnref:1"><a href="http://multix.jp/openssl-command/#fn:1" rel="footnote">1</a></sup>だ。入力ファイルと出力ファイルは同じファイル名にして上書き保存しても良いし別ファイルに変えても良い。普通は使用するサービスが区別できるような名前にするだろう。</p>

<pre><code class="language-brush:plain">openssl rsa -in "PRIVATEKEY.PEM" -out "SERVERKEY.PEM"  
</code></pre>

<p>作成した秘密鍵ファイルは速やかにファイル属性をrootユーザの読み込み専用に変更し、通常は<code>/etc/pki/tls/certs/</code>に適切な名前で格納する。</p>

<pre><code class="language-brush:plain">openssl rsa -in localhost.key -out localhost.key  
chown root.root localhost.key  
chmod 400 localhost.key  
mv localhost.key /etc/pki/tls/certs/  
</code></pre>

<hr>

<h4 id="">証明書のフォーマット変換</h4>

<p>UNIX系サーバソフトやアプリではPEM形式が多用されるが事実上平文も同然なので、外部とのやりとりやWindows/Macintosh上ではDER形式やPKCS12形式を利用するほうが多いだろう。なのでファイルフォーマット変換は利用する機会が多い。<sup id="fnref:2"><a href="http://multix.jp/openssl-command/#fn:2" rel="footnote">2</a></sup></p>

<pre><code class="language-brush:plain">openssl x509 -in "input_file.der" -inform DER -out "output_file.pem" -outform PEM  
</code></pre>

<pre><code class="language-brush:plain">openssl x509 -in "input_file.der" -inform DER -text -noout  
</code></pre>

<pre><code class="language-brush:plain">openssl x509 -in "input_file.pem" -inform PEM -out "output_file.der" -outform DER  
</code></pre>

<pre><code class="language-brush:plain">openssl pkcs12 -export -in "input_file.pem" -out "output_file.p12" -name "FRIENDLYNAME"  
</code></pre>

<p>PKCS12への変換はシステムから外部へのエクスポートなのでパスフレーズの設定入力が求められる。<code>-name</code>は任意入力だがたいていは誰彼が使うために発行したのかが区別できるように書く。</p>

<pre><code class="language-brush:plain">openssl pkcs12 -in "input_file.p12" -nodes -out "output_file.pem"  
</code></pre>

<p>PKCS12からの変換は外部からシステムへのインポートなので正しいパスフレーズを解除入力しないと実行されない。</p>

<p>なおWindowsでは証明書ファイルをフォーマット形式ではなく用途によって既定の拡張子を定めている。用途と合致しない拡張子の証明書をインポートしようとすると正しい場所へストアされずに悩まされることがある。<sup id="fnref:3"><a href="http://multix.jp/openssl-command/#fn:3" rel="footnote">3</a></sup></p>

<hr>

<h4 id="">証明書の正当性検証</h4>

<p>ルートCA証明書は自分自身を証明して他には依存していないため以下のコマンドでOKが返る。<sup id="fnref:4"><a href="http://multix.jp/openssl-command/#fn:4" rel="footnote">4</a></sup></p>

<pre><code class="language-brush:plain">openssl verify ルートCA証明書  
</code></pre>

<p>ルートCA証明書で直接署名された（ルートから見て深さが1の）証明書は<code>-CAfile</code>オプションにルートCA証明書を与えることで直接検証できる。</p>

<pre><code class="language-brush:plain">openssl verify -CAfile 親証明書 子証明書  
</code></pre>

<p>中間CA証明書で署名された（ルートから見て深さが2以上の）証明書の場合は検証確認が煩雑になる。取り敢えず簡便なのは、ルートCA証明書と必要な中間CA証明書をすべて含む一つの親証明書を作成してこれで検証する方法だろう。</p>

<pre><code class="language-brush:plain">cat StartSSL_CA_Root.pem StartSSL_CA_Class1.pem &gt; StartSSL_CA.pem  
openssl verify -CAfile StartSSL_CA.pem cert.pem 

cert.pem: OK  
</code></pre>

<hr>

<h4 id="ssl">アプリケーションのSSL警告を抑制する</h4>

<p>システムにバンドルされていない証明書をシステムに追加するには<code>ca-certificates</code>パッケージに付属の<code>update-ca-trust</code>コマンドを使用する。<code>/etc/pki/ca-trust/source/anchors/</code>に中間CA証明書をコピーして<code>update-ca-trust extract</code>を実行すると<code>/etc/pki/ca-trust/extracted/pem/*.pem</code>証明書ストアに結合される。<sup id="fnref:5"><a href="http://multix.jp/openssl-command/#fn:5" rel="footnote">5</a></sup></p>

<pre><code class="language-brush:plain">cp StartSSL_CA_Class1.pem /etc/pki/ca-trust/source/anchors/  
update-ca-trust extract  
openssl verify -CAfile /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem cert.pem

cert.pem: OK  
</code></pre>

<p>この証明書ストアは<code>wget</code>コマンド等が参照しているので、この作業を行うとこのCA証明書を参照するホストについては<code>--no-check-certificate</code>オプションを付加する必要がなくなる。自作CA証明書（いわゆるオレオレ証明書）もこの方法でシステムに登録できる。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>パスフレーズ未削除運用するときに考慮すべき最大の問題は、サーバマシン自体を再起動した際にサービスそのものが、現場でサーバ管理者が正しいパスフレーズを入力するまで機能停止しつづけることだろう。時にはこのパスフレーズを誰も知らなかったり唯一知っていたはずの人物が何年も前に他界してたりするものである。 <a href="http://multix.jp/openssl-command/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>慣習的なものだが拡張子の用途とファイル形式が合致していないので混乱しやすい。<strong>.pem</strong>はPEM形式であることを示すがそれだけではKEYかCSRかCRTか判らないし、<strong>.crt</strong>が証明書なのは判るもののPEMかDERかPKCS7かはファイルを開いてみるまで判らない。 <a href="http://multix.jp/openssl-command/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>Windowsの場合<strong>.crt</strong>はサーバ用証明書で<strong>.pfx</strong>は個人用クライアント証明書と見做され<strong>.der</strong>はOutlookやアドレス帳へのクライアント証明書インポートに使われる。 <a href="http://multix.jp/openssl-command/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>システムにバンドル済のルートCA証明書は<strong>/etc/pki/tls/certs/ca-bundle.crt</strong>に、信頼済証明書は<strong>/etc/pki/tls/certs/ca-bundle.trust.crt</strong>に結合されて格納されている。これらは<strong>ca-certificates</strong>パッケージで提供されており<strong>yum update</strong>で更新されることもありえるから直接編集するべきではない。 <a href="http://multix.jp/openssl-command/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>更新されるストアファイルは<strong>email-ca-bundle.pem</strong>、<strong>objsign-ca-bundle.pem</strong>、<strong>tls-ca-bundle.pem</strong>の3つで、それぞれメール個人署名、オブジェクトコード署名、SSL通信署名に対応する。 <a href="http://multix.jp/openssl-command/#fnref:5" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item></channel></rss>