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
|
#!/usr/bin/env bash
set -Eeuo pipefail
thisDir="$(dirname "$(readlink -vf "$BASH_SOURCE")")"
source "$thisDir/.constants.sh" \
--flags 'debian,debian-eol,debian-ports,non-debian' \
--flags 'debootstrap:' \
--flags 'debootstrap-script:' \
--flags 'keyring:,arch:,include:,exclude:' \
--flags 'merged-usr,no-merged-usr' \
--flags 'check-gpg,no-check-gpg' \
-- \
'<target-dir> <suite> <timestamp>' \
'rootfs stretch 2017-05-08T00:00:00Z
--debian-eol rootfs squeeze 2016-03-14T00:00:00Z' \
\
'--non-debian [--debootstrap-script=xyz] <target-dir> <suite> <mirror>' \
'--non-debian rootfs xenial http://archive.ubuntu.com/ubuntu'
eval "$dgetopt"
nonDebian=
debianEol=
debianPorts=
debootstrap=
script=
keyring=
arch=
include=
exclude=
noMergedUsr=
noCheckGpg=
while true; do
flag="$1"; shift
dgetopt-case "$flag"
case "$flag" in
--debian) nonDebian= ;;
--debian-eol) nonDebian= ; debianEol=1 ;;
--debian-ports) nonDebian= ; debianPorts=1 ;;
--non-debian) nonDebian=1 ;;
--debootstrap) debootstrap="$1"; shift ;;
--debootstrap-script) script="$1"; shift ;;
--keyring) keyring="$1"; shift ;;
--arch) arch="$1"; shift ;;
--include) include="${include:+$include,}$1"; shift ;;
--exclude) exclude="${exclude:+$exclude,}$1"; shift ;;
--merged-usr) noMergedUsr= ;;
--no-merged-usr) noMergedUsr=1 ;;
--check-gpg) noCheckGpg= ;;
--no-check-gpg) noCheckGpg=1 ;;
--) break ;;
*) eusage "unknown flag '$flag'" ;;
esac
done
targetDir="${1:-}"; shift || eusage 'missing target-dir'
[ -n "$targetDir" ] || eusage 'target-dir required' # must be non-empty
if [ -e "$targetDir" ] && [ -z "$(find "$targetDir" -maxdepth 0 -empty)" ]; then
echo >&2 "error: '$targetDir' already exists (and isn't empty)!"
exit 1
fi
suite="${1:-}"; shift || eusage 'missing suite'
timestamp=
mirror=
if [ -z "$nonDebian" ]; then
timestamp="${1:-}"; shift || eusage 'missing timestamp'
else
mirror="${1:-}"; shift || eusage 'missing mirror'
timestamp="$(
{
wget -qO- "$mirror/dists/$suite/InRelease" 2>/dev/null \
|| wget -qO- "$mirror/dists/$suite/Release" 2>/dev/null
} | awk -F ': ' '$1 == "Date" { print $2; exit }'
)" || :
[ -n "$timestamp" ]
# see "debuerreotype-recalculate-epoch" for a simple way to update this value appropriately after "sources.list" is updated and "apt-get update" has been run (which will be more accurate)
fi
epoch="$(date --date "$timestamp" '+%s')"
export SOURCE_DATE_EPOCH="$epoch"
if [ -z "$nonDebian" ]; then
dpkgArch="${arch:-$(dpkg --print-architecture | awk -F- '{ print $NF }')}"
mirrorArgs=()
if [ -n "$debianPorts" ]; then
mirrorArgs+=( --ports )
fi
if [ -n "$debianEol" ]; then
mirrorArgs+=( --eol )
fi
mirrorArgs+=( "@$epoch" "$suite" "$dpkgArch" main )
mirrors="$("$thisDir/.debian-mirror.sh" "${mirrorArgs[@]}")"
eval "$mirrors"
[ -n "$snapshotMirror" ]
mirror="$snapshotMirror"
fi
debootstrapArgs=()
if [ -z "$noCheckGpg" ]; then
debootstrapArgs+=( --force-check-gpg )
else
debootstrapArgs+=( --no-check-gpg )
fi
: "${script:=$suite}"
script="$(
if [ -s "$thisDir/.debootstrap-scripts/$script" ]; then
readlink -vf "$thisDir/.debootstrap-scripts/$script"
elif [ -s "/usr/share/debootstrap/scripts/$script" ]; then
readlink -vf "/usr/share/debootstrap/scripts/$script"
else
readlink -vf "$script"
fi
)"
if grep -q 'minbase' "$script"; then
# --debian-eol sarge and older do not support minbase
debootstrapArgs+=( --variant=minbase )
fi
[ -n "$noMergedUsr" ] && debootstrapArgs+=( --no-merged-usr ) || debootstrapArgs+=( --merged-usr )
[ -z "$keyring" ] || debootstrapArgs+=( --keyring="$keyring" )
[ -z "$arch" ] || debootstrapArgs+=( --arch="$arch" )
[ -z "$include" ] || debootstrapArgs+=( --include="$include" )
[ -z "$exclude" ] || debootstrapArgs+=( --exclude="$exclude" )
debootstrapArgs+=(
"$suite" "$targetDir" "$mirror" "$script"
)
unshare-debootstrap() {
# avoid bugs in packages that might leave things mounted ("/run/shm" is a common one that sometimes ends up dangling)
unshare --mount debootstrap "$@"
}
: "${debootstrap:=unshare-debootstrap}"
if ! "$debootstrap" "${debootstrapArgs[@]}"; then
if [ -f "$targetDir/debootstrap/debootstrap.log" ]; then
echo >&2
echo >&2 "error: '$debootstrap' failed!"
echo >&2
echo >&2 ' Full command:'
echo >&2
echo >&2 " $(printf ' %q' "$debootstrap" "${debootstrapArgs[@]}")"
echo >&2
echo >&2 ' Logs:'
echo >&2
cat >&2 "$targetDir/debootstrap/debootstrap.log"
echo >&2
fi
exit 1
fi
echo "$epoch" > "$targetDir/debuerreotype-epoch"
if [ -z "$nonDebian" ]; then
"$thisDir/debuerreotype-debian-sources-list" --snapshot \
$([ -z "$debianEol" ] || echo '--eol') \
$([ -z "$debianPorts" ] || echo '--ports') \
"$targetDir" "$suite"
"$thisDir/debuerreotype-apt-get" "$targetDir" update -qq
fi
# since we're minbase, we know everything included is either essential, or a dependency of essential, so let's get clean "apt-mark showmanual" output
"$thisDir/debuerreotype-chroot" "$targetDir" bash -c '
# --debian-eol squeeze and below do not have python in minbase, thus "apt-mark" fails to run
# bash: /usr/bin/apt-mark: /usr/bin/python: bad interpreter: No such file or directory
# (also, squeeze APT does not treat essential packages as special, and will offer to purge them if they get marked as auto-installed)
if apt-mark --help &> /dev/null; then
apt-mark auto ".*" > /dev/null
if [ -n "$1" ]; then
# if the user asked for anything to be included extra (like "xyz-archive-keyring"), mark those packages as manually installed
IFS=","; includePackages=( $1 ); unset IFS
apt-mark manual "${includePackages[@]}"
fi
fi
' -- "$include"
echo 'debuerreotype' > "$targetDir/etc/hostname"
{
echo '# https://1.1.1.1 (privacy-focused, highly-available DNS service)'
echo 'nameserver 1.1.1.1'
echo 'nameserver 1.0.0.1'
} > "$targetDir/etc/resolv.conf"
chmod 0644 \
"$targetDir/etc/hostname" \
"$targetDir/etc/resolv.conf"
# fix ownership/permissions on / (otherwise "debootstrap" leaves them as-is which causes reproducibility issues)
chown 0:0 "$targetDir"
chmod 0755 "$targetDir"
# https://bugs.debian.org/857803
# adjust field 3 in /etc/shadow and /etc/shadow- to $(( epoch / 60 / 60 / 24 )), if it's larger
sp_lstchg="$(( epoch / 60 / 60 / 24 ))"
for shadowFile in etc/shadow etc/shadow-; do
# --debian-eol etch and older do not include /etc/shadow-
[ -e "$targetDir/$shadowFile" ] || continue
newShadowFile="$shadowFile.debuerreotype"
awk -F ':' \
-v OFS=':' \
-v sp_lstchg="$sp_lstchg" \
'{
if ($3 > sp_lstchg) {
$3 = sp_lstchg
}
print
}' "$targetDir/$shadowFile" > "$targetDir/$newShadowFile"
if [ "$(< "$targetDir/$shadowFile")" != "$(< "$targetDir/$newShadowFile")" ]; then
# use "cat" instead of "mv" so permissions don't change
cat "$targetDir/$newShadowFile" > "$targetDir/$shadowFile"
fi
rm -f "$targetDir/$newShadowFile"
done
|