#!/usr/bin/env bash # install-1password.sh — Install 1Password desktop from the official 1Password APT repo. # Hardened: arch-aware, signed-by keyring, debsig verification dir, idempotent, --dry-run. set -euo pipefail IFS=$'\n\t' readonly SCRIPT_NAME="${0##*/}" DRY_RUN=0 usage() { cat <&2; } die() { printf '\033[1;31m[%s] ERROR:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; exit 1; } run() { if (( DRY_RUN )); then printf ' DRY-RUN: %s\n' "$*"; else eval "$@"; fi; } trap 'rc=$?; (( rc )) && printf "\033[1;31m[%s] failed at line %s (exit %d)\033[0m\n" "${SCRIPT_NAME%.sh}" "$LINENO" "$rc" >&2' ERR while (( $# )); do case "$1" in --dry-run) DRY_RUN=1 ;; -h|--help) usage; exit 0 ;; *) die "Unknown argument: $1 (try --help)" ;; esac shift done (( EUID == 0 )) || die "Must run as root. Try: sudo $SCRIPT_NAME" [[ -r /etc/os-release ]] || die "/etc/os-release not found." # shellcheck disable=SC1091 . /etc/os-release case "${ID:-}:${ID_LIKE:-}" in *ubuntu*|*debian*) : ;; *) die "Unsupported distro: ${PRETTY_NAME:-unknown}." ;; esac # 1Password publishes builds for amd64 and arm64. ARCH="$(dpkg --print-architecture)" case "$ARCH" in amd64|arm64) : ;; *) die "1Password is only available for amd64/arm64 (detected: $ARCH)." ;; esac log "Detected: ${PRETTY_NAME:-unknown}, arch: $ARCH" export DEBIAN_FRONTEND=noninteractive KEYRING=/etc/apt/keyrings/1password-archive-keyring.gpg SOURCES=/etc/apt/sources.list.d/1password.list DEBSIG_POLICY_DIR=/etc/debsig/policies/AC2D62742012EA22 DEBSIG_KEYRING_DIR=/usr/share/debsig/keyrings/AC2D62742012EA22 log "Installing prerequisites..." run "apt-get update -qq" run "apt-get install -y curl gpg ca-certificates" log "Configuring 1Password APT repository..." run "install -d -m 0755 /etc/apt/keyrings" if [[ ! -s "$KEYRING" ]]; then run "curl -fsSL https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor -o '$KEYRING'" run "chmod 0644 '$KEYRING'" fi DESIRED_SRC="deb [arch=${ARCH} signed-by=${KEYRING}] https://downloads.1password.com/linux/debian/${ARCH} stable main" if [[ ! -f "$SOURCES" ]] || ! grep -qxF "$DESIRED_SRC" "$SOURCES"; then run "printf '%s\n' '$DESIRED_SRC' > '$SOURCES'" fi log "Installing debsig policy (verifies package signatures on install)..." run "install -d -m 0755 '$DEBSIG_POLICY_DIR' '$DEBSIG_KEYRING_DIR'" if [[ ! -s "${DEBSIG_POLICY_DIR}/1password.pol" ]]; then run "curl -fsSL https://downloads.1password.com/linux/debian/debsig/1password.pol -o '${DEBSIG_POLICY_DIR}/1password.pol'" fi if [[ ! -s "${DEBSIG_KEYRING_DIR}/debsig.gpg" ]]; then run "curl -fsSL https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor -o '${DEBSIG_KEYRING_DIR}/debsig.gpg'" fi log "Installing 1Password..." run "apt-get update -qq" run "apt-get install -y 1password" if (( ! DRY_RUN )) && command -v 1password >/dev/null 2>&1; then log "1Password binary available at: $(command -v 1password)" fi log "Done."