<?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>Thu, 23 Apr 2026 21:16:06 GMT</lastBuildDate><atom:link href="http://multix.jp/tag/linux/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[ヘッドレス用memtest86+の作成]]></title><description><![CDATA[<p>memtest86+はふつうは公式サイト<sup id="fnref:1"><a href="http://multix.jp/buildup-memtest-headless/#fn:1" rel="footnote">1</a></sup>等から出来合いのバイナリを入手してくれば充分だが、通常は VGA出力必須のためシリアルコンソールでのヘッドレス環境では実行できない。しかしソースコードには必要最小限ではあるがシリアルコンソール対応コードが含まれているため、ヘッダを修正して makeしなおせばヘッドレス環境で使えるmemtestバイナリを手に入れることができる。</p>

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

<hr>

<h4 id="">注意点</h4>

<p>memtestのシリアルコンソール対応は、既に充分にはメンテナンスされていないのか若干の不具合がある。例えば configurationメニューから抜けたあと画面が乱れるので起動後のテストオプションが事実上変更できないとか、ESCキーで終了したとき rebootではなく system haltで止まってしまうなどだ。とは言え VGAデバイスを持たない機材のメモリ健全性テストができるだけで充分な利用価値があったりする。</p>

<p>なお memtestバイナリは 32bit実行ファイルとなるので、64bit環境で buildするには <code>/usr/include/gnu/stubs-32.h</code>が要求される。</p>

<hr>

<h4 id="centos">CentOSの場合</h4>

<p>まず必要なパッケージをインストールする。基本的な gccやライブラリの類は <code>yum groupinstall "Development Tools"</code>で揃う。また memtestを makeするために 32bitバージョンの glibc-develもインストールする。</p>

<pre><code class="language-brush:bash">sudo yum groupinstall -y "Development</code></pre>]]></description><link>http://multix.jp/buildup-memtest-headless/</link><guid isPermaLink="false">dadf31ad-7b3a-4598-85e2-3dab8a594530</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 02 Jul 2015 05:54:24 GMT</pubDate><content:encoded><![CDATA[<p>memtest86+はふつうは公式サイト<sup id="fnref:1"><a href="http://multix.jp/buildup-memtest-headless/#fn:1" rel="footnote">1</a></sup>等から出来合いのバイナリを入手してくれば充分だが、通常は VGA出力必須のためシリアルコンソールでのヘッドレス環境では実行できない。しかしソースコードには必要最小限ではあるがシリアルコンソール対応コードが含まれているため、ヘッダを修正して makeしなおせばヘッドレス環境で使えるmemtestバイナリを手に入れることができる。</p>

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

<hr>

<h4 id="">注意点</h4>

<p>memtestのシリアルコンソール対応は、既に充分にはメンテナンスされていないのか若干の不具合がある。例えば configurationメニューから抜けたあと画面が乱れるので起動後のテストオプションが事実上変更できないとか、ESCキーで終了したとき rebootではなく system haltで止まってしまうなどだ。とは言え VGAデバイスを持たない機材のメモリ健全性テストができるだけで充分な利用価値があったりする。</p>

<p>なお memtestバイナリは 32bit実行ファイルとなるので、64bit環境で buildするには <code>/usr/include/gnu/stubs-32.h</code>が要求される。</p>

<hr>

<h4 id="centos">CentOSの場合</h4>

<p>まず必要なパッケージをインストールする。基本的な gccやライブラリの類は <code>yum groupinstall "Development Tools"</code>で揃う。また memtestを makeするために 32bitバージョンの glibc-develもインストールする。</p>

<pre><code class="language-brush:bash">sudo yum groupinstall -y "Development Tools"

# 64bit環境の場合
sudo yum install -y glibc-devel-2.17-78.el7.i686  
</code></pre>

<p>memtestの SRPMをダウンロードし、rpmコマンドで展開する。展開結果は <code>~/rpmbuild</code>ディレクトリ以下に現れる。</p>

<pre><code class="language-brush:bash">rpm -iv ftp://ftp.pbone.net/mirror/vault.centos.org/7.1.1503/os/Source/SPackages/memtest86+-4.20-14.el7.src.rpm  
</code></pre>

<p>シリアルポートの設定は、memtestの condfig.hで行うので、これに対する patchファイルを用意する。<strong>SERIAL_CONSOLE_DEFAULT を1にするだけ</strong>だが、これによって起動オプションとして <code>console=CONFIGURATION</code>が記述できるようになる。</p>

<pre><code class="language-brush:plain gutter:true highlight:[8] title:~/rpmbuild/SOURCES/memtest86+-4.20-serial.patch">--- memtest86+-4.20/config.h        2011-01-24 03:11:04.000000000 +0900
+++ memtest86+-4.20/config.h.serial 2015-07-03 13:15:26.716441211 +0900
@@ -13,7 +13,7 @@
 /* SERIAL_CONSOLE_DEFAULT -  The default state of the serial console. */
 /*     This is normally off since it slows down testing.  Change to a 1 */
 /*     to enable. */
-#define SERIAL_CONSOLE_DEFAULT 0
+#define SERIAL_CONSOLE_DEFAULT 1

 /* SERIAL_TTY - The default serial port to use. 0=ttyS0, 1=ttyS1 */ 
 #define SERIAL_TTY 0
</code></pre>

<p>次いで specファイルを修正する。元の memtest86+.specをコピーして、これらを適切な場所に追加する。</p>

<pre><code class="language-brush:bash">cp ~/rpmbuild/SPECS/memtest86+.spec ~/rpmbuild/SPECS/memtest86+-serial.spec  
</code></pre>

<pre><code class="language-brush:bash title:~/rpmbuild/SPECS/memtest86+-serial.spec fix"># Patch1指示を Patch0指示行の直後に追加
Patch1:   memtest86+-4.20-serial.patch

# %patch1コマンドを %patch0コマンド行の直後に追加
%patch1 -p1 -b .serial
</code></pre>

<p>あとは <code>rpmbuild</code>コマンドを叩けば、RPMパッケージが作成される。これをインストールすると /bootに展開されるが、LiveBootの場合必要なのは memtestバイナリ本体だけなので（LiveBoot環境で buildした場合は）出来上がった memtest.binを <code>/run/initramfs/live/syslinux/mt86plus</code>にコピーして、syslinux.cfgを編集すれば事足りる。<sup id="fnref:2"><a href="http://multix.jp/buildup-memtest-headless/#fn:2" rel="footnote">2</a></sup></p>

<pre><code class="language-brush:bash highlight:[4,7]">rpmbuild -bb rpmbuild/SPECS/memtest86+-serial.spec

ls -l ~/rpmbuild/RPMS/x86_64/memtest86+-4.20-14.el7.centos.x86_64.rpm  
-rw-rw-r-- 1 liveuser liveuser 80616 Jul  2 18:31 rpmbuild/RPMS/x86_64/memtest86+-4.20-14.el7.centos.x86_64.rpm

ls -l ~/rpmbuild/BUILD/memtest86+-4.20/memtest.bin  
-rwxr-xr-x 1 liveuser liveuser 176500 Jul  2 18:31 rpmbuild/BUILD/memtest86+-4.20/memtest.bin

cp ~/rpmbuild/BUILD/memtest86+-4.20/memtest.bin /run/initramfs/live/syslinux/mt86plus  
</code></pre>

<pre><code class="language-brush:plain title:/run/initramfs/live/syslinux/syslinux.cfg fix"># Troubleshootingメニューに追加
label memtest  
  menu label Test ^memory
  kernel mt86plus
  append console=ttyS0,115200n8
</code></pre>

<p>memtest.binは FDDブートイメージなので、FDDへはそのまま ddするだけでも使える。ただしそれでは今回のようなシリアルコンソール設定オプションを渡すすべがないので、syslinux等の汎用ブートローダーを利用して起動するほうが一般的だろう。</p>

<hr>

<h4 id="ubuntu">Ubuntuの場合</h4>

<p>まず build環境を整える。<code>apt-get build-dep PACKAGE</code>で指定パッケージを buildするのに必要なツール類がまとめてインストールされる。</p>

<pre><code class="language-brush:bash">sudo apt-get update  
sudo apt-get install devscripts  
sudo apt-get build-dep memtest86+  
apt-get source memtest86+  
</code></pre>

<p>memtestのシリアルポート設定は CentOSの場合と同様だが（memtest86+-4.20/debian/rulesを修正するのも面倒なので）config.hは直接修正する。その後はソースディレクトリの中で <code>debuild -us -uc</code>を実行すれば memtest.binバイナリと、親ディレクトリに .debファイルが作成される。</p>

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

vi config.h

debuild -us -uc

ls -l memtest.bin  
-rwxr-xr-x 1 ubuntu ubuntu 164216 Jul  3 02:57 memtest.bin
</code></pre>

<p>memtest.binは LiveBootフラッシュドライブの mt86plusと差し替えて使用する。</p>

<pre><code class="language-brush:bash highlight:[2]">ls -l /cdrom/install/mt86plus  
-rwxr-xr-x 1 root root 150024 Jun 22 16:11 /cdrom/install/mt86plus

cp memtest.bin /cdrom/install/mt86plus  
</code></pre>

<pre><code class="language-brush:plain title:isolinux/txt.cfg fix">label memtest  
  menu label Test ^memory
  kernel /install/mt86plus
  append console=ttyS0,115200n8
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><a href="http://www.memtest.org">http://www.memtest.org</a> <a href="http://multix.jp/buildup-memtest-headless/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>syslinuxのバージョンにもよるが kernel/appendコマンドに渡すファイル名に拡張子が付いていると正常認識されないことがあるので、CentOSでは慣習的に8文字以下の拡張子なしファイル名にすることが多い。 <a href="http://multix.jp/buildup-memtest-headless/#fnref:2" 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[DynDNS互換サービスを自作する]]></title><description><![CDATA[<p>筆者が使っている Allied Telesisの AR560S/AR550SにはダイナミックDNS機能があるが、対応サービスにはStandard DNS(Dyn.com)しか使えない。まあ企業用途なら一口25ドルくらいどうってことはないが、個人用途でこれはちょっと面白くないので、DynDNS互換サービスを自作してみた話。</p>

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

<hr>

<h4 id="dyndns">DynDNSプロトコル</h4>

<p>まず基本的な情報だが、ARルータ側の設定項目はこう定義されている。</p>

<pre><code class="language-brush:plain">SET DDNS [SERVER=server] [PORT=port] [USER=userid] [PASSWORD=password] [DYNAMICHOST=hostnames] [PRIMARYINT=ipinterface] [SECONDARYINT=ipinterface] [OFFLINE={YES|NO|ON|OFF}] [PERIODICUPDATE={1..60|ON|OFF}]

server: ダイナミックDNSサーバー名。（1～31文字。英数字）  
port: HTTPポート番号。80(</code></pre>]]></description><link>http://multix.jp/create-compatible-dyndns-service/</link><guid isPermaLink="false">05d79948-33cb-4328-b681-cf62acedd527</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Fri, 15 May 2015 06:50:57 GMT</pubDate><content:encoded><![CDATA[<p>筆者が使っている Allied Telesisの AR560S/AR550SにはダイナミックDNS機能があるが、対応サービスにはStandard DNS(Dyn.com)しか使えない。まあ企業用途なら一口25ドルくらいどうってことはないが、個人用途でこれはちょっと面白くないので、DynDNS互換サービスを自作してみた話。</p>

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

<hr>

<h4 id="dyndns">DynDNSプロトコル</h4>

<p>まず基本的な情報だが、ARルータ側の設定項目はこう定義されている。</p>

<pre><code class="language-brush:plain">SET DDNS [SERVER=server] [PORT=port] [USER=userid] [PASSWORD=password] [DYNAMICHOST=hostnames] [PRIMARYINT=ipinterface] [SECONDARYINT=ipinterface] [OFFLINE={YES|NO|ON|OFF}] [PERIODICUPDATE={1..60|ON|OFF}]

server: ダイナミックDNSサーバー名。（1～31文字。英数字）  
port: HTTPポート番号。80(HTTP)または 8245(HTTP Bypass)  
userid: ユーザーID。（1～15文字。英数字。大文字小文字を区別する）  
password: パスワード（1～15文字。英数字。大文字小文字を区別する）  
hostnames: ホスト名（URL形式。カンマ区切りで複数指定可能。カンマを含め128文字まで。）  
ipinterface: インターフェース名。(ppp0など)  
</code></pre>

<p>その結果、DNSサーバには次のようなリクエストが送信される。</p>

<pre><code class="language-brush:plain">GET /nic/update?system=dyndns&amp;hostname=example.dyndns.org&amp;myip=123.45.67.89&amp;wildcard=OFF&amp;offline=OFF HTTP/1.1  
Host: members.dyndns.org  
Authorization: Basic [BASE64-ENCODED-USERNAME:PASSWORD-PAIR]  
User-Agent: Dyn DNS Client/CentreCOM AR560S version 2.9.2-09 21-Aug-2012  
</code></pre>

<p>これに対してサーバレスポンスに<code>good &lt;IP_ADDR&gt;</code>が返るとそれは正常に受け付けられたことになり、<code>show ddns</code>で確認できるステータス表示が更新される。</p>

<p>送信URIとして<code>/nic/update</code>は固定、ユーザ認証はBASIC認証方式、パラメータとして<code>hostname</code>は必須。CGIインタフェースとして実装すべき内容はこれだけなのでそう難しいことではない。</p>

<p>ここでの実装では要件的には必要ないのだがエラーログを残すのにも使うので、good以外のレスポンス文字列も上げておく。<sup id="fnref:1"><a href="http://multix.jp/create-compatible-dyndns-service/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-table title:DynDNS Responce Code">|応答|状態|説明|
|good &lt;IP_ADDR&gt;|OK|リクエストは正常に受け付けた|
|nochg &lt;IP_ADDR&gt;|OK|リクエストは正常だが更新されるステータスはない|
|nohost|NG|hostnameは存在しない|
|badauth|NG|認証に失敗した|
|badagent|NG|この機器は許可されていない|
|!donator|NG|利用料が支払われていない|
|abuse|NG|ユーザはブロックされている・乱用|
|911|NG|重大なエラー|
</code></pre>

<hr>

<h4 id="nginxperlmodule">nginx perl moduleでの実装</h4>

<p>今回はnginxのperl module機能を用いて実装する。CentOS版のnginxはこれに対応しているが、公式サイトで配布しているバイナリは対応していないため要リビルドである。次のようにしてなにも表示されなければ、このビルドオプションが足りていない。</p>

<pre><code class="language-brush:plain">$ nginx -V 2&gt;&amp;1 | tr ' ' '\n' | grep perl
--with-http_perl_module
</code></pre>

<p>perlモジュールが有効なら、レスポンスハンドラをperlで記述することが出来る。そこで以下のコードを<code>/etc/nginx/lib/dyndns.pm</code>に用意した。</p>

<pre><code class="language-brush:perl gutter:true title:/etc/nginx/lib/dyndns.pm">#
# $Id: dyndns.pm 159 2015-05-15 04:18:23Z askn $
#
package dyndns;  
use strict;  
use utf8;  
use 5.010;  
use nginx;  
use Net::DNS;  
use MIME::Base64;  
use Sys::Syslog;

sub update_handler {  
    my $r = shift;

    $r-&gt;send_http_header("text/plain");
    return OK if $r-&gt;header_only;

    eval {
        openlog("ddns_update", "cons,pid", "daemon");

        die "unknown:nothing parameters\n" unless $r-&gt;args;

        my $auth = $r-&gt;header_in("Authorization") // "";

        die "badauth:bad authorization.\n"
            unless $auth =~ m{^Basic ([0-9a-z\+\/\=]+)}i;

        my $user = (split /:/, decode_base64($1))[0];

        my $q = {};
        foreach my $pair (split /&amp;/, $r-&gt;args) {
            my($key, $val) = split /=/, $pair;
            next      unless defined $key;
            $val = "" unless defined $val;
            $q-&gt;{$r-&gt;unescape($key)} = $r-&gt;unescape($val);
        }

        my $hostname = lc($q-&gt;{hostname} // "");
        my $myip     =    $q-&gt;{myip} || $r-&gt;remote_addr;
#        my $system   =    $q-&gt;{system}   // "";
#        my $wildcard =    $q-&gt;{wildcard} // "";
#        my $offline  =    $q-&gt;{offline}  // "";
#        my $mx       = ls($q-&gt;{mx}       // "");

        syslog("info", "USER=".$user." HOSTNAME=".$hostname." MYIP=".$myip);

        die "unknown:badformat myip.\n"
            unless $myip =~ /^(\d{1,3}\.){3}\d{1,3}$/;

        my $resolver = new Net::DNS::Resolver;
        $resolver-&gt;nameservers("127.0.0.1");

        my @fqdn = split /,/, $hostname;

        die "numhosth:too many hostname.\n"
            if 4 &lt; scalar @fqdn;

        foreach my $fqdn (@fqdn) {

        # ZONE name resolv stage

            defined $fqdn and $fqdn =~ s{(^\s+|\s+$)}{}g;

            die "nohost:badformat hostname.\n"
                unless defined $fqdn;

            die "badfqdn:badformat hostname.\n"
                unless $fqdn =~ /^([\w\-]+\.)*[\w\-]+$/;

            my $zone = "";
            my $ns = "";
            my(@stack, @ns);
            foreach my $dotted (reverse(split /\./, $fqdn)) {
                $zone = $dotted.".".$zone;
                unshift @stack, $zone;
            }

            foreach my $test (@stack) {
                my $query = $resolver-&gt;query($test, "NS");
                if ($query) {
                    foreach my $rr (grep {$_-&gt;type eq "NS"} $query-&gt;answer) {
                        push @ns, $rr-&gt;nsdname;
                    }
                    $zone = $test;
                    syslog("info", "ZONE=".$zone." NS=".join(",", @ns));
                    last;
                }
            }
            die "dnserr:Undefined query nameservers.\n"
                unless @ns;

            $resolver-&gt;nameservers(@ns);

        # Update stage

            my $update = new Net::DNS::Update($zone);
            $update-&gt;push(update =&gt; rr_del($fqdn.". IN A"));
            $update-&gt;push(update =&gt; rr_add($fqdn.". 3600 IN A ".$myip));

            my $reply = $resolver-&gt;send($update);

            die "dnserr:reply resolver ".$resolver-&gt;errorstring."\n"
                unless $reply;

            die "dnserr:reply ".$reply-&gt;header-&gt;rcode."\n"
                unless $reply-&gt;header-&gt;rcode eq "NOERROR";
        }

        $r-&gt;print("good " . $myip . "\n");
        syslog("info", "update success.");
    };
    if ($@) {
        chomp $@;
        syslog("info", "ERROR: " . $@);

        my $result = (split /:/, $@)[0];
        $r-&gt;print($result."\n");
    }

    return OK;
}
1;  
__END__  
</code></pre>

<p>コード中の<code>$r</code>はnginxから渡されるリファレンスオブジェクトで、mod_perlのそれと似た表現になっている。<code>$r-&gt;args</code>に送信されたクエリが入っているのでこれをパースして<code>hostname</code>と<code>myip</code>を受け取っているが、<code>myip</code>がなければ<code>$r-&gt;remote_addr</code>を代わりに使うよう<sup id="fnref:2"><a href="http://multix.jp/create-compatible-dyndns-service/#fn:2" rel="footnote">2</a></sup>にもしている。その他のパラメータについてはこのコードでは対応していない。なお処理過程のログに付いては（perlの）<code>Sys::Syslog</code>モジュールを用いてsyslogのdaemonファシリティに送信するようにしている。</p>

<p>DNS Aレコード処理は（perlの）<code>Net::DNS</code>モジュールで行う。まず<code>localhost</code>ネームサーバにFQDNを問い合わせてNSレコード=当該ネームサーバIPを取得し（resolvステージ）そこに対して既存DNS Aレコードの削除および新規DNS Aレコードの登録（updateステージ）を行う。ここでは処理を端折っているが、本格的な汎用サービス目的で実装するなら、updateステージではアクセス認証キーの処理を追加する必要があるだろう。<sup id="fnref:3"><a href="http://multix.jp/create-compatible-dyndns-service/#fn:3" rel="footnote">3</a></sup></p>

<hr>

<h4 id="nginxconf">nginx.conf側の記述</h4>

<p>nginx.confから要点だけを抜き書きすると次のようになる。</p>

<pre><code class="language-brush:plain">http {  
    perl_modules  /etc/nginx/lib;
    perl_require  dyndns.pm;

    server {
        location /nic/update {
            auth_basic            "closed site";
            auth_basic_user_file  /etc/nginx/_htpasswd;
            perl                  dyndns::update_handler;
        }
    }
}
</code></pre>

<p>まずグローバル設定内で<code>perl_modules</code>と<code>perl_require</code>ディレクティブを用いてperlコードをnginx本体にロードする。プロセス起動時にメモリ上に読み込まれるためコードを書き換えた場合は逐一nginxをreloadする必要がある。ここで読み込まれたperlコードのpackage名前空間名とハンドラ関数名をlocaltionブロック内で指定すると、URL（<code>/nic/update</code>）とperlコードとを紐付けることができる。またこの時BASIC認証を行うので、認証用パスワードファイルを<code>auth_basic_user_file</code>ディレクティブで指定する。</p>

<hr>

<h4 id="namedconf">named.conf側の設定</h4>

<p>named.confの設定では、FQDNが属すZONEを有しており、かつ上記のリクエストレコード送信元IPがallow-updateディレクティブで指定されていればよい。</p>

<pre><code class="language-brush:plain">zone "example.org" IN {  
    type master;
    :
    allow-update {
        127.0.0.1;
        192.168.0.0/24;
        key host1;
    }
}
</code></pre>

<hr>

<h4 id="perlcgi">参考：Perl-CGI版</h4>

<p>nginx版の前に作成・使用していたPerl-CGIバージョンを以下に上げておく。こちらは単に<code>nsupdate</code>コマンドを叩くだけのシンプルなものだ。<sup id="fnref:4"><a href="http://multix.jp/create-compatible-dyndns-service/#fn:4" rel="footnote">4</a></sup></p>

<pre><code class="language-brush:perl collapse:true gutter:true title:/var/www/html/nic/update">#!/usr/bin/perl
use strict;  
use CGI;  
use Sys::Syslog;  
use 5.010;

my($q, $hostname);  
my $myip = '0.0.0.0';  
eval {  
    openlog('ddns_update', 'cons,pid', 'daemon');

    $q = new CGI;
    $hostname = $q-&gt;param('hostname');
    $myip     = $q-&gt;param('myip') || $ENV{HTTP_X_FORWARDED_FOR} || $ENV{REMOTE_ADDR};
#    $system   = $q-&gt;param('system');
#    $wildcard = $q-&gt;param('wildcard');
#    $offline  = $q-&gt;param('offline');
#    $mx       = $q-&gt;param('mx');

    syslog('info', 'HOSTNAME='.$hostname.' MYIP='.$myip);

    unless ($hostname =~ /^([\w\-]+\.)*[\w\-]+$/) {
        die "nohost\n";
    }

    unless ($myip =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
        die "unknown\n";
    }

    open my $PIPE, '|-', '/usr/bin/nsupdate' or die "badcall nsupdate.";
    print $PIPE &lt;&lt;__EOF;
server 127.0.0.1  
update delete ${hostname}. IN A  
update add ${hostname}. 3600 IN A ${myip}  
send

__EOF  
    close $PIPE or die "break nsupdate.\n";

    print 'Content-type: text/plain', "\r\n\r\n";
    print 'good ', $myip, "\n";
    syslog('info', 'Success.');
};
if ($@) {  
    print 'Content-type: text/plain', "\r\n\r\n";
    print 'nochg ', $myip, "\n";
    syslog('info', 'ERROR: '.$@);
}
1;  
__END__  
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>911は米国で言う110番のこと。 <a href="http://multix.jp/create-compatible-dyndns-service/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>通常の用途ではMYIPとremote_addrは常に同一だろう。ルータを多段に組んだ場合でもなければ両者が異なるケースはまずない。 <a href="http://multix.jp/create-compatible-dyndns-service/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>ここでの実装は簡易的なので、ネームサーバ側の設定でこのサービスサーバのIPに直接update許可を与えている。キー認証を使用する場合、named.confのほうではallow-updateディレクティブにアクセス許可するキーIDを追記する。 <a href="http://multix.jp/create-compatible-dyndns-service/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>この実装のままnginx/perlに持って行くとfork負荷が問題になったのでNet::DNS実装に置き換えた。 <a href="http://multix.jp/create-compatible-dyndns-service/#fnref:4" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[日本国内限定IPv4アクセス制限の仕込み方]]></title><description><![CDATA[<p>Webサービスとかやってると世界中と繋がっているのだなと思うことは多々ある。だがサービス内容によってはその範囲を狭めたいこともあるし、それ以前にお断りということもある。ではどうやって日本国内からのIPアクセスとそれ以外を区別しよう？</p>

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

<hr>

<h4 id="ipapnic">IPアドレスリストをAPNICから入手する</h4>

<p>世界中に分配されているIPv4アドレスは総元締めのIANAから、世界を分割支配する5つの団体（ARIN/APNIC/LACNIC/AfriNIC/RIPE NCC）に分配され、そこからさらに各国別の元締め（日本ならJPNIC）に再分配されている。このIP分配リストは各団体のFTPサイト等で公開されているので、これを入手して日本にアサインされたIPリストだけを抽出すればACL（アクセス制限リスト）の元を作ることが出来る。今回の場合はJPNICに割り当てられたIPリストを得るために、APNICのFTPサイトから更新リストを入手する。</p>

<p>この更新リスト<sup id="fnref:1"><a href="http://multix.jp/delegated-apnic-acl/#fn:1" rel="footnote">1</a></sup>はほぼ毎日アップデートされている。ファイルサイズは1.8MBほどで約4万行弱ある。また世界中からダウンロードに来ることもあって、日に何回もアクセスにゆくと回数制限に引っ掛かって数時間ブロックされるということもある。故に基本的には1日1回、cronを使用してダウンロードすることになる。</p>

<p>一方で毎日更新されているとはいえ、日本にアサインされたIPが日々こまめに変化しているというわけでもない。だいたい週に1〜2回程度、僅かな追加と削除<sup id="fnref:2"><a href="http://multix.jp/delegated-apnic-acl/#fn:2" rel="footnote">2</a></sup>が発生している程度だ。そこでダウンロードと共に前回のリストと比較して変化があったらメール通知を行い、さらにACLの更新処理を行うという仕組みを作ることにする。</p>

<p>なお以下の仕掛けはすべてrootユーザが行い、メインのワークエリアとしては<code>/etc/rc.d</code>を使用するものとする。</p>

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

### /etc/</code></pre>]]></description><link>http://multix.jp/delegated-apnic-acl/</link><guid isPermaLink="false">0bd8198c-10c5-4c3d-af4d-0aed7c19e87c</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Wed, 13 May 2015 09:08:40 GMT</pubDate><content:encoded><![CDATA[<p>Webサービスとかやってると世界中と繋がっているのだなと思うことは多々ある。だがサービス内容によってはその範囲を狭めたいこともあるし、それ以前にお断りということもある。ではどうやって日本国内からのIPアクセスとそれ以外を区別しよう？</p>

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

<hr>

<h4 id="ipapnic">IPアドレスリストをAPNICから入手する</h4>

<p>世界中に分配されているIPv4アドレスは総元締めのIANAから、世界を分割支配する5つの団体（ARIN/APNIC/LACNIC/AfriNIC/RIPE NCC）に分配され、そこからさらに各国別の元締め（日本ならJPNIC）に再分配されている。このIP分配リストは各団体のFTPサイト等で公開されているので、これを入手して日本にアサインされたIPリストだけを抽出すればACL（アクセス制限リスト）の元を作ることが出来る。今回の場合はJPNICに割り当てられたIPリストを得るために、APNICのFTPサイトから更新リストを入手する。</p>

<p>この更新リスト<sup id="fnref:1"><a href="http://multix.jp/delegated-apnic-acl/#fn:1" rel="footnote">1</a></sup>はほぼ毎日アップデートされている。ファイルサイズは1.8MBほどで約4万行弱ある。また世界中からダウンロードに来ることもあって、日に何回もアクセスにゆくと回数制限に引っ掛かって数時間ブロックされるということもある。故に基本的には1日1回、cronを使用してダウンロードすることになる。</p>

<p>一方で毎日更新されているとはいえ、日本にアサインされたIPが日々こまめに変化しているというわけでもない。だいたい週に1〜2回程度、僅かな追加と削除<sup id="fnref:2"><a href="http://multix.jp/delegated-apnic-acl/#fn:2" rel="footnote">2</a></sup>が発生している程度だ。そこでダウンロードと共に前回のリストと比較して変化があったらメール通知を行い、さらにACLの更新処理を行うという仕組みを作ることにする。</p>

<p>なお以下の仕掛けはすべてrootユーザが行い、メインのワークエリアとしては<code>/etc/rc.d</code>を使用するものとする。</p>

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

### /etc/rc.d/apnic.list_update
###
### delegated-apnic-latest を取得して変化があれば
### メール通知およびrc.iptablesの再実行を行うスクリプト
###
### /etc/cron.daily からsymlinkを張るか、そこに配置すること
###

new=`mktemp`  
errors=`mktemp`  
DIR="/etc/rc.d"  
list="$DIR/delegated-apnic-latest"

test -f $list || touch $list  
wget -q -O - "http://ftp.apnic.net/stats/apnic/delegated-apnic-latest" &gt; $new 2&gt; $errors

if [ $? -eq 0 ]; then  
    sort_new=`mktemp`
    sort_old=`mktemp`
    diff_out=`mktemp`

    # 日本国内IPについてのみ比較する
    /bin/grep '^apnic|JP|ipv4|' $new &gt; $sort_new
    /bin/grep '^apnic|JP|ipv4|' $list &gt; $sort_old
    diff --ignore-matching-lines="^#" $sort_new $sort_old &gt; $diff_out
    if [ $? -ne 0 ]; then
        (
         echo '-------------------- old delegated-apnic-latest --------------------'
         grep -P '^\d' $list
         echo
         echo '-------------------- new delegated-apnic-latest --------------------'
         grep -P '^\d' $new
         echo '---------------------- difference ----------------------'
         cat $diff_out

         # メールは rootにだす（ /etc/aliases に従う）
        ) | mail -s "delegated-apnic-latest updated $(hostname)" root
        cp -f $new $list

        # iptables の -s に渡せる形式に変換
        $DIR/jponly.pl &lt; $list &gt; $DIR/jponly.list

#        # rc.iptables を再実行する
#        \time $DIR/rc.iptables

         # iptables-save/restoreで更新する
         $DIR/update_mangle.pl
    fi
    rm -f $sort_new $sort_old $diff_out
else  
    cat $errors | mail -s "delegated-apnic-latest update check error $(hostname)" root
fi  
rm -f $new $errors  
</code></pre>

<p>このスクリプトは1日1回、APNICにお伺いを立てて（日本に割り当てられた）IPv4アドレスリストが変化していたら<code>/etc/rc.d/delegated-apnic-latest</code>ファイルを更新する。通知メールはrootに向けて投げるため、<code>/etc/aliases</code>に実際の通知先メールアドレスを書いておこう。</p>

<p>取得したリストの中身は概ね以下のような行の羅列である。</p>

<pre><code class="language-brush:plain">apnic|CN|ipv4|223.214.0.0|131072|20100803|allocated  
apnic|JP|ipv4|223.216.0.0|262144|20100712|allocated  
apnic|CN|ipv4|223.220.0.0|131072|20100723|allocated  
apnic|KR|ipv4|223.222.0.0|65536|20100721|allocated  
apnic|JP|ipv4|223.223.0.0|32768|20100803|allocated  
apnic|IN|ipv4|223.223.128.0|8192|20100809|allocated  
</code></pre>

<p>バーチカルラインで区切られた各カラムは次の意味を持つ。</p>

<ul>
<li>IP管理組織</li>
<li>2文字の国コード</li>
<li>IP種別</li>
<li>割り当てブロック先頭のIPアドレス</li>
<li>割り当てブロックに含まれるIP数（2の冪乗に一致）</li>
<li>割り当てられた日付</li>
<li>現在のステータス</li>
</ul>

<p>注意するのは5カラム目のIP数がIPマスクでもCIDRでもないという点で、ACLとして使用するにはこれをCIDRに変換する必要がある。これを行っているのが43行目の<code>jponly.pl</code>だ。</p>

<hr>

<h4 id="ipcidr">日本国内IPのCIDRを抽出する</h4>

<p>このフィルタスクリプトは、国コードがJPかつステータスがallocatedの行を抽出し、そのIP数からCIDRを計算して出力する。</p>

<pre><code class="language-brush:perl gutter:true title:/etc/rc.d/jponly.pl">#!/usr/bin/perl
use strict;  
use utf8;

###
### /etc/rc.d/jponly.pl
###
### delegated-apnic-latest から日本国内IPのCIDRを抽出する
###

my %CIDR;  
for my $bit ( 0..32 ) {  
    my $len = 2 ** ( 32 - $bit );
    $CIDR{$len} = $bit;
}
while ( &lt;&gt; ) {  
    chomp;
    if ( m{^apnic\|JP\|ipv4\|(\d+(?:\.\d+){3})\|(\d+)\|\d+\|allocated} ) {
        my $addr = $1;
        my $cidr = $CIDR{ $2 || 32 };
        printf "%s/%u\n", $addr, $cidr;
    }
}
exit 0;  
__END__  
</code></pre>

<p>これを通して生成した<code>/etc/rc.d/jponly.list</code>は1行1ブロックのIP/CIDRリストとなる。</p>

<pre><code class="language-brush:plain">223.216.0.0/14  
223.223.0.0/17  
</code></pre>

<hr>

<h4 id="iptablesacl">iptablesでのACL</h4>

<p>これを用いて例えばiptablesのmangleテーブル・Jponlyチェイン（フィルタ）に書き込むには次のようにする。</p>

<pre><code class="language-brush:bash futter:true">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

# mangleテーブルでフィルタを作る
iptables -t mangle -N Jponly  
cat $JPONLY | while read; do  
  iptables -t mangle -A Jponly -s $REPLY -j MARK --set-mark 2/2
done

# 上記のフィルタをINPUT/FORWARDに適用してパケットにmarkビットを立てる
iptables -t mangle -A INPUT -j Jponly  
iptables -t mangle -A FORWARD -j Jponly

# 他のチェインの中で、markビットが立っているならACCEPT、なければDROPする例
iptables -A Accept -m mark --mark 2/2 -j ACCEPT  
iptables -A Accept -j DROP  
</code></pre>

<p>mangleとmarkビットマスクを使うことで、このJponlyフィルタを容易に再利用できるようにしている。この他にホワイトリストチェイン・ブラックリストチェインを併用する場合も、markビットマスクを利用することで柔軟なACLフィルタを作り出すことが出来る。</p>

<hr>

<h4 id="">フィルタチェインの高速更新</h4>

<p><code>apnic.list_update</code>の49行目で呼び出している<code>update_mangle.pl</code>は、iptablesで設定した既存のJponlyチェインをアップデートするスクリプトだ。これはiptables-save・iptables-restoreコマンドを使用して高速に更新処理を行う。初回こそ前項のようにiptablesコマンドで丁寧に設定する必要があるが、これは数秒〜数十秒の処理時間が掛かってしまう。そこで2回目以降は該当チェインだけを書き換えることでダウンタイムを実用上問題ない時間（ミリ秒オーダー）まで切り詰める。<sup id="fnref:3"><a href="http://multix.jp/delegated-apnic-acl/#fn:3" rel="footnote">3</a></sup></p>

<pre><code class="language-brush:perl gutter:true title:/etc/rc.d/update_mangle.pl">#!/usr/bin/perl
use strict;

my $JPONLY  = '/etc/rc.d/jponly.list';  
my $UPDATE  = '/etc/rc.d/iptables.bak';  
my $SAVE    = '/sbin/iptables-save -c';  
my @RESTORE = ('/sbin/iptables-restore', '-c');

my $SEARCH   = qr{-A Jponly -s};  
my $TEMPLATE = "[0:0] -A Jponly -s %s -j MARK --set-xmark 0x2/0x2 \n";

my $BEFORE;  
my $AFTER = [];  
my @INSERT;

my $PIPE;  
open $PIPE, '&lt;', $JPONLY or die;  
while (&lt;$PIPE&gt;) {  
    chomp;
    push @INSERT, sprintf $TEMPLATE, $_;
}

close $PIPE;  
open $PIPE, '-|', $SAVE or die;  
while (&lt;$PIPE&gt;) {  
    unless ($BEFORE) {
        if ($_ =~ $SEARCH) {
            $BEFORE = $AFTER;
            push @$BEFORE, @INSERT;
            next;
        }
    }
    elsif ($_ =~ $SEARCH) {
        next;
    }
    push @$AFTER, $_;
}
close $PIPE;

open $PIPE, '&gt;', $UPDATE or die;  
print $PIPE @$BEFORE;  
print $PIPE @$AFTER;  
close $PIPE;

system @RESTORE, $UPDATE;

1;  
__END__  
</code></pre>

<hr>

<h4 id="apacheacl">ApacheでのACL</h4>

<p>前述の<code>jponly.list</code>からApache用のACLを作り出すには次のようにする。</p>

<pre><code class="language-brush:bash">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

( cat $JPONLY | while read; do
  echo "Allow from $REPLY"
done ) &gt; /etc/httpd/conf/jponly.conf  
</code></pre>

<p>こうして生成したACLを、必要な場所でIncludeディレクティブを使用して読み込む。なおリストが更新されたらhttpdプロセスをreload(あるいはgraceful)しなければ実際には反映されない。<sup id="fnref:4"><a href="http://multix.jp/delegated-apnic-acl/#fn:4" rel="footnote">4</a></sup></p>

<pre><code class="language-brush:plain">Order Allow,Deny  
Allow from localhost  
Allow from 192.168.0.0/16  
Include conf/jponly.conf  
Deny from All  
</code></pre>

<hr>

<h4 id="nginxacl">nginxでのACL</h4>

<p>nginxでACLを掛けるには、geoモジュールを使用する。まずApacheの場合と同様にInclude可能なファイルを作成する。</p>

<pre><code class="language-brush:bash">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

( cat $JPONLY | while read; do
  echo "$REPLY 2;"
done ) &gt; /etc/nginx/jponly.conf  
</code></pre>

<p>これをhttpブロックのgeoモジュールで読み込み、$acl変数に反映させる。これをlocationブロック内で参照して条件分岐する。なおリスト更新後はnginxプロセスをreloadしなければ実際には反映されない。</p>

<pre><code class="language-brush:plain">http {  
    geo $acl {
        default        0;
        127.0.0.1      1;
        192.168.0.0/16 1;
        include jponly.conf;
    }
    server {
        location /jponly/ {
            root /var/www/html_jponly;
            if ($acl = 0) {
                return 403;
            }
        }
        location / {
            root /var/www/html_public;
        }
    }
}
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>このリストにはIPv6も記載されているし、日本以外のアジア諸国もすべて入っている。特定の国のIPだけ弾きたい、といったニーズならばそれに応じたフィルタを書けば同じように対応できる。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>あるときA国で使われていたIPが、気がついたら何時の間にかB国に再割当てされていたということは割とある。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>差分更新処理は端折っているため、実行すると各IPブロックのカウンターはすべてゼロ初期化される。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>GeoIPモジュール <a href="http://dev.maxmind.com/geoip/">http://dev.maxmind.com/geoip/</a> を使ったほうが手間は少ないし処理も高速だし設定もスッキリするが、DBファイルをアップデートしたらサービスリロードが必要になるのは変わらない。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:4" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Node.jsのサービス実行]]></title><description><![CDATA[<p>このブログはNode.jsでGhost BlogをCentOSやWindows上で動かしているが、開発環境はともかく本番用やステージング用はサーバ起動時に一緒にデーモン起動していて欲しい。ところがコレには色々と考えるべきところがあるのだ。</p>

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

<hr>

<h4 id="">デーモンプロセス化を助けるツール</h4>

<p>Node.jsのデーモン化実装にはいくつかあるが、以下が代表的なものだろう。</p>

<ul>
<li>supervisor</li>
<li>forever</li>
<li>pm2 </li>
</ul>

<p>いずれも<code>npm install -g</code>で導入できる。おまけにLinuxでもWindowsでもそれなりに動く。それぞれに特徴がありセールスポイントがあり使い勝手も異なるが、一番リッチなのがpm2で、一番シンプルなのがforeverだろうか。</p>

<p>だがこれらはどういうワケか<strong>suid/sugidをサポートしてない</strong>のである。いやNode.js自体にはPOSIX準拠の<code>process.setuid</code>と<code>process.setgid</code>が一応実装されているのだがそれをこれらで使っているのを見たことがない<sup id="fnref:1"><a href="http://multix.jp/daemon-nodejs/#fn:1" rel="footnote">1</a></sup>。なので上記のこれらを普通にroot権限で起動するとそのままroot権限プロセスで動き続けることになり、実行権限を落として運用したい場合は別の工夫が必要になる。</p>

<hr>

<h4 id="">手っ取り早くデーモンにしてしまう</h4>

<p>WindowsにはWindowsの事情があるから置いておくとして、まずCentOS6ではどうするか。6のinitプロセスにはupstartとsysvinitの2種類<sup id="fnref:2"><a href="http://multix.jp/daemon-nodejs/#fn:2" rel="footnote">2</a></sup>が使われており、特段の事情がなければ使い慣れた後者に従うのが普通だろう。とりあえずroot権限でGhost（に限らずNode.jsで実装した各種サービス）を手っ取り早く動かしてしまうには以下のようにする。なおここではforeverを使用する。また一連の作業はすべてrootユーザで行う。</p>

<p>まずyumでredhat-lsb-coreを導入する。</p>]]></description><link>http://multix.jp/daemon-nodejs/</link><guid isPermaLink="false">2207ab43-c467-411c-b83f-c73635252b8f</guid><category><![CDATA[Linux]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Ghost Blog]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Thu, 07 May 2015 08:17:54 GMT</pubDate><content:encoded><![CDATA[<p>このブログはNode.jsでGhost BlogをCentOSやWindows上で動かしているが、開発環境はともかく本番用やステージング用はサーバ起動時に一緒にデーモン起動していて欲しい。ところがコレには色々と考えるべきところがあるのだ。</p>

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

<hr>

<h4 id="">デーモンプロセス化を助けるツール</h4>

<p>Node.jsのデーモン化実装にはいくつかあるが、以下が代表的なものだろう。</p>

<ul>
<li>supervisor</li>
<li>forever</li>
<li>pm2 </li>
</ul>

<p>いずれも<code>npm install -g</code>で導入できる。おまけにLinuxでもWindowsでもそれなりに動く。それぞれに特徴がありセールスポイントがあり使い勝手も異なるが、一番リッチなのがpm2で、一番シンプルなのがforeverだろうか。</p>

<p>だがこれらはどういうワケか<strong>suid/sugidをサポートしてない</strong>のである。いやNode.js自体にはPOSIX準拠の<code>process.setuid</code>と<code>process.setgid</code>が一応実装されているのだがそれをこれらで使っているのを見たことがない<sup id="fnref:1"><a href="http://multix.jp/daemon-nodejs/#fn:1" rel="footnote">1</a></sup>。なので上記のこれらを普通にroot権限で起動するとそのままroot権限プロセスで動き続けることになり、実行権限を落として運用したい場合は別の工夫が必要になる。</p>

<hr>

<h4 id="">手っ取り早くデーモンにしてしまう</h4>

<p>WindowsにはWindowsの事情があるから置いておくとして、まずCentOS6ではどうするか。6のinitプロセスにはupstartとsysvinitの2種類<sup id="fnref:2"><a href="http://multix.jp/daemon-nodejs/#fn:2" rel="footnote">2</a></sup>が使われており、特段の事情がなければ使い慣れた後者に従うのが普通だろう。とりあえずroot権限でGhost（に限らずNode.jsで実装した各種サービス）を手っ取り早く動かしてしまうには以下のようにする。なおここではforeverを使用する。また一連の作業はすべてrootユーザで行う。</p>

<p>まずyumでredhat-lsb-coreを導入する。そしてforeverとinitd-foreverをnpmで導入する。</p>

<pre><code class="language-brush:plain">$ sudo -s
# yum install redhat-lsb-core
# npm install forever -g
# npm install initd-forever -g

# initd-forever --help

  Usage: initd-forever [options]

  Options:

    -h, --help             output usage information
    -V, --version          output the version number
    -a, --app [path]       Path to node.js main file
    -c, --command [value]  Command to execute on main file
    -e, --env [value]      Export NODE_ENV with value
    -l, --logfile [path]   Logs the daemon output to LOGFILE
    -n, --name [value]     Application name
    -p, --pidfile [path]   The pid file
    -m, --monit [value]    Generate the monit script file with the listen port number
    -f, --forever [value]  The location of forever
</code></pre>

<p>次いでGhostディレクトリに移動してinitd-foreverでinitdスクリプトを生成する。最低限出力ファイル名＝サービス名を決定する<strong>-n</strong>指定は必要だ。</p>

<pre><code class="language-brush:plain"># cd ~user/ghost
# initd-forever -n ghost 
Script daemon file saved to ghost  
</code></pre>

<p>出来上がったファイルに実行権限を付与して<code>/etc/init.d</code>へ移動し、chkconfigで使用可能にしてserviceで起動する。</p>

<pre><code class="language-brush:plain"># chmod +x ghost
# mv ghost /etc/init.d
# chkconfig ghost --add
# chkconfig ghost on
# chkconfig ghost --list
ghost              0:off   1:off   2:on    3:on    4:on    5:on    6:off  
# service ghost start
</code></pre>

<p>initd-foreverが生成したファイルは次のようなものだ。通常の用途ではまず修正するところはないだろう。</p>

<pre><code class="language-brush:bash gutter:true title:/etc/init.d/ghost">#!/bin/bash
### BEGIN INIT INFO
# Provides:          /home/user/ghost/core/index
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: forever running /home/user/ghost/core/index
# Description:       /home/user/ghost/core/index
### END INIT INFO
#
# initd a node app
# Based on a script posted by https://gist.github.com/jinze at https://gist.github.com/3748766
#

# Source function library.
. /lib/lsb/init-functions

pidFile="/var/run/ghost.pid"  
logFile="/var/run/ghost.log"

command="node"  
nodeApp="/home/user/ghost/core/index"  
foreverApp="forever"

start() {  
    echo "Starting $nodeApp"

    # Notice that we change the PATH because on reboot
    # the PATH does not include the path to node.
    # Launching forever with a full path
    # does not work unless we set the PATH.
    PATH=/usr/local/bin:$PATH
    export NODE_ENV=production
    #PORT=80
    $foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
    RETVAL=$?
}

restart() {  
    echo -n "Restarting $nodeApp"
    $foreverApp restart $nodeApp
    RETVAL=$?
}

stop() {  
    echo -n "Shutting down $nodeApp"
    $foreverApp stop $nodeApp
    RETVAL=$?
}

status() {  
    echo -n "Status $nodeApp"
    $foreverApp list
    RETVAL=$?
}

case "$1" in  
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status
        ;;
    restart)
        restart
        ;;
    *)
        echo "Usage: $0 {start|stop|status|restart}"
        exit 1
        ;;
esac  
</code></pre>

<hr>

<h4 id="">もう少し本格的に制御してみる</h4>

<p>実行するNode.jsサービスがひとつやふたつでしかもrootオンリーなら上記の作業を必要なだけ繰り返せば良い。だがGhost Blogの場合これはあまりよろしくない。というのも；</p>

<ul>
<li>Ghost は記事に対して Draft と Publish の2モードを持つが Staging モードは持っていない。つまり投稿記事を一般公開前にレンダリングチェックすることができない。このために結局本番用とステージング用とでは個別のサービスインスタンスを立ち上げて使い分けたいという需要がある。</li>
<li>Ghost はマルチユーザ（グループ）対応だが、ユーザ別にテーマを変更できるわけではなく全体でひとつのテーマに固定される。故にテーマやプラグインを開発するユースケースでもユーザ別インスタンスの需要がある。またこの作業は頻繁にインスタンスを再起動する必要が有るため、start/stopを行うのにいちいちrootへsuするのは現実的ではない。</li>
<li>Ghost は自身のローカルディレクトリ内にアップロードされた画像ファイルと、記事DBファイルを格納<sup id="fnref:3"><a href="http://multix.jp/daemon-nodejs/#fn:3" rel="footnote">3</a></sup>している。当然それらのファイル権限がそのままでは実効rootになってしまうから、非rootの開発ユーザがファイルメンテナンスをするうえで都合が悪い。</li>
</ul>

<p>これらの諸々を助けるためにforeverdというinitdファイルを作成した。これは /etc/forver.d ディレクトリに配置した設定ファイルの数だけNode.jsのインスタンスを起動する。</p>

<pre><code class="language-brush:bash gutter:true title:/etc/forever.d/ghost-stg.conf">NODE_DIR=/home/user/ghost  
NODE_ENV=staging  
NODE_APP=index.js  
NODE_USER=user  
OPTS="--silent"  
</code></pre>

<pre><code class="language-brush:bash gutter:true title:/etc/forever.d/ghost-prd.conf">NODE_DIR=/home/user/ghost  
NODE_ENV=production  
NODE_APP=index.js  
NODE_USER=user  
OPTS="--silent"  
</code></pre>

<pre><code class="language-brush:bash gutter:true title:/etc/init.d/foreverd">#!/bin/sh
#
# chkconfig: 2345 88 14
# description: Description of the Service
#
# Below is the source function library, leave it be
. /etc/init.d/functions
 
# result of whereis node_modules
export NODE_PATH=/usr/lib/node_modules  
 
PATH=/sbin:/bin:/usr/sbin:/usr/bin  
 
fordeamon(){  
    for conf in /etc/forever.d/*.conf; do
        name=$(basename $conf .conf)
        if [ -n "$2" ]; then
            if [ "$name" != "$2" ]; then
                continue
            fi
        fi
        source $conf
        : ${NODE_USER:=root}
        : ${NODE_APP:=index.js}
        : ${NODE_DIR:=~}
        if [ $(whoami) != $NODE_USER ]; then
            su -c "$0 $1 $name" $NODE_USER
        else
            echo $"Process id is $name"
            case "$1" in  
                start)
                    NODE_ENV=$NODE_ENV \
                        forever $OPTS \
                        --sourceDir="$NODE_DIR" \
                        --workingDir="$NODE_DIR" \
                        --minUptime=1000 \
                        --spinSleepTime=1000 \
                        --uid=$name \
                        -a \
                        start $NODE_APP
                    ;;
                stop)
                    NODE_ENV=$NODE_ENV \
                        forever stop $name
                    ;;
                restart)
                    NODE_ENV=$NODE_ENV \
                        forever restart $name
                    ;;
                list)
                    NODE_ENV=$NODE_ENV \
                        forever list | grep /$name.log
                    ;;
            esac
        fi
    done
}
 
case "$1" in  
    start)
        echo "Start service forever"
        fordeamon start $2
        ;;
    stop)
        echo "Stop service forever"
        fordeamon stop $2
        ;;
    restart)
        echo "Restart service forever"
        fordeamon restart $2
        ;;
    status)
        fordeamon list $2
        ;;
    list)
        fordeamon list $2
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status|list} [name]"
        exit 1
        ;;
esac  
</code></pre>

<p>これを実行権限を付与してchkconfigでサービス登録すると、サーバ起動時に /etc/forver.d/*.conf に従ってforeverインスタンスを起動するが、その後は一般ユーザ側で普通に（serviceコマンドを通さずに）foreverコマンドでlist/stop/restart<sup id="fnref:4"><a href="http://multix.jp/daemon-nodejs/#fn:4" rel="footnote">4</a></sup>することができるようになる。またroot側からも<code>service foreverd COMMAND NAME</code>でインスタンス個別にユーザ権限でプロセス操作ができる。</p>

<pre><code class="language-brush:plain title:root側の例"># service foreverd start
# service foreverd restart NAME
# service foreverd stop NAME
</code></pre>

<pre><code class="language-brush:plain title:一般ユーザ側の例">$ forever list
$ forever restart NAME
$ forever stop NAME
$ /etc/init.d/foreverd start NAME
$ forever restartall
</code></pre>

<p>Switch Userは27行目のsuコマンドが行っているが、これは自分自身（/etc/init.d/foreverd）をsuidして再帰的に呼び出すようにした。正攻法ならsudoコマンドでforeverを呼びたくなるところだが、エスケープを含むコマンドラインの組み立てが煩雑になるのを避けたいのと、CnetOS6の/etc/sudoersのデフォルト設定では非TTYでのsudo実行を禁止しており、そのままでは rcプロセス（当然非TTY）で動作しない<sup id="fnref:5"><a href="http://multix.jp/daemon-nodejs/#fn:5" rel="footnote">5</a></sup>という事情による。</p>

<p>なお当然だが well-known port を掴むようなインスタンスはrootユーザで起動しなければ意味が無い。また個々のインスタンスが掴むポート番号を重複しないように調整するのもroot管理者の仕事である。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>比較的新しい機能なので古いNode.jsに配慮するとまだ機能統合できないということか。ゆえにデーモンプロセスツールではなく個々のアプリケーションの中でやってくれという主張らしい。 <a href="http://multix.jp/daemon-nodejs/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>CentOS7になるとsysvinitが非推奨となってsystemctlが標準となる。6/7両方で修正なく動くようにしたいならupstartで実装するのも手だ。 <a href="http://multix.jp/daemon-nodejs/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>DBについてはPostgresql等外部へ逃すことはできるがアップロード画像ファイルはDBに入らないためどうにもならない。ゆえにsupervisorやpm2のファイル更新監視モードは無限再起動に陥るため使うことができず、かえってそれらを使うメリットがない。本項でforeverを主に扱っているのはこういう事情による。 <a href="http://multix.jp/daemon-nodejs/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>ユーザ側でのstartは、root実行時と同じ起動パラメータを指定するようにしないと当然同じ動作にならない。故にサービス起動時はサンプルの4行目のように /etc/init.d/foreverdを直接実行する。 <a href="http://multix.jp/daemon-nodejs/#fnref:4" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:5"><p>visudoで <em>Defaults requiretty</em> を <em>Defaults:system_user !requiretty</em> に修正すれば非TTYプロセス中でもsudoできるようになる。だが今回の場合はrootが非rootプロセスを起動したいのであり、非rootにはrootプロセスを起動させたくないのだから suのほうが理にかなう。 <a href="http://multix.jp/daemon-nodejs/#fnref:5" 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[本番用のサーバ証明書を取得する]]></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><item><title><![CDATA[yumコマンド例文集]]></title><description><![CDATA[<p>yumコマンド関係の備忘録。</p>

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

<hr>

<h4 id="">ソフトウェアのインストール</h4>

<pre><code class="language-brush:plain">yum install PACKAGENAMES  
</code></pre>

<hr>

<h4 id="">インストール済パッケージの一覧</h4>

<pre><code class="language-brush:plain">yum list installed  
</code></pre>

<hr>

<h4 id="">レポジトリ管理外も含むインストール済パッケージの一覧</h4>

<pre><code class="language-brush:plain">rpm -qa  
</code></pre>

<hr>

<h4 id="">アップデート可能なパッケージの一覧</h4>

<pre><code class="language-brush:plain">yum list updates  
</code></pre>

<hr>

<h6 id="">ソフトウェアのアップデート</h6>

<p>引数を指定しなければ<code>yum list updates</code>で示されるパッケージすべてをアップデートしようとする。むしろ引数を指定することのほうが稀だろう。</p>

<pre><code class="language-brush:plain">yum update [PACKAGENAMES]  
</code></pre>

<hr>

<h4 id="">キャッシュ/ワークファイルのクリア</h4>

<pre><code class="language-brush:plain">yum clean all  
</code></pre>

<hr>

<h4 id="">ソフトウェア削除</h4>

<p>そのパッケージに依存している他のパッケージがある場合それも共に列挙される。<sup id="fnref:1"><a href="http://multix.jp/yum-command/#fn:1" rel="footnote">1</a></sup></p>

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

<hr>

<h4 id="rpm">RPMファイルのインストール</h4>

<p>リポジトリを検索せずローカルダウンロード済のパッケージをインストールする場合に使用する。直接URLを指定してもよい。必要ならリポジトリから依存関係解決に必要なパッケージの追加導入やGPGKEYのインポートまでも面倒を見てくれる。かたや依存関係が解決できず、システムに適合しないRPMファイルについては何もしない。</p>

<pre><code class="language-brush:plain">yum localinstall PACKAGE.RPM  
</code></pre>

<hr>

<h4 id="yumrpm">yumリポジトリ管理外のRPMファイルのインストール</h4>

<p><code>yum localinstall</code></p>]]></description><link>http://multix.jp/yum-command/</link><guid isPermaLink="false">a683c4d5-dc7b-472b-a5b3-b9c14b6a7373</guid><category><![CDATA[ユーティリティ]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Wed, 18 Feb 2015 06:41:00 GMT</pubDate><content:encoded><![CDATA[<p>yumコマンド関係の備忘録。</p>

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

<hr>

<h4 id="">ソフトウェアのインストール</h4>

<pre><code class="language-brush:plain">yum install PACKAGENAMES  
</code></pre>

<hr>

<h4 id="">インストール済パッケージの一覧</h4>

<pre><code class="language-brush:plain">yum list installed  
</code></pre>

<hr>

<h4 id="">レポジトリ管理外も含むインストール済パッケージの一覧</h4>

<pre><code class="language-brush:plain">rpm -qa  
</code></pre>

<hr>

<h4 id="">アップデート可能なパッケージの一覧</h4>

<pre><code class="language-brush:plain">yum list updates  
</code></pre>

<hr>

<h6 id="">ソフトウェアのアップデート</h6>

<p>引数を指定しなければ<code>yum list updates</code>で示されるパッケージすべてをアップデートしようとする。むしろ引数を指定することのほうが稀だろう。</p>

<pre><code class="language-brush:plain">yum update [PACKAGENAMES]  
</code></pre>

<hr>

<h4 id="">キャッシュ/ワークファイルのクリア</h4>

<pre><code class="language-brush:plain">yum clean all  
</code></pre>

<hr>

<h4 id="">ソフトウェア削除</h4>

<p>そのパッケージに依存している他のパッケージがある場合それも共に列挙される。<sup id="fnref:1"><a href="http://multix.jp/yum-command/#fn:1" rel="footnote">1</a></sup></p>

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

<hr>

<h4 id="rpm">RPMファイルのインストール</h4>

<p>リポジトリを検索せずローカルダウンロード済のパッケージをインストールする場合に使用する。直接URLを指定してもよい。必要ならリポジトリから依存関係解決に必要なパッケージの追加導入やGPGKEYのインポートまでも面倒を見てくれる。かたや依存関係が解決できず、システムに適合しないRPMファイルについては何もしない。</p>

<pre><code class="language-brush:plain">yum localinstall PACKAGE.RPM  
</code></pre>

<hr>

<h4 id="yumrpm">yumリポジトリ管理外のRPMファイルのインストール</h4>

<p><code>yum localinstall</code>と異なり依存関係の解決は行われないし、GPGKEYが必要な場合でも別にインポートしなければならない。依存性の欠如はどんなファイルが不足しているかだけを示し、リポジトリを参照する訳ではないのでパッケージ名までは列挙しない。</p>

<pre><code class="language-brush:plain">rpm --import GPGKEY-FILE_OR_URL  
rpm -ivh PACKAGE.RPM-FILE_OR_URL  
</code></pre>

<hr>

<h4 id="">ある特定ファイルを含むパッケージ名の検索</h4>

<p>FILENAMEはグロブで指定することができる。既にインストール済の場合はinstalled、そうでなければ見つかったリポジトリ名も示す。</p>

<pre><code class="language-brush:plain">yum provides FILENAME  
yum provides */httpd --disablerepo=base  
</code></pre>

<hr>

<h4 id="">パッケージに含まれるファイルとディレクトリの一覧</h4>

<p><code>yum-utils</code>パッケージに含まれる<code>repoquery</code>コマンドはパッケージに含まれるファイルやディレクトリの一覧を、パッケージをダウンロードせずに取得できる。</p>

<pre><code class="language-brush:plain">repoquery --list PACKAGENAME  
</code></pre>

<hr>

<h4 id="chroot">クリーンなchroot環境を作る</h4>

<p>最初のyum実行ではrpm環境がまだchroot内にないため<code>--releasever</code>を明示することでエラーを避ける。Coreグループインストール完了後以降ではその必要はない。最後に<code>clean all</code>でyumのワークファイルを消して容量を節約する。</p>

<pre><code class="language-brush:plain">rm -fr /tmp/chroot  
yum --installroot=/tmp/chroot --releasever=6.6 groupinstall Core -y  
yum --installroot=/tmp/chroot update -y  
:
yum --installroot=/tmp/chroot clean all

chroot /tmp/chroot  
</code></pre>

<hr>

<h4 id="centos6">古いカーネルを削除する(CentOS6)</h4>

<p>CentOS5まではバージョン番号も省略せずに指定しないと現在実行中のカーネルまでもが削除できてしまった<sup id="fnref:2"><a href="http://multix.jp/yum-command/#fn:2" rel="footnote">2</a></sup>が、CentOS6では正しくプロテクトされるようになったので、逆に<code>kernel</code>とだけ指定することで現在実行中のカーネル以外を一括削除<sup id="fnref:3"><a href="http://multix.jp/yum-command/#fn:3" rel="footnote">3</a></sup>できるようになった。</p>

<pre><code>yum remove kernel  
</code></pre>

<p>カーネルを複数世代保持しておきたい場合は<code>yum</code>ではなく<code>package-cleanup</code>コマンドを使用するとよい。</p>

<pre><code class="language-brush:plain">package-cleanup --oldkernels --count=2  
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>例えばpostfixをインストールする前にsendmailを、rsyslogをインストールする前にsyslogを削除しようとするとシステム崩壊するほど数のパッケージが同時に削除されようとする。そんなこともあり誤操作を避けるためremove時は<strong>-y</strong>オプションを付けるべきではない。 <a href="http://multix.jp/yum-command/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>もちろん気付かずにそのまま再起動してしまうと素晴らしい顛末が待っている。 <a href="http://multix.jp/yum-command/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>このためupdate直後にremoveするとupdate前にロールバックする動作になる。ゆえに意図通りにremoveするには再起動＆正常起動確認の後で行わなければならない。 <a href="http://multix.jp/yum-command/#fnref:3" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[CentOSの追加リポジトリ]]></title><description><![CDATA[<p>標準以外でよく使うリポジトリのメモ</p>

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

<hr>

<h4 id="epel">EPEL</h4>

<p><a href="http://fedoraproject.org/wiki/EPEL">Extra Packages for Enterprise Linux</a></p>

<p>FedoraリポジトリをソースにエンタープライズLinux向けに提供されているアドオンパッケージ群。RHELと互換性のあるスピンオフディストリビューション（CentOSやScientific Linux）で利用可能。Fedoraプロジェクトによって提供されている。Fedoraリポジトリより保守的なため提供されていないパッケージもある。</p>

<ul>
<li><a href="http://download.fedoraproject.org/pub/epel/7/x86_64/repoview/epel-release.html">EL7 epel-release (RPM Download page)</a></li>
<li><a href="http://download.fedoraproject.org/pub/epel/6/i386/repoview/epel-release.html">EL6 epel-release (RPM Download page)</a></li>
<li><a href="http://download.fedoraproject.org/pub/epel/5/i386/repoview/epel-release.html">EL5 epel-release (RPM Download page)</a></li>
</ul>

<p>CentOSの場合は上記のパッケージがextrasリポジトリに含まれているため以下を実行するだけで利用開始可能となる。</p>

<pre><code class="language-brush:plain">yum install epel-release  
</code></pre>

<p>デフォルトでenablerepoであるから、必要ならdisableにしておこう。</p>

<pre><code class="language-brush:plain gutter:true">[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch  
#baseurl=http://download.fedoraproject.org/</code></pre>]]></description><link>http://multix.jp/centos-addon-repos/</link><guid isPermaLink="false">f56d73c3-8d09-4e41-9cb8-2ad6b97b15c5</guid><category><![CDATA[ユーティリティ]]></category><category><![CDATA[Linux]]></category><category><![CDATA[めもらんだむ]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Tue, 17 Feb 2015 09:23:00 GMT</pubDate><content:encoded><![CDATA[<p>標準以外でよく使うリポジトリのメモ</p>

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

<hr>

<h4 id="epel">EPEL</h4>

<p><a href="http://fedoraproject.org/wiki/EPEL">Extra Packages for Enterprise Linux</a></p>

<p>FedoraリポジトリをソースにエンタープライズLinux向けに提供されているアドオンパッケージ群。RHELと互換性のあるスピンオフディストリビューション（CentOSやScientific Linux）で利用可能。Fedoraプロジェクトによって提供されている。Fedoraリポジトリより保守的なため提供されていないパッケージもある。</p>

<ul>
<li><a href="http://download.fedoraproject.org/pub/epel/7/x86_64/repoview/epel-release.html">EL7 epel-release (RPM Download page)</a></li>
<li><a href="http://download.fedoraproject.org/pub/epel/6/i386/repoview/epel-release.html">EL6 epel-release (RPM Download page)</a></li>
<li><a href="http://download.fedoraproject.org/pub/epel/5/i386/repoview/epel-release.html">EL5 epel-release (RPM Download page)</a></li>
</ul>

<p>CentOSの場合は上記のパッケージがextrasリポジトリに含まれているため以下を実行するだけで利用開始可能となる。</p>

<pre><code class="language-brush:plain">yum install epel-release  
</code></pre>

<p>デフォルトでenablerepoであるから、必要ならdisableにしておこう。</p>

<pre><code class="language-brush:plain gutter:true">[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch  
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&amp;arch=$basearch  
failovermethod=priority  
enabled=0  
gpgcheck=1  
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6  
</code></pre>

<p>enabled=0とするとyumコマンドに<code>--enablerepo=REPONAME</code>オプションが必要となる。付けなければこのリポジトリを参照しない。インストール時だけでなくアップデート時にも必要とする。<sup id="fnref:1"><a href="http://multix.jp/centos-addon-repos/#fn:1" rel="footnote">1</a></sup></p>

<pre><code class="language-brush:plain">yum install PACKAGENAME --enablerepo=epel  
</code></pre>

<hr>

<h4 id="remi">remi</h4>

<p><a href="http://rpms.famillecollet.com/">Les RPM de Remi - Repository</a></p>

<p>Remi Collet がメンテナンスしているリポジトリ。標準リポジトリには含まれていない最新バージョンのパッケージを多数提供しているが、そのぶん標準側アップデート時に不整合を起こしやすいため長期運用時はピーキーになりがちである。</p>

<ul>
<li><a href="http://rpms.famillecollet.com/enterprise/remi-release-7.rpm">Enterprise Linux 7 (RPM Direct link)</a></li>
<li><a href="http://rpms.famillecollet.com/enterprise/remi-release-6.rpm">Enterprise Linux 6 (RPM Direct link)</a></li>
<li><a href="http://rpms.famillecollet.com/enterprise/remi-release-5.rpm">Enterprise Linux 5 (RPM Direct link)</a></li>
</ul>

<pre><code class="language-brush:plain">yum localinstall http://rpms.famillecollet.com/enterprise/remi-release-6.rpm  
</code></pre>

<pre><code class="language-brush:plain">yum install PACKAGENAME --enablerepo=remi  
</code></pre>

<hr>

<h4 id="ius">ius</h4>

<p><a href="http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/repoview/">ius community</a></p>

<p>Python2.7や3.3, PHP5.5、Python3.3、Git 1.8 などの最新リリースを提供している。</p>

<pre><code class="language-brush:plain">yum localinstall http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/ius-release-1.0-13.ius.centos6.noarch.rpm  
</code></pre>

<pre><code class="language-brush:plain">yum install python3.3 --enablerepo=ius  
</code></pre>

<hr>

<h4 id="rpmforge">RPMforge</h4>

<p><a href="http://repoforge.org/use/">RepoForge</a></p>

<p>Fedora系用アドオンコレクション。主にデスクトップ向けのパッケージが多いがサーバでも使えるものは多い。サーバ上で各種メディアファイルの編集加工等を扱う場合にはここからツール類を入手することが多い。とはいえremi同様標準リポジトリとバージョン管理が同期しているわけではないのでコンフリクトも起こしやすい。最近RepoForgeに名を改めたようだ。 <br>
導入は上記サイトから適切なバージョンとアーキテクチャのRPMファイルを示すURLを求めて<code>yum localinstall</code>で行う。</p>

<pre><code class="language-brush:plain">yum localinstall http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm  
</code></pre>

<hr>

<h4 id="wandisco">WANdisco</h4>

<p><a href="http://www.wandisco.com/jp">WANdisco</a></p>

<p>最新のsubversionを入手できる。repo.confパッケージは配布していないようなので以下のように自力で記述する必要がある。</p>

<pre><code class="language-brush:plain gutter:true">[wandisco-svn]
name=WANDisco Repository - snv-1.8 centos6  
baseurl=http://opensource.wandisco.com/centos/6/svn-1.8/RPMS/$basearch/  
enabled=0  
gpgcheck=1  
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-WANdisco  
</code></pre>

<pre><code class="language-brush:plain">rpm --import http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco  
</code></pre>

<pre><code class="language-brush:plain">yum install subversion --enablerepo=wandisco-svn --disablerepo=base,updates,epel  
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>オプションの記述位置はyumコマンドの直後でも行末でもどちらでもよい。このポストではサンプルコードが横スクロールすることから行末にまとめて記述しているが実際の運用ではヒストリ編集＆連続実行の容易さもあり最初に書くほうが多数派だろう。 <a href="http://multix.jp/centos-addon-repos/#fnref:1" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item></channel></rss>