logo

scripts

A bunch of scripts, some to be moved to their own repository git clone https://hacktivis.me/git/scripts.git

lxc-abyss (8916B)


  1. #!/bin/sh
  2. # vim: set ts=4:
  3. # Exit on error and treat unset variables as an error.
  4. set -eu
  5. #
  6. # LXC template for Abyss Linux, based on Alpine one
  7. #
  8. # Note: Do not replace tabs with spaces, it would break heredocs!
  9. # Authors:
  10. # Jakub Jirutka <jakub@jirutka.cz>
  11. # Haelwenn (lanodan) Monnier <contact+abyss@hacktivis.me>
  12. # This library is free software; you can redistribute it and/or
  13. # modify it under the terms of the GNU Lesser General Public
  14. # License as published by the Free Software Foundation; either
  15. # version 2.1 of the License, or (at your option) any later version.
  16. #
  17. # This library is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. # Lesser General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU Lesser General Public
  23. # License along with this library; if not, write to the Free Software
  24. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  25. #=========================== Constants ============================#
  26. # Make sure the usual locations are in PATH
  27. export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
  28. readonly LOCAL_STATE_DIR='/var/lib'
  29. readonly LXC_TEMPLATE_CONFIG='/usr/share/lxc/config'
  30. #readonly LXC_CACHE_DIR="${LXC_CACHE_PATH:-"$LOCAL_STATE_DIR/cache/lxc"}/abyss"
  31. readonly LXC_CACHE_DIR="/var/cache/apk"
  32. readonly DEFAULT_MIRROR_URL='https://mirror.getabyss.com/abyss'
  33. : ${APK:=$(command -v apk || true)}
  34. if [ ! -x "$APK" ]; then
  35. APK="$LXC_CACHE_DIR/bootstrap/usr/bin/apk"
  36. fi
  37. readonly APK
  38. #======================== Helper Functions ========================#
  39. usage() {
  40. cat <<-EOF
  41. Template specific options can be passed to lxc-create after a '--' like this:
  42. lxc-create --name=NAME [lxc-create-options] -- [template-options] [PKG...]
  43. PKG Additional APK package(s) to install into the container.
  44. Template options:
  45. -a ARCH, --arch=ARCH The container architecture (e.g. x86, x86_64); defaults
  46. to the host arch.
  47. -d, --debug Run this script in a debug mode (set -x and wget w/o -q).
  48. -m URL --mirror=URL The Abyss mirror to use; defaults to $DEFAULT_MIRROR_URL.
  49. Environment variables:
  50. APK The apk-tools binary to use when building rootfs. If not set
  51. or not executable and apk is not on PATH, then the script
  52. will download the latest apk-tools.
  53. LXC_CACHE_PATH Path to the cache directory where to store bootstrap files
  54. and APK packages.
  55. EOF
  56. }
  57. die() {
  58. local retval=$1; shift
  59. printf 'ERROR: %s\n' "$@" 1>&2
  60. exit $retval
  61. }
  62. einfo() {
  63. printf "\n==> $1\n"
  64. }
  65. fetch() {
  66. if [ "$DEBUG" = 'yes' ]; then
  67. wget -T 10 -O - $@
  68. else
  69. wget -T 10 -O - -q $@
  70. fi
  71. }
  72. parse_arch() {
  73. case "$1" in
  74. x86 | i[3-6]86) echo 'x86';;
  75. x86_64 | amd64) echo 'x86_64';;
  76. aarch64 | arm64) echo 'aarch64';;
  77. armv7) echo 'armv7';;
  78. arm*) echo 'armhf';;
  79. ppc64le) echo 'ppc64le';;
  80. *) return 1;;
  81. esac
  82. }
  83. run_exclusively() {
  84. local lock_name="$1"
  85. local timeout=$2
  86. shift 2
  87. mkdir -p "$LOCAL_STATE_DIR/lock/subsys"
  88. local retval
  89. {
  90. echo -n "Obtaining an exclusive lock..."
  91. if ! flock -x 9; then
  92. echo ' failed.'
  93. return 1
  94. fi
  95. echo ' done'
  96. "$@"; retval=$?
  97. } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-alpine-$lock_name"
  98. return $retval
  99. }
  100. #============================ Bootstrap ===========================#
  101. bootstrap() {
  102. if [ ! -x "$APK" ]; then
  103. einfo 'Fetching apk-tools static binary'
  104. local host_arch=$(parse_arch $(uname -m))
  105. fetch_apk_static "$LXC_CACHE_DIR/bootstrap" "$host_arch"
  106. fi
  107. }
  108. fetch_apk_static() {
  109. local dest="$1"
  110. local arch="$2"
  111. local pkg_name='apk-tools'
  112. mkdir -p "$dest"
  113. local pkg_ver=$(fetch "$MIRROR_URL/core/$arch/APKINDEX.tar.gz" \
  114. | tar -xzO APKINDEX \
  115. | sed -n "/P:${pkg_name}/,/^$/ s/V:\(.*\)$/\1/p")
  116. [ -n "$pkg_ver" ] || die 2 "Cannot find a version of $pkg_name in APKINDEX"
  117. fetch "$MIRROR_URL/core/$arch/${pkg_name}-${pkg_ver}.apk" \
  118. | tar -xz -C "$dest" usr/bin/ # --extract --gzip --directory
  119. [ -s "$dest/usr/bin/apk" ] || die 2 'apk not found'
  120. # Note: apk doesn't return 0 for --version
  121. local out="$("$dest"/usr/bin/apk --version)"
  122. echo "$out"
  123. [ "${out%% *}" = 'apk-tools' ] || die 3 'apk --version failed'
  124. }
  125. #============================ Install ============================#
  126. install() {
  127. local dest="$1"
  128. local arch="$2"
  129. local extra_packages="$3"
  130. einfo "Installing Abyss Linux in $dest"
  131. cd "$dest"
  132. mkdir -p usr/bin usr/lib
  133. ln -s bin usr/sbin
  134. ln -s usr/bin bin
  135. ln -s usr/bin sbin
  136. ln -s usr/lib lib
  137. mkdir -p etc/apk
  138. ln -s /var/cache/apk etc/apk/cache
  139. install_packages "$arch" "$dest" "filesystem"
  140. install_packages "$arch" "$dest" "abyss-keyring ca-certificates apk-tools busybox openrc"
  141. install_packages "$arch" "$dest" "$extra_packages"
  142. busybox --install -s ./usr/bin
  143. $APK -X "$MIRROR_URL/core" --update-cache --allow-untrusted --arch="$arch" --root="$dest" fix || true
  144. echo "$MIRROR_URL/core" >> etc/apk/repositories
  145. # Busybox has getty but /etc/init.d/agetty is configured for agetty
  146. # which is provided by util-linux
  147. install_packages "$arch" "$dest" "util-linux"
  148. ln -sf openrc-init ./usr/bin/init
  149. for i in 1 2 3 4 5 6
  150. do
  151. ln -s agetty ./etc/init.d/agetty.tty"$i"
  152. ln -s /etc/init.d/agetty.tty"$i" ./etc/runlevels/boot/
  153. done
  154. chroot . /bin/true \
  155. || die 3 'Failed to execute /bin/true in chroot, the builded rootfs is broken!'
  156. rm etc/apk/cache
  157. cd - >/dev/null
  158. }
  159. install_packages() {
  160. local arch="$1"
  161. local dest="$2"
  162. local packages="$3"
  163. $APK -v -X "$MIRROR_URL/core" --update-cache --allow-untrusted --arch="$arch" --root="$dest" \
  164. --initdb add $packages || true
  165. }
  166. #=========================== Configure ===========================#
  167. configure_container() {
  168. local config="$1"
  169. local hostname="$2"
  170. local arch="$3"
  171. cat <<-EOF >> "$config"
  172. # Specify container architecture.
  173. lxc.arch = $arch
  174. # Set hostname.
  175. lxc.uts.name = $hostname
  176. # If something doesn't work, try to comment this out.
  177. # Dropping sys_admin disables container root from doing a lot of things
  178. # that could be bad like re-mounting lxc fstab entries rw for example,
  179. # but also disables some useful things like being able to nfs mount, and
  180. # things that are already namespaced with ns_capable() kernel checks, like
  181. # hostname(1).
  182. lxc.cap.drop = sys_admin
  183. # Comment this out if you have to debug processes by tracing.
  184. lxc.cap.drop = sys_ptrace
  185. # Comment this out if required by your applications.
  186. lxc.cap.drop = setpcap
  187. # Include common configuration.
  188. lxc.include = $LXC_TEMPLATE_CONFIG/alpine.common.conf
  189. EOF
  190. }
  191. #============================= Main ==============================#
  192. if [ "$(id -u)" != "0" ]; then
  193. die 1 "This script must be run as 'root'"
  194. fi
  195. # Parse command options.
  196. options=$(getopt -o a:dFm:n:p:r:h -l arch:,debug,mirror:,name:,\
  197. path:,release:,rootfs:,help,mapped-uid:,mapped-gid: -- "$@")
  198. eval set -- "$options"
  199. # Clean variables and set defaults.
  200. arch="$(uname -m)"
  201. debug='no'
  202. mirror_url=
  203. name=
  204. path=
  205. rootfs=
  206. # Process command options.
  207. while [ $# -gt 0 ]; do
  208. case $1 in
  209. -a | --arch)
  210. arch=$2; shift 2
  211. ;;
  212. -d | --debug)
  213. debug='yes'; shift 1
  214. ;;
  215. -m | --mirror)
  216. mirror_url=$2; shift 2
  217. ;;
  218. -n | --name)
  219. name=$2; shift 2
  220. ;;
  221. -p | --path)
  222. path=$2; shift 2
  223. ;;
  224. --rootfs)
  225. rootfs=$2; shift 2
  226. ;;
  227. -h | --help)
  228. usage; exit 0
  229. ;;
  230. --)
  231. shift; break
  232. ;;
  233. --mapped-[ug]id)
  234. die 1 "This template can't be used for unprivileged containers." \
  235. 'You may want to try the "download" template instead.'
  236. ;;
  237. *)
  238. echo "Unknown option: $1" 1>&2
  239. usage; exit 1
  240. ;;
  241. esac
  242. done
  243. extra_packages="$@"
  244. [ "$debug" = 'yes' ] && set -x
  245. # Set global variables.
  246. readonly DEBUG="$debug"
  247. readonly MIRROR_URL="${mirror_url:-$DEFAULT_MIRROR_URL}"
  248. # Validate options.
  249. [ -n "$name" ] || die 1 'Missing required option --name'
  250. [ -n "$path" ] || die 1 'Missing required option --path'
  251. if [ -z "$rootfs" ] && [ -f "$path/config" ]; then
  252. rootfs="$(sed -nE 's/^lxc.rootfs.path\s*=\s*(.*)$/\1/p' "$path/config")"
  253. fi
  254. if [ -z "$rootfs" ]; then
  255. rootfs="$path/rootfs"
  256. fi
  257. arch=$(parse_arch "$arch") \
  258. || die 1 "Unsupported architecture: $arch"
  259. # Here we go!
  260. run_exclusively 'bootstrap' 10 bootstrap
  261. run_exclusively "$arch" 30 install "$rootfs" "$arch" "$extra_packages"
  262. configure_container "$path/config" "$name" "$arch"
  263. einfo "Container's rootfs and config have been created"
  264. cat <<-EOF
  265. Edit the config file $path/config to check/enable networking setup.
  266. The installed system isn't configured for any networking setup, but lxc.network.type="none" (no additionnal network, reuse the host one) works out of the box.
  267. To start the container, run "lxc-start -n $name".
  268. As root isn't locked with a password, you can already enter via "lxc-console -n $name".
  269. EOF