You need to sign in or sign up before continuing.
98-add-lxc-system-generator 6.69 KB
Newer Older
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 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
#!/bin/sh

# This scripts is borrowed from distrobuilder.
# awk '/content := `/ {match($0, /.*`([^`]*)/, l); print l[1]; a=1; next} /^`$/ {a=0} a {print $0}' < main.go
# Probably, it will need to be updated from time to time.

[ -h /sbin/init ] || exit 0

generators_dir=/etc/systemd/system-generators
mkdir -p "$generators_dir"

cat > "$generators_dir"/lxc <<'@@@'
#!/bin/sh
# NOTE: systemctl is not available for systemd-generators
set -eu

## Helper functions
# is_lxc_container succeeds if we're running inside a LXC container
is_lxc_container() {
	grep -qa container=lxc /proc/1/environ
}

# is_lxd_vm succeeds if we're running inside a LXD VM
is_lxd_vm() {
	[ -e /dev/virtio-ports/org.linuxcontainers.lxd ]
}

# is_in_path succeeds if the given file exists in on of the paths
is_in_path() {
	# Don't use $PATH as that may not include all relevant paths
	for path in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin; do
		[ -e "${path}/$1" ] && return 0
	done

	return 1
}

## Fix functions
# fix_ro_paths avoids udevd issues with /sys and /proc being writable
fix_ro_paths() {
	mkdir -p /run/systemd/system/$1.d
	cat <<-EOF > /run/systemd/system/$1.d/zzz-lxc-ropath.conf
[Service]
BindReadOnlyPaths=/sys /proc
EOF
}

# fix_nm_force_up sets up a unit override to force NetworkManager to start the system connection
fix_nm_force_up() {
	# Check if the device exists
	[ -e "/sys/class/net/$1" ] || return 0

	# Check if NetworkManager exists
	[ "${nm_exists}" -eq 1 ] || return 0

	cat <<-EOF > /run/systemd/system/network-connection-activate.service
[Unit]
Description=Activate connection
After=NetworkManager.service NetworkManager-wait-online.service

[Service]
ExecStart=-/usr/bin/nmcli c up "System $1"
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=default.target
EOF

	mkdir -p /run/systemd/system/default.target.wants
	ln -sf /run/systemd/system/network-connection-activate.service /run/systemd/system/default.target.wants/network-connection-activate.service
}

# fix_nm_link_state forces the network interface to a DOWN state ahead of NetworkManager starting up
fix_nm_link_state() {
	[ -e "/sys/class/net/$1" ] || return 0

	ip_path=
	if [ -f /sbin/ip ]; then
		ip_path=/sbin/ip
	elif [ -f /bin/ip ]; then
		ip_path=/bin/ip
	else
		return 0
	fi

	cat <<-EOF > /run/systemd/system/network-device-down.service
[Unit]
Description=Turn off network device
Before=NetworkManager.service

[Service]
ExecStart=-${ip_path} link set $1 down
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=default.target
EOF

	mkdir -p /run/systemd/system/default.target.wants
	ln -sf /run/systemd/system/network-device-down.service /run/systemd/system/default.target.wants/network-device-down.service
}

# fix_systemd_override_unit generates a unit specific override
fix_systemd_override_unit() {
	dropin_dir="/run/systemd/${1}.d"
	mkdir -p "${dropin_dir}"
	echo "[Service]" > "${dropin_dir}/zzz-lxc-service.conf"
	[ "${systemd_version}" -ge 247 ] && echo "ProtectProc=default" >> "${dropin_dir}/zzz-lxc-service.conf"
	[ "${systemd_version}" -ge 232 ] && echo "ProtectControlGroups=no" >> "${dropin_dir}/zzz-lxc-service.conf"
	[ "${systemd_version}" -ge 232 ] && echo "ProtectKernelTunables=no" >> "${dropin_dir}/zzz-lxc-service.conf"

	# Additional settings for privileged containers
	if grep -q 4294967295 /proc/self/uid_map; then
		echo "ProtectHome=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		echo "ProtectSystem=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		echo "PrivateDevices=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		echo "PrivateTmp=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		[ "${systemd_version}" -ge 244 ] && echo "ProtectKernelLogs=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		[ "${systemd_version}" -ge 232 ] && echo "ProtectKernelModules=no" >> "${dropin_dir}/zzz-lxc-service.conf"
		echo "ReadWritePaths=" >> "${dropin_dir}/zzz-lxc-service.conf"
	fi
}

# fix_systemd_mask masks the systemd unit
fix_systemd_mask() {
	ln -sf /dev/null /run/systemd/system/$1
}

# fix_systemd_udev_trigger overrides the systemd-udev-trigger.service to match the latest version
# of the file which uses "ExecStart=-" instead of "ExecStart=".
fix_systemd_udev_trigger() {
	mkdir -p /run/systemd/system/systemd-udev-trigger.service.d
	cat <<-EOF > /run/systemd/system/systemd-udev-trigger.service.d/zzz-lxc-override.conf
[Service]
ExecStart=
ExecStart=-udevadm trigger --type=subsystems --action=add
ExecStart=-udevadm trigger --type=devices --action=add
EOF
}

# fix_systemd_sysctl overrides the systemd-sysctl.service to use "ExecStart=-" instead of "ExecStart=".
fix_systemd_sysctl() {
	mkdir -p /run/systemd/system/systemd-sysctl.service.d
	cat <<-EOF > /run/systemd/system/systemd-sysctl.service.d/zzz-lxc-override.conf
[Service]
ExecStart=
ExecStart=-/usr/lib/systemd/systemd-sysctl
EOF
}

## Main logic
# Exit immediately if not a LXC/LXD container or VM
if ! is_lxd_vm && ! is_lxc_container; then
	exit
fi

# Check for NetworkManager and cloud-init
nm_exists=0
cloudinit_exists=0

is_in_path NetworkManager && nm_exists=1
is_in_path cloud-init && cloudinit_exists=1

# Determine systemd version
for path in /usr/lib/systemd/systemd /lib/systemd/systemd; do
	[ -x "${path}" ] || continue

	systemd_version="$("${path}" --version | head -n1 | cut -d' ' -f2)"
	break
done

# Determine distro name and release
ID=""
VERSION_ID=""
if [ -e /etc/os-release ]; then
	. /etc/os-release
fi

# Apply systemd overrides
if [ "${systemd_version}" -ge 244 ]; then
	fix_systemd_override_unit system/service
else
	# Setup per-unit overrides
	find /lib/systemd /etc/systemd /run/systemd /usr/lib/systemd -name "*.service" -type f | sed 's#/\(lib\|etc\|run\|usr/lib\)/systemd/##g'| while read -r service_file; do
		fix_systemd_override_unit "${service_file}"
	done
fi

# Workarounds for all containers
if is_lxc_container; then
	fix_systemd_udev_trigger
	fix_systemd_sysctl
	fix_systemd_mask dev-hugepages.mount
	fix_systemd_mask systemd-journald-audit.socket
	fix_systemd_mask run-ribchester-general.mount
	fix_systemd_mask systemd-modules-load.service
	fix_systemd_mask systemd-pstore.service
	fix_systemd_mask ua-messaging.service
	if [ ! -e /dev/tty1 ]; then
		fix_systemd_mask vconsole-setup-kludge@tty1.service
	fi
	if ! grep -q 4294967295 /proc/self/uid_map && { [ "${ID}" = "altlinux" ] || [ "${ID}" = "arch" ] || [ "${ID}" = "fedora" ]; }; then
		fix_ro_paths systemd-networkd.service
		fix_ro_paths systemd_resolved.servce
	fi
fi

# Workarounds for cloud containers
if is_lxc_container && { [ "${ID}" = "fedora" ] || [ "${ID}" = "rhel" ]; } && [ "${cloudinit_exists}" -eq 1 ]; then
	fix_nm_force_up eth0
fi

# Workarounds for NetworkManager in containers
if [ "${nm_exists}" -eq 1 ]; then
	if [ "${ID}" = "ol" ] || [ "${ID}" = "centos" ]; then
		fix_nm_force_up eth0
	fi

	fix_nm_link_state eth0
fi
@@@

chmod 755 "$generators_dir"/lxc