Содержание

Загрузка Linux по сети с iSCSI диска

Как я понимаю, прелесть данного метода в том, что с сервера снимаеются накладные расходы на обслуживание файловой системы, т.к. клиент работает с блочным устройством и её обслуживание осуществляется полностью самим клиентом.

В реализации данного метода загрузки будут задействованы следующие службы:

  • DHCP-сервер, который должен передавать клиенту данные о адресе TFTP-сервера и имени файла с которого нужно начать загрузку системы
  • TFTP-сервер, с которого производится чтение базовой системы управляющей загрузкой
  • Модуль загрузчика iPXE, который осуществляет подключение iSCSI-диска и инициализирует с него загрузку
  • Сервер сетевого хранилища, содержащий блочные устройства, доступ к которым выдается по средствам протокола iSCSI

Следует сразу оговориться, что данная статья орентирована на системы, которые поддерживают Legacy-режим (или CSM) загруки через стандартный PXE протокол. Не все современные BIOS-ы в десктопных системах поддерживают данный режим.

1 Настройка DHCP-сервера

Настройка DHCP-сервера сводится к добавлению параметра, передающего клиенту данные о расположении TFTP-сервера и файла с которого нужно начать загрузку. Для различных маршрутизаторов это будет по разному, но суть везде одна: передать IP-адрес next-server - адрес TFTP сервера и имя файла для загрузки, в нашем случае - pxelinux.0.

2 Настройка TFTP-сервера

В Ubuntu 20.04 нам понадобится установить следующие пакеты:

1
sudo apt install syslinux-common pxelinux tftpd-hpa p7zip-full

Скачиваем и извлекаем модуль загрузчика для подключения iSCSI-диска:

1
2
3
4
cd /srv/tftp/
wget https://boot.ipxe.org/ipxe.iso
7z x ipxe.iso ipxe.lkrn
rm ipxe.iso

Копируем загрузчик и набор модулей, которые могут пригодиться (на случай, если нам понадобится меню), в правильное место:

1
2
cp -v /usr/lib/PXELINUX/{pxelinux.0,lpxelinux.0} /srv/tftp/
cp -v /usr/lib/syslinux/modules/bios/{ldlinux.c32,libutil.c32,menu.c32} /srv/tftp/

Создаем файл конфигурации загрузчика:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
mkdir /srv/tftp/pxelinux.cfg

cat << EOF > /srv/tftp/pxelinux.cfg/default
SAY iPXE boot
TIMEOUT 30
DEFAULT ipxe.lkrn
LABEL ipxe.lkrn
 KERNEL ipxe.lkrn
 INITRD linux.ipxe
EOF

Если нужно чтобы разные машины в сети получали собственную конфигурацию для загрузки, то необходимо для каждой создать свой конфигурационный файл. Файл должен находиться в папке /srv/tftp/pxelinux.cfg/ и иметь формат: 01-88-99-aa-bb-cc-dd, где 01 - это префикс, а 88-99-aa-bb-cc-dd - это mac-адрес клиента.

Создаем файл конфигурации модуля ipxe.lkrn, для подключения iSCSI диска и передачи эстафеты загруки с этого диска:

1
2
3
4
5
6
7
cat << EOF > /srv/tftp/linux.ipxe
#!ipxe
dhcp net0
set keep-san 1
#login
sanboot iscsi:192.168.0.10::::iqn.2005-10.org.freenas.ctl:boot1
EOF

Естественно, что в последней строке данного файла, адрес 192.168.0.10 и имя iSCSI-таргета iqn.2005-10.org.freenas.ctl:boot1 могут быть другими.

3 Настройка сетевого хранилища и создание iSCSI таргета

Для простоты администрирования я использую для этих целей TrueNAS CORE. Процесс установки самого TrueNAS описывать не буду, опишу только шаги по настройке самого таргета.

Для начала, в разделе “Storage” необходимо создать пул, внутри которого уже будут создаваться тома (volume) дисков экспортируемых через iSCSI-таргеты. В конечном итоге это будет выглядеть примерно так:

/linux-iscsi-boot/images/freenas_pools.png
TrueNas Pools

После чего, в разделе “Services”, необходимо включить сервис iSCSI. Выглядит это примерно так:

/linux-iscsi-boot/images/truenas_services.png
TrueNAS Services

Далее, в разделе “Sharing ➔ Block Shares (iSCSI)”, нам нужно создать таргет для загрузки. Проще всего это сделать в режиме Wizard-а, где необходимо выполнить чертыре шага:

/linux-iscsi-boot/images/iscsi_wizard_step1.png
TrueNAS iSCSI Wizard step 1
/linux-iscsi-boot/images/iscsi_wizard_step2.png
TrueNAS iSCSI Wizard step 2
/linux-iscsi-boot/images/iscsi_wizard_step3.png
TrueNAS iSCSI Wizard step 3
/linux-iscsi-boot/images/iscsi_wizard_step4.png
TrueNAS iSCSI Wizard step 4
В данном примере важные параметры выделены красным подчеркиванием. Прмер подразумевает самый не безопасный режим экспорта блочного устройчтва, в нем отсутствуют какие либо проверки имени инициатора, имени пользователя, пароля, принадлежности к какой-либо подсети и т.д., но для начала думаю этого хватит, т.к. в последствии это всё можно донастроить при необходимости.

3.1 Подключение/отключение iSCSI диска к системе с которой будет произодиться установка конечной системы

Чтобы работать с iSCSI в системе должен быть установлен пакет open-iscsi:

1
sudo apt-get install -y open-iscsi

Для того чтобы получить список доступных iSCSI-таргетов, выполняем:

1
sudo iscsiadm -m discovery -t st -p 192.168.0.10

В результате мы должны получить примерно такой вывод:

1
192.168.0.10:3260,-1 iqn.2005-10.org.freenas.ctl:boot1

Чтобы подключить данный таргет, выполняем:

1
sudo iscsiadm -m node -l -T iqn.2005-10.org.freenas.ctl:boot1

В случе успешнго подключения, мы должны увидеть примерно следующее:

1
2
Logging in to [iface: default, target: iqn.2005-10.org.freenas.ctl:boot1, portal: 192.168.0.10,3260]
Login to [iface: default, target: iqn.2005-10.org.freenas.ctl:boot1, portal: 192.168.0.10,3260] successful.

Проверяем что устройство появилось в системе, выполнив lsblk. В результате мы должны увидеть новое устройство. В моем случае это выглядело так:

1
2
3
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
...
sdc      8:32   0    40G  0 disk 

Чтобы отключить:

1
sudo iscsiadm -m node -u -T iqn.2005-10.org.freenas.ctl:boot1

4 Создание загрузочного образа / установка системы

Мною было прочитано энное колличество туториалов по организации загрузки с iSCSI диска, и в результате экспериментов по настройке данной функии, в какой-то момент, я устал от повторяющихся действий, где так же, плюс ко всему, можно допустить ошибку, совершая множество действий. В результате я написал bash-скрипт, который выполняет все эти действия автоматически. Скрипт может генерировать образ загрузочного диска заданного размера, либо производить установку прямо на блчное устройство. В начале скрипта, в виде переменных, перечислены основные параметры. Сам скрипт выглядит так:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/bin/bash

IMAGE_SIZE=10           # Image size in gigabytes
MAKE_IMAGE=true         # Installation mode: image / block device
TARGET_DEVICE=boot1.img # Target device or image file
#TARGET_DEVICE=/dev/sdc
BOOT_DISK_SIZE=500M     # Boot partition size
APT_SOURCE='https://mirror.yandex.ru/ubuntu/' # Bootstrap installation packages source
CODENAME='jammy'        # Distribution codename
USERNAME='superadmin'   # System administrator user name (sudo user)
PASSWORD='MegaUltraPa$$word!' # System administrator user password
HOSTNAME='pxe-boot-01'  # System host name
ETH_IF='enp3s0'         # Device for make it unmanaged by Network Manager
ISCSI_INITIATOR='iqn.2005-10.org.freenas.ctl:client1' # iSCSI initiator name
ISCSI_TARGET_NAME='iqn.2005-10.org.freenas.ctl:boot1' # iSCSI target name
ISCSI_TARGET_IP='192.168.0.10'                        # iSCSI target IP

if [ $(whoami) != 'root' ]; then echo "You must be root user!"; exit 0; fi

if [ $MAKE_IMAGE == true ]; then
    if [ ! -f $TARGET_DEVICE ]; then
        echo "Make image file..."
        dd if=/dev/zero of=${TARGET_DEVICE} bs=1M count=$((IMAGE_SIZE * 1024)) conv=fdatasync status=progress
        ERROR=$?; if [ $ERROR -ne 0 ]; then echo "Error creation if image file!"; exit $ERROR; fi
    else
        echo "Image file exists!"
        exit 0
    fi
fi

echo "Create disk partitions..."
echo -e ",${BOOT_DISK_SIZE},L,*\n,+,L\n" | sfdisk -X dos ${TARGET_DEVICE}
ERROR=$?; if [ $ERROR -ne 0 ]; then echo "Error creating disk partitions!"; exit $ERROR; fi

mkdir -p /mnt/bootstrap
if [ $MAKE_IMAGE == true ]; then
    echo "Create filesystem on image partitions..."
    LOOP_DEVICE=$(losetup -f ${TARGET_DEVICE} -P --show)
    mkfs.ext4 ${LOOP_DEVICE}p1
    mkfs.ext4 ${LOOP_DEVICE}p2
    mount ${LOOP_DEVICE}p2 /mnt/bootstrap
    mkdir -p /mnt/bootstrap/boot
    mount ${LOOP_DEVICE}p1 /mnt/bootstrap/boot
else
    echo "Create filesystem on target device partitions..."
    mkfs.ext4 -F ${TARGET_DEVICE}1
    mkfs.ext4 -F ${TARGET_DEVICE}2
    mount ${TARGET_DEVICE}2 /mnt/bootstrap
    mkdir -p /mnt/bootstrap/boot
    mount ${TARGET_DEVICE}1 /mnt/bootstrap/boot
fi
ERROR=$?; if [ $ERROR -ne 0 ]; then echo "Error creating filesystems!"; exit $ERROR; fi
rm -rf /mnt/bootstrap/lost+found
rm -rf /mnt/bootstrap/boot/lost+found

debootstrap --include=linux-image-generic,openssh-server,language-pack-ru,apt,sudo,nano,vim,bash-completion,wget,curl,open-iscsi,initramfs-tools,grub2-common,grub-pc ${CODENAME} /mnt/bootstrap ${APT_SOURCE}
ERROR=$?; if [ $ERROR -ne 0 ]; then echo "Error bootstrap system!"; exit $ERROR; fi

echo "Mount system devices to target for chroot..."
mount --bind /proc /mnt/bootstrap/proc
mount --bind /sys /mnt/bootstrap/sys
mount --bind /dev /mnt/bootstrap/dev
mount --bind /run /mnt/bootstrap/run

echo "Make target system modifications..."
chrun () { chroot /mnt/bootstrap bash -c "$1"; }

echo "Update target hostname..."
echo "${HOSTNAME}" > /mnt/bootstrap/etc/hostname

echo "Configure iSCSI options..."
echo "iscsi" >> /mnt/bootstrap/etc/initramfs-tools/modules
echo "InitiatorName=${ISCSI_INITIATOR}" > /mnt/bootstrap/etc/iscsi/initiatorname.iscsi
touch /mnt/bootstrap/etc/iscsi/iscsi.initramfs
sed -i 's#^\(GRUB_CMDLINE_LINUX_DEFAULT="quiet splash\)"$#\1 ip=dhcp ISCSI_INITIATOR='${ISCSI_INITIATOR}' ISCSI_TARGET_NAME='${ISCSI_TARGET_NAME}' ISCSI_TARGET_IP='${ISCSI_TARGET_IP}' ISCSI_TARGET_PORT=3260"#' /mnt/bootstrap/etc/default/grub

echo "Configure target network..."
cat > /mnt/bootstrap/etc/netplan/10-iscsi-interface.yaml << EOF
network:
    ethernets:
        ${ETH_IF}:
            dhcp4: false
            dhcp6: false
    version: 2
EOF

if [ $MAKE_IMAGE == true ]; then
    BOOT_UUID=$(blkid -s UUID -o value ${LOOP_DEVICE}p1)
    DATA_UUID=$(blkid -s UUID -o value ${LOOP_DEVICE}p2)
else
    BOOT_UUID=$(blkid -s UUID -o value ${TARGET_DEVICE}1)
    DATA_UUID=$(blkid -s UUID -o value ${TARGET_DEVICE}2)
fi

echo "Update /etc/fstab file..."
cat > /mnt/bootstrap/etc/fstab << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
UUID=${DATA_UUID}       /       ext4    errors=remount-ro       0 1
UUID=${BOOT_UUID}       /boot   ext4    defaults                0 2
EOF

echo "Update APT sources..."
cat > /mnt/bootstrap/etc/apt/sources.list << EOF
deb $APT_SOURCE $CODENAME main restricted
deb $APT_SOURCE $CODENAME universe
deb $APT_SOURCE $CODENAME multiverse
#deb $APT_SOURCE $CODENAME partner
deb $APT_SOURCE $CODENAME-security main restricted
deb $APT_SOURCE $CODENAME-security universe
deb $APT_SOURCE $CODENAME-security multiverse
EOF

chrun "apt-get update"

echo "Make administrator user..."
chrun "useradd ${USERNAME} -m -s /bin/bash"
chrun "echo \"${USERNAME}:${PASSWORD}\" | chpasswd"
chrun "usermod -aG sudo ${USERNAME}"

echo "Install GRUB to target system..."
if [ $MAKE_IMAGE == true ]; then
    chrun "grub-install ${LOOP_DEVICE}"
else
    chrun "grub-install ${TARGET_DEVICE}"
fi
chrun "update-grub"

echo "Update intiramfs..."
chrun "update-initramfs -u"

echo "Unmount all mounted resources..."
umount /mnt/bootstrap/proc
umount /mnt/bootstrap/dev
umount /mnt/bootstrap/sys
umount /mnt/bootstrap/run
umount /mnt/bootstrap/boot 
umount /mnt/bootstrap
if [ $MAKE_IMAGE == true ]; then losetup -d ${LOOP_DEVICE}; fi

Если приведенный выше скрипт был запущен в режиме создания файла-образа, то перенести его на результирующий диск можно с помощью приведенной ниже команды:

1
sudo dd if=/tmp/boot1.img of=/dev/sdc bs=1M conv=fdatasync status=progress

После выполнения всех операций по установке системы следует, на всякий случай, выполнить комманду sync и отключить iSCSI-устройство.

Если всё получилось, то теперь можно пробовать загрузиться в новоиспеченную систему и размышлять над тем как это всё модифицировать под собственные нужды.

Еще, в качестве бонуса, приведу модифицированную версию скрипта установки, целью которого была установка Linux Mint 21 (Vanessa). Для его использования необходимо обратить внимание на то что он копирует GPG-ключ от репозитория Linux Mint в результирующую систему, который нужно сначала где-то взять. Мне это было легко, т.к. я под ней и работаю и у меня этот ключ есть на локальном диске. Вы можете извлечь его из офицального образа и расположить в папке resources рядом со скриптом.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#!/bin/bash

IMAGE_SIZE=10 # Image size in gigabytes
MAKE_IMAGE=true
TARGET_DEVICE=mint.img
#TARGET_DEVICE=/dev/sdc
BOOT_DISK_SIZE=500M
APT_SOURCE='https://mirror.yandex.ru/ubuntu/'
CODENAME='jammy'
USERNAME='superadmin'
PASSWORD='MegaPa$$word!'
HOSTNAME='pxe-boot-01'
ETH_IF='enp3s0'
ISCSI_INITIATOR='iqn.2005-10.org.freenas.ctl:client1'
ISCSI_TARGET_NAME='iqn.2005-10.org.freenas.ctl:boot1'
ISCSI_TARGET_IP='192.168.0.10'

C_YELLOW='\033[1;33m'; C_RED='\033[1;31m'; C_NC='\033[0m'
if [ $(whoami) != 'root' ]; then echo "You must be root user!"; exit 0; fi

if [ $MAKE_IMAGE == true ]; then
    if [ ! -f $TARGET_DEVICE ]; then
        echo -e "${C_YELLOW}Make image file...${C_NC}"
        dd if=/dev/zero of=${TARGET_DEVICE} bs=1M count=$((IMAGE_SIZE * 1024)) conv=fdatasync status=progress
        ERROR=$?; if [ $ERROR -ne 0 ]; then echo "Error creation if image file!"; exit $ERROR; fi
    else
        echo -e "${C_RED}Image file exists!${C_NC}"
        exit 0
    fi
fi

echo -e "${C_YELLOW}Create disk partitions...${C_NC}"
echo -e ",${BOOT_DISK_SIZE},L,*\n,+,L\n" | sfdisk -X dos ${TARGET_DEVICE}
ERROR=$?; if [ $ERROR -ne 0 ]; then echo -e "${C_RED}Error creating disk partitions!${C_NC}"; exit $ERROR; fi

mkdir -p /mnt/bootstrap
if [ $MAKE_IMAGE == true ]; then
    echo -e "${C_YELLOW}Create filesystem on image partitions...${C_NC}"
    LOOP_DEVICE=$(losetup -f ${TARGET_DEVICE} -P --show)
    mkfs.ext4 ${LOOP_DEVICE}p1
    mkfs.ext4 ${LOOP_DEVICE}p2
    mount ${LOOP_DEVICE}p2 /mnt/bootstrap
    mkdir -p /mnt/bootstrap/boot
    mount ${LOOP_DEVICE}p1 /mnt/bootstrap/boot
else
    echo -e "${C_YELLOW}Create filesystem on target device partitions...${C_NC}"
    mkfs.ext4 -F ${TARGET_DEVICE}1
    mkfs.ext4 -F ${TARGET_DEVICE}2
    mount ${TARGET_DEVICE}2 /mnt/bootstrap
    mkdir -p /mnt/bootstrap/boot
    mount ${TARGET_DEVICE}1 /mnt/bootstrap/boot
fi
ERROR=$?; if [ $ERROR -ne 0 ]; then echo -e "${C_RED}Error creating filesystems!${C_NC}"; exit $ERROR; fi
rm -rf /mnt/bootstrap/lost+found
rm -rf /mnt/bootstrap/boot/lost+found

debootstrap --include=linux-image-generic,openssh-server,language-pack-ru,apt,sudo,nano,vim,bash-completion,wget,curl,open-iscsi,initramfs-tools,grub2-common,grub-pc ${CODENAME} /mnt/bootstrap ${APT_SOURCE}
ERROR=$?; if [ $ERROR -ne 0 ]; then echo -e "${C_RED}Error bootstrap system!"; exit $ERROR; fi

echo -e "${C_YELLOW}Mount system devices to target for chroot...${C_NC}"
mount --bind /proc /mnt/bootstrap/proc
mount --bind /sys /mnt/bootstrap/sys
mount --bind /dev /mnt/bootstrap/dev
mount --bind /run /mnt/bootstrap/run

echo -e "${C_YELLOW}Make target system modifications...${C_NC}"
chrun () { chroot /mnt/bootstrap bash -c "$1"; }

echo -e "${C_YELLOW}Update target hostname...${C_NC}"
echo "${HOSTNAME}" > /mnt/bootstrap/etc/hostname

echo -e "${C_YELLOW}Configure iSCSI options...${C_NC}"
echo "iscsi" >> /mnt/bootstrap/etc/initramfs-tools/modules
echo "InitiatorName=${ISCSI_INITIATOR}" > /mnt/bootstrap/etc/iscsi/initiatorname.iscsi
touch /mnt/bootstrap/etc/iscsi/iscsi.initramfs
sed -i 's#^\(GRUB_CMDLINE_LINUX_DEFAULT="quiet splash\)"$#\1 ip=dhcp ISCSI_INITIATOR='${ISCSI_INITIATOR}' ISCSI_TARGET_NAME='${ISCSI_TARGET_NAME}' ISCSI_TARGET_IP='${ISCSI_TARGET_IP}' ISCSI_TARGET_PORT=3260"#' /mnt/bootstrap/etc/default/grub

echo -e "${C_YELLOW}Configure target network...${C_NC}"
cat > /mnt/bootstrap/etc/netplan/10-iscsi-interface.yaml << EOF
network:
    ethernets:
        ${ETH_IF}:
            dhcp4: false
            dhcp6: false
    version: 2
EOF

if [ $MAKE_IMAGE == true ]; then
    BOOT_UUID=$(blkid -s UUID -o value ${LOOP_DEVICE}p1)
    DATA_UUID=$(blkid -s UUID -o value ${LOOP_DEVICE}p2)
else
    BOOT_UUID=$(blkid -s UUID -o value ${TARGET_DEVICE}1)
    DATA_UUID=$(blkid -s UUID -o value ${TARGET_DEVICE}2)
fi

echo -e "${C_YELLOW}Update /etc/fstab file...${C_NC}"
cat > /mnt/bootstrap/etc/fstab << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
UUID=${DATA_UUID}       /       ext4    errors=remount-ro       0 1
UUID=${BOOT_UUID}       /boot   ext4    defaults                0 2
EOF

echo -e "${C_YELLOW}Update lsb_release file...${C_NC}"
cat > /mnt/bootstrap/etc/lsb-release << EOF
DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=21
DISTRIB_CODENAME=vanessa
DISTRIB_DESCRIPTION="Linux Mint 21 Vanessa"
EOF

echo -e "${C_YELLOW}Add Linux Mint GPG key file...${C_NC}"
cp -v ./resources/linuxmint-keyring.gpg /mnt/bootstrap/etc/apt/trusted.gpg.d/

echo -e "${C_YELLOW}Update APT sources...${C_NC}"
cat > /mnt/bootstrap/etc/apt/sources.list.d/official-package-repositories.list << EOF
deb http://packages.linuxmint.com vanessa main upstream import backport #id:linuxmint_main

deb $APT_SOURCE $CODENAME main restricted universe multiverse
deb $APT_SOURCE $CODENAME-updates main restricted universe multiverse
deb $APT_SOURCE $CODENAME-backports main restricted universe multiverse

deb http://security.ubuntu.com/ubuntu/ $CODENAME-security main restricted universe multiverse
# deb http://archive.canonical.com/ubuntu/ $CODENAME partner
EOF

cat > /mnt/bootstrap/etc/apt/sources.list << EOF
# This system was installed using small removable media
# (e.g. netinst, live or single CD). The matching "deb cdrom"
# entries were disabled at the end of the installation process.
# For information about how to configure apt package sources,
# see the sources.list(5) manual.
EOF

cat > /mnt/bootstrap/etc/apt/preferences.d/nosnap.pref << EOF
# To prevent repository packages from triggering the installation of Snap,
# this file forbids snapd from being installed by APT.
# For more information: https://linuxmint-user-guide.readthedocs.io/en/latest/snap.html

Package: snapd
Pin: release a=*
Pin-Priority: -10
EOF

cat > /mnt/bootstrap/etc/apt/preferences.d/official-extra-repositories.pref << EOF
Package: *
Pin: origin "build.linuxmint.com"
Pin-Priority: 700
EOF

cat > /mnt/bootstrap/etc/apt/preferences.d/official-package-repositories.pref << EOF
Package: *
Pin: origin live.linuxmint.com
Pin-Priority: 750

Package: *
Pin: release o=linuxmint,c=upstream
Pin-Priority: 700

Package: *
Pin: release o=LP-PPA-linuxmint-daily-build-team-daily-builds
Pin-Priority: 700
EOF

chrun "apt-get update"

echo -e "${C_YELLOW}Install Linux Mint packages...${C_NC}"

chrun "DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true apt-get install -o DPkg::Options::=\"--force-confnew\" -y cinnamon-desktop-environment lightdm mint-backgrounds-vanessa"
echo "/usr/sbin/lightdm" > /mnt/bootstrap/etc/X11/default-display-manager
chrun "DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true dpkg-reconfigure lightdm; echo set shared/default-x-display-manager lightdm | debconf-communicate"

echo -e "${C_YELLOW}Make administrator user..."
chrun "useradd ${USERNAME} -m -s /bin/bash"
chrun "echo \"${USERNAME}:${PASSWORD}\" | chpasswd"
chrun "usermod -aG sudo ${USERNAME}"

echo -e "${C_YELLOW}Install GRUB to target system...${C_NC}"
if [ $MAKE_IMAGE == true ]; then
    chrun "grub-install ${LOOP_DEVICE}"
else
    chrun "grub-install ${TARGET_DEVICE}"
fi
chrun "update-grub"

echo -e "${C_YELLOW}Update intiramfs...${C_NC}"
chrun "update-initramfs -u"

echo -e "${C_YELLOW}Unmount all mounted resources...${C_NC}"
umount /mnt/bootstrap/proc
umount /mnt/bootstrap/dev
umount /mnt/bootstrap/sys
umount /mnt/bootstrap/run
umount /mnt/bootstrap/boot
umount /mnt/bootstrap
if [ $MAKE_IMAGE == true ]; then losetup -d ${LOOP_DEVICE}; fi