VNC

RedHat系では、VNCサーバは vnc-server パッケージ (RHEL/CentOS 6 以降では tigervnc-server パッケージ) で提供される。また、xinetd 起動にする場合は xinetd パッケージもインストールしておく必要がある。

Table of Contents

hostsファイルの整形

RedHat系の /etc/hosts ファイルは伝統的に書き方が悪く、このあとの VNCサーバの設定でエラーが出る場合がある。

127.0.0.1    localhost.localdomain  localhost  centos51u

のようになっているのを、

127.0.0.1    localhost.localdomain  localhost
192.168.0.8  centos51u.mydomain.com  centos51u

のように直しておく。

VNCサーバのいろいろな起動方法

筆者のお薦めとしては、RHEL/CentOS 5 (EL5) なら xinetd の wait=yes 方式、EL6 では Upstart。EL7 では systemdsocketユニットを併用した"方式2"がぎりぎり及第点で、環境によっては xrdp に逃げる。

SysVinitで起動

この方法だと、やや動作が軽いように感じるのと、X が裏で常に稼働していなくて済むのでゲストのメモリが節約できる。また、VNCウィンドウ自体を ×で閉じた時にはセッションは存続し、次に同じユーザでログインすると同じセッションに再び接続できる(※)。ただし、VNC上で ログアウト してしまうと無応答のデスクトップ画面が残り、vncserver を再起動しなければならなくなるなど、セッションの管理が不気味。vncservers ファイルの中に書いている -IdelTimeout はそれを避けるための苦肉の策だ。

※ セッション維持が目的で SysVinit起動を選択するのはお勧めできない。上に述べたように rc起動はあまりにも粗雑だ。

1. vncserversファイルの編集

/etc/sysconfig/vncservers を編集し、ユーザとディスプレイナンバーの対応、仮想ディスプレイの解像度などを定義する。下記のものは、root はディスプレイナンバー1 (つまりポート 5901)、hoge は 2 (ポート 5902) に紐付けし、仮想ターミナルのパラメータはどちらも「解像度 1024x768, 色深度 16bit」にする設定だ。このファイルは起動スクリプト /etc/init.d/vncserver によってシェルスクリプトの断片として読み込まれる (source される)。つまり、下記の記述はいずれも bash の配列の宣言である。-IdeleTimeout などその他のオプションは Xserver の man で見ていただきたい。

※ RHEL4.x の vncserver は `-nohttpd' オプションに対応しておらず、付けるとエラーになって vncserver が立ち上がらない。X11 XFIXESエクステンションも、エラーが出るようなら取り除くべし。

VNCSERVERS="1:root 2:hoge"
VNCSERVERARGS[1]="-geometry 1024x768 -depth 16 -nolisten tcp -IdleTimeout 600 -nohttpd +extension XFIXES"
VNCSERVERARGS[2]="-geometry 1024x768 -depth 16 -nolisten tcp -IdleTimeout 600 -nohttpd +extension XFIXES"

2. VNCパスワードファイルの作成

ホストに目的の UNIX ユーザでログインした状態で、VNCパスワードファイルを作る。VNC のパスワードとシステム上の UNIXパスワードは管理が全く別。下記操作によって ~/.vnc/passwd ファイルができる。

hoge$ vncpasswd
Password: <タイプ>
Verify: <再タイプ>

3. ユーザ環境ファイルの生成

~/.vnc/ 下にその他の必要ファイルを作らせるため、VNCサーバを一旦手動で起動させ、すぐに終了させる。引数の `:2' はディスプレイナンバーで、前述の vncservers ファイルの定義と合わせる。起動時に `bad display name "centos51u.hoge.cxm:1" in "add" command' といったエラーが出たら、それは RedHat系デフォルトの、悪い /etc/hosts ファイルを直していないからだろう (既に述べたhostsファイルの整形の項を参照)。当該のユーザで;

hoge$ vncserver :2
hoge$ vncserver -kill :2

4. xstartupファイルの編集

~/.vnc/xstartup ファイルを編集する;

#!/bin/sh
 
unset SESSION_MANAGER    <--アンコメントして有効化
exec /etc/X11/xinit/xinitrc  <--アンコメントして有効化
 
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup  <--これ以降は書いてあっても無意味
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
twm &

exec で呼ばれている /etc/X11/xinit/xinitrc は (たとえ `exec' を `source' に替えたとしても)、最終的に Gnome (あるいはKDE) をまた exec で起動して終わるので、スクリプトのシェルプロセスはこの .vnc/xstartup にはもう帰って来ない。よって、上の例の場合、後半の `[ -x /etc/vnc/xstartup] && ...' 以下は一瞥だにされない。もし、`vncconfig -iconic &' (VNCサーバ/クライアント間でクリップボードによるコピー&ペーストを可能にする仕掛け) なども実行されるようにしたければ、`exec /etc/X11/xinit/xinitrc' の位置を下記のように変える;

#!/bin/sh
 
unset SESSION_MANAGER
 
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
exec /etc/X11/xinit/xinitrc
#xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
#twm &

5. VNCサーバの起動

VNCサーバがエラーなく起動するかどうか確認;

root# service vncserver start

ホスト起動時に vncserver サービスが自動的に開始されるようスタートアップに登録;

root# chkconfig vncserver on

ホストの X-window 設定ファイル (/etc/X11/xorg.conf) は、こと VNCに関する限り、最近ではほとんどさわる必要がなくなった。そもそも、最近の Fedora Core や CentOS/RHEL 6 には xorg.conf 自体が既定では存在しない。また、Screen セクション内の Display セクションの解像度 (Modes) も、vncserver には影響がないようだ。 "1024x768" が書いてなくても、/etc/sysconfig/vncservers できちんと定義してあれば解像度 1024x768 が成り立つ。

xinetdで起動

こちらの場合、VNC画面上で ログアウト すると xinetd がセッションを殺すので、クリーンに終了することができる。VNCウィンドウを × で閉じた時も同じだ。ただし、裏側で常に X が待機しているせいなのか、割り当てメモリの少ないドメインでは、SysVinit起動の時よりも動きがやや重い気がする (※1 裏技あり)。ポート (ディスプレイナンバー) さえかぶらなければ SysVinit起動と両方待機させておくことも可能だ。

1. システムのデフォルトRunレベルを 5 にする

/etc/inittab のデフォルト runレベルは 5 にしなくてはならない。以下の設定に必要となる gdm (prefdm) は、 inittab (RHEL6ではupstart) の中で、ランレベル 5 でのみ起動するよう定義されているからだ。ここでランレベルを変更した場合は、以下の作業の前にゲストドメインを一度再起動しておく。

2. xinetd用VNCサーバ起動定義ファイルの作成

/etc/xinetd.d/ 下に下記のような内容のファイル xvnc (root:root 644) を作成する。ファイル名は任意。

service xvnc51
{
    socket_type     = stream
    type            = UNLISTED
    port            = 5951
    wait            = yes
    user            = hoge
    flags           = IPv4
    server          = /usr/bin/Xvnc
    server_args     = -inetd -query localhost -once -geometry 1024x768 -depth 16 -PasswordFile /home/hoge/.vnc/passwd +extension XFIXES
    log_on_failure += USERID
    cps             = 1 30
    instances       = 2
    per_source      = 2
}
 
service xvnc52
{
    socket_type     = stream
    type            = UNLISTED
    port            = 5952
    wait            = no
    user            = nobody
    group           = tty
    flags           = IPv4
    server          = /usr/bin/Xvnc
    server_args     = -inetd -query localhost -once -geometry 1024x768 -depth 16 securitytypes=none +extension XFIXES
    log_on_failure += USERID
    cps             = 1 30
    instances       = 2
    per_source      = 2
    only_from       = 192.168.122.0/24 192.168.1.0/24
}

protocol=tcp (または udp) を書くとうまくいかない。赤字で示している `type=UNLISTED' と port を書けば、後述の services ファイルへのポートナンバー登録は必要なくなる。`server_args' の X11 XFIXES エクステンション(ホームページ, 規格) は古い Linuxゲストにはないかもしれないので、エラーが出るようなら削除していただきたい。`cps' 以下はちょっとしたセキュリティ。

最初の例の `service xvnc51' として定義しているものが、筆者の最も気に入っているやり方だ。ポイントは `wait=yes' にしてある点。こうすると、VNCクライアント画面を × で閉じてもセッションは終わらない、つまり再度つなぎに行けばさっきの作業の続きができる。一方、ログアウトをすると、ちゃんとセッションは終了し、変なプロセスが残ったりはしない。新しくセッションを開始する際にパスワードを 2回打たなくてはならないのが玉に瑕だが、ある意味理想的な挙動が得られる。このパターンの場合、xinetd にはリモートホストの IPアドレスが分からないようで、`only_from' を入れたり tcp-wrappers (/etc/hosts.allowhosts.deny) で IP規制するとつながらない。`flags=INTERCEPT' も試したがダメ。規制したければ iptables でも使うしかなさそうだ。

3. VNCパスワードファイルの作成

`wait=yes' のパターンの場合は、当該ユーザの VNCパスワードファイルを作成しておく必要がある。SysVinit起動方式の中で述べたように、hoge でログインした状態で vncpasswd を実行すればいい。

4. servicesにポート定義を追加

xinetd用VNCサーバ起動定義に `type=UNLISTED' と `port=port ' を書かない場合には、xvnc ファイル内の service で定義したサービス名を、ポートとプロトコルとともに /etc/service ファイルに加える。ポート番号は 5900 から 5960 の間が他のサービスと被りにくく無難だが、自分の環境の services ファイルをよく確認して決めること。

xvnc50        5950/tcp
xvnc50        5950/udp
xvnc51        5951/tcp
xvnc51        5951/udp
xvnc-hoge     5952/tcp
xvnc-hoge     5952/udp

5. GDMの設定

やり方はディストリビューションによって異なる。

gdmsetupのあるディストリビューションの場合(RHEL/CentOS 5など)

`gdmsetup' とコマンドするか、Gnomeメニューの システム -> 管理 -> ログイン画面 を選び、GDM のセットアップを実行する。設定ツール自体が X-Windowgdm を使うので、テキストコンソールでは行えない。また、CentOS 5.1 では、root でログインした X-Window 上でしか設定GUI が呼び出せなかった。

リモート タブの スタイル:リモートログインを無効にする 以外 にする。これにより XDMCP プロトコルが有効になる (※2)。

上記の代わりに XDMCP タブのあるリリースでは、XDMCPを有効にする にチェックを付ける。

設定を反映させるには gdm の再起動が必要なので、`gdm-restart' とコマンドして gdm だけを再起動するか、マシンをリブートする。なお、この手のディストリビューションでも、手動で custom.conf を編集する方法も使える。

gdmsetupのないディストリビューションの場合

Fedora Core 9 以降や RHEL6 では、gdmsetup が存在しない。そのため、XDMCP を有効にするにはこの方法しかない。GDM は 2.21 でコードがまったく新たに書き直された模様で、2.20 以前にあった複数サーバ定義機能が実装に至っておらず、gdmsetup も存在しないのだ。

/etc/gdm/custom.conf (RHEL4 は /etc/X11/gdm/gdm.conf) をテキストエディタで開き、[xdmcp] セクションに `Enable=true' と書く。ついでに、root での VNC ログインを許可したい場合は、[security] セクションに `AllowRemoteRoot=true' も書いておく。

6. xinetdを再起動

root# service xinetd restart
※1. デフォルト runレベルを 5 にしても、ローカルログオンの時 (仮想化ゲストの場合はつまり ハイパーバイザ内蔵VNC への接続時) だけはテキストモードにする方法がある。ただしこの手は GDM (Gnome Desktop Manager) Ver. 2.16 前後でしか効かないので、CentOS 5/RHEL5 に限定される。gdmsetupセキュリティ タブにある Xサーバの設定 ボタンを押し、起動するサーバ 一覧にある (おそらく唯一の) Standard を消す。これは、設定ファイル /etc/gdm/custom.conf[servers] セクションを `0=inactive' に書き換えるカタチで反映される (設定ファイルを直接書き換えても可)。つまり、「ディスプレイ 0 (デュアルヘッドなどでなければ唯一のモニタ) は GDM で管理しない」 を意味し、ローカルログイン時の画面は runレベル 3 と同様の黒いテキスト画面になり、うまくいけば 100MB くらいのメモリ節約になる。

※2. 仮想化ゲストの場合、この時点で設定に使っている Xen や libvirt 組み込みの VNC スクリーンは 800x600 の解像度しかないため、gdmsetup の [閉じる] ボタンが画面からはみ出ていて見えない。まさにブラインドタッチだが [リモート] タブで [ウェルカムメッセージ] のところにフォーカスしてタブを 3回叩くと [閉じる] ボタンにフォーカスが移るのでそこで Enter キーを叩くと設定が完了できる。

Upstartで起動

使い心地としては xinetdwait=yes の場合と同じとなり、具合がよい。xinetd だとVNCサーバを再起動する際に他の xinetd 所轄のサービスまで道連れになってしまうが、Upstart ならVNCサーバだけを停止したり再起動したりできるというのも利点だ。

1. システムのデフォルトRunレベルを 5 にする

xinetd 起動と同様。

2. Upstartジョブファイルの作成

Upstart-1.4 以降なら setuidsetgid ディレクティブを使って全てのコマンドやプロセスを特定のユーザおよびグループで実行することが可能なのだが、RHEL/CentOS 6 の Upstart はまだ 0.9.x なのでその手が使えない。そこで su を挟む。プロセスは、Upstartの発生させるシェルプロセス[親] => su のプロセス[子] => 正味の Xvnc プロセス[孫] という構造となる。`status xvnc51' で表示されるのは[親]のPIDだ。VNCクライアントを × で閉じると次回も継続して同じ画面につながる。対して、VNC画面上でログオフをすると、Xvnc プロセスが終了 => su が終了 => Upstartシェルプロセスが消失するが、ジョブ定義に respawn が指定しあるため Upstart がまたプロセスツリーを再発生させる。

# xvnc51 - VNC Server listening on :51
#
# Starts Xvnc server
 
env DISPNO=":51"
env RUNUSER=hoge
 
start on started rc RUNLEVEL=5
stop on started rc RUNLEVEL=[!5]
 
console output
respawn
respawn limit 10 120
kill timeout 30
 
pre-start script
    su -s /bin/sh -c "/usr/bin/vncserver -kill $DISPNO >/dev/null 2>&1 || :" $RUNUSER
end script
 
script
    su -s /bin/sh -c "/usr/bin/Xvnc -query localhost -once \
    -geometry 1280x1024 -depth 16 -PasswordFile /home/$RUNUSER/.vnc/passwd \
    +extension XFIXES $DISPNO" $RUNUSER
end script
 
pre-stop script
    su -s /bin/sh -c "/usr/bin/vncserver -kill $DISPNO >/dev/null 2>&1 || :" $RUNUSER
end script

3. VNCパスワードファイルの作成

xinetd の時と同様。

4. servicesにポート定義を追加

xinetd の時と同様。ただし、やらなくても一応は動く。

5. GDMの設定

スタートscriptに `-query localhost -once' を使っているので、xinetd の時と同様に XDMCPを有効化する。

6. Upstartへの反映と起動

root# initctl reload-configuration
root# start xvnc51
root# status xvnc51
root# stop xvnc51

systemdで起動

RHEL/CentOS 7 では、サービスの起動フレームワークが SysVinitUpstart に代わって、Systemd というやつにすげ替えられた。

1. システムのデフォルトRunレベルは 3 でも 5 でもよし

systemd に牛耳られる RHEL/CentOS 7 では、ローカルのデフォルト起動モードが graphical.target (initでの runレベル5 に当たる) でも multi-user.target (initでの 3 相当) でも VNC は成り立つ。「ローカルコンソールは CLI にかぎる!」というスパルタンな人は、デフォルト Target をキャラクタモードにしておいてもよい。デフォルト Target を切り替えるには

root# systemctl set-default multi-user.target

でどうぞ。

2. ユニットファイルの作成と有効化

まず、VNC の待ち受けポートを計画する。ここでは、5951 つまりディスプレイナンバー :51 で待ち受けると想定しよう。

方式1 - ポートテンプレート方式

この方式では、上で紹介した Upstart の方法や xinetd方式wait=yes の時と同じように、ログアウトすれば Xvnc プロセスがクリーンに終了するし × で閉じると次回も継続して同じ画面に接続できる。gdm/custom.confXDMCP 有効化は不要。

この方式では、VNCセッションから"ログアウト"すると vncserver プロセスが終了してリスポーンするのだが、時々、vncserver が "address already in use" と言って再立ち上げに失敗する。正確には、vncserver は起動しているけれども `netstat -lnt' で見てみるとポートがリッスンされていない。どうもうまくない。

tigervnc-server パッケージをインストールすると、/usr/lib/systemd/system/ に雛形 vncserver@.service がインストールされるので、これを xvnc@<DISP>.service として /etc/systemd/system/ 下へコピーする。この例なら、

root# cp /usr/lib/systemd/system/vncserver@.service \
     /etc/systemd/system/xvnc@:51.service
root# chmod 644 /etc/systemd/system/xvnc@:51.service

という塩梅だ。名前の後に @ の付いたこの種のユニットファイルは、Unit Template という少し特別なタイプで、@ と拡張子の間の文字列 (インスタンス文字列) がユニットファイル内の %i に代入される仕組みになっている。VNCのこのユニットファイルに関する限り、インスタンス文字列 がそのまま vncserver コマンドにメイン引数として渡されるので、@ の後に : (コロン) を忘れないように。そしてコピーしたユニットテンプレートを (かなり) カスタマイズする。User=hoge を Linux 上のあなたのユーザに修正のこと。

[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target
 
[Service]
Type=simple
User=hoge
PAMName=login
Restart=always
RestartSec=1
CPUShares=500
MemoryLimit=150M
ExecStartPre=-/bin/sh -c '/usr/bin/vncserver -kill %i >/dev/null 2>&1 || :'
ExecStart=/usr/bin/vncserver -geometry 1152x864 -depth 24 -fg %i
ExecStop=-/bin/sh -c '/usr/bin/vncserver -kill %i'
ExecStopPost=-/bin/sh -c '/usr/bin/vncserver -kill %i >/dev/null 2>&1 || :'
 
[Install]
WantedBy=multi-user.target

この方式では VNCパスワードファイルが必要なので、ユニットファイルの "User=" で指定したユーザで一旦 Xvnc を起動しすぐ終了する。その際にパスワードを設定する。

hoge$ vncserver :51
hoge$ vncserver -kill :51

ユニットが起動するかテストして自動起動を設定。

root# systemctl daemon-reload
root# systemctl start xvnc@\:51.service
root# systemctl enable xvnc@\:51.service
方式2 - Socket連動方式

systemdxinetd のような動作をエミュレートする方式。同じポート(ここでは 5951)で、複数のユーザがそれぞれ別々のセッションにログインできる。VNCクライアントを × で閉じることによって TCPセッションが終わり、それによりVNCセッションが必ず終了するので、ログアウトは必要でない。これは、TigerVNC WIKI にある Multi-user mode そのまんまである。xinetd の時と同様に XDMCPの有効化は必要だ。ユーザの ~/.vnc/ ディレクトリやVNCパスワードファイルは要らない。

/usr/lib/systemd/system/ の雛形 vncserver@.servicexvnc@service として /etc/systemd/system/ 下へコピーする。/usr/lib 側にあるのと同じファイル名だと上手く機能しない。これは[Install] セクションのない、他ユニットから呼ばれるのが専門のいわゆる Staticなサービスユニットというやつだ。

root# cp /usr/lib/systemd/system/vncserver@.service \
     /etc/systemd/system/xvnc@.service
root# chmod 644 /etc/systemd/system/xvnc@.service

そしてもうひとつ。xvnc.socket というユニットファイルも作る。拡張子の前は必ず対の service ユニットと同じ名前でなければならない(ただし@は不要)。

xvnc@.service

[Unit]
Description=Remote desktop service (VNC)
 
[Service]
Type=simple
User=nobody
CPUShares=500
MemoryLimit=150M
ExecStart=-/usr/bin/Xvnc -inetd -query localhost -once -geometry 1152x864 -depth 24 -SecurityTypes=None +extension XFIXES
StandardInput=socket

xvnc.socket

[Unit]
Description=Remote desktop socket (VNC)
 
[Socket]
ListenStream=0.0.0.0:5951
FreeBind=true
Accept=true
 
[Install]
WantedBy=sockets.target

ListenStream の右辺はポート番号だけでも書式としては有効なのだが、それだと IPv6 もリッスンしてしまう。この例では IPv4 だけリッスンするよう x.x.x.x の形式で全てのインターフェースIPを示す 0.0.0.0 付けている。systemd には BindIPv6Only=true というディレクティブがあるが BindIPv4Only というディレクティブはないのだ。

自動起動にすべきは socket ユニットのほうのみ。socket がコネクションを受けて service を起動する仕組みだ。

root# systemctl daemon-reload
root# systemctl start xvnc.socket
root# systemctl enable xvnc.socket

3. スクリーンロックの無効化

CentOS 7.2 で試したところ、GNOMEpolkit かのバグにより、VNCセッション上で一旦スクリーンがロックされると、正しいはずのパスワードを入力してもロックが解除できくなる。そのユーザのデスクトップの上部ツールバー右端から工具のアイコンをセレクト => プライバシー を開き、画面ロック をオフにしておこう。全てのアカウントでオフにしたいなら、「よろづ環境設定」に書いた GNOMEデスクトップ関連アプリのデフォルト値を変更したい の手順で、gschema.override ファイルに

[org.gnome.desktop.screensaver]
lock-enabled=false

を含めればいい。

4. "カラーマネージメントされたデバイスを作成するには認証が必要です" ダイアログの無効化

CentOS 7.2 では、VNCでデスクトップセッションにログインする前後に「カラーマネージメントされたデバイスを作成するには認証が必要です」というダイアログが何度も何度も出て疎ましい。よろず環境設定 を参照のこと。

xrdpという選択肢

単独ページに移しました。