mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 20:03:12 +02:00
- Add common.sh library functions: require_imagemagick, install_missing_pacman_packages, handle_arg_help_or_unknown - Create android.sh shared library for Android utilities - Create hosts-guard-common.sh for pacman hooks shared functions - Update multiple scripts to source common.sh and use shared helpers - Add print_shutdown_schedule helper in setup_midnight_shutdown.sh - Remove duplicate log(), usage(), install_packages patterns across scripts - Format all shell scripts with shfmt (2-space indent)
1276 lines
39 KiB
Bash
Executable File
1276 lines
39 KiB
Bash
Executable File
#!/bin/bash
|
|
# Nextcloud Installation Script for Raspberry Pi
|
|
# This script installs and configures Nextcloud on a Raspberry Pi
|
|
#
|
|
# Usage:
|
|
# ./raspberry_pi_nextcloud.sh install - Install Nextcloud (run on Pi or via SSH)
|
|
# ./raspberry_pi_nextcloud.sh fix - Fix common Nextcloud issues
|
|
# ./raspberry_pi_nextcloud.sh install-remote - Install Nextcloud via SSH from laptop
|
|
|
|
set -euo pipefail
|
|
|
|
# Script directory for config file
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
CONFIG_FILE="${SCRIPT_DIR}/.raspberry_pi.conf"
|
|
|
|
# Load configuration from gitignored config file if it exists
|
|
if [[ -f $CONFIG_FILE ]]; then
|
|
# shellcheck source=/dev/null
|
|
source "$CONFIG_FILE"
|
|
fi
|
|
|
|
# Configuration
|
|
PI_HOSTNAME="${PI_HOSTNAME:-nextcloud-pi}"
|
|
PI_USER="${PI_USER:-pi}"
|
|
PI_PASSWORD="${PI_PASSWORD:-}"
|
|
PI_TIMEZONE="${PI_TIMEZONE:-Europe/Warsaw}"
|
|
PI_LOCALE="${PI_LOCALE:-en_US.UTF-8}"
|
|
NEXTCLOUD_ADMIN_USER="${NEXTCLOUD_ADMIN_USER:-admin}"
|
|
NEXTCLOUD_ADMIN_PASSWORD="${NEXTCLOUD_ADMIN_PASSWORD:-}"
|
|
NEXTCLOUD_DATA_DIR="${NEXTCLOUD_DATA_DIR:-/var/www/nextcloud/data}"
|
|
NEXTCLOUD_VERSION="${NEXTCLOUD_VERSION:-latest}"
|
|
|
|
# DuckDNS for free domain and Let's Encrypt SSL
|
|
# Get your free subdomain at https://www.duckdns.org/
|
|
DUCKDNS_DOMAIN="${DUCKDNS_DOMAIN:-}" # e.g., "mycloud" for mycloud.duckdns.org
|
|
DUCKDNS_TOKEN="${DUCKDNS_TOKEN:-}" # Your DuckDNS token
|
|
LETSENCRYPT_EMAIL="${LETSENCRYPT_EMAIL:-}" # Email for Let's Encrypt notifications
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1" >&2
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
}
|
|
|
|
die() {
|
|
log_error "$1"
|
|
exit 1
|
|
}
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
die "This script must be run as root. Use: sudo $0"
|
|
fi
|
|
}
|
|
|
|
save_config() {
|
|
cat >"$CONFIG_FILE" <<EOF
|
|
# Raspberry Pi Nextcloud Setup - Auto-generated config
|
|
# This file is gitignored and stores discovered settings
|
|
|
|
# Pi configuration
|
|
PI_HOSTNAME="${PI_HOSTNAME}"
|
|
PI_USER="${PI_USER}"
|
|
PI_TIMEZONE="${PI_TIMEZONE}"
|
|
NEXTCLOUD_ADMIN_USER="${NEXTCLOUD_ADMIN_USER}"
|
|
|
|
# Generated passwords (KEEP THIS FILE SECURE!)
|
|
PI_PASSWORD="${PI_PASSWORD}"
|
|
NEXTCLOUD_ADMIN_PASSWORD="${NEXTCLOUD_ADMIN_PASSWORD}"
|
|
|
|
# DuckDNS for Let's Encrypt SSL (optional)
|
|
DUCKDNS_DOMAIN="${DUCKDNS_DOMAIN:-}"
|
|
DUCKDNS_TOKEN="${DUCKDNS_TOKEN:-}"
|
|
LETSENCRYPT_EMAIL="${LETSENCRYPT_EMAIL:-}"
|
|
EOF
|
|
chmod 600 "$CONFIG_FILE"
|
|
log_info "Configuration saved to $CONFIG_FILE"
|
|
}
|
|
|
|
generate_password() {
|
|
local length="${1:-16}"
|
|
local chars
|
|
chars=$(dd if=/dev/urandom bs=256 count=1 2>/dev/null | tr -dc 'A-Za-z0-9!@#$%&*' | cut -c1-"$length")
|
|
echo "$chars"
|
|
}
|
|
|
|
auto_generate_nextcloud_password() {
|
|
if [[ -z $NEXTCLOUD_ADMIN_PASSWORD ]]; then
|
|
NEXTCLOUD_ADMIN_PASSWORD=$(generate_password 20)
|
|
log_info "Auto-generated Nextcloud admin password (will be saved to config file)"
|
|
fi
|
|
}
|
|
|
|
wait_for_apt_lock() {
|
|
local max_wait=600
|
|
local waited=0
|
|
|
|
while fuser /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do
|
|
if [[ $waited -eq 0 ]]; then
|
|
log_info "Waiting for other apt/dpkg processes to finish..."
|
|
pgrep -a 'apt|dpkg' | head -5 >&2 || true
|
|
fi
|
|
sleep 5
|
|
waited=$((waited + 5))
|
|
if [[ $waited -ge $max_wait ]]; then
|
|
die "Timeout waiting for apt lock after ${max_wait}s"
|
|
fi
|
|
if [[ $((waited % 30)) -eq 0 ]]; then
|
|
log_info "Still waiting... (${waited}s elapsed)"
|
|
fi
|
|
done
|
|
|
|
if [[ $waited -gt 0 ]]; then
|
|
log_success "Apt lock acquired after ${waited}s"
|
|
fi
|
|
}
|
|
|
|
# =============================================================================
|
|
# Network Discovery (for remote installation)
|
|
# =============================================================================
|
|
|
|
ensure_dependencies() {
|
|
local missing_packages=()
|
|
|
|
if ! command -v nmap &>/dev/null; then
|
|
missing_packages+=("nmap")
|
|
fi
|
|
|
|
if ! command -v sshpass &>/dev/null; then
|
|
missing_packages+=("sshpass")
|
|
fi
|
|
|
|
if [[ ${#missing_packages[@]} -gt 0 ]]; then
|
|
log_info "Installing missing packages: ${missing_packages[*]}"
|
|
|
|
if command -v pacman &>/dev/null; then
|
|
sudo pacman -S --noconfirm "${missing_packages[@]}"
|
|
elif command -v apt-get &>/dev/null; then
|
|
sudo apt-get update && sudo apt-get install -y "${missing_packages[@]}"
|
|
elif command -v dnf &>/dev/null; then
|
|
sudo dnf install -y "${missing_packages[@]}"
|
|
else
|
|
die "Could not detect package manager. Please install manually: ${missing_packages[*]}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
discover_raspberry_pi() {
|
|
log_info "Auto-discovering Raspberry Pi on local network..."
|
|
|
|
ensure_dependencies
|
|
|
|
local my_ip
|
|
my_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)(?!127\.)\d+(\.\d+){3}' | head -1)
|
|
local gateway
|
|
gateway=$(ip route | grep default | awk '{print $3}' | head -1)
|
|
local network="${gateway%.*}.0/24"
|
|
|
|
log_info "Local IP: $my_ip, Network: $network"
|
|
log_info "Scanning for Raspberry Pi (hostname: $PI_HOSTNAME)..."
|
|
|
|
local pi_ip=""
|
|
|
|
# Try resolving hostname directly
|
|
pi_ip=$(getent hosts "$PI_HOSTNAME" 2>/dev/null | awk '{print $1}' | head -1) || true
|
|
if [[ -z $pi_ip ]]; then
|
|
pi_ip=$(getent hosts "${PI_HOSTNAME}.local" 2>/dev/null | awk '{print $1}' | head -1) || true
|
|
fi
|
|
|
|
if [[ -n $pi_ip ]]; then
|
|
log_success "Found Pi by hostname: $pi_ip"
|
|
echo "$pi_ip"
|
|
return
|
|
fi
|
|
|
|
log_info "Hostname resolution failed, scanning network..."
|
|
nmap -sn -T4 "$network" &>/dev/null || true
|
|
|
|
local ssh_hosts
|
|
ssh_hosts=$(nmap -p 22 --open -sT -T4 "$network" 2>/dev/null | grep "Nmap scan report" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -vw "$my_ip" | sort -u) || true
|
|
|
|
if [[ -z $ssh_hosts ]]; then
|
|
die "No SSH-enabled devices found. Is the Pi connected and booted?"
|
|
fi
|
|
|
|
log_info "Found SSH-enabled devices: $(echo "$ssh_hosts" | tr '\n' ' ')"
|
|
|
|
for ip in $ssh_hosts; do
|
|
log_info "Trying $ip with user '$PI_USER'..."
|
|
|
|
if sshpass -p "$PI_PASSWORD" ssh -o BatchMode=no -o ConnectTimeout=5 -o StrictHostKeyChecking=no "${PI_USER}@${ip}" "hostname" 2>/dev/null | grep -qi "$PI_HOSTNAME"; then
|
|
log_success "Found Raspberry Pi at $ip"
|
|
echo "$ip"
|
|
return
|
|
fi
|
|
|
|
if sshpass -p "$PI_PASSWORD" ssh -o BatchMode=no -o ConnectTimeout=5 -o StrictHostKeyChecking=no "${PI_USER}@${ip}" "echo ok" 2>/dev/null | grep -q "ok"; then
|
|
log_success "Found device responding to Pi credentials at $ip"
|
|
echo "$ip"
|
|
return
|
|
fi
|
|
done
|
|
|
|
die "Could not find Raspberry Pi on network."
|
|
}
|
|
|
|
# =============================================================================
|
|
# System Configuration Phase
|
|
# =============================================================================
|
|
|
|
phase_configure_system() {
|
|
check_root
|
|
|
|
log_info "=== Configuring Raspberry Pi System ==="
|
|
|
|
wait_for_apt_lock
|
|
|
|
log_info "Fixing any broken packages..."
|
|
DEBIAN_FRONTEND=noninteractive dpkg --configure -a --force-confdef --force-confold || true
|
|
|
|
log_info "Updating system packages..."
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade -y
|
|
|
|
log_info "Setting timezone to $PI_TIMEZONE..."
|
|
timedatectl set-timezone "$PI_TIMEZONE"
|
|
|
|
log_info "Configuring locale..."
|
|
sed -i "s/^# *$PI_LOCALE/$PI_LOCALE/" /etc/locale.gen
|
|
locale-gen
|
|
update-locale LANG="$PI_LOCALE"
|
|
|
|
log_info "Hardening SSH configuration..."
|
|
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
|
|
|
|
cat >>/etc/ssh/sshd_config.d/hardening.conf <<'EOF'
|
|
# Security hardening
|
|
PermitRootLogin no
|
|
PasswordAuthentication yes
|
|
PubkeyAuthentication yes
|
|
X11Forwarding no
|
|
MaxAuthTries 3
|
|
ClientAliveInterval 300
|
|
ClientAliveCountMax 2
|
|
EOF
|
|
|
|
systemctl restart sshd
|
|
|
|
log_info "Installing useful packages..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
vim \
|
|
htop \
|
|
curl \
|
|
wget \
|
|
git \
|
|
ufw \
|
|
fail2ban \
|
|
unattended-upgrades
|
|
|
|
log_info "Configuring firewall..."
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw allow ssh
|
|
ufw allow 80/tcp
|
|
ufw allow 443/tcp
|
|
ufw --force enable
|
|
|
|
log_info "Configuring fail2ban..."
|
|
cat >/etc/fail2ban/jail.local <<'EOF'
|
|
[DEFAULT]
|
|
bantime = 1h
|
|
findtime = 10m
|
|
maxretry = 5
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = ssh
|
|
filter = sshd
|
|
logpath = /var/log/auth.log
|
|
maxretry = 3
|
|
EOF
|
|
|
|
systemctl enable fail2ban
|
|
systemctl restart fail2ban
|
|
|
|
log_info "Enabling automatic security updates..."
|
|
cat >/etc/apt/apt.conf.d/50unattended-upgrades <<'EOF'
|
|
Unattended-Upgrade::Origins-Pattern {
|
|
"origin=Debian,codename=${distro_codename},label=Debian-Security";
|
|
"origin=Raspbian,codename=${distro_codename},label=Raspbian";
|
|
};
|
|
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
|
|
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
|
EOF
|
|
|
|
cat >/etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
APT::Periodic::AutocleanInterval "7";
|
|
EOF
|
|
|
|
log_success "System configuration complete!"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Nextcloud Installation Phase
|
|
# =============================================================================
|
|
|
|
phase_install_nextcloud() {
|
|
check_root
|
|
|
|
log_info "=== Installing Nextcloud ==="
|
|
|
|
wait_for_apt_lock
|
|
|
|
log_info "Installing Apache, PHP, MariaDB, and dependencies..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
apache2 \
|
|
mariadb-server \
|
|
php \
|
|
php-gd \
|
|
php-json \
|
|
php-mysql \
|
|
php-curl \
|
|
php-mbstring \
|
|
php-intl \
|
|
php-imagick \
|
|
php-xml \
|
|
php-zip \
|
|
php-bz2 \
|
|
php-bcmath \
|
|
php-gmp \
|
|
php-apcu \
|
|
php-redis \
|
|
php-ldap \
|
|
libapache2-mod-php \
|
|
redis-server \
|
|
certbot \
|
|
python3-certbot-apache \
|
|
imagemagick \
|
|
libmagickcore-6.q16-6-extra
|
|
|
|
log_success "Packages installed"
|
|
|
|
# Configure MariaDB
|
|
log_info "Configuring MariaDB..."
|
|
|
|
local db_password
|
|
db_password=$(generate_password 32)
|
|
|
|
mysql -u root <<EOF
|
|
CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
|
CREATE USER IF NOT EXISTS 'nextcloud'@'localhost' IDENTIFIED BY '${db_password}';
|
|
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
|
|
FLUSH PRIVILEGES;
|
|
EOF
|
|
|
|
echo "$db_password" >/root/.nextcloud_db_password
|
|
chmod 600 /root/.nextcloud_db_password
|
|
log_success "MariaDB configured"
|
|
|
|
# Download Nextcloud
|
|
log_info "Downloading Nextcloud..."
|
|
|
|
cd /tmp
|
|
if [[ ! -f nextcloud.zip ]]; then
|
|
wget -q --show-progress "https://download.nextcloud.com/server/releases/latest.zip" -O nextcloud.zip >&2
|
|
fi
|
|
|
|
rm -rf /var/www/nextcloud
|
|
unzip -q nextcloud.zip -d /var/www/
|
|
chown -R www-data:www-data /var/www/nextcloud
|
|
|
|
log_success "Nextcloud downloaded and extracted"
|
|
|
|
# Configure Apache
|
|
log_info "Configuring Apache..."
|
|
|
|
cat >/etc/apache2/sites-available/nextcloud.conf <<'EOF'
|
|
<VirtualHost *:80>
|
|
ServerAdmin admin@localhost
|
|
DocumentRoot /var/www/nextcloud
|
|
|
|
<Directory /var/www/nextcloud/>
|
|
Require all granted
|
|
AllowOverride All
|
|
Options FollowSymLinks MultiViews
|
|
|
|
<IfModule mod_dav.c>
|
|
Dav off
|
|
</IfModule>
|
|
</Directory>
|
|
|
|
ErrorLog ${APACHE_LOG_DIR}/nextcloud_error.log
|
|
CustomLog ${APACHE_LOG_DIR}/nextcloud_access.log combined
|
|
</VirtualHost>
|
|
EOF
|
|
|
|
a2enmod rewrite
|
|
a2enmod headers
|
|
a2enmod env
|
|
a2enmod dir
|
|
a2enmod mime
|
|
a2enmod ssl
|
|
a2dissite 000-default
|
|
a2ensite nextcloud
|
|
|
|
systemctl restart apache2
|
|
|
|
log_success "Apache configured"
|
|
|
|
# Configure PHP
|
|
log_info "Configuring PHP..."
|
|
|
|
local php_version
|
|
php_version=$(php -v | head -1 | grep -oP '\d+\.\d+')
|
|
|
|
local php_ini="/etc/php/${php_version}/apache2/php.ini"
|
|
|
|
sed -i 's/memory_limit = .*/memory_limit = 512M/' "$php_ini"
|
|
sed -i 's/upload_max_filesize = .*/upload_max_filesize = 16G/' "$php_ini"
|
|
sed -i 's/post_max_size = .*/post_max_size = 16G/' "$php_ini"
|
|
sed -i 's/max_execution_time = .*/max_execution_time = 3600/' "$php_ini"
|
|
sed -i 's/max_input_time = .*/max_input_time = 3600/' "$php_ini"
|
|
sed -i 's/;date.timezone =.*/date.timezone = Europe\/Warsaw/' "$php_ini"
|
|
|
|
if ! grep -q "opcache.interned_strings_buffer" "$php_ini"; then
|
|
cat >>"$php_ini" <<'EOF'
|
|
|
|
; Nextcloud optimizations
|
|
opcache.enable=1
|
|
opcache.interned_strings_buffer=16
|
|
opcache.max_accelerated_files=10000
|
|
opcache.memory_consumption=128
|
|
opcache.save_comments=1
|
|
opcache.revalidate_freq=1
|
|
|
|
; APCu configuration
|
|
apc.enable_cli=1
|
|
EOF
|
|
fi
|
|
|
|
systemctl restart apache2
|
|
|
|
log_success "PHP configured"
|
|
|
|
# Configure Redis
|
|
log_info "Configuring Redis..."
|
|
|
|
systemctl enable redis-server
|
|
systemctl start redis-server
|
|
|
|
log_success "Redis configured"
|
|
|
|
# Install Nextcloud
|
|
log_info "Installing Nextcloud..."
|
|
|
|
auto_generate_nextcloud_password
|
|
|
|
local pi_ip
|
|
pi_ip=$(hostname -I | awk '{print $1}')
|
|
|
|
cd /var/www/nextcloud
|
|
sudo -u www-data php occ maintenance:install \
|
|
--database "mysql" \
|
|
--database-name "nextcloud" \
|
|
--database-user "nextcloud" \
|
|
--database-pass "$db_password" \
|
|
--admin-user "$NEXTCLOUD_ADMIN_USER" \
|
|
--admin-pass "$NEXTCLOUD_ADMIN_PASSWORD" \
|
|
--data-dir "$NEXTCLOUD_DATA_DIR"
|
|
|
|
# Configure trusted domains
|
|
sudo -u www-data php occ config:system:set trusted_domains 1 --value="$pi_ip"
|
|
sudo -u www-data php occ config:system:set trusted_domains 2 --value="$PI_HOSTNAME"
|
|
sudo -u www-data php occ config:system:set trusted_domains 3 --value="${PI_HOSTNAME}.local"
|
|
|
|
# Configure caching
|
|
sudo -u www-data php occ config:system:set memcache.local --value='\OC\Memcache\APCu'
|
|
sudo -u www-data php occ config:system:set memcache.distributed --value='\OC\Memcache\Redis'
|
|
sudo -u www-data php occ config:system:set memcache.locking --value='\OC\Memcache\Redis'
|
|
sudo -u www-data php occ config:system:set redis host --value='localhost'
|
|
sudo -u www-data php occ config:system:set redis port --value=6379 --type=integer
|
|
|
|
# Set default phone region
|
|
sudo -u www-data php occ config:system:set default_phone_region --value='PL'
|
|
|
|
# Set maintenance window
|
|
sudo -u www-data php occ config:system:set maintenance_window_start --value=1 --type=integer
|
|
|
|
log_success "Nextcloud installed"
|
|
|
|
# Setup background jobs
|
|
log_info "Setting up Nextcloud background jobs..."
|
|
|
|
sudo -u www-data php occ background:cron
|
|
|
|
# Add cron job
|
|
(
|
|
crontab -u www-data -l 2>/dev/null || true
|
|
echo "*/5 * * * * php -f /var/www/nextcloud/cron.php"
|
|
) | sort -u | crontab -u www-data -
|
|
|
|
log_success "Cron jobs configured"
|
|
|
|
# Verify installation
|
|
log_info "Verifying Nextcloud installation..."
|
|
|
|
if sudo -u www-data php occ status | grep -q "installed: true"; then
|
|
log_success "Nextcloud is responding!"
|
|
sudo -u www-data php occ status
|
|
else
|
|
log_warning "Nextcloud may not be fully configured"
|
|
fi
|
|
|
|
save_config
|
|
|
|
log_success "========================================"
|
|
log_success "Nextcloud installation complete!"
|
|
log_success "========================================"
|
|
log_info "Access Nextcloud at: http://$pi_ip"
|
|
log_info "Admin user: $NEXTCLOUD_ADMIN_USER"
|
|
log_info "Admin password: $NEXTCLOUD_ADMIN_PASSWORD"
|
|
log_info "Database password saved at: /root/.nextcloud_db_password"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Fix Nextcloud Issues
|
|
# =============================================================================
|
|
|
|
phase_fix_issues() {
|
|
check_root
|
|
|
|
log_info "=== Fixing Nextcloud Issues ==="
|
|
|
|
cd /var/www/nextcloud
|
|
|
|
# 1. Fix background jobs (cron not running properly)
|
|
log_info "Fixing background jobs..."
|
|
|
|
# Ensure cron is set as background job method
|
|
sudo -u www-data php occ background:cron
|
|
|
|
# Ensure cron job exists and is correct
|
|
(
|
|
crontab -u www-data -l 2>/dev/null | grep -v "cron.php"
|
|
echo "*/5 * * * * php -f /var/www/nextcloud/cron.php"
|
|
) | crontab -u www-data -
|
|
|
|
# Run cron manually now to reset the timer
|
|
log_info "Running cron job manually..."
|
|
sudo -u www-data php /var/www/nextcloud/cron.php
|
|
|
|
log_success "Background jobs configured"
|
|
|
|
# 2. Setup HTTPS with proper CA-signed certificate
|
|
log_info "Setting up HTTPS with trusted CA..."
|
|
|
|
local pi_ip
|
|
pi_ip=$(hostname -I | awk '{print $1}')
|
|
|
|
local ssl_dir="/etc/ssl/nextcloud"
|
|
mkdir -p "$ssl_dir"
|
|
chmod 700 "$ssl_dir"
|
|
|
|
# Generate CA if it doesn't exist
|
|
if [[ ! -f "$ssl_dir/ca.crt" ]]; then
|
|
log_info "Creating Certificate Authority (CA)..."
|
|
|
|
# Generate CA private key
|
|
openssl genrsa -out "$ssl_dir/ca.key" 4096
|
|
chmod 600 "$ssl_dir/ca.key"
|
|
|
|
# Generate CA certificate (valid for 10 years)
|
|
openssl req -x509 -new -nodes -key "$ssl_dir/ca.key" \
|
|
-sha256 -days 3650 \
|
|
-out "$ssl_dir/ca.crt" \
|
|
-subj "/C=PL/ST=Home/L=Local/O=Nextcloud Home CA/OU=Certificate Authority/CN=Nextcloud Home CA"
|
|
|
|
log_success "CA created: $ssl_dir/ca.crt"
|
|
fi
|
|
|
|
# Generate server certificate signed by our CA
|
|
local regenerate="${1:-}"
|
|
if [[ ! -f "$ssl_dir/server.crt" ]] || [[ $regenerate == "--regenerate" ]]; then
|
|
log_info "Generating server certificate signed by CA..."
|
|
|
|
# Generate server private key
|
|
openssl genrsa -out "$ssl_dir/server.key" 2048
|
|
chmod 600 "$ssl_dir/server.key"
|
|
|
|
# Create certificate signing request (CSR)
|
|
openssl req -new -key "$ssl_dir/server.key" \
|
|
-out "$ssl_dir/server.csr" \
|
|
-subj "/C=PL/ST=Home/L=Local/O=Nextcloud/OU=Server/CN=$PI_HOSTNAME"
|
|
|
|
# Create extension file for SAN (Subject Alternative Names)
|
|
# This allows the certificate to be valid for hostname, IP, and .local
|
|
cat >"$ssl_dir/server.ext" <<EXTEOF
|
|
authorityKeyIdentifier=keyid,issuer
|
|
basicConstraints=CA:FALSE
|
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
|
subjectAltName = @alt_names
|
|
|
|
[alt_names]
|
|
DNS.1 = $PI_HOSTNAME
|
|
DNS.2 = ${PI_HOSTNAME}.local
|
|
DNS.3 = localhost
|
|
IP.1 = $pi_ip
|
|
IP.2 = 127.0.0.1
|
|
EXTEOF
|
|
|
|
# Sign the certificate with our CA (valid for 2 years)
|
|
openssl x509 -req -in "$ssl_dir/server.csr" \
|
|
-CA "$ssl_dir/ca.crt" \
|
|
-CAkey "$ssl_dir/ca.key" \
|
|
-CAcreateserial \
|
|
-out "$ssl_dir/server.crt" \
|
|
-days 730 \
|
|
-sha256 \
|
|
-extfile "$ssl_dir/server.ext"
|
|
|
|
rm -f "$ssl_dir/server.csr" "$ssl_dir/server.ext"
|
|
|
|
log_success "Server certificate created and signed by CA"
|
|
fi
|
|
|
|
# Copy CA to web-accessible location for easy download
|
|
mkdir -p /var/www/nextcloud/ca
|
|
cp "$ssl_dir/ca.crt" /var/www/nextcloud/ca/nextcloud-ca.crt
|
|
chown -R www-data:www-data /var/www/nextcloud/ca
|
|
|
|
log_info "CA certificate available at: https://$PI_HOSTNAME/ca/nextcloud-ca.crt"
|
|
|
|
# Create HTTPS Apache config
|
|
cat >/etc/apache2/sites-available/nextcloud-ssl.conf <<EOF
|
|
<VirtualHost *:443>
|
|
ServerAdmin admin@localhost
|
|
DocumentRoot /var/www/nextcloud
|
|
ServerName $PI_HOSTNAME
|
|
ServerAlias ${PI_HOSTNAME}.local $pi_ip
|
|
|
|
SSLEngine on
|
|
SSLCertificateFile $ssl_dir/server.crt
|
|
SSLCertificateKeyFile $ssl_dir/server.key
|
|
|
|
<Directory /var/www/nextcloud/>
|
|
Require all granted
|
|
AllowOverride All
|
|
Options FollowSymLinks MultiViews
|
|
|
|
<IfModule mod_dav.c>
|
|
Dav off
|
|
</IfModule>
|
|
</Directory>
|
|
|
|
# Security headers
|
|
Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
|
|
Header always set X-Content-Type-Options "nosniff"
|
|
Header always set X-Frame-Options "SAMEORIGIN"
|
|
Header always set X-XSS-Protection "1; mode=block"
|
|
Header always set Referrer-Policy "no-referrer"
|
|
|
|
ErrorLog \${APACHE_LOG_DIR}/nextcloud_ssl_error.log
|
|
CustomLog \${APACHE_LOG_DIR}/nextcloud_ssl_access.log combined
|
|
</VirtualHost>
|
|
|
|
# Redirect HTTP to HTTPS
|
|
<VirtualHost *:80>
|
|
ServerName $PI_HOSTNAME
|
|
ServerAlias $pi_ip
|
|
Redirect permanent / https://$PI_HOSTNAME/
|
|
</VirtualHost>
|
|
EOF
|
|
|
|
a2enmod ssl
|
|
a2enmod headers
|
|
a2ensite nextcloud-ssl
|
|
|
|
# Update Nextcloud config for HTTPS
|
|
sudo -u www-data php occ config:system:set overwrite.cli.url --value="https://$PI_HOSTNAME"
|
|
sudo -u www-data php occ config:system:set overwriteprotocol --value="https"
|
|
|
|
systemctl restart apache2
|
|
|
|
log_success "HTTPS configured with CA-signed certificate"
|
|
|
|
# 3. Run mimetype migrations
|
|
log_info "Running mimetype migrations..."
|
|
sudo -u www-data php occ maintenance:repair --include-expensive
|
|
log_success "Mimetype migrations complete"
|
|
|
|
# 4. Add missing database indices
|
|
log_info "Adding missing database indices..."
|
|
sudo -u www-data php occ db:add-missing-indices
|
|
log_success "Database indices added"
|
|
|
|
# 5. Install ImageMagick SVG support
|
|
log_info "Installing ImageMagick SVG support..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y libmagickcore-6.q16-6-extra
|
|
|
|
# Enable SVG in ImageMagick policy
|
|
local policy_file="/etc/ImageMagick-6/policy.xml"
|
|
if [[ -f $policy_file ]]; then
|
|
# Remove SVG restrictions if present
|
|
sed -i 's/<policy domain="coder" rights="none" pattern="SVG" \/>/<policy domain="coder" rights="read|write" pattern="SVG" \/>/' "$policy_file"
|
|
# If no SVG policy exists, add one allowing it
|
|
if ! grep -q 'pattern="SVG"' "$policy_file"; then
|
|
sed -i '/<policymap>/a\ <policy domain="coder" rights="read|write" pattern="SVG" \/>' "$policy_file"
|
|
fi
|
|
fi
|
|
|
|
systemctl restart apache2
|
|
log_success "ImageMagick SVG support configured"
|
|
|
|
# 6. Set up basic SMTP (placeholder - user needs to configure actual mail server)
|
|
log_info "Note: Email server not configured - please configure in Nextcloud admin settings"
|
|
|
|
# 7. Clear any remaining warnings
|
|
log_info "Clearing Nextcloud caches..."
|
|
sudo -u www-data php occ maintenance:repair
|
|
sudo -u www-data php occ files:scan --all
|
|
|
|
# 8. Verify all fixes
|
|
log_info "Verifying fixes..."
|
|
|
|
# Run cron again to update last run time
|
|
sudo -u www-data php /var/www/nextcloud/cron.php
|
|
|
|
log_success "========================================"
|
|
log_success "Nextcloud issues fixed!"
|
|
log_success "========================================"
|
|
echo
|
|
log_info "Summary of changes:"
|
|
log_info " ✓ Background jobs (cron) configured and running"
|
|
log_info " ✓ HTTPS enabled with CA-signed certificate"
|
|
log_info " ✓ Strict-Transport-Security header added"
|
|
log_info " ✓ Mimetype migrations completed"
|
|
log_info " ✓ Missing database indices added"
|
|
log_info " ✓ ImageMagick SVG support installed"
|
|
echo
|
|
log_info "Current certificate: self-signed CA (requires manual install on devices)"
|
|
log_info " - Run: $0 install-ca (on your laptop)"
|
|
log_info " - Or download: https://$PI_HOSTNAME/ca/nextcloud-ca.crt"
|
|
echo
|
|
log_info "For auto-trusted HTTPS on ALL devices (recommended):"
|
|
log_info " 1. Get free domain at https://www.duckdns.org/"
|
|
log_info " 2. Run: $0 setup-ssl"
|
|
echo
|
|
log_info "Access Nextcloud at: https://$PI_HOSTNAME"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Setup Let's Encrypt SSL with DuckDNS
|
|
# =============================================================================
|
|
|
|
phase_setup_ssl() {
|
|
check_root
|
|
|
|
log_info "=== Setting up Let's Encrypt SSL with DuckDNS ==="
|
|
|
|
# Check if DuckDNS is configured
|
|
if [[ -z $DUCKDNS_DOMAIN ]] || [[ -z $DUCKDNS_TOKEN ]]; then
|
|
echo
|
|
log_info "To get auto-trusted HTTPS, you need a free DuckDNS domain."
|
|
log_info "1. Go to https://www.duckdns.org/ and sign in with Google/GitHub/etc."
|
|
log_info "2. Create a subdomain (e.g., 'myhomecloud' for myhomecloud.duckdns.org)"
|
|
log_info "3. Copy your token from the DuckDNS page"
|
|
echo
|
|
|
|
read -r -p "Enter your DuckDNS subdomain (without .duckdns.org): " DUCKDNS_DOMAIN
|
|
read -r -p "Enter your DuckDNS token: " DUCKDNS_TOKEN
|
|
read -r -p "Enter your email (for Let's Encrypt notifications): " LETSENCRYPT_EMAIL
|
|
|
|
if [[ -z $DUCKDNS_DOMAIN ]] || [[ -z $DUCKDNS_TOKEN ]] || [[ -z $LETSENCRYPT_EMAIL ]]; then
|
|
die "All fields are required"
|
|
fi
|
|
fi
|
|
|
|
local full_domain="${DUCKDNS_DOMAIN}.duckdns.org"
|
|
local pi_local_ip
|
|
pi_local_ip=$(hostname -I | awk '{print $1}')
|
|
|
|
# Get public IP for DuckDNS (Let's Encrypt needs external access)
|
|
local public_ip
|
|
public_ip=$(curl -s https://api.ipify.org) || public_ip=$(curl -s https://ifconfig.me) || true
|
|
|
|
log_info "Domain: $full_domain"
|
|
log_info "Pi local IP: $pi_local_ip"
|
|
log_info "Public IP: $public_ip"
|
|
|
|
echo
|
|
log_warning "=== IMPORTANT: Port Forwarding Required ==="
|
|
log_warning "For Let's Encrypt to work, you MUST forward ports on your router:"
|
|
log_warning " - Forward port 80 (HTTP) to $pi_local_ip"
|
|
log_warning " - Forward port 443 (HTTPS) to $pi_local_ip"
|
|
log_warning ""
|
|
log_warning "Go to your router admin page (usually http://192.168.1.1)"
|
|
log_warning "and set up port forwarding before continuing."
|
|
echo
|
|
read -r -p "Have you set up port forwarding? (yes/no): " port_forward_done
|
|
|
|
if [[ $port_forward_done != "yes" ]]; then
|
|
log_info "Please set up port forwarding and run this command again."
|
|
log_info "Without port forwarding, Let's Encrypt cannot verify your domain."
|
|
exit 0
|
|
fi
|
|
|
|
# Update DuckDNS to point to PUBLIC IP (not local IP)
|
|
log_info "Updating DuckDNS to point to public IP $public_ip..."
|
|
local duckdns_response
|
|
# When ip= is empty, DuckDNS auto-detects the public IP
|
|
duckdns_response=$(curl -s "https://www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}&ip=")
|
|
|
|
if [[ $duckdns_response != "OK" ]]; then
|
|
die "Failed to update DuckDNS: $duckdns_response"
|
|
fi
|
|
log_success "DuckDNS updated to public IP"
|
|
|
|
# Set up automatic DuckDNS updates (cron) - auto-detect public IP
|
|
log_info "Setting up automatic DuckDNS IP updates..."
|
|
mkdir -p /opt/duckdns
|
|
cat >/opt/duckdns/duck.sh <<DUCKEOF
|
|
#!/bin/bash
|
|
echo url="https://www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}&ip=" | curl -k -o /opt/duckdns/duck.log -K -
|
|
DUCKEOF
|
|
chmod 700 /opt/duckdns/duck.sh
|
|
|
|
# Add cron job for DuckDNS update every 5 minutes
|
|
(crontab -l 2>/dev/null || true) | grep -v "duckdns" | {
|
|
cat
|
|
echo "*/5 * * * * /opt/duckdns/duck.sh >/dev/null 2>&1"
|
|
} | crontab -
|
|
|
|
log_success "DuckDNS auto-update configured"
|
|
|
|
# Wait for DNS propagation
|
|
log_info "Waiting for DNS propagation (this may take a minute)..."
|
|
local dns_ip=""
|
|
local attempts=0
|
|
while [[ $dns_ip != "$public_ip" ]] && [[ $attempts -lt 12 ]]; do
|
|
sleep 5
|
|
dns_ip=$(dig +short "$full_domain" 2>/dev/null | tail -1) || true
|
|
attempts=$((attempts + 1))
|
|
log_info " DNS lookup: $dns_ip (expecting $public_ip, attempt $attempts/12)"
|
|
done
|
|
|
|
if [[ $dns_ip != "$public_ip" ]]; then
|
|
log_warning "DNS may not have propagated yet. Continuing anyway..."
|
|
else
|
|
log_success "DNS verified: $full_domain -> $public_ip"
|
|
fi
|
|
|
|
# Install certbot if not present
|
|
if ! command -v certbot &>/dev/null; then
|
|
log_info "Installing certbot..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y certbot python3-certbot-apache
|
|
fi
|
|
|
|
# Get Let's Encrypt certificate
|
|
log_info "Obtaining Let's Encrypt certificate..."
|
|
|
|
# First update Apache config with the new domain
|
|
cat >/etc/apache2/sites-available/nextcloud-ssl.conf <<EOF
|
|
<VirtualHost *:443>
|
|
ServerAdmin ${LETSENCRYPT_EMAIL}
|
|
DocumentRoot /var/www/nextcloud
|
|
ServerName ${full_domain}
|
|
|
|
SSLEngine on
|
|
# Certbot will update these paths
|
|
SSLCertificateFile /etc/ssl/nextcloud/server.crt
|
|
SSLCertificateKeyFile /etc/ssl/nextcloud/server.key
|
|
|
|
<Directory /var/www/nextcloud/>
|
|
Require all granted
|
|
AllowOverride All
|
|
Options FollowSymLinks MultiViews
|
|
|
|
<IfModule mod_dav.c>
|
|
Dav off
|
|
</IfModule>
|
|
</Directory>
|
|
|
|
# Security headers
|
|
Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
|
|
Header always set X-Content-Type-Options "nosniff"
|
|
Header always set X-Frame-Options "SAMEORIGIN"
|
|
Header always set X-XSS-Protection "1; mode=block"
|
|
Header always set Referrer-Policy "no-referrer"
|
|
|
|
ErrorLog \${APACHE_LOG_DIR}/nextcloud_ssl_error.log
|
|
CustomLog \${APACHE_LOG_DIR}/nextcloud_ssl_access.log combined
|
|
</VirtualHost>
|
|
|
|
<VirtualHost *:80>
|
|
ServerName ${full_domain}
|
|
Redirect permanent / https://${full_domain}/
|
|
</VirtualHost>
|
|
EOF
|
|
|
|
systemctl reload apache2
|
|
|
|
# Run certbot
|
|
certbot --apache -d "$full_domain" --non-interactive --agree-tos --email "$LETSENCRYPT_EMAIL" --redirect
|
|
|
|
log_success "Let's Encrypt certificate obtained!"
|
|
|
|
# Update Nextcloud trusted domains
|
|
log_info "Updating Nextcloud configuration..."
|
|
cd /var/www/nextcloud
|
|
sudo -u www-data php occ config:system:set trusted_domains 0 --value="$full_domain"
|
|
sudo -u www-data php occ config:system:set overwrite.cli.url --value="https://$full_domain"
|
|
sudo -u www-data php occ config:system:set overwriteprotocol --value="https"
|
|
sudo -u www-data php occ config:system:set overwritehost --value="$full_domain"
|
|
|
|
# Keep local access working
|
|
sudo -u www-data php occ config:system:set trusted_domains 1 --value="$pi_local_ip"
|
|
sudo -u www-data php occ config:system:set trusted_domains 2 --value="$PI_HOSTNAME"
|
|
sudo -u www-data php occ config:system:set trusted_domains 3 --value="${PI_HOSTNAME}.local"
|
|
|
|
log_success "Nextcloud configured for $full_domain"
|
|
|
|
# Set up auto-renewal
|
|
log_info "Setting up automatic certificate renewal..."
|
|
systemctl enable certbot.timer
|
|
systemctl start certbot.timer
|
|
|
|
log_success "========================================"
|
|
log_success "Let's Encrypt SSL configured!"
|
|
log_success "========================================"
|
|
echo
|
|
log_info "Your Nextcloud is now accessible at:"
|
|
log_info " https://$full_domain (from anywhere on the internet)"
|
|
log_info " https://$pi_local_ip (from your local network)"
|
|
echo
|
|
log_info "This certificate is trusted by ALL browsers and devices automatically!"
|
|
log_info "No manual certificate installation required."
|
|
echo
|
|
log_info "Certificate auto-renewal is enabled."
|
|
log_info "DuckDNS IP auto-update is enabled."
|
|
}
|
|
|
|
phase_setup_ssl_remote() {
|
|
log_info "=== Setting up Let's Encrypt SSL via SSH ==="
|
|
|
|
if [[ -z $PI_PASSWORD ]]; then
|
|
die "PI_PASSWORD not set. Run install-remote first."
|
|
fi
|
|
|
|
local pi_ip
|
|
pi_ip=$(discover_raspberry_pi)
|
|
|
|
if [[ -z $pi_ip ]]; then
|
|
die "Failed to discover Raspberry Pi"
|
|
fi
|
|
|
|
# Get DuckDNS credentials if not set
|
|
if [[ -z $DUCKDNS_DOMAIN ]] || [[ -z $DUCKDNS_TOKEN ]]; then
|
|
echo
|
|
log_info "To get auto-trusted HTTPS, you need a free DuckDNS domain."
|
|
log_info "1. Go to https://www.duckdns.org/ and sign in"
|
|
log_info "2. Create a subdomain (e.g., 'myhomecloud')"
|
|
log_info "3. Copy your token"
|
|
echo
|
|
|
|
read -r -p "Enter your DuckDNS subdomain (without .duckdns.org): " DUCKDNS_DOMAIN
|
|
read -r -p "Enter your DuckDNS token: " DUCKDNS_TOKEN
|
|
read -r -p "Enter your email (for Let's Encrypt): " LETSENCRYPT_EMAIL
|
|
fi
|
|
|
|
save_config
|
|
|
|
log_info "Copying script to Pi..."
|
|
sshpass -p "$PI_PASSWORD" scp -o StrictHostKeyChecking=no "$0" "${PI_USER}@${pi_ip}:/tmp/raspberry_pi_nextcloud.sh"
|
|
|
|
log_info "Running SSL setup on Pi..."
|
|
sshpass -p "$PI_PASSWORD" ssh -o StrictHostKeyChecking=no "${PI_USER}@${pi_ip}" \
|
|
"echo '$PI_PASSWORD' | sudo -S DUCKDNS_DOMAIN='$DUCKDNS_DOMAIN' DUCKDNS_TOKEN='$DUCKDNS_TOKEN' LETSENCRYPT_EMAIL='$LETSENCRYPT_EMAIL' bash /tmp/raspberry_pi_nextcloud.sh setup-ssl"
|
|
|
|
local full_domain="${DUCKDNS_DOMAIN}.duckdns.org"
|
|
|
|
log_success "========================================"
|
|
log_success "SSL setup complete!"
|
|
log_success "========================================"
|
|
echo
|
|
log_info "Access your Nextcloud at: https://$full_domain"
|
|
log_info "This works on ALL devices without certificate warnings!"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Remote Installation
|
|
# =============================================================================
|
|
|
|
phase_install_remote() {
|
|
log_info "=== Installing Nextcloud via SSH ==="
|
|
|
|
if [[ -z $PI_PASSWORD ]]; then
|
|
die "PI_PASSWORD not set. Did you run flash script first?"
|
|
fi
|
|
|
|
local pi_ip
|
|
pi_ip=$(discover_raspberry_pi)
|
|
|
|
if [[ -z $pi_ip ]]; then
|
|
die "Failed to discover Raspberry Pi"
|
|
fi
|
|
|
|
log_info "Using Raspberry Pi at: $pi_ip"
|
|
|
|
# Remove old host key if present
|
|
ssh-keygen -R "$pi_ip" 2>/dev/null || true
|
|
|
|
log_info "Copying script to Pi..."
|
|
sshpass -p "$PI_PASSWORD" scp -o StrictHostKeyChecking=no "$0" "${PI_USER}@${pi_ip}:/tmp/raspberry_pi_nextcloud.sh"
|
|
|
|
log_info "Running system configuration on Pi..."
|
|
sshpass -p "$PI_PASSWORD" ssh -o StrictHostKeyChecking=no "${PI_USER}@${pi_ip}" \
|
|
"echo '$PI_PASSWORD' | sudo -S bash /tmp/raspberry_pi_nextcloud.sh configure"
|
|
|
|
log_info "Installing Nextcloud on Pi..."
|
|
auto_generate_nextcloud_password
|
|
save_config
|
|
|
|
log_success "Nextcloud admin user: $NEXTCLOUD_ADMIN_USER"
|
|
log_success "Nextcloud admin password: $NEXTCLOUD_ADMIN_PASSWORD"
|
|
|
|
sshpass -p "$PI_PASSWORD" ssh -o StrictHostKeyChecking=no "${PI_USER}@${pi_ip}" \
|
|
"echo '$PI_PASSWORD' | sudo -S NEXTCLOUD_ADMIN_PASSWORD='$NEXTCLOUD_ADMIN_PASSWORD' NEXTCLOUD_ADMIN_USER='$NEXTCLOUD_ADMIN_USER' bash /tmp/raspberry_pi_nextcloud.sh install-local"
|
|
|
|
log_info "Fixing Nextcloud issues..."
|
|
sshpass -p "$PI_PASSWORD" ssh -o StrictHostKeyChecking=no "${PI_USER}@${pi_ip}" \
|
|
"echo '$PI_PASSWORD' | sudo -S bash /tmp/raspberry_pi_nextcloud.sh fix"
|
|
|
|
log_success "========================================"
|
|
log_success "Remote Nextcloud installation complete!"
|
|
log_success "========================================"
|
|
echo
|
|
log_info "=== Access Information ==="
|
|
log_info "Nextcloud URL: https://$pi_ip"
|
|
log_info "Admin user: $NEXTCLOUD_ADMIN_USER"
|
|
log_info "Admin password: $NEXTCLOUD_ADMIN_PASSWORD"
|
|
log_info "All credentials saved in: $CONFIG_FILE"
|
|
echo
|
|
log_info "=== Trust the certificate ==="
|
|
log_info "Run: $0 install-ca"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Install CA Certificate on Client
|
|
# =============================================================================
|
|
|
|
phase_install_ca() {
|
|
log_info "=== Installing Nextcloud CA Certificate ==="
|
|
|
|
if [[ -z $PI_PASSWORD ]]; then
|
|
die "PI_PASSWORD not set. Run this after running install-remote or flash."
|
|
fi
|
|
|
|
local pi_ip
|
|
pi_ip=$(discover_raspberry_pi)
|
|
|
|
if [[ -z $pi_ip ]]; then
|
|
die "Failed to discover Raspberry Pi"
|
|
fi
|
|
|
|
log_info "Downloading CA certificate from Pi..."
|
|
|
|
local ca_file="/tmp/nextcloud-ca.crt"
|
|
|
|
# Use SSH with sudo to cat the file (since it's in a protected directory)
|
|
sshpass -p "$PI_PASSWORD" ssh -o StrictHostKeyChecking=no \
|
|
"${PI_USER}@${pi_ip}" "echo '$PI_PASSWORD' | sudo -S cat /etc/ssl/nextcloud/ca.crt" >"$ca_file" 2>/dev/null
|
|
|
|
if [[ ! -f $ca_file ]] || [[ ! -s $ca_file ]]; then
|
|
die "Failed to download CA certificate"
|
|
fi
|
|
|
|
log_success "CA certificate downloaded to: $ca_file"
|
|
|
|
# Detect OS and install appropriately
|
|
if [[ -f /etc/arch-release ]]; then
|
|
log_info "Detected Arch Linux - installing CA..."
|
|
sudo cp "$ca_file" /etc/ca-certificates/trust-source/anchors/nextcloud-ca.crt
|
|
sudo trust extract-compat
|
|
log_success "CA installed in system trust store"
|
|
|
|
elif [[ -f /etc/debian_version ]]; then
|
|
log_info "Detected Debian/Ubuntu - installing CA..."
|
|
sudo cp "$ca_file" /usr/local/share/ca-certificates/nextcloud-ca.crt
|
|
sudo update-ca-certificates
|
|
log_success "CA installed in system trust store"
|
|
|
|
elif [[ -f /etc/redhat-release ]]; then
|
|
log_info "Detected RHEL/Fedora - installing CA..."
|
|
sudo cp "$ca_file" /etc/pki/ca-trust/source/anchors/nextcloud-ca.crt
|
|
sudo update-ca-trust
|
|
log_success "CA installed in system trust store"
|
|
|
|
elif [[ "$(uname)" == "Darwin" ]]; then
|
|
log_info "Detected macOS - installing CA..."
|
|
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$ca_file"
|
|
log_success "CA installed in system keychain"
|
|
|
|
else
|
|
log_warning "Unknown OS - please install CA manually from: $ca_file"
|
|
fi
|
|
|
|
# Install in browser certificate stores
|
|
log_info "Installing CA in browser certificate stores..."
|
|
|
|
# Chrome/Chromium (uses NSS)
|
|
if [[ -d ~/.pki/nssdb ]] || command -v certutil &>/dev/null; then
|
|
mkdir -p ~/.pki/nssdb
|
|
if ! certutil -d sql:~/.pki/nssdb -L 2>/dev/null | grep -q "Nextcloud"; then
|
|
# Initialize NSS db if needed
|
|
certutil -d sql:~/.pki/nssdb -N --empty-password 2>/dev/null || true
|
|
if certutil -d sql:~/.pki/nssdb -A -n "Nextcloud Home CA" -t "CT,C,C" -i "$ca_file" 2>/dev/null; then
|
|
log_success "CA installed in Chrome/Chromium"
|
|
else
|
|
log_warning "Could not install in Chrome/Chromium NSS db"
|
|
fi
|
|
else
|
|
log_info "CA already installed in Chrome/Chromium"
|
|
fi
|
|
fi
|
|
|
|
# Firefox (has its own profile NSS databases)
|
|
if [[ -d ~/.mozilla/firefox ]]; then
|
|
local installed=0
|
|
for profile_dir in ~/.mozilla/firefox/*.default* ~/.mozilla/firefox/*.esr*; do
|
|
if [[ -d $profile_dir ]]; then
|
|
if ! certutil -d sql:"$profile_dir" -L 2>/dev/null | grep -q "Nextcloud"; then
|
|
certutil -d sql:"$profile_dir" -A -n "Nextcloud Home CA" -t "CT,C,C" -i "$ca_file" 2>/dev/null &&
|
|
installed=1
|
|
else
|
|
installed=1
|
|
fi
|
|
fi
|
|
done
|
|
if [[ $installed -eq 1 ]]; then
|
|
log_success "CA installed in Firefox"
|
|
else
|
|
log_warning "Could not install in Firefox - you may need to import manually"
|
|
fi
|
|
fi
|
|
|
|
# Add hostname to /etc/hosts if not present
|
|
if ! grep -q "$PI_HOSTNAME" /etc/hosts 2>/dev/null; then
|
|
log_info "Adding $PI_HOSTNAME to /etc/hosts..."
|
|
echo "$pi_ip $PI_HOSTNAME ${PI_HOSTNAME}.local" | sudo tee -a /etc/hosts >/dev/null
|
|
log_success "Added $PI_HOSTNAME to /etc/hosts"
|
|
else
|
|
log_info "$PI_HOSTNAME already in /etc/hosts"
|
|
fi
|
|
|
|
# Verify
|
|
log_info "Verifying HTTPS connection..."
|
|
if curl -s --max-time 5 "https://$PI_HOSTNAME/status.php" 2>/dev/null | grep -q "installed"; then
|
|
log_success "HTTPS connection verified - no certificate warnings!"
|
|
else
|
|
log_warning "Could not verify HTTPS - you may need to restart your browser"
|
|
fi
|
|
|
|
log_success "========================================"
|
|
log_success "CA Certificate installed!"
|
|
log_success "========================================"
|
|
echo
|
|
log_info "Access Nextcloud at: https://$PI_HOSTNAME"
|
|
log_info "Your browser should now trust the certificate without warnings."
|
|
echo
|
|
log_info "For other devices (phones, tablets, other computers):"
|
|
log_info " Download: https://$PI_HOSTNAME/ca/nextcloud-ca.crt"
|
|
log_info " Then install the certificate in your device's trust store."
|
|
}
|
|
|
|
# =============================================================================
|
|
# Main
|
|
# =============================================================================
|
|
|
|
show_help() {
|
|
cat <<'EOF'
|
|
Nextcloud Installation Script for Raspberry Pi
|
|
|
|
Usage: ./raspberry_pi_nextcloud.sh <command>
|
|
|
|
Commands:
|
|
install-remote Install Nextcloud via SSH from your laptop (recommended)
|
|
setup-ssl-remote Setup Let's Encrypt SSL with DuckDNS (auto-trusted on all devices)
|
|
install-ca Install self-signed CA on this machine (alternative to setup-ssl)
|
|
configure Configure Pi system (run on Pi)
|
|
install-local Install Nextcloud (run on Pi)
|
|
fix Fix common Nextcloud issues (run on Pi)
|
|
setup-ssl Setup Let's Encrypt SSL (run on Pi)
|
|
help Show this help message
|
|
|
|
The script will:
|
|
1. Configure the Raspberry Pi system (SSH hardening, firewall, etc.)
|
|
2. Install Apache, PHP, MariaDB, Redis
|
|
3. Download and install Nextcloud
|
|
4. Configure caching, background jobs, and security
|
|
|
|
For HTTPS trusted on ALL devices automatically:
|
|
./raspberry_pi_nextcloud.sh install-remote
|
|
./raspberry_pi_nextcloud.sh setup-ssl-remote
|
|
|
|
This uses DuckDNS (free) + Let's Encrypt for real trusted certificates.
|
|
Go to https://www.duckdns.org/ to get your free domain first.
|
|
|
|
For self-signed certificates (requires manual CA install on each device):
|
|
./raspberry_pi_nextcloud.sh install-remote
|
|
./raspberry_pi_nextcloud.sh install-ca
|
|
|
|
For local installation (on Pi):
|
|
sudo ./raspberry_pi_nextcloud.sh configure
|
|
sudo ./raspberry_pi_nextcloud.sh install-local
|
|
sudo ./raspberry_pi_nextcloud.sh fix
|
|
sudo ./raspberry_pi_nextcloud.sh setup-ssl
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
local command="${1:-help}"
|
|
|
|
case "$command" in
|
|
install-remote)
|
|
phase_install_remote
|
|
;;
|
|
setup-ssl-remote)
|
|
phase_setup_ssl_remote
|
|
;;
|
|
setup-ssl)
|
|
phase_setup_ssl
|
|
;;
|
|
install-ca)
|
|
phase_install_ca
|
|
;;
|
|
configure)
|
|
phase_configure_system
|
|
;;
|
|
install-local | install)
|
|
phase_install_nextcloud
|
|
;;
|
|
fix)
|
|
phase_fix_issues
|
|
;;
|
|
help | --help | -h)
|
|
show_help
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $command"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|