Ubuntu 20.04をAutoInstallで自動インストールしてみた

# はじめに

Ubuntu 20.04から提供されるISOファイルはliveイメージに統一されました。

それによって、Preseed(debian-installer)が利用できなくなり、自動インストールはAutoInstallに統一されています。

ThinkPadやらサーバーやら2桁前半のマシン群を設定するのに、それぞれを手動でインスールするのはとても大変なので、Preseedは便利だったのですが、AutoInstallを利用することにしました。

とはいえRAIDなど特殊なパーティションを設定したい場合には、ドキュメントやUtilityがまだ整備されていないので、実機にまずLiveインストーラーで対話的にUbuntuを導入した後でログファイルから適切な設定値を確認する必要があると思います。

## 変更点

:::note
【2022/04/07追記】
次期LTSのベータ版が配布されていますが、ubuntu-20.04.4-live-server-amd64.iso イメージをベースにして作業した結果を反映させました。現在はstorageセクションでのファイルシステム作成において'%'や'-1'といったサイズ指定がきちんと動作していますので、その結果を主に反映させています。
:::

:::note
【2022/06/24追記】
22.04については、別の記事にまとめています。22.04用にはGitHubにてファイルを公開しているので参考になれば幸いです。

[Ubuntu 22.04をAutoInstallでインストールしてみた (UEFI&MBR両対応)](https://qiita.com/YasuhiroABE/items/063a442b7e45633e7cb0)
:::




# 参考資料

* https://askubuntu.com/questions/1233454/how-to-preseed-ubuntu-20-04-desktop
* https://ubuntu.com/server/docs/install/autoinstall-quickstart
* https://ubuntu.com/server/docs/install/autoinstall-reference
* https://cloudinit.readthedocs.io/en/latest/topics/datasources/nocloud.html
* https://unix.stackexchange.com/questions/52108/how-to-create-sha512-password-hashes-on-command-line
* https://github.com/CanonicalLtd/subiquity.git
* https://curtin.readthedocs.io/en/latest/topics/storage.html
* https://unix.stackexchange.com/questions/52108/how-to-create-sha512-password-hashes-on-command-line

## 作業環境

* TX100 s3p (CPU: Xeon E3-1230 V2, Memory: 32GB) 
* Ubuntu 20.04 LTS 64bit版

## インストール対象

UEFIブートを有効にしている環境で検証しています。MBR環境では boot/grub/grub.conf ではなく、isolinux/txt.cfg を編集する点が異なります。

* VMware Workstation 16 Pro (ISOファイルの検証用)
* ThinkPad x200/x230 (Memory 8～12GB, SSD: 250GB or 500GB)

ThinkPadへの導入時にはbalenaEtcherを利用して、ISOファイルからブータブルなUSBメモリを作成しています。

# はじめに

参考資料に挙げた[askubuntu.comの記事](https://askubuntu.com/questions/1233454/how-to-preseed-ubuntu-20-04-desktop)で、これまで配布されていたAlternative ISOファイルがなくなり、Liveイメージに統一されたことを知りました。

[AutoInstall Quick Startの記事](https://ubuntu.com/server/docs/install/autoinstall-quickstart)に従って、Ubuntu 20.04 Serverイメージを元に、Xubuntu DesktopとAnsibleのターゲットとするためのファイル配置などを自動化するISOファイルの作成を目指します。

# 最初の作業ログ

Quick Startに沿って作業した時のログですが、最終的なuser-dataの内容などは最後に配置しています。

```bash:ISOファイルの展開
$ wget http://releases.ubuntu.com/20.04/ubuntu-20.04.1-live-server-amd64.iso
$ sudo mkdir -p /mnt/iso
$ sudo mount -o ro,loop ubuntu-20.04.1-live-server-amd64.iso /mnt/iso/
$ mkdir iso_root
$ sudo rsync -av /mnt/iso/. 
$ cd iso_root
$ sudo chown -R $(id -un) .
$ chmod 0755 .
$ chmod 0755 isolinux

## 必要なファイルの設置
$ cat > user-data << 'EOF'
#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: ubuntu-server
    password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0"
    username: ubuntu
EOF
touch meta-data

$ chmod 755 iso_root/boot/grub/
$ sed -i -e '/vmlinuz   quiet  ---/ s!quiet!autoinstall ds=nocloud;s=file:///cdrom/ quiet !' iso_root/boot/grub/grub.cfg.
$ sed -i -e 's/timeout=5/timeout=0/' iso_root/boot/grub/grub.cfg
$ chmod 555 iso_root/boot/grub/

## iso_rootの内容をisoファイルとして作成
$ ../
$ ls -F
iso_root/ ubuntu-20.04.1-live-server-amd64.iso
$ sudo dd if=ubuntu-20.04.1-live-server-amd64.iso of=iso_root/isolinux/isohdpfx.bin bs=512 count=1
$ sudo xorriso -as mkisofs -volid "AUTOINST" -output ../ubuntu-20.04.1-xubuntu-amd64.20200819.172038.iso -eltorito-boot isolinux/isolinux.bin -eltorito-catalog isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -isohybrid-mbr iso_root/isolinux/isohdpfx.bin iso_root
$ ls ../
kitting-ubuntu2004/  ubuntu-20.04.1-xubuntu-amd64.20200819.172038.iso
```

# とりあえず試した結果

自分の理解のなさも反映されて、いろいろな問題に遭遇します。

## AutoInstall Quick Startの通りではうまくいかない

参考資料のAutoInstall Quick Startに従って作業を進めていきましたが、最終的にKVMにイメージを導入しているため、そのままではDVD-Rに書き込めるようにISOファイルを作成することはできません。

Quick Startでは"seed.iso"を作成していましたが、これは単純な user_data と meta_data の2ファイルを含むISOイメージで、これをKVMのイメージでは、インストーラーとは別のディスクとして与えています。

そのため以下の手順では、Ubuntu 18.04用にPreseedで利用した手法を参考にして、UEFI環境で利用するGRUBのgrub.cfgを編集しています。もし古いシステムでBIOSからのブートを行なう場合には、isolinux/txt.cfgのappend行を変更する必要がありますが、参考情報として後半で言及しています。

## 作業中のログの確認方法

ISOファイルでシステムをブートするテストには、KVMやVirtualbox/VMware Playerなどの利用が便利だと思います。何らかの原因でエラーになったり、システムが停止した場合には、インストーラーのHELPメニューの中からSHELLが起動できるので、/var/log/installer/ 以下のログファイルが利用できます。しかし、処理の途中であると十分な情報は得られないかもしれません。

むしろ、通常のLiveイメージを利用して対話的にインストールが完了したサーバー上の/var/log/install/installer/ディレクトリからの方が有益な手掛りが得られるかもしれません。

20.04.4で確認した際には、エラー時にコンソールからShellに落ちることができて、スムーズに検証ができています。

## user-data, meta-dataファイルへアクセスするパス

UbuntuのインストーラーがISOファイルを/cdromにマウントしてくれる挙動に変更はないようなので、s=file:///cdrom/ と指定しています。ここら辺のcdromや記憶装置をどの場所にマウントするかは、インストーラーの作者次第なので、前述の方法でSHELLに落ちてみて、dfコマンドなどで状況を確認してください。

### ISOイメージから起動した時の主なマウントポイント

* /target -- curtinで作成したROOTパーティションのマウントポイント
* /cdrom -- ISOイメージがloopデバイス経由でマウントされている
* /target/cdrom -- curin in-target でISOイメージにアクセスするためのloopデバイスの第二マウントポイント (20.04.4ではアクセスできませんでした)

# 改めて作業を再開

Quick Startのuser-dataでは、処理が中断し、UIが立ち上がりました。

/var/log/installer/以下のファイルをみてみると、Ubuntu Server Installerであるsubiquityが起動していることが分かって、検索すると、github上にコードが発見できました。

* https://github.com/CanonicalLtd/subiquity.git

この他に、InstallerからSHELLに落ちて、/snap/subiquity/current/usr/lib/python3.6/以下にもライブラリは確認できます。

どうやら、ログとコードを突き合せると、self.autoinstall_configがセットされていない場合にinteractiveモードに落ちてしまうようなので、user-dataファイルが認識されていない、正しくロードされていない可能性から確認していきます。

```bash:HELPメニューからSHELLに落ちて実行
root@ubuntu-server:/# cat /proc/cmdline
initrd=/casper/initrd quiet -- maybe-ubiquity
```

メッセージはisolinuxから起動しているように見えて、この時点で、カーネルオプションにautoload等が含まれていないので、UEFIではなく、MBRから起動している事に気がつきました。これはVirtualBoxの設定で、VMの起動処理をMBRからUEFIに変更していないのが原因で、単純な設定ミスでした。

オプションを変更して改めてUEFIから起動すると次のようなメッセージに変化しました。

```bash:HELPオプションからメニューに落ちて実行
root@ubuntu-server:/# cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz autoinstall ds=cloud-init
```

どうやらgrubから起動したものの、指定したパラメーターの';'から右側が渡されていないようです。
SHELLのようにセミコロンが改行を意味するのか、grubの仕様は詳しくないのですが、とりあえず""でパラメータを囲んで再度試します。

# 正常に動作する設定ファイル群

20.04.1のISOイメージの内容をコピー(rsync)した後に、上書きしているファイルは次のとおりです。

* user-data (新規配置)
* meta-data (新規配置、空ファイル)
* isolinux/txt.cfg
* boot/grub/grub.cfg

実際に利用したgrub.cfgファイルの全体は以下のとおりです。

```text:変更後のiso_root/boot/grub/grub.cfg

if loadfont /boot/grub/font.pf2 ; then
        set gfxmode=auto
        insmod efi_gop
        insmod efi_uga
        insmod gfxterm
        terminal_output gfxterm
fi

set menu_color_normal=white/black
set menu_color_highlight=black/light-gray

set timeout=0
menuentry "Install Ubuntu Server" {
        set gfxpayload=keep
        linux   /casper/vmlinuz autoinstall "ds=nocloud-net;s=file:///cdrom/" quiet  ---
        initrd  /casper/initrd
}
```

念のため先ほどの反省を込めてMBRブート用のファイルも配置しておきます。grubではないので""でオプションを囲む必要はありません。

```text:変更後のiso_root/isolinux/txt.cfg
default install
label install
  menu label ^Install Ubuntu Server with AutoInstall
  kernel /casper/vmlinuz
  append   initrd=/casper/initrd autoinstall ds=nocloud-net;s=file:///cdrom/ quiet  ---
```

user-dataはiso_root/user-data(ISOファイル上の'/')に配置したままで、後から変更していきます。

作業を効率化するため、実際の作業はMakefileを作成しています。
この時点では ubuntu-20.04.4-live-server-amd64.iso を使用していますが、後から利用する場合は実際に利用する公式のLiveイメージファイル名に変更してください。

```makefile:Makefile

.PHONY: download init setup geniso clean update-md5sum

ORIGINAL_ISO = ubuntu-20.04.4-live-server-amd64.iso
ISO_MOUNTPOINT = /mnt/iso
ISO_ROOT = iso_root

GRUBCFG_SRC = config/boot/grub/grub.cfg
GRUBCFG_DEST = iso_root/boot/grub/grub.cfg
ISOLINUXTXT_SRC = config/isolinux/txt.cfg
ISOLINUXTXT_DEST = iso_root/isolinux/txt.cfg
USERDATA_SRC = config/user-data
USERDATA_DEST = iso_root/user-data
METADATA_SRC = config/meta-data
METADATA_DEST = iso_root/meta-data
UBUNTUSUDOER_SRC = config/ubuntu.sudoers
UBUNTUSUDOER_DEST = iso_root/ubuntu.sudoers

ISO_LABEL = 20220407
ISO_FILENAME = ubuntu-20.04.4-xubuntu-desktop-amd64.`date +%Y%m%d.%H%M%S`.iso

download:
	wget -N https://releases.ubuntu.com/20.04/$(ORIGINAL_ISO)

init:
	(test -d $(ISO_ROOT) && mv -f $(ISO_ROOT) $(ISO_ROOT).`date +%Y%m%d.%H%M%S`) || true
	mkdir -p $(ISO_ROOT)
	(mountpoint $(ISO_MOUNTPOINT) && sudo umount -q $(ISO_MOUNTPOINT)) || true
	sudo mount -o ro,loop $(ORIGINAL_ISO) $(ISO_MOUNTPOINT)
	rsync -av $(ISO_MOUNTPOINT)/. $(ISO_ROOT)/.
	chmod 755 $(ISO_ROOT)/isolinux
	dd if=$(ORIGINAL_ISO) of=$(ISO_ROOT)/isolinux/isohdpfx.bin bs=512 count=1
	sudo umount $(ISO_MOUNTPOINT)

setup:
	chmod 644 $(GRUBCFG_DEST) $(ISOLINUXTXT_DEST)
	cp -f $(GRUBCFG_SRC) $(GRUBCFG_DEST)
	cp -f $(ISOLINUXTXT_SRC) $(ISOLINUXTXT_DEST)
	chmod 755 $(ISO_ROOT)
	cp -f $(USERDATA_SRC) $(USERDATA_DEST)
	cp -f $(METADATA_SRC) $(METADATA_DEST)
	cp -f $(UBUNTUSUDOER_SRC) $(UBUNTUSUDOER_DEST)

geniso: setup update-md5sum
	sudo xorriso -as mkisofs -volid $(ISO_LABEL) \
	-output ~/$(ISO_FILENAME) -eltorito-boot isolinux/isolinux.bin \
	-eltorito-catalog isolinux/boot.cat -no-emul-boot \
	-boot-load-size 4 -boot-info-table -eltorito-alt-boot \
	-e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat \
	-isohybrid-mbr "${ISO_ROOT}/isolinux/isohdpfx.bin" "${ISO_ROOT}"

clean:
	find . -type f -a -user "$(shell id -un)" -a -name '*~' -exec rm {} \; -print

update-md5sum:
	(cd $(ISO_ROOT) ; find . -type f -not -path ./md5sum.txt -not -path './user-data' -not -path './isolinux/*' | xargs md5sum | sudo tee md5sum.txt)

```

基本的な操作方法は、次のとおりです。

```bash
## 初回のみの初期設定作業
$ make download
$ make init

## user-dataファイルなどを編集後に、ISOファイルの作成
$ make geniso
```

この他にgit管理下の別ディレクトリで編集したuser-dataをコピーするタスクなどもありますが、省略しています。

# user-dataファイルを編集

自分の環境に合わせて、user-dataファイルを編集していきます。
今回の導入先のThinkpadは複雑な構成を取らないため、シンプルな書式で済んでいますが、それでもいくつかの設定については、インストーラーに記述する事が難しかったため、導入後に使用しているansibleに移しています。

ここでは主にansibleを利用するために必要なユーザー作成、sshとsudo関連の作業を主に行なっています。

単純なファイルコピーであれば、ISOファイルの中からlate-commandsで/target, /target/cdromのマウントポイントを利用することでコピーできますが、ISOファイルに含めるべき内容かどうか判断する必要があります。

```yaml:編集したuser-dataファイル
#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: ub2004
    password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0"
    username: ubuntu
  early-commands: []
  ssh:
    install-server: true
    allow-pw: false
    authorized-keys:
      - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPhWnnwd1vhUtJ7p1JgkTjv19eoNp9d2HqfsZsr7wu9s for ansible"
  packages:
    - xubuntu-desktop
    - lsof
  late-commands:
    - curtin in-target --target=/target -- apt remove -y gnome-desktop3-data
    - curtin in-target --target=/target -- apt autoremove -y
    - cp /cdrom/ubuntu.sudoers /target/etc/sudoers.d/99-user-ubuntu
    - cp /cdrom/meta-data /target/meta-data.txt
```

xubuntu-desktopを導入しただけでは、gnomeがデフォルトになってしまうため、利用時にlightdm上で、xubuntu-sessionやxfce4を選択する必要があります。
今回は、xfce4だけを利用したいので、gnome-desktop3-dataを削除しています。

curtin in-target を利用せずにファイルをコピーする方法を最下行に記述しています。
これは```curtin in-target --target=/target -- cp /cdrom/meta-data /metadata.txt```と同じです。

## 作業時に悩んだところ

AutoInstallの問題ではないものも含んでいますが、起きたこと、困ったことをメモしておきます。

### 試行錯誤していると突然、自動インストールが中断され、UI(CLI)が起動する

user-dataを変更してISOイメージを作り直し、VMで動作を確認、という作業をしていると、突然うまくいかなくなる現象に遭遇しています。

ログファイルをみても明確にはエラー箇所の判断ができないと思います。少なくともuser-dataファイルを作成する中でエディタがタブ(\t)文字を空白の代りに挿入したりすると問題が発生することは分かっています。

このことから、インストールがうまくいかなくなった場合には、まずuser-dataが正しく記述されているか確認することが大切です。成功したuser-dataは保管しておき、うまくいかなかった場合に、まず正しく動くuser-dataで検証する事が大切です。

### live DVDイメージのダウンロード速度が遅い

```$ make download```を実行してから環境によってダウンロードスピードが異常に遅い事に気がつきました。
これは"http://"を利用した場合に、おそらくネットワークスキャナが間に入ることで、スピードが遅くなることに気がついたので、"https://"に変更しています。

### インストールした後にsshdが起動しない

user-dataファイルの記述を間違えて、*install-server*と書かなければいけないところをtypoで'instal-server'と書いていました。

```yaml:user-dataファイルの不具合箇所の差分
  ssh:
    instal-server: true
```

正解はもちろん*install-server: true*です。
はずかしい。

でもschemaとvalidatorは必須ですよね。世の中全部XMLとRelaxNGがデフォルトになれば良いのに…。

### パーティションの削除に失敗する

VirtualBox/VMware上では問題なかったのですが、最終的に導入したいThinkPadなどの実機群には、Preseedを使用してUbuntu 18.04 LTSをインストールしているので、これを上書きする形で導入しようとするとAutoInstallの処理が途中で停止します。

ちなみに18.04を導入した時のpreseedでのpartman-auto/expert_recipeの設定は次のようになっています。

```text:preseedでのpartman-auto設定抜粋
d-i partman-auto/expert_recipe string         \
   gpt-boot-root-swap ::                      \
      1 1 1 free                              \
         $bios_boot{ }                        \
         method{ biosgrub } .                 \
      200 200 200 fat32                       \
         $primary{ }                          \
         method{ efi } format{ } .            \
      512 512 512 ext2                        \
         $primary{ } $bootable{ }             \
         method{ format } format{ }           \
         use_filesystem{ } filesystem{ ext2 } \
         mountpoint{ /boot } .                \
      1000 20000 -1 ext4                      \
         $primary{ }                          \
         method{ format } format{ }           \
         use_filesystem{ } filesystem{ ext4 } \
         mountpoint{ / } .
```


これを消去するために、curtinのマニュアルを読みましたが解決できず、最終的にはearly-commandsでGPTのパーティションテーブルを削除する方法に落ち着きました。

```text:user-dataファイルの該当箇所抜粋
  early-commands:
    - dd if=/dev/zero of=/dev/sda bs=512 count=34
```

VirtualBoxでも18.04をPreseed ISOファイルで導入した後に、作成した20.04のAutoInstall ISOファイルでは同様の現象が再現できていますが、詳細は不明です。いまのところ、これ以外の良い対応策は発見できていません。

### storageコマンドのドキュメントが良く整備されていない

関連するドキュメントは次のとおりです。

* https://ubuntu.com/server/docs/install/autoinstall-reference
* https://curtin.readthedocs.io/en/latest/topics/storage.html
* https://curtin.readthedocs.io/en/latest/topics/config.html#grub

とりあえず成功したuser-dataから該当箇所の抜粋を掲載しておきます。

20.04.4 からはより汎用的なデバイスに利用できるように、次のような指定をしています。
Kubernetesもswapを利用できるようになっているので、swapに最低限の領域を割り当てています。

* ターゲットディスクの選択に、最大サイズ、かつ、SSDのデバイスを指定
* EFIが対象 (size: 256GB, path: /boot/efi)
* Swapに4GBを確保している (size: 4G)
* / は残りの全領域を指定している (size: -1)

```yaml:成功したuser-dataから抜粋
  storage:
    grub:
      install_devices:
        - partition-1
    config:
      - id: root-ssd
        type: disk
        ptable: gpt
        match:
          size: largest
          ssd: true
        wipe: superblock-recursive
        preserve: false
        grub_device: true
        name: "CrucialSSD"
      - id: partition-1
        type: partition
        size: 256M
        number: 1
        device: root-ssd
        wipe: superblock
        flag: boot
        preserve: false
        grub_device: true
      - id: partition-2
        type: partition
        size: 4G
        number: 2
        device: root-ssd
        wipe: superblock
        flag: swap
        preserve: false
      - id: partition-3
        type: partition
        size: -1
        number: 3
        device: root-ssd
        wipe: superblock
        preserve: false
      - id: format-1
        type: format
        fstype: fat32
        volume: partition-1
        label: ESP
        preserve: false
      - id: format-2
        type: format
        fstype: swap
        volume: partition-2
        label: SWAP
        flag: swap
        preserve: false
      - id: format-3
        type: format
        fstype: ext4
        volume: partition-3
        label: ROOT
        preserve: false
      - id: format-1-efi
        type: mount
        path: /boot/efi
        device: format-1
      - id: format-3-root
        type: mount
        path: /
        device: format-3
        options: 'noatime,errors=remount-ro'
```

type: disk,partition,format は必ず指定する必要がありましたが、現在は最後のパーティションに限って、**size: -1**の指定が利用できます。

書式についてまとまったドキュメントはありませんが、手動でインストールすると、/var/log/installer/curtin-install.logファイルに設定がコピーされるのでその設定を参考にすると良いでしょう。

また、grubのinstall-devicesの設定がないと、bootloaderが設定できないとエラーになります。
この時の指定は、partitionのidでも/dev/sdXNでも指定が可能です。

```yaml:
    grub:
      install_devices:
        - /dev/sda1
```

### user-dataファイルを編集する際の注意点

エディタが自動的にタブを利用して保管してくれていたので、YAML形式のファイル作成時にタブを利用しないように注意が必要です。

* タブ文字(\t)は必ず、スペース(4 or 8つ分)に変換すること

### UEFI環境でのbootloader導入先のディレクトリ名

AutoInstallが途中で止まった場合は、メッセージを手掛りにsubiquityのコードをみるのが良さそうです。

```c:subiquity/controllers/filesystem.py
        if self.model.needs_bootloader_partition():
            raise Exception(
                "autoinstall config did not create needed bootloader "
                "partition")
```

controllerからmodelが呼び出されているので、そちらを確認すると、type: partitionなセクションでgrub_device=Trueを明示的に設定して、type: mountなセクションでそのパーティションを、path:が/boot/efi となるようにマウントする必要があることが分かります。

```c:subiquity/models/filesystem.py
        elif self.bootloader == Bootloader.UEFI:
            for esp in self._all(type='partition', grub_device=True):
                if esp.fs() and esp.fs().mount():
                    if esp.fs().mount().path == '/boot/efi':
                        return False
            return True
```

こんな感じでエラーメッセージから、該当のコードを探すことで状況が改善する場合もありました。

### ansibleのためにsudoをパスワードなしで実行したい

ansible自体はsudoにパスワードを渡す事もできますが、今回はubuntuユーザーがsudoコマンドを実行した時にパスワードを要求しないようNOPASSWD:をsudoersファイルに加えます。

late-commandsで実行するため、iso_root/ubuntu.sudoersファイルを配置して、これをインストール時に/etc/sudoers.d/99-user-ubuntuファイルに配置することにしました。最初は、既に/etc/sudoers.d/99-snapd.confファイルが配置されているので、同様に配置してしまったのですが、/etc/sudoers.d/READMEファイルを確認すると、emacsのバックアップファイルのように末尾がチルダ'~`になっているファイルや、ピリオド'.'が含まれているファイルは認識しないとあるので、**.conf**のようなピリオドで区切られたsuffixが付いている場合には認識されないことになります。

このため配置するファイルは、[0-9A-z-]だけを含むようにしました。

```text:config/ubuntu.sudoers 
ubuntu ALL=(ALL:ALL) NOPASSWD: ALL
```

```yaml:user-dataファイルのlate-commandsに追加した箇所の抜粋
    - curtin in-target --target=/target -- cp /cdrom/ubuntu.sudoers /etc/sudoers.d/99-user-ubuntu
    - cp /target/cdrom/ubuntu.sudoers /target/etc/sudoers.d/99-user-ubuntu
```

過去にはこのコードは機能していましたが、20.04.4では /target/cdrom にアクアセスできません

### 20.04.04では/target/cdromのloopデバイスマウントに失敗する

ISOイメージからVMを起動すると、md5sumのチェックが終った次の時点で、/dev/loop2デバイスの/cdromへのマウントに失敗する旨のエラーメッセージが表示されています。

この他の /target, /cdrom は利用が可能なので、ISOイメージからのファイルコピーにcurtin in-targetを使わずに行なうことで無事にファイルのコピーが行えます。

```yaml:20.04.4でのubuntu.sudoersファイルのコピーのlate_commands設定
    - cp /cdrom/ubuntu.sudoers /target/etc/sudoers.d/99-user-ubuntu
```

### SHA512パスワードハッシュの生成

公式ドキュメントをみても手動での生成方法についての記述は発見できず、以下のようにwhoisパッケージのmkpasswdコマンドを利用する方法は他の検索結果にもよく出ていました。

* https://unix.stackexchange.com/questions/52108/how-to-create-sha512-password-hashes-on-command-line

次候補として、whoisパッケージの次に出てくる、よりプラットフォームを選ばないと思われるopenssl passwdコマンドを利用して生成したハッシュが利用できるか確認することにしました。

```bash:opensslによるSHA512パスワードハッシュの生成
## パスワード:"password"に対応するハッシュ値の生成
$ openssl passwd -6 -salt $(openssl rand -hex 8) password
$6$256074873fa7ecc1$vpI6ychXECJ7Wts3VlOT/9zbI.Lm6M1fyfyWhWrd6xR.RVmBsFttMVbAIZY7hx7ZvPZOqurC.nNnCx6DZGUeq/
```

これをuser-dataのpassword:の値に設定してみると、無事にインストールしたシステムにログインできました。

とはいえ、SHA512ハッシュのSaltの長さが何ビットなのか、man openssl-passwdなどからは確認できなかったので、RFCでもなんでも定義を確認したかったのですが、検索結果からはすぐに分かりませんでした。

opensslコマンドのソースコードをみれば分かるだろうと、apt sourceでコード一式を入手してみます。

```bash:
$ apt source openssl
$ cd openssl-1.1.1f/
$ less apps/passwd.c
```

shacrypt()関数の周りをみてみると、charで16文字分の長さが確保されているので、間違いではなかったようです。
mkpasswd.cではmaxlength 16に設定されていました。

この後、いろいろ資料を探してみて、Wikipediaのbcryptの記述に辿り着きました。

* https://ja.wikipedia.org/wiki/Bcrypt

そこから Modular Crypt Format (MCF)の記事に飛んでハッシュのフォーマットについての記述をみつけることができました。

* https://passlib.readthedocs.io/en/stable/modular_crypt_format.html

Saltについては、openssl passwdを使用すると、16文字より先は無視されて、0文字だとハッシュ値が生成されない事を確認してとりあえず終りにしました。