dotfiles

custom linux config files managed with gnu stow

dotfiles

setup


#!/bin/bash
# usage:
# curl -L https://raw.githubusercontent.com/xero/dotfiles/main/setup > x && chmod +x x && sudo ./x
# make sure your edit the ARCH var for your architecture
#
# setup.sh by xero (http://git.io/.files)
# a script to setup a new deb 11 vps with
# all my tools and configs, ready to roll
# it relies heavily on my 1password vault
# so...
#   ▄▄▄▄  ▓█████ █     █ ▄████ ██▀██▄▓█████
#  ▓█████▄▓█░  ▀▓█░ █ ░█▓█ ▒ █▓██ ▒ █▓█▒░ ▓
#  ▒██▒ ▄█▒███  ▒█░ █ ░█▓█ ░ █▓██ ░▄█▒███
#  ▒██░█▀ ▒▓█  ▄░█▄ █░▄█▒█▀▀▀█▒██▀▀█▄▒▓█  ▄
#  ░▓█  ▀█░▒████░░██▒██░██▓ ▒█░██▓ ▒█░▒████▒
#  ░▒▓███▀░░ ▒░ ░ ▓░▒ ▒░ ▒▓ ░▒░ ▒▓ ░▒░░ ▒░ ░
#  ▒░▒   ░ ░ ░  ░ ▒ ░ ░  ░▒ ░ ▒ ░▒ ░ ▒░ ░  ░
#   ░    ░   ░    ░   ░  ░░   ░ ░░   ░  ░
#   ░  ░     ░  ░   ░     ░      ░         ░
#         this is how i setup my shiz    ░
#      ░ it might not be great for you  ░

# user vars
export ME='x0'
export X_UID=10806
export PORT=60806
export HOSTNAME='thirteen'
export ARCH='arm' # arm, x86
# script vars
MYHOME="/home/$ME"
ASME="sudo -u $ME"
PKGARCH=$ARCH

# helper functions
function _echo() { printf "\n╓───── %s \n╙────────────────────────────────────── ─ ─ \n" ""; }
function get() {
	f="${3:-notesPlain}"
	op item get "" --account "" --fields "$f" --format json | jq -rM '.value'
}
function getfile() {
	f="${4:-notesPlain}"
	op --account "" read "op:////$f"
}
function account() {
	domain="${3:-my}.1password.com"
	op account add \
		--address "$domain" \
		--email "" \
		--shorthand ""
}

[ "$(id -u)" -ne 0 ] && {
	_echo "got root?" >&2
	exit 1
}

_echo "creating swap"
dd if=/dev/zero of=/swapmeet bs=128M count=32
chmod 600 /swapmeet
mkswap /swapmeet
swapon /swapmeet
swapon -s
echo "/swapmeet swap swap defaults 0 0" >> /etc/fstab
echo "vm.swappiness=0" >> /etc/sysctl.conf
sysctl vm.swappiness=0

_echo "installing runtime deps"
apt update && apt install -y git gpg bash curl locales gnupg software-properties-common

_echo "setting up locales and console"
locale-gen "en_US.UTF-8"
localectl set-locale en_US.UTF-8
dpkg-reconfigure locales
dpkg-reconfigure console-setup
systemctl daemon-reload
systemctl restart console-setup.service

_echo "adding apt sources"
# add backports
echo "deb http://deb.debian.org/debian bullseye-backports main" > /etc/apt/sources.list.d/backports.list

# add makedeb prebuilt-mpr
curl -sS https://proget.makedeb.org/debian-feeds/prebuilt-mpr.pub | gpg --dearmor | tee /usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg 1> /dev/null
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg] https://proget.makedeb.org prebuilt-mpr $(lsb_release -cs)" | tee /etc/apt/sources.list.d/prebuilt-mpr.list > /dev/null

# add github
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg &&
	chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg &&
	echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null

# add 1pw
curl -sS https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg &&
	echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | tee /etc/apt/sources.list.d/1password.list > /dev/null
mkdir -p /etc/debsig/policies/AC2D62742012EA22/ &&
	curl -sS https://downloads.1password.com/linux/debian/debsig/1password.pol | tee /etc/debsig/policies/AC2D62742012EA22/1password.pol
mkdir -p /usr/share/debsig/keyrings/AC2D62742012EA22 &&
	curl -sS https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor --output /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg

# add hashicorp
curl -sL https://apt.releases.hashicorp.com/gpg |
	gpg --dearmor |
	tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring \
	--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
	--fingerprint
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
	https://apt.releases.hashicorp.com $(lsb_release -cs) main" |
	tee /etc/apt/sources.list.d/hashicorp.list

# install all the things \o/
_echo "installing packages"
apt update &&
	DEBIAN_FRONTEND=noninteractive apt install -y \
		1password-cli \
		apparmor \
		apt-utils \
		autoconf \
		automake \
		awscli \
		bash \
		bash-completion \
		bc \
		bind9-host \
		bsdutils \
		build-essential \
		ca-certificates \
		clamav-base \
		cmake \
		cmatrix \
		coreutils \
		curl \
		dash \
		dbus \
		dbus-user-session \
		debianutils \
		diffutils \
		dnsutils \
		docker.io \
		doxygen \
		dpkg \
		e2fsprogs \
		eslint \
		ethtool \
		expect \
		fail2ban \
		findutils \
		fzf \
		g++ \
		gawk \
		gcc-10-base \
		gcc-9-base \
		gettext \
		gh \
		git \
		golang \
		golang-doc \
		golang-src \
		gpg \
		gpg-agent \
		gpgv \
		gzip \
		htop \
		iptables \
		iputils-ping \
		isc-dhcp-client \
		jq \
		keychain \
		kubernetes-client \
		libncurses5-dev \
		libprotobuf-dev \
		libssl-dev \
		libtool \
		libtool-bin \
		libutempter-dev \
		libx11-dev \
		libxfixes-dev \
		lsb-base \
		lua5.4 \
		luajit \
		luarocks \
		man-db \
		manpages \
		mawk \
		ncurses-base \
		ncurses-bin \
		ncurses-term \
		net-tools \
		netbase \
		ninja-build \
		nmap \
		ocproxy \
		openconnect \
		openssh-client \
		openssh-server \
		openssl \
		pciutils \
		perl \
		perl-base \
		pkg-config \
		protobuf-compiler \
		proxychains4 \
		psmisc \
		python3 \
		python3-boto \
		python3-pip \
		python3-venv \
		resolvconf \
		ripgrep \
		rkhunter \
		rsyslog \
		secure-delete \
		shellcheck \
		silversearcher-ag \
		socat \
		stow \
		sudo \
		tar \
		tcpdump \
		terraform \
		tmux/bullseye-backports \
		toilet \
		traceroute \
		tree \
		tzdata \
		unzip \
		util-linux \
		uuid-runtime \
		vim-tiny \
		vpnc \
		whiptail \
		whois \
		xsel \
		xvfb \
		xz-utils \
		zlib1g \
		zlib1g-dev \
		zsh \
		zsh-syntax-highlighting

_echo "systemd housekeeping"
rm /etc/resolv.conf
ln -s /run/resolvconf/resolv.conf /etc/resolv.conf
systemctl restart networking.service
systemctl restart resolvconf.service
systemctl stop docker.service
systemctl stop containerd.service
systemctl enable clamav-freshclam.service

_echo "setting up timezone and hostname"
timedatectl set-timezone America/New_York
hostname "$HOSTNAME"
hostnamectl set-hostname "$HOSTNAME"
sed -i '/^127\.0\.0\.1\s/s/$/ '"$HOSTNAME"'/' /etc/hosts
sed -i '/^127\.0\.0\.1\s/s/$/ '"$HOSTNAME"'/' /etc/cloud/templates/hosts.debian.tmpl

_echo "creating local user"
adduser --uid "$X_UID" --shell "$(which zsh)" "$ME"

_echo "granting root access"
echo "${ME} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$ME"

_echo "setting up 1password"
account x0 "x@xero.style"
eval "$(op signin --account x0)"

_echo "setting up key keychain"
get x0 GH_TOKEN | $ASME gh auth login -p ssh --with-token
mkdir -p $MYHOME/.ssh
if [[ -d /home/admin/.ssh ]]; then
	cp /home/admin/.ssh/authorized_keys $MYHOME/.ssh/authorized_keys
else
	cp /root/.ssh/authorized_keys $MYHOME/.ssh/authorized_keys
fi
get x0 id_ed25519_github privateKey > $MYHOME/.ssh/id_ed25519
get x0 id_ed25519_github publicKey > $MYHOME/.ssh/id_ed25519.pub
ssh-keyscan -p 22 -H github.com gist.github.com > /root/.ssh/known_hosts
$ASME ssh-keyscan -p 22 -H github.com gist.github.com > $MYHOME/.ssh/known_hosts
chown -R $ME:$ME /home/$ME/.ssh
chmod 700 $MYHOME/.ssh
chmod 600 $MYHOME/.ssh/*

# i do not want these dirs to be symlinks
_echo "creating directory skeletons"
$ASME mkdir -p \
	$MYHOME/.{config,local} \
	$MYHOME/.local/{bin,docs,cache,lib,share,src,state} \
	$MYHOME/.local/state/zsh

_echo "setting up rust"
curl https://sh.rustup.rs -sSf | $ASME CARGO_HOME="$MYHOME/.local/lib/cargo" RUSTUP_HOME="$MYHOME/.local/lib/rustup" sh -s -- -y --default-toolchain stable --profile default &&
	$ASME CARGO_HOME="$MYHOME/.local/lib/cargo" RUSTUP_HOME="$MYHOME/.local/lib/rustup" $MYHOME/.local/lib/cargo/bin/cargo install cargo-quickinstall &&
	$ASME CARGO_HOME="$MYHOME/.local/lib/cargo" RUSTUP_HOME="$MYHOME/.local/lib/rustup" $MYHOME/.local/lib/cargo/bin/cargo quickinstall lolcat stylua

_echo "setting up starship"
if [[ "$ARCH" == "arm" ]]; then
	curl -sS https://starship.rs/install.sh | sh
else
	$ASME CARGO_HOME="$MYHOME/.local/lib/cargo" RUSTUP_HOME="$MYHOME/.local/lib/rustup" $MYHOME/.local/lib/cargo/bin/cargo quickinstall starship
fi

_echo "building neovim"
if [[ "$ARCH" == "arm" ]]; then
	$ASME git clone --depth=1 https://github.com/neovim/neovim.git -b stable $MYHOME/.local/src/neovim &&
		cd $MYHOME/.local/src/neovim &&
		make CMAKE_BUILD_TYPE=RelWithDebInfo &&
		make install
else
	apt install -y neovim/prebuilt-mpr
fi

_echo "setting up dotfiles"
$ASME git clone git@github.com:xero/dotfiles.git $MYHOME/.local/src/dotfiles &&
	cd $MYHOME/.local/src/dotfiles &&
	$ASME stow bin fun git gpg ssh tmux neovim zsh -t $MYHOME
# tmux
$ASME mkdir $MYHOME/.config/tmux/plugins &&
	$ASME git clone --depth=1 https://github.com/tmux-plugins/tpm $MYHOME/.config/tmux/plugins/tpm &&
	$ASME $MYHOME/.config/tmux/plugins/tpm/scripts/install_plugins.sh &&
	cd $MYHOME/.config/tmux/plugins/tmux-thumbs &&
	expect -c "spawn ./tmux-thumbs-install.sh; send \"\r1\r\"; expect complete" 1> /dev/null
# nvim
$ASME mkdir $MYHOME/.local/nvim &&
	$ASME git clone --filter=blob:none --single-branch https://github.com/folke/lazy.nvim.git $MYHOME/.local/share/nvim/lazy
$ASME nvim --headless "+Lazy! sync" +qa
$ASME nvim --headless "+MasonInstallAll" +qa

# shellcheck disable=SC2016
echo 'export ZDOTDIR="$HOME"/.config/zsh' >> /etc/zsh/zshenv

_echo "creating ~src, ~docs, and ~dotfiles aliases"
useradd -g src -d $MYHOME/.local/src src
useradd -d $MYHOME/.local/src/dotfiles dotfiles
useradd -d $MYHOME/docs docs

_echo "building mosh"
# OSC52 clipboard support - https://github.com/mobile-shell/mosh/pull/1104
$ASME git clone --depth=1 https://github.com/mgulick/mosh.git -b osc-52-clipboard-types $MYHOME/.local/src/mosh &&
	cd $MYHOME/.local/src/mosh &&
	./autogen.sh &&
	./configure &&
	make &&
	make install

_echo "building clipmenu"
$ASME git clone --recurse-submodules git@github.com:xero/clipmenu.git $MYHOME/.local/src/clipmenu &&
	cd $MYHOME/.local/src/clipmenu/clipnotify &&
	make install && cd .. && make install &&
	$ASME systemctl --user daemon-reload &&
	$ASME systemctl --user restart clipmenud.service &> /dev/null

_echo "setting up kubectx"
$ASME git clone --depth=1 https://github.com/ahmetb/kubectx.git $MYHOME/.local/src/kubectx &&
	mv $MYHOME/.local/src/kubectx/kubectx $MYHOME/.local/src/kubectx/kubens $MYHOME/.local/bin/ &&
	chmod +x $MYHOME/.local/bin/kubens $MYHOME/.local/bin/kubectx

_echo "setting up k9s"
[[ "$ARCH" == "arm" ]] && PKGARCH="arm64" || PKGARCH="amd64"
$ASME curl -L "https://github.com/derailed/k9s/releases/download/v0.27.4/k9s_Linux_${PKGARCH}.tar.gz" -o $MYHOME/.local/src/k9s.tar.gz &&
	tar xzf $MYHOME/.local/src/k9s.tar.gz -C $MYHOME/.local/bin/ k9s &&
	chmod +x $MYHOME/.local/bin/k9s &&
	rm $MYHOME/.local/src/k9s.tar.gz

echo "updating the aws cli to v2"
[[ "$ARCH" == "arm" ]] && PKGARCH="aarch64" || PKGARCH="x86_64"
curl -L "https://awscli.amazonaws.com/awscli-exe-linux-${PKGARCH}.zip" -o /tmp/awscliv2.zip &&
	cd /tmp && unzip awscliv2.zip &&
	./aws/install --bin-dir /usr/bin --install-dir /usr/local/aws-cli --update &&
	rm /tmp/awscliv2.zip

_echo "setting up node with nave"
$ASME mkdir -p $MYHOME/.config/node
curl -L https://raw.githubusercontent.com/isaacs/nave/main/nave.sh > $MYHOME/.local/bin/nave &&
	chmod +x $MYHOME/.local/bin/nave &&
	NAVE_DIR="$MYHOME/.local/lib/nodejs" $MYHOME/.local/bin/nave usemain lts &&
	$ASME npm i -g \
		neovim \
		sofancy

_echo "setting up python + pip"
$ASME pip3 -V
$ASME pip3 install --upgrade pip
$ASME pip3 install --no-warn-script-location \
	boto3 \
	cryptography \
	docutils \
	emoji-fzf \
	greynoise \
	https://github.com/PaulSec/API-dnsdumpster.com/archive/master.zip \
	json-spec \
	mycli \
	neovim \
	pgcli \
	six \
	urllib3 \
	wcwidth

_echo "setting up ascii/ansi art tools"
rm -rf /usr/share/figlet/
git clone --depth=1 https://github.com/xero/figlet-fonts.git /usr/share/figlet/
$ASME git clone --depth=1 https://github.com/digitallyserviced/tdfgo.git $MYHOME/.local/src/tdfgo &&
	cd $MYHOME/.local/src/tdfgo &&
	$ASME go build &&
	mv ./tdfgo $MYHOME/.local/bin/tdfgo &&
	chmod +x $MYHOME/.local/bin/tdfgo &&
	mkdir -p $MYHOME/.config/tdfgo &&
	mv ./fonts $MYHOME/.config/tdfgo/fonts

_echo "updating motd"
rm /etc/motd /etc/update-motd.d/*
cat << 'X0' > /etc/update-motd.d/00-banner
#!/bin/bash
draw() {
  out=
  perc=
  size=
  inc=$(( perc * size / 100 ))
  color=36
  color2=95
  for v in $(seq 0 $(( size - 1 ))); do
    [ "$v" -le "$inc" ] && out="${out}\e[1;${color}m${FULL}" || out="${out}\e[0;${color2}m${EMPTY}"
  done
  printf "$out"
}

i=1
c=$(printf "\e[0m\e[31m░▒")
while [ $i -le 6 ];do
  c=${c}$(printf "\e[$((i+41))m\e[$((i+30))m█▓▒░")
  i=$((i+1))
done
COLORS=${c}$(printf "\e[37m█\e[0m▒░")

FULL=━
EMPTY=┄
HOST=$(hostname)
IP=$(curl -s icanhazip.com) #$(hostname -I | cut -d' ' -f1)
DISTRO=$(grep PRETTY </etc/os-release | sed 's/PRETTY_NAME=//;s/"//g;s/GNU.Linux / /') # TAKE THAT RMS ;P
KERNEL=$(uname -r)
PKGS=$(apt list --installed 2>/dev/null | grep -c 'installed')
UPTIME=$(uptime -p | cut -d " " -f2-)
[[ ${#UPTIME} -ge 22 ]] && UPTIME=$(echo "$UPTIME" | sed 's/ hours/hrs/;s/ minutes/mins/')

c_lvl=$(printf "%.0f" `grep 'cpu ' /proc/stat | awk '{usage=(+)*100/(++)} END {print usage}'`)
CPU=$(printf "\e[0;36m%-4s \e[1;95m%-5s %-25s \n" "cpu" "$c_lvl%" `draw "$c_lvl" 18`)

ram_lvl=$(free | awk '/Mem:/ {print int(/ * 100.0)}')
RAM=$(printf "\e[0;36m%-4s \e[1;95m%-5s %-25s \n" "ram" "$ram_lvl%" `draw "$ram_lvl" 18`)

disk_lvl=$(df -h | grep '/$' | tr -s ' ' | cut -d ' ' -f5 | sed 's/%//')
DISK=$(printf "\e[0;36m%-4s \e[1;95m%-5s %-25s \n" "disk" "$disk_lvl%" `draw "$disk_lvl" 18`)

PPID1=$(grep PPid <"/proc/$PPID/status" | awk '{ print  }')
PPID2=$(grep PPid <"/proc/$PPID1/status" | awk '{ print  }')
USERNAME=$(pgrep "$PPID2" | awk '{ print  }' | head -1)
[ -z "$USERNAME" ] && USERNAME=$USER

files=0
IFS=':' read -r -a PATHS <<<"$PATH"
mapfile -t DIRS <<<"$(printf "%s\n" "${PATHS[@]}" | sort -u)"
for d in "${DIRS[@]}"; do
  [ -d "$d" ] && { new=$(find "$d" -maxdepth 1 -type f -executable -print | wc -l); files=$(( files+new )); }
done

cat << EOF

  ▄████      ▄███▄  
 ▄▄ ▒▓█ ▄▄▀ █▓░ ░▓█ ▀▄▄    welcome to $HOST, $USERNAME
 ▒▒ ░▒▓ ▒▒ ▓▓░▌▄▐░▓▓ ▒▒ $COLORS
 ▀▀ ░░▒ ▀▀ ▓▒░ ▀ ░▒▓ ▀▀ distro: $DISTRO
 ░░ ░░░ ░░▄▄▄▄ ▀ ░░░ ░░ kernel: $KERNEL
 ▒▒ ▒░░ ▒▒▓▓▓▓ ░░░░▒ ▒▒ public address:  $IP
 ▓▓ ▒▒░ ▓▓▀▀▀▀ ▄ ▒▒▓ ▓▓ uptime:  $UPTIME
 ██ ▓▒▒ ██ ▓▓▓ ▓ ██▓ ██ packages: $PKGS  +   bins: $files
 ▄▄ █▓▓ ▄▄ █▓▓ ▄ ▓██ ▄▄ $CPU
 ▒▒ ██▓ ▒▒ ██▓ ▀ ███ ▒▒ $RAM
 ▀▀ ███ ▀▀▄ ███ ███ ▄▀▀ $DISK
  ███████    ▀███▀     

EOF
X0
chmod +x /etc/update-motd.d/00-banner

# run ssh on a non-standard port
_echo "setting up ssh"
echo "Port $PORT" >> /etc/ssh/sshd_config
systemctl restart ssh.service
systemctl restart sshd

_echo "setting up fail2ban"
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
fail2ban-client reload

_echo "final user setup stuff"
getfile x0 keys runme runme > $MYHOME/runme
op signout --account x0 --forget
$ASME rm $MYHOME/.profile $MYHOME/.bash*
mv $MYHOME/go $MYHOME/.local/lib/
mv $MYHOME/.cache $MYHOME/cache
chmod +x $MYHOME/runme
chown -R $ME:$ME /home/$ME

_echo "removing default user"
userdel -rf admin &> /dev/null &&
	rm /etc/sudoers.d/90-cloud-init-users

_echo "setup complete. rebooting the vps. reconnect as yourself via mosh"
IP=$(curl -sL icanhazip.com)
echo "$HOSTNAME: $ME@$IP:$PORT"
echo "mosh location: $(which mosh-server)"

# self destruct
srm -dvrl "{{&blob}}" &> /dev/null
systemctl reboot

Download

raw zip tar