Compare commits

...
12 Commits
Author SHA1 Message Date
mk ce5041bc50 todo md 2026-07-28 07:23:10 +03:00
mk 9f164f61ef angie(debian): pin VERSION_ID in apt repo URL — needs angie/<ID>/<VERSION_ID>/ (debian/13/dists/trixie/) not angie/<ID>/ (debian/dists/trixie/ -> 404 'no Release file') on trixie; suite stays VERSION_CODENAME
packages(debian): rm stale /etc/apt/sources.list.d/eza.list + gierens.gpg left by the pre-fix run that used the now-dead apt.fury.io PPA — otherwise 'apt-get update' keeps hitting the 401 forever even though we now install eza from GH releases
2026-07-28 07:11:22 +03:00
mk 0cfe10e387 packages(debian): fix eza+starship+zoxide installs
- eza: apt.fury.io PPA is dead (401 Unauthorized); install the single binary from the upstream GitHub release tarball (eza_x86_64-unknown-linux-gnu.tar.gz) into /usr/local/bin — no apt repo, system-wide
- starship: scrub $ARCH/$PLATFORM from the installer env — our lib/detect.sh exports ARCH=amd64, which the starship installer reuses (it checks ${ARCH-}) and skips its own amd64->x86_64 mapping, producing an unsupported 'amd64-unknown-linux-musl' target. Default bin dir is /usr/local/bin anyway
- zoxide: install system-wide (--bin-dir /usr/local/bin) instead of ~/.local/bin — when run as root the default landed in /root (wrong user for the create-user path) and wasn't on $PATH
2026-07-28 07:08:04 +03:00
mk efdf056352 hardening(angie): default_server needs a cert on its 443 listener or 'angie -t' fails with 'no ssl_certificate defined' — generate a one-time self-signed dummy (CN=bootstrap-default, 10y) in /etc/angie/ssl so unknown-SNI clients handshake then get 444; mark listen 443 as ssl explicitly 2026-07-28 06:55:31 +03:00
mk 435d66aad2 shell-zsh: derive ZSH/ZDOTDIR from $TARGET_HOME unconditionally — ignore inherited $ZSH env var (omz exports it, which pinned paths to the old home e.g. /root while the chown step retargeted to the new user, causing 'insecure completion dirs' owned by the wrong user) 2026-07-28 06:16:45 +03:00
mk 94a0bac9f4 hardening(angie): resolve mime.types from whichever path the package ships (angie-bin=/etc/angie, Arch source=/etc/nginx via mailcap dep) — fixes 'angie -t' 'open() mime.types failed' on source path; fallback ships a minimal mime.types; ensure modules/ include dirs exist before glob 2026-07-28 06:04:57 +03:00
mk 26185978ac hardening(angie): source-angie bridge — symlink /etc/nginx/nginx.conf -> /etc/angie/angie.conf so the _on/targets layout actually loads under the source AUR package's '/usr/bin/nginx' service (angie-bin/Debian already read /etc/angie directly); backup original once 2026-07-28 05:59:08 +03:00
mk cb0e517a7d hardening(angie): fix 'angie_user: unbound variable' under set -u — init extraction locals to empty; also read source AUR package's /etc/nginx/nginx.conf as alt stock config path 2026-07-28 05:57:44 +03:00
mk b653191de2 angie(arch): prefer angie-bin (repackaged upstream .deb, instant) over source build; verified http_acme/ssl/v2/v3/realip all compiled in; fall back to aur/angie if bin unavailable 2026-07-28 05:53:43 +03:00
mk ca8f01083c packages(arch): route byobu through yay (AUR-only); split official vs AUR lists so pacman --needed can't abort on a missing target; as_user prefers TARGET_USER for AUR builds 2026-07-28 05:44:45 +03:00
mk ff9fc4db4e fix(sanity): hostname missing on minimal Arch (not in coreutils) — use uname -n with fallbacks; refresh TLS follow-up to angie-issue 2026-07-28 05:35:40 +03:00
mk f7e4bb96d7 fix(zsh plugins): correct clone orgs — zsh-users/zsh-history-substring-search, Aloxaf/fzf-tab (were typos causing github auth prompts) 2026-07-28 05:28:20 +03:00
7 changed files with 179 additions and 48 deletions

No files matched your search

+5 -4
View File
@@ -9,12 +9,13 @@ ok() { printf '\033[1;32mok\033[0m %s\n' "$*"; }
# run a command as root when not already root
sudo_() { if (( EUID == 0 )); then "$@"; else sudo "$@"; fi; }
# run a command AS the invoking (non-root) user — used for AUR builds, etc.
# when root: drop to SUDO_USER (or USER) via runuser; when not root: run directly.
# run a command AS the target non-root user — used for AUR builds, nvm, etc.
# precedence (when running as root): TARGET_USER (set by create-user) > SUDO_USER > USER.
# when not root: run directly as ourselves.
as_user() {
local u="$USER"
local u="${SUDO_USER:-$USER}"
if (( EUID == 0 )); then
u="${SUDO_USER:-$USER}"
u="${TARGET_USER:-${SUDO_USER:-$USER}}"
command -v runuser >/dev/null 2>&1 && runuser -u "$u" -- "$@" || su "$u" -c "$*"
else
"$@"
+62 -19
View File
@@ -20,32 +20,54 @@ run_packages() {
}
# ── arch ─────────────────────────────────────────────────────────────────────
# official-repo packages (verified in extra/core). Any name not in the official
# repos would abort the whole `pacman --needed` batch, so keep this list clean
# and route AUR-only tools through _install_aur_packages instead.
ARCH_OFFICIAL=(git ufw mosh btop fzf ripgrep fd bat dust procs jq yq tree
ncdu duf tldr httpie rsync lazygit neovim curl unzip less starship
eza zoxide openssh)
# AUR-only packages (not in official repos) — installed through yay after build.
ARCH_AUR=(byobu)
_pkg_arch() {
sudo_ pacman -Sy --noconfirm --needed base-devel openssh
# most tools live in extra/community (the merged repos); names map 1:1
local p=(git byobu ufw mosh btop fzf ripgrep fd bat dust procs jq yq tree
ncdu duf tldr httpie rsync lazygit neovim curl unzip less starship
eza zoxide openssh)
sudo_ pacman -Sy --noconfirm --needed "${p[@]}"
sudo_ pacman -Sy --noconfirm --needed "${ARCH_OFFICIAL[@]}"
_install_yay
_install_aur_packages
# fd/bat ship with their real names on arch — no shims needed
}
_install_yay() {
if command -v yay >/dev/null 2>&1; then ok "yay already installed"; return; fi
log "building yay from AUR (requires a non-root sudoer)"
if (( EUID == 0 )) && [[ -z "${SUDO_USER:-}" ]]; then
err "running as root with no SUDO_USER — yay cannot build. Re-run bootstrap as a normal user with sudo."
if (( EUID == 0 )) && [[ -z "${TARGET_USER:-${SUDO_USER:-}}" ]]; then
err "running as root with no target/sudo user — yay cannot build. Run the create-user step first, or re-run bootstrap as a normal user with sudo."
return 1
fi
local tmp; tmp="$(mktemp -d)"
# chown the build dir so the non-root build user can write into it
if (( EUID == 0 )); then
chown -R "${TARGET_USER:-${SUDO_USER}}" "$tmp"
fi
as_user git clone --depth 1 https://aur.archlinux.org/yay.git "$tmp/yay"
( cd "$tmp/yay" && as_user makepkg -si --noconfirm )
rm -rf "$tmp"
command -v yay >/dev/null 2>&1 && ok "yay installed" || warn "yay install may need a manual pass"
}
_install_aur_packages() {
[[ "${#ARCH_AUR[@]}" -eq 0 ]] && return
if ! command -v yay >/dev/null 2>&1; then
warn "yay not available — skipping AUR packages: ${ARCH_AUR[*]}"
return
fi
log "installing AUR-only packages: ${ARCH_AUR[*]}"
# yay -S as the non-root target user (makepkg refuses root; TARGET_USER via as_user).
as_user yay -S --noconfirm --needed "${ARCH_AUR[@]}" \
|| warn "some AUR packages failed: ${ARCH_AUR[*]} (install manually with: yay -S ${ARCH_AUR[*]})"
}
# ── debian/ubuntu ────────────────────────────────────────────────────────────
_pkg_debian() {
sudo_ apt-get update -y
@@ -56,24 +78,45 @@ _pkg_debian() {
# rename packages: fd -> fdfind, bat -> batcat (fix via aliases in zsh)
sudo_ apt-get install -y fdfind batcat 2>/dev/null || true
# eza via the gierens PPA (debs not in base apt)
# eza: the old apt.fury.io/gierens PPA is dead (401 Unauthorized). Install the
# single binary from the upstream GitHub release tarball into /usr/local/bin
# (system-wide — works for root and every sudo user; no HOME dependency).
# Remove a stale eza.list left by the old (pre-fix) run that used the now-dead
# apt.fury.io PPA — otherwise 'apt-get update' keeps hitting the 401 forever.
sudo_ rm -f /etc/apt/sources.list.d/eza.list /etc/apt/keyrings/gierens.gpg
if ! command -v eza >/dev/null 2>&1; then
sudo_ install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://raw.githubusercontent.com/eza-community/eza/main/deb.asc \
| sudo_ tee /etc/apt/keyrings/gierens.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://apt.fury.io/eza/ /" \
| sudo_ tee /etc/apt/sources.list.d/eza.list >/dev/null
sudo_ apt-get update -y && sudo_ apt-get install -y eza || warn "eza PPA failed"
local eza_arch; case "$ARCH" in
amd64) eza_arch=x86_64-unknown-linux-gnu ;;
arm64) eza_arch=aarch64-unknown-linux-gnu ;;
*) eza_arch="${ARCH}-unknown-linux-gnu" ;;
esac
local url="https://github.com/eza-community/eza/releases/latest/download/eza_${eza_arch}.tar.gz"
local tmp; tmp="$(mktemp -d)"
if wget -qO "$tmp/eza.tgz" "$url" && tar xzf "$tmp/eza.tgz" -C "$tmp"; then
sudo_ install -m 0755 "$tmp/eza" /usr/local/bin/eza
else
warn "eza download failed ($url)"
fi
rm -rf "$tmp"
fi
# starship: official installer
# starship: official installer. NOTE — scrub $ARCH/$PLATFORM from the env first:
# the installer reuses a pre-set ARCH (our lib/detect.sh exports ARCH=amd64) and
# skips its own amd64->x86_64 mapping, yielding an unsupported 'amd64-unknown-linux-
# musl' target ('builds for amd64-unknown-linux-musl are not yet available').
# Default bin dir is /usr/local/bin (system-wide).
if ! command -v starship >/dev/null 2>&1; then
curl -sS https://starship.rs/install.sh | sh -s -- -y || warn "starship install failed"
curl -sS https://starship.rs/install.sh \
| env -u ARCH -u PLATFORM sh -s -- -y || warn "starship install failed"
fi
# zoxide: official installer
# zoxide: official installer. Install system-wide (/usr/local/bin) instead of the
# default ~/.local/bin — when run as root the default lands in /root (wrong user
# for the create-user path) and isn't on $PATH; /usr/local/bin is on PATH for all.
if ! command -v zoxide >/dev/null 2>&1; then
curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash || warn "zoxide install failed"
curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh \
| env -u ARCH sh -s -- --bin-dir /usr/local/bin --man-dir /usr/local/share/man \
|| warn "zoxide install failed"
fi
# yq: direct binary from mikefarah/yq releases (stable URL)
+23 -6
View File
@@ -27,12 +27,25 @@ run_angie() {
}
_angie_arch() {
if command -v yay >/dev/null 2>&1; then
as_user yay -S --noconfirm angie 2>/dev/null \
|| as_user yay -S --noconfirm aur/angie 2>/dev/null \
|| warn "angie AUR build failed — try: yay -S angie"
if ! command -v yay >/dev/null 2>&1; then
warn "yay not available — install angie manually from AUR: yay -S angie-bin"
return
fi
# Prefer angie-bin: it repackages the upstream official Ubuntu .deb, so it's
# instant (no source compile) and carries the same compiled-in modules our
# config relies on — http_acme, http_ssl, http_v2/v3, http_realip, stream*
# (verified via the .deb's 'angie -V'). Drop back to the source 'angie'
# package only if the binary package is unavailable.
log "AUR install: angie-bin first, fall back to angie (source build)"
if as_user yay -S --noconfirm --needed angie-bin 2>/dev/null; then
ok "angie-bin installed from AUR"
return
fi
log "angie-bin unavailable/failed — building angie from source (may take a few minutes)"
if as_user yay -S --noconfirm --needed aur/angie 2>/dev/null; then
ok "angie (source) installed from AUR"
else
warn "yay not available — install angie manually from AUR: yay -S angie"
warn "angie install failed — try manually: yay -S angie-bin (or: yay -S angie)"
fi
}
@@ -41,7 +54,11 @@ _angie_debian() {
sudo_ install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://angie.software/keys/angie-signing.gpg \
| sudo_ tee /etc/apt/keyrings/angie-signing.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/angie-signing.gpg] https://download.angie.software/angie/${ID}/ ${VERSION_CODENAME} main" \
# Repo path is angie/<ID>/<VERSION_ID>/ with the codename as the apt suite, e.g.
# debian/13/dists/trixie/ or ubuntu/22.04/dists/jammy/. Omitting VERSION_ID (just
# angie/debian/) fetches debian/dists/trixie -> 404 'no Release file' — must pin the
# numeric version in the URL path.
echo "deb [signed-by=/etc/apt/keyrings/angie-signing.gpg] https://download.angie.software/angie/${ID}/${VERSION_ID}/ ${VERSION_CODENAME} main" \
| sudo_ tee /etc/apt/sources.list.d/angie.list >/dev/null
sudo_ apt-get update -y
sudo_ apt-get install -y angie
+9 -5
View File
@@ -6,9 +6,13 @@ step_desc="zsh + oh-my-zsh + plugins, set default shell, ship config"
step_run="run_zsh"
# All paths resolve against the config TARGET (set by 05-create-user).
ZSH="${ZSH:-$TARGET_HOME/.oh-my-zsh}"
ZSH_CUSTOM="${ZSH_CUSTOM:-$ZSH/custom}"
ZDOTDIR_TARGET="${ZDOTDIR_TARGET:-$TARGET_HOME/.config/zsh}"
# NOTE: we deliberately do NOT honor an inherited $ZSH env var — oh-my-zsh exports
# ZSH when it loads, so a setup.sh run from an omz-equipped shell would otherwise
# pin these to the *old* home (e.g. /root) while the chown step retargets to the
# *new* user, mis-owning the wrong tree (the 'insecure completion dirs' bug).
ZSH="$TARGET_HOME/.oh-my-zsh"
ZSH_CUSTOM="$ZSH/custom"
ZDOTDIR_TARGET="$TARGET_HOME/.config/zsh"
run_zsh() {
log "installing zsh + oh-my-zsh"
@@ -53,8 +57,8 @@ _install_plugins() {
_clone_plugin zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
_clone_plugin zsh-users/zsh-syntax-highlighting "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
_clone_plugin zsh-users/zsh-completions "$ZSH_CUSTOM/plugins/zsh-completions"
_clone_plugin Aloofenlore/history-substring-search "$ZSH_CUSTOM/plugins/history-substring-search"
_clone_plugin Aloof98/fzf-tab "$ZSH_CUSTOM/plugins/fzf-tab"
_clone_plugin zsh-users/zsh-history-substring-search "$ZSH_CUSTOM/plugins/history-substring-search"
_clone_plugin Aloxaf/fzf-tab "$ZSH_CUSTOM/plugins/fzf-tab"
}
_clone_plugin() { # repo dest
+77 -12
View File
@@ -206,20 +206,38 @@ _h_angie_config() {
sudo_ install -d -m 0755 "$d/_on" "$d/targets" "$d/modules" "$d/modules/http"
# ── extract packaging-specific values from the stock angie.conf ──
# Angie packages set user/pid/error_log differently per distro; preserve them.
local oldcfg="$d/angie.conf" angie_user angie_pid angie_err angie_modules=""
if [[ -f "$oldcfg" ]]; then
angie_user=$(grep -E '^\s*user\s+' "$oldcfg" | head -1 | awk '{print $2}' | tr -d ';')
angie_pid=$(grep -E '^\s*pid\s+' "$oldcfg" | head -1 | awk '{print $2}' | tr -d ';')
angie_err=$(grep -E '^\s*error_log\s+' "$oldcfg" | head -1 | awk '{print $2}' | tr -d ';')
# Different Angie packages set user/pid/error_log differently (Arch source
# package → /etc/nginx/nginx.conf with 'user http'; angie-bin / Debian .deb
# → /etc/angie/angie.conf with 'user angie'). Preserve them when present,
# fall back to sane defaults otherwise (defaults cover all distros here).
local oldcfg="$d/angie.conf"
local altcfg="/etc/nginx/nginx.conf" # the source AUR package's path
local stock="$oldcfg"
[[ -f "$oldcfg" ]] || stock="$altcfg"
local angie_user="" angie_pid="" angie_err="" angie_modules=""
if [[ -f "$stock" ]]; then
angie_user=$(grep -E '^\s*user\s+' "$stock" | head -1 | awk '{print $2}' | tr -d ';')
angie_pid=$(grep -E '^\s*pid\s+' "$stock" | head -1 | awk '{print $2}' | tr -d ';')
angie_err=$(grep -E '^\s*error_log\s+' "$stock" | head -1 | awk '{print $2}' | tr -d ';')
# collect any load_module lines (dynamic modules)
angie_modules=$(grep -E '^\s*load_module\s+' "$oldcfg" || true)
angie_modules=$(grep -E '^\s*load_module\s+' "$stock" || true)
fi
# sane defaults if not found
[[ -z "$angie_user" ]] && angie_user=$({ [[ "$DISTRO" == "arch" ]] && echo http || echo angie; })
[[ -z "$angie_user" ]] && angie_user=$({ [[ "${DISTRO:-}" == "arch" ]] && echo http || echo angie; })
[[ -z "$angie_pid" ]] && angie_pid=/run/angie.pid
[[ -z "$angie_err" ]] && angie_err=/var/log/angie/error.log
# locate mime.types: angie-bin/Debian ship /etc/angie/mime.types; the Arch source
# 'angie' package instead pulls it from the 'mailcap' dep at /etc/nginx/mime.types
# (and deletes its own). Pick whichever exists so 'angie -t' doesn't fail on include.
local angie_mime=""
for m in "$d/mime.types" /etc/nginx/mime.types /etc/mime.types; do
[[ -f "$m" ]] && { angie_mime="$m"; break; }
done
[[ -z "$angie_mime" ]] && { warn "no mime.types found; installing a minimal one"; _angie_install_mime "$d/mime.types"; angie_mime="$d/mime.types"; }
# the modules/ include dir must exist or the glob fails the config test
sudo_ install -d -m 0755 "$d/modules" "$d/modules/http"
# backup the original once
if [[ -f "$oldcfg" && ! -f "$oldcfg.orig.bootstrap" ]]; then
sudo_ cp -a "$oldcfg" "$oldcfg.orig.bootstrap"
@@ -248,7 +266,7 @@ events {
}
http {
include /etc/angie/mime.types;
include $angie_mime;
default_type application/octet-stream;
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
@@ -304,14 +322,25 @@ EOF
# ── default catch-all server: unmatched Host -> drop ──
if [[ ! -f "$d/_on/00-default.conf" ]]; then
# The default_server also listens on 443 to catch unknown-SNI TLS clients.
# It needs SOME cert to complete the handshake before `return 444` drops them;
# angie won't start a 443 listener without ssl_certificate. Generate a self-signed
# dummy once (CN='bootstrap-default', 10y) so unknown-SNI clients get dropped
# after connecting while real vhosts are picked by SNI.
if [[ ! -f "$d/ssl/bootstrap-default.crt" ]]; then
sudo_ install -d -m 0755 "$d/ssl"
sudo_ openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -keyout "$d/ssl/bootstrap-default.key" -out "$d/ssl/bootstrap-default.crt" -subj "/CN=bootstrap-default/O=bootstrap" 2>/dev/null
fi
sudo_ tee "$d/_on/00-default.conf" >/dev/null <<'EOF'
# default_server: drop requests that match no enabled host (drive-by scanners, bare-IP)
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 default_server;
listen [::]:443 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_certificate /etc/angie/ssl/bootstrap-default.crt;
ssl_certificate_key /etc/angie/ssl/bootstrap-default.key;
return 444;
}
EOF
@@ -320,7 +349,25 @@ EOF
# ── angie-enable / angie-disable helpers ──
_angie_helpers
# validate + (best-effort) reload
# ── source-angie bridge: point /etc/nginx/nginx.conf at our /etc/angie layout ─
# The source AUR 'angie' package compiles --conf-path=/etc/nginx/nginx.conf and
# its service runs '/usr/bin/nginx' with no -c flag, so it ignores /etc/angie.
# When that path is where angie actually reads, back it up once and symlink it to
# our managed angie.conf so the _on/targetsmodules layout takes effect. The
# angie-bin / Debian packages already read /etc/angie/angie.conf directly.
if [[ -f /etc/nginx/nginx.conf && -f "$d/angie.conf" ]]; then
local nginxconf=/etc/nginx/nginx.conf
if [[ "$(readlink -f "$nginxconf" 2>/dev/null)" == "$d/angie.conf" ]]; then
ok "$nginxconf already symlinks to $d/angie.conf"
else
if [[ ! -f "${nginxconf}.orig.bootstrap" && ! -L "$nginxconf" ]]; then
sudo_ cp -a "$nginxconf" "${nginxconf}.orig.bootstrap"
fi
sudo_ ln -sfn "$d/angie.conf" "$nginxconf"
ok "symlinked $nginxconf -> $d/angie.conf (source-angie bridge); backup at ${nginxconf}.orig.bootstrap"
fi
fi
if sudo_ angie -t 2>&1; then
ok "angie config valid"
if command -v systemctl >/dev/null 2>&1 && systemctl is-active angie >/dev/null 2>&1; then
@@ -339,6 +386,24 @@ EOF
# ufw already opened 80/443 in _h_ufw
}
_angie_install_mime() { # dest — write a minimal mime.types if none exists on the host
sudo_ tee "$1" >/dev/null <<'EOF'
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
image/png png;
image/svg+xml svg svgz;
application/javascript js;
application/json json;
application/pdf pdf;
application/zip zip;
application/octet-stream bin exe dll so deb dmg iso img;
}
EOF
}
_angie_helpers() {
sudo_ tee /usr/local/bin/angie-enable >/dev/null <<'EOF'
#!/usr/bin/env bash
+2 -2
View File
@@ -15,7 +15,7 @@ run_sanity() {
echo " memory : $(free -h | awk '/^Mem/ {print $2}') total"
echo " swap : $(swapon --show 2>/dev/null | awk 'NR>1{print $3; found=1} END{if(!found) print "none"}')"
echo " timezone : $(timedatectl show -p Timezone --value 2>/dev/null || cat /etc/timezone 2>/dev/null || echo '?')"
echo " hostname : $(hostname)"
echo " hostname : $(uname -n 2>/dev/null || hostnamectl --static 2>/dev/null || cat /etc/hostname 2>/dev/null || echo '?')"
echo " locale : $(locale 2>/dev/null | awk -F= '/^LANG/ {print $2}')"
echo " ufw : $(sudo_ ufw status 2>/dev/null | head -1 || echo 'not installed')"
echo " fail2ban : $(sudo_ fail2ban-client status 2>/dev/null | head -2 || echo 'not running')"
@@ -32,7 +32,7 @@ run_sanity() {
- Add starship config if you want a custom prompt: starship preset list
(defaults ship with the bootstrap).
- Consider 'newgrp docker' or re-login to activate the docker group.
- Install domain + TLS for angie separately (certbot/acme.sh), not handled here.
- Issue TLS certs for angie hosts with: angie-issue <domain> you@email.com
EOF
ok "bootstrap sanity report done."
}
+1
View File
@@ -0,0 +1 @@
- [ ] pm2