haini.sh/haini.sh

282 lines
8.5 KiB
Bash
Executable File

#!/bin/sh
# Fallar ante cualquier error
set -e
# Por ahora sólo soportamos x86_64
uname -m | grep -q x86_64 || exit 1
DIR="$(dirname "$(realpath "$0")")"
ROOT="$(dirname "$DIR")"
SELF="$(basename "$0")"
SSH_ADHOC=false
# Sólo se puede correr desde el directorio de Sutty
if test "$ROOT" != "$(dirname "$PWD")" && test "$ROOT" != "$PWD"; then
echo "¡No estás corriendo dentro de una carpeta de Sutty!" >&2
fi
# Necesitamos bubblewrap
if ! test "$HAIN_ENV" && ! type bwrap >/dev/null 2>&1 ; then
echo "Por favor, instala el paquete bubblewrap" >&2
exit 1
fi
if test -f /proc/sys/kernel/unprivileged_userns_clone && test "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -ne 1 ; then
echo "Necesitamos configurar tu sistema, ingresa tu contraseña para correr el comando" >&2
echo "sudo sysctl -a kernel.unprivileged_userns_clone=1" >&2
sudo sysctl -a kernel.unprivileged_userns_clone=1
fi
# Si estamos corriendo el comando desde la raíz de trabajo no hay que
# agregar el directorio.
if test "$ROOT" = "$PWD"; then
WORKDIR="/Sutty"
else
WORKDIR="/Sutty/${PWD##*/}/"
fi
# Podemos cambiar el entorno
ENTORNO=${ENTORNO:-${ROOT}/hain}
correr() {
echo "> $1" >&2
if test "$AS_ROOT"; then
SET_UID=0
SET_GID=0
else
SET_UID="$(id -u)"
SET_GID="$(id -g)"
fi
env -i \
DISPLAY="$DISPLAY" \
TERM="$TERM" \
USER="suttier" \
HOME="/home/suttier" \
HAIN_ENV=true \
RAILS_ENV="${RAILS_ENV:-development}" \
JEKYLL_ENV="${JEKYLL_ENV:-development}" \
$(test -f "$ENV_FILE" && (grep -v '^#' "$ENV_FILE" | xargs -0) || true) \
EDITOR="nano" \
PAGER="less -niSFX" \
SSH_AUTH_SOCK="${SSH_AUTH_SOCK}" \
bwrap \
--die-with-parent \
--unshare-user \
--uid "$SET_UID" \
--gid "$SET_GID" \
--unshare-ipc \
--unshare-uts \
--unshare-cgroup-try \
--bind "$ENTORNO" / \
--bind "$ROOT" /Sutty \
$(test -f ~/.Xauthority && echo "--ro-bind $HOME/.Xauthority /home/suttier/.Xauthority") \
--ro-bind /etc/hosts /etc/hosts \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/localtime /etc/localtime \
--dev-bind /dev /dev \
--dev-bind /sys /sys \
--dev-bind /proc /proc \
--dev-bind /tmp /tmp \
--chdir "$WORKDIR" \
/bin/sh -l -c "$1" < "${stdin:-/dev/null}"
}
generar_certificado() {
chmod 700 "$ENTORNO/etc/ssl/private"
ca_key="/etc/ssl/private/ca-sutty.key"
ca_crt="/usr/local/share/ca-certificates/ca-sutty.crt"
domain_key="/etc/ssl/private/sutty.local.key"
domain_csr="/etc/ssl/private/sutty.local.csr"
domain_crt="/etc/ssl/certs/sutty.local.crt"
if test -f "$ROOT/sutty.local/domain/sutty.local.crt"; then
SUTTY_LOCAL="$ROOT/sutty.local"
echo "Migrando certificados de sutty.local..." >&2
cp "$SUTTY_LOCAL/ca/key.key" "$ENTORNO$ca_key"
cp "$SUTTY_LOCAL/ca/crt.crt" "$ENTORNO$ca_crt"
cp "$SUTTY_LOCAL/domain/sutty.local.key" "$ENTORNO$domain_key"
cp "$SUTTY_LOCAL/domain/sutty.local.csr" "$ENTORNO$domain_csr"
cp "$SUTTY_LOCAL/domain/sutty.local.crt" "$ENTORNO$domain_crt"
return
fi
echo "Generando certificados..." >&2
correr "openssl req -x509 -nodes -new -sha256 -days 3650 -newkey rsa:2048 \
-keyout $ca_key -out $ca_crt.pem -subj '/C=AR/CN=Sutty-Local-CA'"
correr "openssl x509 -outform pem -in $ca_crt.pem -out $ca_crt"
correr "update-ca-certificates"
correr "openssl req -new -nodes -newkey rsa:2048 \
-keyout $domain_key -out $domain_csr \
-subj '/C=AR/ST=Ninguno/L=Interdimension/O=Sutty-Local/CN=sutty.local'"
correr "openssl x509 -req -sha256 -days 3650 \
-in $domain_csr -CA $ca_crt -CAkey $ca_key \
-CAcreateserial -extfile /Sutty/haini.sh/domains.ext -out $domain_crt"
rm "$ENTORNO$ca_crt.pem"
echo "Instalando certificados..." >&2
if which update-ca-certificates 2>/dev/null; then
sudo install -Dm 644 "$ENTORNO$ca_crt" /usr/share/ca-certificates/extra/sutty.crt
sudo dpkg-reconfigure ca-certificates
sudo update-ca-certificates
else
sudo trust anchor "$ENTORNO$ca_crt"
fi
}
crear_entorno() {
ALPINE="3.13.5"
ALPINE_URL="https://dl-cdn.alpinelinux.org/alpine/v${ALPINE%.*}/releases/x86_64/alpine-minirootfs-${ALPINE}-x86_64.tar.gz"
# Definir si vamos a usar wget o curl
type wget >/dev/null 2>&1 && download="wget -O -"
type busybox >/dev/null 2>&1 && download="busybox wget -O -"
type curl >/dev/null 2>&1 && download="curl"
# Si no hay ninguno de los dos, salir
test -z "${download}" && exit 1
# Darle permiso de lectura a otres también
umask 022
# Crear el directorio de trabajo
mkdir -p "$ENTORNO"
# Descargar y extraer Alpine
test -f "$ENTORNO/etc/os-release" || ${download} "${ALPINE_URL}" | tar xz --directory "$ENTORNO"
# Configurar los repositorios de Sutty
grep -q sutty "$ENTORNO/etc/apk/repositories" || echo "https://alpine.sutty.nl/alpine/v${ALPINE%\.*}/sutty" >> "$ENTORNO/etc/apk/repositories"
test -f "$ENTORNO/etc/apk/keys/alpine@sutty.nl-5ea884cd.rsa.pub" || wget https://alpine.sutty.nl/alpine/sutty.pub -O "$ENTORNO/etc/apk/keys/alpine@sutty.nl-5ea884cd.rsa.pub"
# Instalar las dependencias solo si cambiaron
if test "$ENTORNO/etc/apk/world" -ot "$DIR/packages"; then
echo "Instalando paquetes..." >&2
packages="$(tr "\n" " " < "$DIR/packages")"
correr "apk add --no-cache $packages"
fi
# Habilitar la instalación de gemas binarias
sed -re "s/#(@platforms = )/\1/" -i "$ENTORNO/usr/lib/ruby/2.7.0/rubygems.rb"
# Deshabilitar el usuario de nginx
sed -re "/user nginx/d" -i "$ENTORNO/etc/nginx/nginx.conf"
# Crear el directorio del PID
install -dm 755 "$ENTORNO/run/nginx"
# Instalar la configuración
install -m 640 "$DIR/nginx.conf" "$ENTORNO/etc/nginx/http.d/default.conf"
mkdir -p "$ENTORNO/home"
# migrar de versiones anteriores de hainish
test -d "$ENTORNO$HOME" && test ! -d "$ENTORNO/home/suttier" \
&& mv "$ENTORNO$HOME" "$ENTORNO/home/suttier"
mkdir -p "$ENTORNO/home/suttier"
if ! grep ^suttier: "$ENTORNO/etc/group" >/dev/null 2>&1 ; then
AS_ROOT=true correr "addgroup \
-g $(id -g) \
suttier"
fi
if ! correr "id suttier" >/dev/null 2>&1 ; then
AS_ROOT=true correr "adduser \
--disabled-password \
--gecos '' \
--home /home/suttier \
--no-create-home \
--uid $(id -u) \
--ingroup suttier \
suttier"
fi
# Configurar rubygems para que descargue las gemas desde Sutty
install -m 640 "$DIR/.gemrc" "$ENTORNO/home/suttier/.gemrc"
# Resaltado de sintaxis en nano
grep -q "^include " "$ENTORNO/etc/nanorc" || echo "include \"/usr/share/nano/*.nanorc\"" >> "$ENTORNO/etc/nanorc"
# Instalar scripts
for script in "$DIR/bin/"*; do
install -m 755 "$script" "$ENTORNO/usr/local/bin/${script##*/}"
done
# Configurar SSH
install -m 700 -d "$ENTORNO/home/suttier/.ssh"
install -m 644 "$DIR/ssh/known_hosts" "$ENTORNO/home/suttier/.ssh/known_hosts"
test -f "$ENTORNO/etc/ssl/certs/sutty.local.crt" || generar_certificado
test -f "$ENTORNO/usr/local/share/ca-certificates/ca-sutty.crt" || mv "$ENTORNO/etc/ssl/certs/ca-sutty.crt" "$ENTORNO/usr/local/share/ca-certificates/ca-sutty.crt"
}
# Auto-actualizar una vez por hora
actualizar() {
test ! "$TERM" = "dumb" || return
last_update="$(find "$DIR/.git/FETCH_HEAD" -mmin +60 | wc -l)"
if test ! $last_update -ne 0; then
return
fi
echo -n "Actualizando haini.sh... " >&2
if ping -q -c 1 0xacab.org >/dev/null 2>&1; then
git -C "$DIR" pull --ff-only
if test "$DIR/.git/FETCH_HEAD" -ot "$DIR/.git/ORIG_HEAD"; then
echo "haini.sh se actualizó, por favor volvé a ejecutar el comando" >&2
exit 0
fi
else
echo "no se pudo conectar 0xacab.org, intentando la próxima vez." >&2
fi
}
DEFAULT="sh"
case $1 in
init)
echo "Advertencia: haini.sh init está deprecado, usá \`export PATH=$DIR:\$PATH\`." >&2
echo "export PATH=$DIR:\$PATH"
exit
;;
serve) shift; DEFAULT=nginx ;;
*)
if ! test "$HAIN_ENV" && ! type "$SELF" >/dev/null 2>&1 ; then
echo "Tip: Usá \`export PATH=$DIR:\$PATH\` para poder correr comandos de haini.sh con solo 'haini.sh'" >&2
fi
;;
esac
if test "$HAIN_ENV"; then
${*:-$DEFAULT}
else
if test -z "${SSH_AUTH_SOCK}"; then
if ! type ssh-agent >/dev/null 2>&1 ; then
echo "Instala ssh-agent para poder trabajar con git remoto dentro de haini.sh" >&2
else
SSH_ADHOC=true
echo "Iniciando un ssh-agent temporal." >&2
eval "$(ssh-agent)"
ssh-add
fi
fi
actualizar
crear_entorno
stdin="$(test "$TERM" = "dumb" || echo "/dev/stdin")" correr "${*:-$DEFAULT}" ; salida=$?
${SSH_ADHOC} && ssh-agent -k
exit $salida
fi