まるさや

技術っぽいことかきます

KVMのゲストマシン上でSSHのポート閉じてしまいシリアル接続もできないのでディスクイメージをホストにマウントしてどうにかする

どうも、まるさ@maruuusa83です.




KVMで複数の仮想マシンを立てて色々な実験環境を運用しているのですが,そのうちの一つでオペミスでSSHのポートを閉じてしまい,シリアル接続用の設定もしていなかったせいでコンソールに接続することが全くできなくなってしまいました.

試行錯誤しつつ解決したのでメモ.







状況と方針

ゲストOSの側は全てのポートが閉じてしまっているので,ネットワークからはなす術がありません.

また,シリアル通信でコンソールにログインを試みるも,事前の設定がなされていないためにこれも失敗.

というわけで,一旦ゲストOSを落としてイメージファイルをホストOSにマウントしてゴニョゴニョいじります.

最後にオマケで非常用にシリアルコンソールが使えるように設定する方法を書いておきます.







イメージファイルについて調べる

KVMで動作するゲストOSのディスクは,基本的にはqemu-imgで作られるraw形式かqcow2形式になっているはずです.

とりあえず,次のコマンドでディスクイメージの形式を調べます.


# qemu-img info disk.img
image: disk.img
file format: qcow2
virtual size: 300G (322122547200 bytes)
disk size: 57G
cluster_size: 65536
Format specific information:
    compat: 1.1
    lazy refcounts: true


raw形式であれば kpartx -av disc.img して mount /dev/mapper/loop0p1 /mnt 的なことをすれば簡単にマウントできますが,qcow2形式の場合には圧縮が施されているのでそいつを何とかしてあげる必要があります.

ぐう・・・







qemu-nbdを使えるようにする

qcow2形式のディスクイメージのマウントには, qemu-nbd を使うとよいです.

これでraw形式の時のようにループバックマウントして中身を覗けるようになります.

これを利用するためまずは nbd モジュールを読み込みます.


# modprobe nbd
modprobe: FATAL: Module nbd not found.


やめてくれmodprobe その術はオレに効く




Linuxカーネルをビルドしてnbdを手に入れる

CentOS7ではnbdのビルドと組込みは行われないようなので,カーネルを落としてきて頑張って自分でビルドします.

とりあえずソースコードをゲットしましょう.

uname -r とかで自分のカーネルのバージョンを調べて同じバージョンのソースコードをゲットしてください.


# uname -r
# wget http://vault.centos.org/7.2.1511/updates/Source/SPackages/kernel-3.10.0-327.28.3.el7.src.rpm
# rpm -ivh kernel-3.10.0-327.28.3.el7.src.rpm


次にビルドの準備をします. 頑張って依存性を解決しながら rpmbuild を進めてください.


# mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
# echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros
# cd ~/rpmbuild/SPECS
# rpmbuild -bp --target=$(uname -m) kernel.spec
# cd ~/rpmbuild/BUILD/kernel-3.10.0-327.28.3.el7/linux-3.10.0-327.28.3.el7.centos.x86_64/


make menuconfigDevice Driver > Block devices とたどって, Network block device supportM をセットしてください.

menuconfigができたら,makeしましょう.


# make prepare && make modules_prepare && make

...

  CC [M]  drivers/block/nbd.o
error: ‘REQ_TYPE_SPECIAL’ undeclared (first use in this function)
sreq.cmd_type = REQ_TYPE_SPECIAL;
^


やめてくれ・・・


nbdはRedhatのサポート外なので,このバージョンではどうもうまくコンパイルできないらしい・・・

REQ_TYPE_SPECIALREQ_TYPE_DRV_PRIV に変更されているので, nbd.c 内の該当箇所を修正してもう一度ビルドします.


fsync_bdev(bdev);
mutex_lock(&nbd->tx_lock);
blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_DRV_PRIV; // これを修正した
nbd_cmd(&sreq) = NBD_CMD_DISC;


改めてビルドを進める・・・


# make prepare && make modules_prepare && make
# make M=drivers/block -j8


通った!のでmodを読み込んでいく.


# modinfo drivers/block/nbd.ko
filename:       /root/rpmbuild/BUILD/kernel-3.10.0-862.el7/linux-3.10.0-862.el7.centos.x86_64/drivers/block/nbd.ko
license:        GPL
description:    Network Block Device
retpoline:      Y
rhelversion:    7.5
srcversion:     A36E8E32685072269226933
depends:
vermagic:       3.10.0 SMP mod_unload modversions
parm:           nbds_max:number of network block devices to initialize (default: 16) (int)
parm:           max_part:number of partitions per device (default: 0) (int)
parm:           debugflags:flags for controlling debug output (int)
# cp drivers/block/nbd.ko /lib/modules/3.10.0-327.28.3.el7.x86_64/extra/
# depmod -a && modprobe nbd


modprobeinvalid argument 的な事を言われてしまう場合はソースのバージョンとOSのカーネルのバージョンがちゃんとマッチしているか確認してください.

正しくnbdが読み込まれたか確認するために /dev を覗いてみます.


# ls /dev |grep nbd
nbd0
nbd1
nbd10
nbd11
nbd12
...


よしよし







qcow2イメージをループバックマウントする

nbdが使えるようになったので, qemu-nbd を使ってマッピング接続します.


# qemu-nbd -c /dev/nbd0 disk.img


とりあえず fdisk して正しくパーティションが読めてるか確認してみます.


# fdisk -lu /dev/nbd0
WARNING: fdisk GPT support is currently new, and therefore in an experimental phase. Use at your own discretion.

Disk /dev/nbd0: 322.1 GB, 322122547200 bytes, 629145600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: gpt
Disk identifier: 49524BB2-F36A-41E3-BB7D-9C5ED6ABA100


#         Start          End    Size  Type            Name
 1         2048         4095      1M  BIOS boot
 2         4096    629143551    300G  Linux filesyste


きちんと認識できているようなので,ループバックマウントします. パーティション分けがなされているので, kpartx でバチッと分けてあげてからマウントします.


# kpartx -av /dev/nbd0
add map nbd0p1 (253:3): 0 2048 linear /dev/nbd0 2048
add map nbd0p2 (253:4): 0 629139456 linear /dev/nbd0 4096 # これが本体っぽい
# mkdir /mnt/nbd0p2
# mount -o loop /dev/mapper/nbd0p2 /mnt/nbd0p2


ls してみる


# ls /mnt/nbd0p2
bin   dev  home  ...
boot  etc  initrd.img  ...


やったーファイルシステムが読めるようになったぞー!!!







chroot して設定を書き換えていく

方針としては,chroot で強引にゲストOSのシェルを起動して nfw で22番をこじ開ける感じです.

今回ホスト側がCentOS7でゲスト側がUbunt18だったので,さすがに無理かなーと思いつつ試しましたが,結論から言うとこれでうまくいきました


chroot コマンドでルートをすげ替えます.OSが違ったり /dev の中身が無かったりするので色々と具合が悪いこともありますが,とにかく試します.


# chroot /mnt/nbd0p2
# ufw allow 22
Rule added
Rule added (v6)


えっめっちゃあっさり通った・・・!







アンマウントして再起動

マウントの時と逆の手順を踏んでアンマウントしていきます.


# umount /mnt/nbd0p2
# kpartx -dv disk.img
# qemu-nbd -d /dev/nbd0


あとは仮想環境を立ち上げてみてsshを繋ぐなりしてみて設定が正しく行われているか確認してください.







【おまけ】非常用にシリアルコンソールから接続できるように設定する

今回シリアルでつながってればこんなに大変なことにはならなかったに違いないので,シリアルコンソール接続ができるように設定します.




grubに起動オプションを設定

イメージファイルの中のgrubの設定を書き換えていきます.

/mnt/nbd0p2/etc/default/grub に起動オプション console=tty0 console=ttyS0,115200n8r を追加します.


# cd /mnt/nbd0p2/etc/default/
# vim grub

...

GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8r" # ここに書く




mkconfigして接続テストしてみる

そして grub-mkconfig -o /boot/grub2/grub.cfg します.

再起動して,接続テストをしてみてください.







今回はこのへんで.

つかれた ノ


まるさ