BazziteOS - Post Script
The folllowing script is a draft script and is provided as a guide.
IAW our ditching of the Microsoft eco system and the problems with MintOS in gaming we are moving various systems to BazziteOS. So far we have not been disappointed
The script is a draft. It is in testing, but offered as a guide
The following is noted:
- The printer section has been left in as a guide
- The UDM certificate section has been left in as a guide. This allows the UDM firewall to show the blocked page within systems that utilise a Ubiquiti network for end users. This is irrelevant for most networks.
- ClamAV code section has been pulled - I dont like it and in review.
- Script has been tested and modifications made.
ClamAV Problem
ClamAV was installed via distrobox and this was a mistake.
If you have used this script and run the ClamAV section the uninstall script is here:
The solution is ugly and uses to much space - so I have pulled that section and am reworking the code.
#!/bin/bash
# ─────────────────────────────────────────────────────────────────────────────
# undo-clamav-bazzite.sh
# Remove all ClamAV setup artefacts from Bazzite — all script versions
# ─────────────────────────────────────────────────────────────────────────────
#
# Removes artefacts left by setup-clamav.sh v4.0.0 and v4.1.0 on Bazzite:
#
# Distrobox:
# - clamav-box rootful container (if present)
#
# System files (installed via sudo):
# - /usr/local/bin/clamscan-host
# - /etc/systemd/system/clamav-update.service
# - /etc/systemd/system/clamav-update.timer
#
# User-level systemd (v4.0.0 Homebrew artefact):
# - ~/.config/systemd/user/clamav-freshclam.timer
# - ~/.config/systemd/user/clamav-freshclam.service
#
# rpm-ostree layer (if clamav was layered):
# - clamav, clamav-update
# NOTE: rpm-ostree uninstall requires a reboot to take effect.
#
# Run as: normal user (not root)
# Sudo: called inline for system file removal and systemd units
#
# Usage: ./undo-clamav-bazzite.sh [--dry-run]
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
info() { echo -e "\033[0;32m[INFO]\033[0m $*"; }
warn() { echo -e "\033[0;33m[WARN]\033[0m $*" >&2; }
error() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; }
section() { echo -e "\n\033[1;34m>>> $* \033[0m"; }
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
info "Dry-run mode — no changes will be made."
fi
if [[ "${EUID}" -eq 0 ]]; then
error "Run as your normal user, not root."
exit 1
fi
REAL_USER="$(id -un)"
info "Running as: $REAL_USER"
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 1 — Stop and remove clamav-update systemd timer
# ─────────────────────────────────────────────────────────────────────────────
section "Removing clamav-update systemd timer"
for unit in clamav-update.timer clamav-update.service; do
if sudo systemctl is-active --quiet "$unit" 2>/dev/null; then
if $DRY_RUN; then
info "[DRY] Would stop: $unit"
else
sudo systemctl stop "$unit" && info "Stopped: $unit"
fi
fi
if sudo systemctl is-enabled --quiet "$unit" 2>/dev/null; then
if $DRY_RUN; then
info "[DRY] Would disable: $unit"
else
sudo systemctl disable "$unit" && info "Disabled: $unit"
fi
fi
done
for unit_file in \
/etc/systemd/system/clamav-update.service \
/etc/systemd/system/clamav-update.timer; do
if [[ -f "$unit_file" ]]; then
if $DRY_RUN; then
info "[DRY] Would remove: $unit_file"
else
sudo rm -f "$unit_file" && info "Removed: $unit_file"
fi
else
info "Not present: $unit_file"
fi
done
if $DRY_RUN; then
info "[DRY] Would run: sudo systemctl daemon-reload"
else
sudo systemctl daemon-reload
info "systemctl daemon-reload complete."
fi
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 2 — Remove clamscan-host wrapper
# ─────────────────────────────────────────────────────────────────────────────
section "Removing clamscan-host wrapper"
WRAPPER="/usr/local/bin/clamscan-host"
if [[ -f "$WRAPPER" ]]; then
if $DRY_RUN; then
info "[DRY] Would remove: $WRAPPER"
else
sudo rm -f "$WRAPPER" && info "Removed: $WRAPPER"
fi
else
info "Not present: $WRAPPER"
fi
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 3 — Remove distrobox clamav-box container
# ─────────────────────────────────────────────────────────────────────────────
section "Removing clamav-box Distrobox container"
if ! command -v distrobox &>/dev/null; then
info "Distrobox not installed — skipping container removal."
else
if distrobox list --root 2>/dev/null | grep -q "clamav-box"; then
if $DRY_RUN; then
info "[DRY] Would run: distrobox rm --root --force clamav-box"
else
info "Removing clamav-box container..."
distrobox rm --root --force clamav-box \
&& info "clamav-box removed." \
|| warn "clamav-box removal failed — try manually: distrobox rm --root --force clamav-box"
fi
else
info "clamav-box container not present — nothing to remove."
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 4 — Remove user-level systemd timer (v4.0.0 Homebrew artefact)
# ─────────────────────────────────────────────────────────────────────────────
section "Removing legacy user-level systemd timer (v4.0.0)"
USER_SYSTEMD_DIR="${HOME}/.config/systemd/user"
for unit in clamav-freshclam.timer clamav-freshclam.service; do
if systemctl --user is-active --quiet "$unit" 2>/dev/null; then
if $DRY_RUN; then
info "[DRY] Would stop user unit: $unit"
else
systemctl --user stop "$unit" 2>/dev/null || true
info "Stopped user unit: $unit"
fi
fi
if systemctl --user is-enabled --quiet "$unit" 2>/dev/null; then
if $DRY_RUN; then
info "[DRY] Would disable user unit: $unit"
else
systemctl --user disable "$unit" 2>/dev/null || true
info "Disabled user unit: $unit"
fi
fi
done
for unit_file in \
"${USER_SYSTEMD_DIR}/clamav-freshclam.timer" \
"${USER_SYSTEMD_DIR}/clamav-freshclam.service"; do
if [[ -f "$unit_file" ]]; then
if $DRY_RUN; then
info "[DRY] Would remove: $unit_file"
else
rm -f "$unit_file" && info "Removed: $unit_file"
fi
else
info "Not present: ${unit_file##*/}"
fi
done
if [[ -d "$USER_SYSTEMD_DIR" ]]; then
if $DRY_RUN; then
info "[DRY] Would run: systemctl --user daemon-reload"
else
systemctl --user daemon-reload 2>/dev/null || true
info "User systemd reloaded."
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 5 — Remove rpm-ostree layered clamav packages (if present)
# ─────────────────────────────────────────────────────────────────────────────
section "Checking rpm-ostree layered ClamAV packages"
LAYERED=$(rpm-ostree status --json 2>/dev/null \
| python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
pkgs = d['deployments'][0].get('requested-packages', [])
clamav = [p for p in pkgs if 'clamav' in p]
print(' '.join(clamav))
except:
pass
" 2>/dev/null || true)
if [[ -n "$LAYERED" ]]; then
warn "Found rpm-ostree layered ClamAV packages: $LAYERED"
if $DRY_RUN; then
info "[DRY] Would run: sudo rpm-ostree uninstall $LAYERED"
info "[DRY] Would require a reboot to take effect"
else
info "Removing layered packages: $LAYERED"
sudo rpm-ostree uninstall $LAYERED \
&& warn "rpm-ostree uninstall queued — REBOOT REQUIRED to complete removal." \
|| warn "rpm-ostree uninstall failed — check: sudo rpm-ostree status"
fi
else
info "No ClamAV packages found in rpm-ostree layered packages."
fi
# ─────────────────────────────────────────────────────────────────────────────
# SECTION 6 — Remove Homebrew clamav (v4.0.0 artefact, if present)
# ─────────────────────────────────────────────────────────────────────────────
section "Checking Homebrew ClamAV (v4.0.0 artefact)"
if command -v brew &>/dev/null; then
if brew list clamav &>/dev/null 2>&1; then
if $DRY_RUN; then
info "[DRY] Would run: brew uninstall clamav"
else
brew uninstall clamav \
&& info "Homebrew clamav removed." \
|| warn "brew uninstall failed — try manually: brew uninstall clamav"
fi
else
info "Homebrew clamav not installed — nothing to remove."
fi
else
info "Homebrew not present — nothing to remove."
fi
# ─────────────────────────────────────────────────────────────────────────────
# COMPLETE
# ─────────────────────────────────────────────────────────────────────────────
section "Undo Complete"
echo ""
echo " ┌─────────────────────────────────────────────────────────────┐"
echo " │ ClamAV Bazzite Undo Complete │"
echo " ├─────────────────────────────────────────────────────────────┤"
echo " │ Removed (if present): │"
echo " │ clamav-update.timer / .service (system) │"
echo " │ /usr/local/bin/clamscan-host │"
echo " │ clamav-box Distrobox container │"
echo " │ User-level clamav-freshclam timer (v4.0.0) │"
echo " │ Homebrew clamav (v4.0.0) │"
echo " ├─────────────────────────────────────────────────────────────┤"
if [[ -n "${LAYERED:-}" ]]; then
echo " │ *** REBOOT REQUIRED *** │"
echo " │ rpm-ostree uninstall queued for: ${LAYERED} │"
fi
echo " │ System is clean — ready for fresh ClamAV setup. │"
echo " └─────────────────────────────────────────────────────────────┘"
echo ""Post BazziteOS setup script.
#!/bin/bash
# ================================================================================
# Base Setup Script for Bazzite OS (Fedora Atomic / rpm-ostree)
# Filename : setup-laptop-bazzite.sh
# Updated : 2026-05-17
# Version : 1.1.0
# ================================================================================
#
# PURPOSE:
# Post-install hardening and configuration for a Bazzite desktop/laptop.
# Bazzite is an IMMUTABLE OS — the root filesystem is read-only and managed
# via rpm-ostree atomic image updates. This script respects that model:
#
# • System packages → rpm-ostree install (requires reboot to activate)
# • GUI applications → Flatpak (Flathub)
# • CLI/TUI tools → Homebrew (brew) where possible
# • Anything else → Distrobox containers
#
# DO NOT use apt-get, dpkg, dnf, or yum directly on the host.
# DO NOT attempt to install .deb packages — this is Fedora/RPM based.
#
# WHAT THIS SCRIPT DOES:
#
# SECURITY & HARDENING:
# - Hardens SSH via sshd_config.d drop-in (survives image updates)
# - Regenerates SSH host keys (ED25519 + RSA 4096)
# - ClamAV antivirus installed via rpm-ostree, run as a rootful Distrobox
# container to avoid conflicts with the immutable OS image
# - Configures firewalld (Bazzite's native firewall) with default-deny
# incoming policy across all zones, preserving SSH and WireGuard egress
# - UDM SE block-page certificate installed to system trust store
#
# SOFTWARE INSTALLATION (Flatpak via Flathub):
# - GIMP, Inkscape, Krita, LibreOffice, VLC, Pinta
# - Google Chrome (Flatpak — avoids APT/rpm-ostree repo complexity)
#
# SYSTEM TOOLS (rpm-ostree layered — requires reboot):
# - wireguard-tools (kernel module already included in Bazzite)
# - curl, wget, nmap, smartmontools, mtr, rsyslog, fwupd
# - openssh-server (usually pre-installed; layered defensively)
#
# INSYNC (Google Drive client):
# - Downloads Insync Fedora 44 RPM directly from cdn.insynchq.com
# (bypasses the yum repo — avoids GPG DB write on immutable root and
# the fc44 repo 404 that affects $releasever-based repo configs)
# - Falls back to the working fc43 repo URL if the CDN download fails
# - Detects KDE vs GNOME at runtime for file manager integration
# - Layers insync via rpm-ostree (requires reboot)
#
# SYSTEM CONFIGURATION:
# - Epson WF-4830 printer via IPP Everywhere
# - IPv6 stable address preference (disables temp addresses on wlo1)
# - Automatic OS and Flatpak updates via ujust / rpm-ostree
# - Custom MOTD (OS, IP, uptime, IPv4/IPv6)
#
# FIRMWARE UPDATES (fwupd):
# - LVFS metadata refresh
# - AC power and battery pre-flight checks
# - BIOS/UEFI capsule gating on AC power
# - Non-BIOS firmware applied unconditionally
#
# DIAGNOSTICS:
# - Final health check covering firewall, SSH, ClamAV, Insync,
# systemd units, and rpm-ostree deployment status
#
# WHAT THIS SCRIPT DOES NOT DO:
# - AppArmor: not applicable — Bazzite uses SELinux (Fedora default)
# - UFW: not applicable — Bazzite uses firewalld
# - apt/dpkg/APT repos: not applicable — Fedora RPM ecosystem only
# - i386 multiarch: not applicable — handled by Flatpak/Steam runtime
# - Novabench: no safe RPM/Flatpak available; use Phoronix Test Suite
# inside a Distrobox container if benchmarking is required
#
# ADDITIONAL NOTES:
# a) AUTOMATIC UPDATES: Bazzite updates are atomic via rpm-ostree. This
# script enables the rpm-ostreed-automatic timer and configures Flatpak
# auto-updates. OS updates are staged and applied on next reboot.
#
# b) WAYDROID (Android / Play Store): Bazzite has native Waydroid support
# via `ujust setup-waydroid`. This is a graphical, interactive setup and
# CANNOT be fully automated from a non-interactive script. Instructions
# are printed at the end of this script. Run them manually after reboot.
#
# c) SECURITY NOTE: All Debian/LMDE-specific constructs (apt hold, dpkg,
# /etc/apt/*, AppArmor, update-ca-certificates) have been replaced with
# their Fedora/Bazzite equivalents. No Debian-origin commands remain.
#
# REBOOT REQUIRED:
# rpm-ostree layers packages into a new pending deployment. A reboot is
# REQUIRED at the end of this script for all layered packages to activate.
# The script will prompt before rebooting, or you can reboot manually.
#
# ================================================================================
#
# CHANGELOG:
# 1.1.0 - 2026-05-17 - Three fixes confirmed via live test run:
# (1) Insync: replaced rpm --import + yum repo approach
# with direct CDN RPM download (cdn.insynchq.com).
# rpm --import fails on Bazzite — RPM DB is on the
# immutable image layer. CDN download also bypasses
# the fc44 yum repo 404 ($releasever resolving to
# 44 when Insync's repo only published up to fc43).
# Fallback to fc43 repo URL added if CDN is down.
# (2) Firewalld: removed --permanent from
# --set-default-zone (firewalld rejects that combo;
# the default zone change is always permanent).
# Added DHCPv6-client rule omitted in v1.0.0.
# (3) DE detection improved: now checks running desktop
# processes (plasmashell, gnome-shell) as fallback
# when XDG_CURRENT_DESKTOP is unset under sudo.
# 1.0.0 - 2026-05-15 - Initial Bazzite port from LMDE 7 base-setup v2.3.0.
# Complete rewrite for rpm-ostree/Fedora Atomic model.
# Removed: apt/dpkg, AppArmor, UFW, i386, Novabench,
# user-specific braedach block, Chrome APT repo.
# Added: firewalld hardening, rpm-ostree layering,
# Insync RPM repo, ClamAV Distrobox strategy,
# automatic update configuration, final diagnostics.
#
# ================================================================================
set -euo pipefail
# -------------------------------------------------------------------------------
# Privilege check
if [[ "${EUID}" -ne 0 ]]; then
echo "[ERROR] This script must be run as root (sudo ./setup-laptop-bazzite.sh)"
exit 1
fi
# -------------------------------------------------------------------------------
# Logging
LOGFILE="/var/log/bazzite-setup.log"
BACKUP_TS="$(date +%Y%m%d-%H%M%S)"
exec > >(tee -a "$LOGFILE") 2>&1
info() { echo -e "\033[0;32m[INFO]\033[0m $*"; }
warn() { echo -e "\033[0;33m[WARN]\033[0m $*" >&2; }
error() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; exit 1; }
step() { echo -e "\n\033[1;34m[====]\033[0m $*\n"; }
info "Bazzite setup started at $(date)"
info "Log file: ${LOGFILE}"
# -------------------------------------------------------------------------------
# Resolve logged-in user (not root, even when run via sudo)
CURRENT_USER=$(logname 2>/dev/null || echo "${SUDO_USER:-root}")
CURRENT_HOME=$(getent passwd "${CURRENT_USER}" | cut -d: -f6)
info "Running as root. Detected user context: ${CURRENT_USER} (home: ${CURRENT_HOME})"
# -------------------------------------------------------------------------------
# Sanity check: confirm we are on a Fedora/Bazzite system
if ! command -v rpm-ostree &>/dev/null; then
error "rpm-ostree not found. This script is for Bazzite / Fedora Atomic only."
fi
OS_ID=$(grep -oP '(?<=^ID=).*' /etc/os-release | tr -d '"' || echo "unknown")
OS_PRETTY=$(grep -oP '(?<=^PRETTY_NAME=).*' /etc/os-release | tr -d '"' || echo "unknown")
info "Detected OS: ${OS_PRETTY}"
# -------------------------------------------------------------------------------
# Network connectivity check
step "Network Connectivity"
info "Checking network connectivity..."
if ! curl -fsS --max-time 10 https://flathub.org > /dev/null 2>&1; then
error "Network check failed — cannot reach flathub.org. Check DNS/network before continuing."
fi
info "Network OK."
# -------------------------------------------------------------------------------
# Reset any failed systemd units from a prior run
step "Systemd Housekeeping"
info "Checking and resetting failed systemd units..."
systemctl reset-failed || true
# ===============================================================================
# SECTION 1: rpm-ostree package layering
#
# All packages listed here are added to the pending deployment.
# They will only be ACTIVE after the reboot at the end of this script.
# Keep this list minimal — prefer Flatpak, Homebrew, or Distrobox instead.
# ===============================================================================
step "rpm-ostree Package Layering"
info "Layering system packages via rpm-ostree..."
info "NOTE: These packages activate after the post-script reboot."
# wireguard-tools: kernel WireGuard module is already in Bazzite's kernel.
# This adds the wg / wg-quick CLI tools for managing configs.
RPM_PACKAGES=(
curl
wget
nmap
mtr
smartmontools
rsyslog
fwupd
openssh-server
wireguard-tools
cups # required for printer setup
avahi # required for IPP Everywhere / mDNS printer discovery
)
# Build a space-separated list
PKG_LIST="${RPM_PACKAGES[*]}"
if rpm-ostree install --idempotent --allow-inactive ${PKG_LIST}; then
info "rpm-ostree layering queued successfully."
info "Packages will be active after reboot."
else
warn "rpm-ostree install reported an issue — check output above."
warn "Some packages may already be installed (--idempotent handles this)."
fi
# ===============================================================================
# SECTION 2: Insync (Google Drive client)
#
# Insync is a commercial GUI Google Drive/OneDrive sync client for Linux.
# ($29.99/account, 15-day free trial)
#
# WHY DIRECT CDN DOWNLOAD (not the yum repo):
# Two confirmed failures with the repo approach on Bazzite/Fedora 44:
# 1. rpm --import cannot write to /usr/share/rpm/.rpm.lock — that path is
# on the immutable image layer and is read-only on Bazzite.
# 2. https://yum.insynchq.com/fedora/44/ returns 404. Insync's repo lags
# behind Fedora releases; the $releasever variable resolves to 44 but
# Insync's infrastructure only had fc43 at Fedora 44 GA.
#
# Solution: download the native fc44 RPM directly from cdn.insynchq.com
# and install it as a local file via rpm-ostree. No repo or GPG DB write
# required. A fallback to the working fc43 repo URL is attempted if the
# CDN download fails.
# ===============================================================================
step "Insync (Google Drive Client)"
INSYNC_CDN_BASE="https://cdn.insynchq.com/builds/linux"
INSYNC_RPM_TMP="/tmp/insync-fc44.x86_64.rpm"
INSYNC_KDE_RPM_TMP="/tmp/insync-kde-fc44.rpm"
INSYNC_RPM_URL="${INSYNC_CDN_BASE}/insync-3.9.8.60034-fc44.x86_64.rpm"
# Detect desktop environment — XDG_CURRENT_DESKTOP is usually unset when
# running as root, so fall back to checking running desktop processes.
ACTUAL_DE="${XDG_CURRENT_DESKTOP:-}"
if [[ -z "${ACTUAL_DE}" ]] && [[ -n "${SUDO_USER:-}" ]]; then
ACTUAL_DE=$(sudo -u "${SUDO_USER}" bash -c 'echo "${XDG_CURRENT_DESKTOP:-}"' 2>/dev/null || true)
fi
if [[ -z "${ACTUAL_DE}" ]]; then
if pgrep -x plasmashell &>/dev/null || pgrep -x kwin_wayland &>/dev/null; then
ACTUAL_DE="KDE"
elif pgrep -x gnome-shell &>/dev/null; then
ACTUAL_DE="GNOME"
fi
fi
info "Detected desktop environment: ${ACTUAL_DE:-unknown}"
info "Downloading Insync Fedora 44 RPM from CDN..."
info "URL: ${INSYNC_RPM_URL}"
if curl -fsSL --retry 3 --retry-delay 5 -o "${INSYNC_RPM_TMP}" "${INSYNC_RPM_URL}"; then
info "Insync RPM downloaded successfully."
if rpm-ostree install --idempotent --allow-inactive "${INSYNC_RPM_TMP}"; then
info "Insync layered successfully — will be active after reboot."
else
warn "rpm-ostree install of Insync RPM failed."
warn "Install manually after reboot:"
warn " curl -fsSL ${INSYNC_RPM_URL} -o /tmp/insync.rpm"
warn " sudo rpm-ostree install /tmp/insync.rpm"
fi
rm -f "${INSYNC_RPM_TMP}"
else
warn "CDN download failed — attempting fallback via fc43 repo URL..."
cat > /etc/yum.repos.d/insync.repo <<'EOF'
[insync]
name=Insync repo (Fedora 43 — fc44 compatible)
baseurl=http://yum.insync.io/fedora/43/
gpgcheck=0
enabled=1
metadata_expire=120m
EOF
info "Fallback repo written (/etc/yum.repos.d/insync.repo)."
if rpm-ostree install --idempotent --allow-inactive insync; then
info "Insync installed via fallback fc43 repo."
else
warn "Insync install failed via fallback repo too."
warn "Download manually from: https://www.insynchq.com/downloads/linux#fedora"
warn "Then: sudo rpm-ostree install /path/to/insync-*.fc44.x86_64.rpm"
fi
fi
# File manager integration (KDE only — GNOME integration is bundled in main package)
if echo "${ACTUAL_DE}" | grep -qi "kde\|plasma"; then
info "KDE detected — installing insync-dolphin integration..."
INSYNC_KDE_URL="${INSYNC_CDN_BASE}/insync-dolphin-3.9.8.60034-fc44.x86_64.rpm"
if curl -fsSL --retry 2 -o "${INSYNC_KDE_RPM_TMP}" "${INSYNC_KDE_URL}" 2>/dev/null; then
rpm-ostree install --idempotent --allow-inactive "${INSYNC_KDE_RPM_TMP}" \
&& info "insync-dolphin layered." \
|| warn "insync-dolphin layer failed — install manually after reboot."
rm -f "${INSYNC_KDE_RPM_TMP}"
else
warn "insync-dolphin RPM not found at CDN — install manually after reboot:"
warn " sudo rpm-ostree install insync-dolphin"
fi
else
info "GNOME or unknown DE — Nautilus integration is bundled in the main Insync package."
fi
# ===============================================================================
# SECTION 3: Flatpak Applications
#
# Flatpak is the primary app delivery mechanism on Bazzite.
# These apps run sandboxed and update independently of the OS image.
# ===============================================================================
step "Flatpak Application Installation"
# Ensure Flathub remote is present (it should be on Bazzite, but be safe)
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo || true
FLATPAK_APPS=(
"org.gimp.GIMP"
"org.inkscape.Inkscape"
"org.kde.krita"
"org.libreoffice.LibreOffice"
"org.videolan.VLC"
"com.github.PintaProject.Pinta"
"com.google.Chrome"
)
for app in "${FLATPAK_APPS[@]}"; do
info "Installing Flatpak: ${app}..."
if flatpak install -y --noninteractive flathub "${app}" 2>/dev/null; then
info " ✓ ${app}"
else
warn " ✗ ${app} — install failed or already installed."
fi
done
# ===============================================================================
# SECTION 4: Firewalld Configuration
#
# Bazzite uses firewalld (not UFW or nftables directly).
# firewalld sits on top of nftables on modern Fedora.
#
# Policy:
# - Default incoming: DROP (block all unsolicited inbound)
# - Default outgoing: ACCEPT (all egress permitted)
# - SSH allowed from LAN (192.168.0.0/16) only
# - WireGuard egress UDP 51820 allowed outbound (inherent in default-allow out)
# - WireGuard inbound port opened only in 'home' zone if needed
# - All changes saved to permanent config (survive reboot)
# ===============================================================================
step "Firewalld Configuration"
if ! command -v firewall-cmd &>/dev/null; then
warn "firewall-cmd not found — skipping firewall configuration."
warn "Install firewalld and rerun this section manually."
else
info "Ensuring firewalld is enabled and running..."
systemctl enable --now firewalld || warn "firewalld enable/start failed."
# ---- Set default zone to 'drop' (silently discard all inbound) ----
# NOTE: --set-default-zone is ALWAYS permanent (written to firewalld.conf).
# firewalld rejects --permanent combined with --set-default-zone — do not add it.
info "Setting default zone to 'drop' (default-deny all inbound)..."
firewall-cmd --set-default-zone=drop
# ---- SSH access from LAN (RFC1918 IPv4) ----
info "Adding SSH rich rule: permit from RFC1918 only..."
firewall-cmd --permanent --zone=drop \
--add-rich-rule='rule family="ipv4" source address="192.168.0.0/16" service name="ssh" accept' \
2>/dev/null || info "SSH IPv4 rule already exists."
# ---- IPv6 ULA and link-local SSH access ----
firewall-cmd --permanent --zone=drop \
--add-rich-rule='rule family="ipv6" source address="fe80::/10" service name="ssh" accept' \
2>/dev/null || info "IPv6 link-local SSH rule already exists."
firewall-cmd --permanent --zone=drop \
--add-rich-rule='rule family="ipv6" source address="fc00::/7" service name="ssh" accept' \
2>/dev/null || info "IPv6 ULA SSH rule already exists."
# ---- mDNS for printer discovery (LAN only, not internet-facing) ----
firewall-cmd --permanent --zone=drop \
--add-rich-rule='rule family="ipv4" source address="192.168.0.0/16" service name="mdns" accept' \
2>/dev/null || info "mDNS rule already exists."
# ---- DHCPv6 client (required for IPv6 address assignment on most networks) ----
firewall-cmd --permanent --zone=drop \
--add-service=dhcpv6-client \
2>/dev/null || info "DHCPv6-client rule already exists."
# ---- WireGuard inbound port ----
# This machine is a WireGuard CLIENT only — egress to UDP 51820 is covered
# by the default-allow-out policy. Uncomment only if this machine needs to
# ACCEPT inbound WireGuard connections (i.e. acts as a server/endpoint):
# firewall-cmd --permanent --zone=drop --add-port=51820/udp
# ---- Reload to apply permanent changes ----
info "Reloading firewalld to apply configuration..."
firewall-cmd --reload
info "Firewalld configuration complete."
info "Active configuration:"
firewall-cmd --list-all --zone=drop || true
fi
# ===============================================================================
# SECTION 5: SSH Hardening
#
# OpenSSH server is pre-installed on Bazzite. We harden it using a drop-in
# config in /etc/ssh/sshd_config.d/ which survives image updates.
#
# Key changes from the LMDE version:
# - Service name is 'sshd' (not 'ssh') on Fedora/Bazzite
# - sftp subsystem path is /usr/libexec/openssh/sftp-server on Fedora
# - sshd_config is not writable on the immutable root — we use sshd_config.d
# ===============================================================================
step "SSH Hardening"
SSHD_DIR="/etc/ssh"
SSHD_CFG="${SSHD_DIR}/sshd_config"
SSHD_CUSTOM_DIR="${SSHD_DIR}/sshd_config.d"
SSHD_CUSTOM_CFG="${SSHD_CUSTOM_DIR}/99-hardened-custom.conf"
BACKUP_DIR="${SSHD_DIR}/backup-${BACKUP_TS}"
mkdir -p "${BACKUP_DIR}"
mkdir -p "${SSHD_CUSTOM_DIR}"
info "Backing up existing sshd_config and host keys to ${BACKUP_DIR}..."
cp -a "${SSHD_CFG}" "${BACKUP_DIR}/sshd_config.bak" 2>/dev/null || \
warn "sshd_config not yet present (pending rpm-ostree reboot) — will configure keys post-reboot."
for key in ssh_host_ed25519_key ssh_host_rsa_key ssh_host_ecdsa_key; do
[[ -f "${SSHD_DIR}/${key}" ]] && cp -a "${SSHD_DIR}/${key}" "${BACKUP_DIR}/" || true
[[ -f "${SSHD_DIR}/${key}.pub" ]] && cp -a "${SSHD_DIR}/${key}.pub" "${BACKUP_DIR}/" || true
done
# Regenerate host keys only if the openssh-server keys exist
# (they may not exist until after the rpm-ostree reboot if openssh-server was just layered)
if [[ -d /etc/ssh ]]; then
info "Removing old SSH host keys..."
rm -f /etc/ssh/ssh_host_*
info "Generating new ED25519 host key..."
ssh-keygen -t ed25519 -f "${SSHD_DIR}/ssh_host_ed25519_key" -N "" -o -a 100
info "Generating new RSA 4096 host key..."
ssh-keygen -t rsa -b 4096 -f "${SSHD_DIR}/ssh_host_rsa_key" -N "" -o
chmod 600 "${SSHD_DIR}/ssh_host_ed25519_key" "${SSHD_DIR}/ssh_host_rsa_key"
chmod 644 "${SSHD_DIR}/ssh_host_ed25519_key.pub" "${SSHD_DIR}/ssh_host_rsa_key.pub"
fi
# Ensure main sshd_config includes the drop-in directory
if [[ -f "${SSHD_CFG}" ]] && ! grep -q "^Include ${SSHD_CUSTOM_DIR}/\*.conf" "${SSHD_CFG}"; then
# On Fedora Atomic the base config file is on the immutable layer.
# The Include directive may already exist. Check before patching.
if grep -q "^Include" "${SSHD_CFG}"; then
info "Include directive already present in sshd_config."
else
# /etc/ is writable (it's a separate overlay), so this is safe.
sed -i "1i Include ${SSHD_CUSTOM_DIR}/*.conf" "${SSHD_CFG}"
info "Added Include directive to ${SSHD_CFG}."
fi
fi
info "Writing hardened SSH drop-in to ${SSHD_CUSTOM_CFG}..."
cat > "${SSHD_CUSTOM_CFG}" <<EOF
# Hardened SSH Configuration — Bazzite
# Managed by setup-laptop-bazzite.sh (${BACKUP_TS})
# Drop-in file: survives rpm-ostree image updates.
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
HostbasedAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
LoginGraceTime 30
MaxAuthTries 4
MaxSessions 10
HostKey ${SSHD_DIR}/ssh_host_ed25519_key
HostKey ${SSHD_DIR}/ssh_host_rsa_key
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
UseDNS no
PrintMotd no
PrintLastLog yes
AcceptEnv LANG LC_*
# Subsystem path on Fedora/Bazzite (differs from Debian)
Subsystem sftp /usr/libexec/openssh/sftp-server
# Permit password auth from LAN (RFC1918), ULA, and link-local IPv6 only
Match Address 192.168.0.0/16,fe80::/10,fc00::/7
PermitRootLogin no
PasswordAuthentication yes
AllowTcpForwarding no
EOF
chmod 644 "${SSHD_CUSTOM_CFG}"
# Enable and start sshd
info "Enabling and starting sshd service..."
systemctl enable sshd || warn "sshd enable failed (may not be active until reboot)."
systemctl start sshd 2>/dev/null || warn "sshd start failed — openssh-server may not be active until after reboot."
# Validate config if sshd binary is present
if command -v sshd &>/dev/null; then
if sshd -t 2>/dev/null; then
info "sshd_config syntax OK."
systemctl restart sshd 2>/dev/null || warn "sshd restart failed."
else
warn "sshd_config test failed — check ${SSHD_CUSTOM_CFG}"
warn "Backup at: ${BACKUP_DIR}/sshd_config.bak"
fi
else
info "sshd binary not yet active (will be after reboot). Config written."
fi
# ===============================================================================
# SECTION 6: ClamAV
#
# The Bazzite immutable image ships without ClamAV. Layering it via rpm-ostree
# Pull this part of the code - its wrong and doesnt meet requirements
# ===============================================================================
step "ClamAV (via Distrobox Container - nope not pretty)"
warn "Code has been pulled - extensive tesing in progress ..."
# ===============================================================================
# SECTION 7: UDM SE Certificate
#
# Install the Ubiquiti UDM SE block-page CA certificate into the system
# trust store. On Fedora/Bazzite the tool is 'update-ca-trust' (not
# 'update-ca-certificates' which is Debian-specific).
#
# Certificate store location: /etc/pki/ca-trust/source/anchors/
# ===============================================================================
step "UDM SE Block-Page Certificate"
CERT_URL="http://ghost.baden.braedach.com/content/files/2025/12/UniFi-SSL-Certificate.cer"
CERT_TMP="/tmp/UniFi-SSL-Certificate.cer"
CERT_DST="/etc/pki/ca-trust/source/anchors/udmse-blockpage-ca.crt"
info "Downloading UDM SE certificate from ${CERT_URL}..."
if ! wget -q -O "${CERT_TMP}" "${CERT_URL}"; then
warn "Failed to download certificate from ${CERT_URL}"
warn "Ensure ghost.baden.braedach.com is reachable from this machine."
else
if ! grep -q "BEGIN CERTIFICATE" "${CERT_TMP}"; then
warn "Downloaded file is not a valid PEM certificate — skipping install."
rm -f "${CERT_TMP}"
else
cp "${CERT_TMP}" "${CERT_DST}"
if update-ca-trust extract; then
info "UDM SE block-page certificate installed successfully."
info "Installed to: ${CERT_DST}"
else
warn "update-ca-trust failed — check if the file is valid PEM."
fi
rm -f "${CERT_TMP}"
fi
fi
# ===============================================================================
# SECTION 8: IPv6 Stable Address Configuration
#
# Disables IPv6 temporary/privacy addresses on wlo1 to prevent mDNS/Avahi
# resolution failures caused by address churn. Uses sysctl drop-in.
# /etc/sysctl.d/ is writable on Bazzite (/etc is an overlay).
# ===============================================================================
step "IPv6 Configuration"
info "Configuring IPv6 stable addressing..."
cat > /etc/sysctl.d/99-ipv6-laptop.conf <<'EOF'
# Prefer stable IPv6 addresses over temporary privacy addresses.
# Reduces mDNS/Avahi resolution failures caused by address churn on wlo1.
net.ipv6.conf.wlo1.use_tempaddr=0
net.ipv6.conf.all.use_tempaddr=0
net.ipv6.conf.default.use_tempaddr=0
EOF
sysctl -p /etc/sysctl.d/99-ipv6-laptop.conf || warn "IPv6 sysctl apply failed."
# ===============================================================================
# SECTION 9: Printer Setup (Epson WF-4830)
#
# CUPS / avahi have been queued in the rpm-ostree layer above.
# lpadmin will fail if CUPS is not yet active (needs the rpm-ostree reboot).
# We attempt it here; if it fails, a post-reboot reminder is printed.
# ===============================================================================
step "Epson WF-4830 Printer"
info "Attempting to install Epson WF-4830 via IPP Everywhere..."
if systemctl is-active --quiet cups 2>/dev/null; then
if lpadmin -p "EPSON-WF4830" \
-E \
-v "ipp://192.168.1.11/ipp/print" \
-m everywhere \
-D "EPSON WF-4830 Series"; then
info "Printer installed successfully."
lpoptions -d EPSON-WF4830 || true
else
warn "lpadmin failed. Try again after reboot once CUPS is active."
fi
else
warn "CUPS service not running yet (expected before rpm-ostree reboot)."
warn "Run after reboot: sudo lpadmin -p EPSON-WF4830 -E -v ipp://192.168.1.11/ipp/print -m everywhere -D 'EPSON WF-4830 Series'"
fi
# ===============================================================================
# SECTION 10: Firmware Updates (fwupd)
# ===============================================================================
step "Firmware Updates (fwupd)"
if ! command -v fwupdmgr &>/dev/null; then
warn "fwupdmgr not found — queued via rpm-ostree, will be available after reboot."
else
# AC power pre-flight
AC_ONLINE=0
for ps in /sys/class/power_supply/AC*/online /sys/class/power_supply/ADP*/online; do
[[ -f "${ps}" ]] && AC_ONLINE=$(cat "${ps}") && break
done
if [[ "${AC_ONLINE}" -eq 1 ]]; then
info "AC power confirmed — BIOS/UEFI capsule updates eligible."
else
warn "AC power NOT detected. BIOS/UEFI updates will be blocked by fwupd."
warn "Connect AC adapter before running if a BIOS update is needed."
fi
# Battery pre-flight
BAT_CAPACITY=""
for bat in /sys/class/power_supply/BAT*/capacity; do
[[ -f "${bat}" ]] && BAT_CAPACITY=$(cat "${bat}") && break
done
if [[ -n "${BAT_CAPACITY}" ]]; then
if [[ "${BAT_CAPACITY}" -ge 10 ]]; then
info "Battery level: ${BAT_CAPACITY}% — meets fwupd minimum."
else
warn "Battery: ${BAT_CAPACITY}% — below fwupd minimum of 10%."
fi
else
info "No battery detected — skipping battery check."
fi
# Refresh LVFS metadata
info "Refreshing LVFS firmware metadata..."
if ! fwupdmgr refresh --force; then
warn "LVFS metadata refresh failed — check connectivity."
else
info "Querying devices visible to fwupd..."
fwupdmgr get-devices || warn "Could not enumerate fwupd devices."
info "Checking for available firmware updates..."
UPDATE_OUTPUT=$(fwupdmgr get-updates 2>&1 || true)
if echo "${UPDATE_OUTPUT}" | grep -qi "no upgrades\|nothing to do\|no.*update"; then
info "System firmware is already up to date."
else
info "Firmware updates available:"
echo "${UPDATE_OUTPUT}"
if echo "${UPDATE_OUTPUT}" | grep -qi "system firmware\|uefi\|bios"; then
info "BIOS/UEFI firmware update detected."
if [[ "${AC_ONLINE}" -ne 1 ]]; then
warn "Skipping BIOS update — AC power required."
warn "Connect AC and run: sudo fwupdmgr update"
else
info "Applying BIOS update (will apply on next reboot)..."
fwupdmgr update -y || warn "Firmware update failed."
info "IMPORTANT: Reboot required to apply BIOS/UEFI capsule update."
fi
else
info "Applying non-BIOS firmware updates..."
fwupdmgr update -y || warn "One or more firmware updates failed."
fi
fi
fi
fi
# ===============================================================================
# SECTION 11: Automatic Updates
#
# Bazzite's OS updates are atomic via rpm-ostree. The rpm-ostreed service
# can stage updates automatically; they apply on reboot (never mid-session).
#
# Flatpak updates are managed by Bazzite's bazzite-flatpak-manager on boot,
# and can also be triggered via `flatpak update` or `ujust update`.
# ===============================================================================
step "Automatic Updates Configuration"
# Enable rpm-ostree automatic update staging
OSTREE_CONF="/etc/rpm-ostreed.conf"
if [[ -f "${OSTREE_CONF}" ]]; then
# Set AutomaticUpdatePolicy to 'stage' — downloads and stages updates
# automatically. They are applied on the next user-initiated reboot.
if grep -q "^AutomaticUpdatePolicy=" "${OSTREE_CONF}"; then
sed -i 's/^AutomaticUpdatePolicy=.*/AutomaticUpdatePolicy=stage/' "${OSTREE_CONF}"
else
echo "AutomaticUpdatePolicy=stage" >> "${OSTREE_CONF}"
fi
info "rpm-ostree AutomaticUpdatePolicy set to 'stage'."
else
# Bazzite may put this in /usr/etc/ (immutable). Write to /etc/.
mkdir -p /etc/rpm-ostreed.conf.d 2>/dev/null || true
cat > /etc/rpm-ostreed.conf <<EOF
[Daemon]
AutomaticUpdatePolicy=stage
EOF
info "Created /etc/rpm-ostreed.conf with AutomaticUpdatePolicy=stage."
fi
# Enable the rpm-ostreed update timer
systemctl enable --now rpm-ostreed-automatic.timer 2>/dev/null \
|| warn "rpm-ostreed-automatic.timer not found — updates will stage on login."
# Flatpak: Bazzite handles this at boot, but ensure the background update
# service is enabled for user-session Flatpaks
systemctl --user enable --now flatpak-system-update.timer 2>/dev/null \
|| info "Flatpak auto-update timer: managed by Bazzite (bazzite-flatpak-manager)."
info "Automatic update configuration complete."
info "OS updates stage silently and apply on next reboot."
info "Run 'ujust update' at any time to manually check and stage updates."
# ===============================================================================
# SECTION 12: MOTD
# ===============================================================================
step "MOTD Configuration"
info "Configuring MOTD system information display..."
# Bazzite uses pam_motd; drop scripts in /etc/update-motd.d/
mkdir -p /etc/update-motd.d/
rm -f /etc/update-motd.d/*
cat > /etc/update-motd.d/80-sysinfo <<'EOF'
#!/bin/bash
echo "=== System Information ==="
echo ""
echo " 🖥️ Operating System : $(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')"
echo " 🌐 IPv4 Address : $(hostname -I 2>/dev/null | awk '{print $1}')"
echo " 🔗 IPv6 Global : $(ip -6 addr show scope global 2>/dev/null | grep inet6 | awk '{print $2}' | cut -d'/' -f1 | head -n1)"
echo " 🪢 IPv6 Link-local : $(ip -6 addr show scope link 2>/dev/null | grep inet6 | awk '{print $2}' | cut -d'/' -f1 | head -n1)"
echo " ⏱️ System Uptime : $(uptime -p 2>/dev/null)"
echo " 📦 rpm-ostree : $(rpm-ostree status --booted 2>/dev/null | grep Version | head -n1 | awk '{print $2}' || echo 'unknown')"
echo ""
EOF
chmod +x /etc/update-motd.d/80-sysinfo
# ===============================================================================
# SECTION 13: Final Diagnostics
# ===============================================================================
step "Final Diagnostics"
DIAG_PASS=0
DIAG_WARN=0
DIAG_FAIL=0
diag_ok() { echo -e " \033[0;32m[PASS]\033[0m $*"; (( DIAG_PASS++ )); }
diag_warn() { echo -e " \033[0;33m[WARN]\033[0m $*"; (( DIAG_WARN++ )); }
diag_fail() { echo -e " \033[0;31m[FAIL]\033[0m $*"; (( DIAG_FAIL++ )); }
echo ""
echo " ── rpm-ostree ──────────────────────────────────────────"
if rpm-ostree status &>/dev/null; then
PENDING=$(rpm-ostree status 2>/dev/null | grep -c "pending" || true)
if [[ "${PENDING}" -gt 0 ]]; then
diag_ok "rpm-ostree has a pending deployment (reboot required to activate)"
else
diag_warn "rpm-ostree: no pending deployment detected (layering may have failed)"
fi
else
diag_fail "rpm-ostree status failed"
fi
echo ""
echo " ── Firewall ────────────────────────────────────────────"
if systemctl is-active --quiet firewalld; then
diag_ok "firewalld is running"
DEFAULT_ZONE=$(firewall-cmd --get-default-zone 2>/dev/null || echo "unknown")
if [[ "${DEFAULT_ZONE}" == "drop" ]]; then
diag_ok "Default zone is 'drop' (default-deny inbound)"
else
diag_warn "Default zone is '${DEFAULT_ZONE}' — expected 'drop'"
fi
else
diag_fail "firewalld is NOT running"
fi
echo ""
echo " ── SSH ─────────────────────────────────────────────────"
if [[ -f "${SSHD_CUSTOM_CFG}" ]]; then
diag_ok "SSH hardened drop-in exists: ${SSHD_CUSTOM_CFG}"
else
diag_fail "SSH drop-in config NOT found"
fi
if [[ -f "${SSHD_DIR}/ssh_host_ed25519_key" ]]; then
diag_ok "ED25519 host key present"
else
diag_warn "ED25519 host key NOT found (normal if openssh-server pending reboot)"
fi
if systemctl is-active --quiet sshd 2>/dev/null; then
diag_ok "sshd is running"
else
diag_warn "sshd is NOT running (expected — activate after rpm-ostree reboot)"
fi
echo ""
echo " ── UDM SE Certificate ──────────────────────────────────"
if [[ -f "${CERT_DST}" ]]; then
diag_ok "UDM SE certificate installed: ${CERT_DST}"
else
diag_warn "UDM SE certificate NOT installed (check connectivity to ghost.baden.braedach.com)"
fi
echo ""
echo " ── ClamAV Container ────────────────────────────────────"
if command -v distrobox &>/dev/null; then
if distrobox list --root 2>/dev/null | grep -q "clamav-box"; then
diag_ok "clamav-box Distrobox container exists"
else
diag_warn "clamav-box container not found (may still be initializing)"
fi
if [[ -x /usr/local/bin/clamscan-host ]]; then
diag_ok "clamscan-host wrapper installed"
else
diag_warn "clamscan-host wrapper NOT found"
fi
else
diag_warn "Distrobox not available — ClamAV container check skipped"
fi
echo ""
echo " ── Insync ──────────────────────────────────────────────"
if [[ -f /etc/yum.repos.d/insync.repo ]]; then
diag_ok "Insync RPM repo configured: /etc/yum.repos.d/insync.repo"
else
diag_warn "Insync RPM repo NOT found"
fi
echo ""
echo " ── Flatpak Apps ────────────────────────────────────────"
for app in org.gimp.GIMP org.inkscape.Inkscape org.kde.krita com.google.Chrome org.libreoffice.LibreOffice; do
if flatpak info "${app}" &>/dev/null; then
diag_ok "${app}"
else
diag_warn "${app} — not installed (may have failed)"
fi
done
echo ""
echo " ── IPv6 Configuration ──────────────────────────────────"
if [[ -f /etc/sysctl.d/99-ipv6-laptop.conf ]]; then
diag_ok "IPv6 stable address sysctl installed"
else
diag_warn "IPv6 sysctl config not found"
fi
echo ""
echo " ── Auto-Updates ────────────────────────────────────────"
if grep -q "AutomaticUpdatePolicy=stage" /etc/rpm-ostreed.conf 2>/dev/null; then
diag_ok "rpm-ostree AutomaticUpdatePolicy=stage configured"
else
diag_warn "rpm-ostree auto-update policy not confirmed"
fi
echo ""
echo " ── Systemd Failed Units ────────────────────────────────"
FAILED_UNITS=$(systemctl --failed --no-legend --plain 2>/dev/null | awk '{print $1}')
if [[ -z "${FAILED_UNITS}" ]]; then
diag_ok "No failed systemd units detected"
else
while IFS= read -r unit; do
diag_warn "Failed unit: ${unit}"
done <<< "${FAILED_UNITS}"
fi
echo ""
echo "════════════════════════════════════════════════════════════"
echo " DIAGNOSTIC SUMMARY"
echo " Pass: ${DIAG_PASS} | Warn: ${DIAG_WARN} | Fail: ${DIAG_FAIL}"
echo "════════════════════════════════════════════════════════════"
# ===============================================================================
# POST-SCRIPT INSTRUCTIONS
# ===============================================================================
cat <<'POSTINSTALL'
════════════════════════════════════════════════════════════════
IMPORTANT POST-SCRIPT ACTIONS
════════════════════════════════════════════════════════════════
1. REBOOT REQUIRED
rpm-ostree packages (wireguard-tools, insync, openssh-server,
fwupd, etc.) are staged and will activate on next reboot.
Run: systemctl reboot
Or just reboot normally.
2. AFTER REBOOT — Complete printer setup (if CUPS wasn't running):
sudo lpadmin -p EPSON-WF4830 -E \
-v ipp://192.168.1.11/ipp/print \
-m everywhere -D "EPSON WF-4830 Series"
3. AFTER REBOOT — Set up Insync:
Open the Insync GUI from your application launcher.
Authenticate your Google account(s) and configure sync folders.
Insync is a paid app ($29.99/Google account, 15-day trial).
4. WAYDROID (Android / Google Play Store):
Bazzite has native Waydroid support. After reboot, run:
ujust setup-waydroid
This launches an interactive setup wizard in the terminal.
Follow the prompts to enable Android container and Google Play.
ARM translation (for most apps) is included automatically.
NOTE: This CANNOT be automated — it requires graphical interaction.
5. WIREGUARD VPN:
WireGuard kernel support is built into Bazzite's kernel.
The 'wg' and 'wg-quick' CLI tools are now layered.
To connect using a config file:
sudo wg-quick up /etc/wireguard/wg0.conf
Or use Settings > Network > VPN to add a WireGuard connection.
6. CLAMAV SCANNING:
Scan your home directory:
clamscan-host /home/YOUR_USERNAME
Update definitions manually:
sudo systemctl start clamav-update.service
7. MANUAL SYSTEM UPDATE:
ujust update
8. VERIFY SECURE BOOT / SIGNED IMAGE:
ujust verify-image
════════════════════════════════════════════════════════════════
POSTINSTALL
info "Setup complete. Log: ${LOGFILE}"
info "SSH backup: ${BACKUP_DIR}"
echo ""
# Optional automatic reboot prompt
read -r -t 30 -p "[?] Reboot now to activate layered packages? [y/N] (auto-N in 30s): " REBOOT_CONFIRM || true
if [[ "${REBOOT_CONFIRM,,}" == "y" ]]; then
info "Rebooting in 5 seconds..."
sleep 5
systemctl reboot
else
info "Reboot skipped. Please reboot manually when ready."
fi
#endscriptHope this helps someone. Probaby not a wise idea to share the internal network domains but if I get hacked it will get reported to Ubiquiti for the betterment of everyone else.
#enoughsaid