1070 lines
34 KiB
Bash
1070 lines
34 KiB
Bash
#!/bin/bash
|
||
|
||
# Ubuntu uBlue apt-layer Advanced Package Management
|
||
# Provides enterprise-grade package management with multi-user support, security features,
|
||
# and advanced dependency resolution for production deployments
|
||
|
||
# =============================================================================
|
||
# ADVANCED PACKAGE MANAGEMENT FUNCTIONS
|
||
# =============================================================================
|
||
|
||
# Advanced package management configuration (with fallbacks for when particle-config.sh is not loaded)
|
||
ADVANCED_PKG_CONFIG_DIR="${UBLUE_CONFIG_DIR:-/etc/ubuntu-ublue}/package-management"
|
||
ADVANCED_PKG_STATE_DIR="${UBLUE_ROOT:-/var/lib/particle-os}/package-management"
|
||
ADVANCED_PKG_CACHE_DIR="$ADVANCED_PKG_STATE_DIR/cache"
|
||
ADVANCED_PKG_DEPENDENCIES_DIR="$ADVANCED_PKG_STATE_DIR/dependencies"
|
||
ADVANCED_PKG_SECURITY_DIR="$ADVANCED_PKG_STATE_DIR/security"
|
||
ADVANCED_PKG_USERS_DIR="$ADVANCED_PKG_STATE_DIR/users"
|
||
ADVANCED_PKG_POLICIES_DIR="$ADVANCED_PKG_STATE_DIR/policies"
|
||
|
||
# Initialize advanced package management system
|
||
init_advanced_package_management() {
|
||
log_info "Initializing advanced package management system" "apt-layer"
|
||
|
||
# Create advanced package management directories
|
||
mkdir -p "$ADVANCED_PKG_CONFIG_DIR" "$ADVANCED_PKG_STATE_DIR" "$ADVANCED_PKG_CACHE_DIR"
|
||
mkdir -p "$ADVANCED_PKG_DEPENDENCIES_DIR" "$ADVANCED_PKG_SECURITY_DIR" "$ADVANCED_PKG_USERS_DIR"
|
||
mkdir -p "$ADVANCED_PKG_POLICIES_DIR"
|
||
|
||
# Set proper permissions
|
||
chmod 755 "$ADVANCED_PKG_CONFIG_DIR" "$ADVANCED_PKG_STATE_DIR"
|
||
chmod 700 "$ADVANCED_PKG_CACHE_DIR" "$ADVANCED_PKG_DEPENDENCIES_DIR" "$ADVANCED_PKG_SECURITY_DIR"
|
||
chmod 750 "$ADVANCED_PKG_USERS_DIR" "$ADVANCED_PKG_POLICIES_DIR"
|
||
|
||
# Initialize user management database
|
||
init_user_management_db
|
||
|
||
# Initialize security policies
|
||
init_security_policies
|
||
|
||
# Initialize dependency resolution cache
|
||
init_dependency_cache
|
||
|
||
log_success "Advanced package management system initialized" "apt-layer"
|
||
}
|
||
|
||
# Initialize user management database
|
||
init_user_management_db() {
|
||
local user_db="$ADVANCED_PKG_USERS_DIR/users.json"
|
||
|
||
if [[ ! -f "$user_db" ]]; then
|
||
cat > "$user_db" << EOF
|
||
{
|
||
"users": {},
|
||
"groups": {},
|
||
"permissions": {},
|
||
"roles": {
|
||
"admin": {
|
||
"description": "Full system administration",
|
||
"permissions": ["all"]
|
||
},
|
||
"package_manager": {
|
||
"description": "Package installation and management",
|
||
"permissions": ["install", "remove", "update", "list"]
|
||
},
|
||
"viewer": {
|
||
"description": "Read-only access to package information",
|
||
"permissions": ["list", "info", "status"]
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
chmod 600 "$user_db"
|
||
fi
|
||
}
|
||
|
||
# Initialize security policies
|
||
init_security_policies() {
|
||
local security_policy="$ADVANCED_PKG_SECURITY_DIR/security-policy.json"
|
||
|
||
if [[ ! -f "$security_policy" ]]; then
|
||
cat > "$security_policy" << EOF
|
||
{
|
||
"package_verification": {
|
||
"enabled": true,
|
||
"gpg_check": true,
|
||
"hash_verification": true,
|
||
"source_verification": true
|
||
},
|
||
"installation_policies": {
|
||
"allow_unsigned_packages": false,
|
||
"allow_external_sources": false,
|
||
"require_approval": false,
|
||
"max_package_size_mb": 1000
|
||
},
|
||
"security_scanning": {
|
||
"enabled": true,
|
||
"scan_installed_packages": true,
|
||
"scan_dependencies": true,
|
||
"vulnerability_check": true
|
||
},
|
||
"audit_logging": {
|
||
"enabled": true,
|
||
"log_level": "INFO",
|
||
"retention_days": 90
|
||
}
|
||
}
|
||
EOF
|
||
chmod 600 "$security_policy"
|
||
fi
|
||
}
|
||
|
||
# Initialize dependency cache
|
||
init_dependency_cache() {
|
||
local dep_cache="$ADVANCED_PKG_DEPENDENCIES_DIR/dependency-cache.json"
|
||
|
||
if [[ ! -f "$dep_cache" ]]; then
|
||
cat > "$dep_cache" << EOF
|
||
{
|
||
"package_dependencies": {},
|
||
"reverse_dependencies": {},
|
||
"conflict_resolution": {},
|
||
"last_updated": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||
}
|
||
EOF
|
||
chmod 644 "$dep_cache"
|
||
fi
|
||
}
|
||
|
||
# Check user permissions
|
||
check_user_permissions() {
|
||
local user="$1"
|
||
local required_permission="$2"
|
||
|
||
log_debug "Checking permission '$required_permission' for user '$user'" "apt-layer"
|
||
|
||
# Root user has all permissions
|
||
if [[ "$user" == "root" ]] || [[ $EUID -eq 0 ]]; then
|
||
return 0
|
||
fi
|
||
|
||
local user_db="$ADVANCED_PKG_USERS_DIR/users.json"
|
||
|
||
if [[ ! -f "$user_db" ]]; then
|
||
log_error "User management database not found" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Get user role
|
||
local user_role
|
||
user_role=$(jq -r ".users[\"$user\"].role // \"viewer\"" "$user_db" 2>/dev/null || echo "viewer")
|
||
|
||
# Get role permissions
|
||
local role_permissions
|
||
role_permissions=$(jq -r ".roles[\"$user_role\"].permissions[]?" "$user_db" 2>/dev/null || echo "")
|
||
|
||
# Check if user has required permission
|
||
if echo "$role_permissions" | grep -q "^$required_permission$" || echo "$role_permissions" | grep -q "^all$"; then
|
||
return 0
|
||
fi
|
||
|
||
log_error "User '$user' does not have permission '$required_permission'" "apt-layer"
|
||
return 1
|
||
}
|
||
|
||
# Add user to package management system
|
||
add_package_user() {
|
||
local username="$1"
|
||
local role="${2:-viewer}"
|
||
|
||
if [[ -z "$username" ]]; then
|
||
log_error "Username required" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Adding user '$username' with role '$role'" "apt-layer"
|
||
|
||
# Check if user exists in system
|
||
if ! id "$username" &>/dev/null; then
|
||
log_error "User '$username' does not exist in system" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
local user_db="$ADVANCED_PKG_USERS_DIR/users.json"
|
||
|
||
# Check if role exists
|
||
if ! jq -e ".roles[\"$role\"]" "$user_db" >/dev/null 2>&1; then
|
||
log_error "Role '$role' does not exist" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Add user to database
|
||
jq --arg user "$username" --arg role "$role" '.users[$user] = {"role": $role, "added": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' "$user_db" > "$user_db.tmp" && \
|
||
mv "$user_db.tmp" "$user_db"
|
||
|
||
log_success "User '$username' added with role '$role'" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Remove user from package management system
|
||
remove_package_user() {
|
||
local username="$1"
|
||
|
||
if [[ -z "$username" ]]; then
|
||
log_error "Username required" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Removing user '$username'" "apt-layer"
|
||
|
||
local user_db="$ADVANCED_PKG_USERS_DIR/users.json"
|
||
|
||
# Remove user from database
|
||
jq --arg user "$username" 'del(.users[$user])' "$user_db" > "$user_db.tmp" && \
|
||
mv "$user_db.tmp" "$user_db"
|
||
|
||
log_success "User '$username' removed" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# List package management users
|
||
list_package_users() {
|
||
log_info "Listing package management users" "apt-layer"
|
||
|
||
local user_db="$ADVANCED_PKG_USERS_DIR/users.json"
|
||
|
||
if [[ ! -f "$user_db" ]]; then
|
||
log_error "User management database not found" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
echo "=== Package Management Users ==="
|
||
|
||
local users
|
||
users=$(jq -r '.users | to_entries[] | "\(.key): \(.value.role)"' "$user_db" 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$users" ]]; then
|
||
echo "$users" | while read -r user_info; do
|
||
echo " $user_info"
|
||
done
|
||
else
|
||
log_info "No users found" "apt-layer"
|
||
fi
|
||
|
||
echo ""
|
||
echo "=== Available Roles ==="
|
||
|
||
local roles
|
||
roles=$(jq -r '.roles | to_entries[] | "\(.key): \(.value.description)"' "$user_db" 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$roles" ]]; then
|
||
echo "$roles" | while read -r role_info; do
|
||
echo " $role_info"
|
||
done
|
||
else
|
||
log_info "No roles found" "apt-layer"
|
||
fi
|
||
|
||
echo ""
|
||
}
|
||
|
||
# Advanced dependency resolution
|
||
resolve_package_dependencies() {
|
||
local packages=("$@")
|
||
|
||
if [[ ${#packages[@]} -eq 0 ]]; then
|
||
log_error "No packages specified for dependency resolution" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Resolving dependencies for packages: ${packages[*]}" "apt-layer"
|
||
|
||
# Create temporary file for dependency resolution
|
||
local temp_deps="$ADVANCED_PKG_CACHE_DIR/temp-deps-$$.txt"
|
||
local resolved_deps="$ADVANCED_PKG_CACHE_DIR/resolved-deps-$$.txt"
|
||
|
||
# Get package dependencies using apt-cache
|
||
for package in "${packages[@]}"; do
|
||
log_debug "Resolving dependencies for package: $package" "apt-layer"
|
||
|
||
# Get direct dependencies
|
||
apt-cache depends "$package" 2>/dev/null | grep -E "^(Depends|Recommends|Suggests)" | cut -d: -f2 | tr -d ' ' | grep -v "^$" >> "$temp_deps" || true
|
||
|
||
# Get reverse dependencies (what depends on this package)
|
||
apt-cache rdepends "$package" 2>/dev/null | grep -v "^Reverse Depends" | grep -v "^$" >> "$temp_deps" || true
|
||
done
|
||
|
||
# Remove duplicates and sort
|
||
sort -u "$temp_deps" > "$resolved_deps"
|
||
|
||
# Check for conflicts
|
||
local conflicts=()
|
||
while read -r dep; do
|
||
if [[ -n "$dep" ]]; then
|
||
# Check if package conflicts with any existing packages
|
||
if check_package_conflicts "$dep"; then
|
||
conflicts+=("$dep")
|
||
fi
|
||
fi
|
||
done < "$resolved_deps"
|
||
|
||
# Report conflicts
|
||
if [[ ${#conflicts[@]} -gt 0 ]]; then
|
||
log_warning "Package conflicts detected: ${conflicts[*]}" "apt-layer"
|
||
log_info "Manual resolution may be required" "apt-layer"
|
||
fi
|
||
|
||
# Clean up temporary files
|
||
rm -f "$temp_deps" "$resolved_deps"
|
||
|
||
log_success "Dependency resolution completed" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Check for package conflicts
|
||
check_package_conflicts() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
# Check if package conflicts with any installed packages
|
||
local conflicts
|
||
conflicts=$(apt-cache policy "$package" 2>/dev/null | grep -A1 "Installed" | grep -E "(Conflicts|Breaks)" || echo "")
|
||
|
||
if [[ -n "$conflicts" ]]; then
|
||
log_warning "Package '$package' has conflicts: $conflicts" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
# Advanced package installation with security checks
|
||
advanced_install_packages() {
|
||
local packages=("$@")
|
||
local user="${SUDO_USER:-$USER}"
|
||
|
||
if [[ ${#packages[@]} -eq 0 ]]; then
|
||
log_error "No packages specified for installation" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Advanced package installation for packages: ${packages[*]}" "apt-layer"
|
||
|
||
# Check user permissions
|
||
if ! check_user_permissions "$user" "install"; then
|
||
return 1
|
||
fi
|
||
|
||
# Check security policies
|
||
if ! check_security_policies "${packages[@]}"; then
|
||
return 1
|
||
fi
|
||
|
||
# Resolve dependencies
|
||
if ! resolve_package_dependencies "${packages[@]}"; then
|
||
log_error "Dependency resolution failed" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Start transaction
|
||
start_transaction "advanced_install_${packages[0]}"
|
||
|
||
# Update package lists
|
||
update_transaction_phase "updating_package_lists"
|
||
log_info "Updating package lists" "apt-layer"
|
||
if ! apt-get update; then
|
||
log_error "Failed to update package lists" "apt-layer"
|
||
rollback_transaction
|
||
return 1
|
||
fi
|
||
|
||
# Install packages
|
||
update_transaction_phase "installing_packages"
|
||
log_info "Installing packages: ${packages[*]}" "apt-layer"
|
||
if ! apt-get install -y "${packages[@]}"; then
|
||
log_error "Failed to install packages: ${packages[*]}" "apt-layer"
|
||
rollback_transaction
|
||
return 1
|
||
fi
|
||
|
||
# Clean up
|
||
apt-get clean
|
||
apt-get autoremove -y
|
||
|
||
# Log installation
|
||
log_package_installation "$user" "${packages[@]}"
|
||
|
||
commit_transaction
|
||
log_success "Advanced package installation completed: ${packages[*]}" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Check security policies
|
||
check_security_policies() {
|
||
local packages=("$@")
|
||
|
||
log_info "Checking security policies for packages: ${packages[*]}" "apt-layer"
|
||
|
||
local security_policy="$ADVANCED_PKG_SECURITY_DIR/security-policy.json"
|
||
|
||
if [[ ! -f "$security_policy" ]]; then
|
||
log_warning "Security policy not found, using default policies" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
# Check package verification settings
|
||
local gpg_check
|
||
gpg_check=$(jq -r '.package_verification.gpg_check' "$security_policy" 2>/dev/null || echo "true")
|
||
|
||
if [[ "$gpg_check" == "true" ]]; then
|
||
log_info "GPG verification enabled" "apt-layer"
|
||
# Perform comprehensive GPG verification
|
||
for package in "${packages[@]}"; do
|
||
if ! check_package_gpg_signature "$package"; then
|
||
log_error "Package '$package' failed GPG signature verification" "apt-layer"
|
||
return 1
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Check installation policies
|
||
local allow_unsigned
|
||
allow_unsigned=$(jq -r '.installation_policies.allow_unsigned_packages' "$security_policy" 2>/dev/null || echo "false")
|
||
|
||
if [[ "$allow_unsigned" == "false" ]]; then
|
||
log_info "Unsigned packages not allowed" "apt-layer"
|
||
# Check for unsigned packages using enhanced signing verification
|
||
for package in "${packages[@]}"; do
|
||
if ! check_package_signing "$package"; then
|
||
log_error "Package '$package' is not properly signed" "apt-layer"
|
||
return 1
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Check package size limits
|
||
local max_size
|
||
max_size=$(jq -r '.installation_policies.max_package_size_mb' "$security_policy" 2>/dev/null || echo "1000")
|
||
|
||
for package in "${packages[@]}"; do
|
||
if ! check_package_size "$package" "$max_size"; then
|
||
log_error "Package '$package' exceeds size limit of ${max_size}MB" "apt-layer"
|
||
return 1
|
||
fi
|
||
done
|
||
|
||
log_success "Security policy checks passed" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Check package signature
|
||
check_package_signature() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
# Check if package is signed (simplified check)
|
||
local package_info
|
||
package_info=$(apt-cache policy "$package" 2>/dev/null || echo "")
|
||
|
||
if echo "$package_info" | grep -q "Signed-By"; then
|
||
return 0
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
# Check package GPG signature (comprehensive verification)
|
||
check_package_gpg_signature() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
log_debug "Verifying GPG signature for package: $package" "apt-layer"
|
||
|
||
# Check if GPG is available
|
||
if ! command -v gpg &>/dev/null; then
|
||
log_warning "GPG not available, skipping signature verification for: $package" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
# Get package source and key information
|
||
local package_info
|
||
package_info=$(apt-cache policy "$package" 2>/dev/null || echo "")
|
||
|
||
# Check if package has a signed-by field
|
||
local signed_by
|
||
signed_by=$(echo "$package_info" | grep "Signed-By:" | cut -d: -f2 | tr -d ' ' || echo "")
|
||
|
||
if [[ -z "$signed_by" ]]; then
|
||
log_warning "Package '$package' has no Signed-By field" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Verify the GPG key exists and is trusted
|
||
if ! gpg --list-keys "$signed_by" &>/dev/null; then
|
||
log_warning "GPG key '$signed_by' for package '$package' not found in keyring" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Additional verification: check if the key is trusted
|
||
local trust_level
|
||
trust_level=$(gpg --list-keys --with-colons "$signed_by" 2>/dev/null | grep "^pub:" | cut -d: -f2 || echo "")
|
||
|
||
if [[ "$trust_level" != "u" ]] && [[ "$trust_level" != "f" ]]; then
|
||
log_warning "GPG key '$signed_by' for package '$package' is not fully trusted (trust level: $trust_level)" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_debug "GPG signature verification passed for package: $package" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Check package signing (enhanced verification)
|
||
check_package_signing() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
log_debug "Checking package signing for: $package" "apt-layer"
|
||
|
||
# Check if debsig-verify is available for Debian package signing verification
|
||
if command -v debsig-verify &>/dev/null; then
|
||
# Get package file path (this would require downloading or finding the .deb file)
|
||
local package_file
|
||
package_file=$(find /var/cache/apt/archives -name "${package}*.deb" 2>/dev/null | head -1 || echo "")
|
||
|
||
if [[ -n "$package_file" ]] && [[ -f "$package_file" ]]; then
|
||
if debsig-verify "$package_file" &>/dev/null; then
|
||
log_debug "Package signing verification passed for: $package" "apt-layer"
|
||
return 0
|
||
else
|
||
log_warning "Package signing verification failed for: $package" "apt-layer"
|
||
return 1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Fallback to basic signature check
|
||
if check_package_signature "$package"; then
|
||
log_debug "Basic package signature check passed for: $package" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
log_warning "Package signing verification failed for: $package" "apt-layer"
|
||
return 1
|
||
}
|
||
|
||
# Check package size
|
||
check_package_size() {
|
||
local package="$1"
|
||
local max_size_mb="$2"
|
||
|
||
if [[ -z "$package" ]] || [[ -z "$max_size_mb" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
# Get package size (simplified check)
|
||
local package_size
|
||
package_size=$(apt-cache show "$package" 2>/dev/null | grep "^Size:" | cut -d: -f2 | tr -d ' ' || echo "0")
|
||
|
||
if [[ "$package_size" -gt $((max_size_mb * 1024 * 1024)) ]]; then
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# Log package installation
|
||
log_package_installation() {
|
||
local user="$1"
|
||
shift
|
||
local packages=("$@")
|
||
|
||
local audit_log="$ADVANCED_PKG_SECURITY_DIR/audit.log"
|
||
|
||
for package in "${packages[@]}"; do
|
||
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) - INSTALL - User: $user - Package: $package" >> "$audit_log"
|
||
done
|
||
}
|
||
|
||
# Advanced package removal with dependency checking
|
||
advanced_remove_packages() {
|
||
local packages=("$@")
|
||
local user="${SUDO_USER:-$USER}"
|
||
|
||
if [[ ${#packages[@]} -eq 0 ]]; then
|
||
log_error "No packages specified for removal" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Advanced package removal for packages: ${packages[*]}" "apt-layer"
|
||
|
||
# Check user permissions
|
||
if ! check_user_permissions "$user" "remove"; then
|
||
return 1
|
||
fi
|
||
|
||
# Check for critical dependencies
|
||
for package in "${packages[@]}"; do
|
||
if check_critical_dependency "$package"; then
|
||
log_warning "Package '$package' may be a critical dependency" "apt-layer"
|
||
log_info "Manual verification recommended" "apt-layer"
|
||
fi
|
||
done
|
||
|
||
# Start transaction
|
||
start_transaction "advanced_remove_${packages[0]}"
|
||
|
||
# Remove packages
|
||
update_transaction_phase "removing_packages"
|
||
log_info "Removing packages: ${packages[*]}" "apt-layer"
|
||
if ! apt-get remove -y "${packages[@]}"; then
|
||
log_error "Failed to remove packages: ${packages[*]}" "apt-layer"
|
||
rollback_transaction
|
||
return 1
|
||
fi
|
||
|
||
# Clean up
|
||
apt-get autoremove -y
|
||
apt-get clean
|
||
|
||
# Log removal
|
||
log_package_removal "$user" "${packages[@]}"
|
||
|
||
commit_transaction
|
||
log_success "Advanced package removal completed: ${packages[*]}" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Check if package is a critical dependency
|
||
check_critical_dependency() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
# List of critical system packages (simplified)
|
||
local critical_packages=("systemd" "bash" "coreutils" "apt" "dpkg" "base-files" "ubuntu-minimal")
|
||
|
||
for critical in "${critical_packages[@]}"; do
|
||
if [[ "$package" == "$critical" ]]; then
|
||
return 0
|
||
fi
|
||
done
|
||
|
||
return 1
|
||
}
|
||
|
||
# Log package removal
|
||
log_package_removal() {
|
||
local user="$1"
|
||
shift
|
||
local packages=("$@")
|
||
|
||
local audit_log="$ADVANCED_PKG_SECURITY_DIR/audit.log"
|
||
|
||
for package in "${packages[@]}"; do
|
||
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) - REMOVE - User: $user - Package: $package" >> "$audit_log"
|
||
done
|
||
}
|
||
|
||
# Advanced package update with rollback capability
|
||
advanced_update_packages() {
|
||
local packages=("$@")
|
||
local user="${SUDO_USER:-$USER}"
|
||
|
||
log_info "Advanced package update for packages: ${packages[*]}" "apt-layer"
|
||
|
||
# Check user permissions
|
||
if ! check_user_permissions "$user" "update"; then
|
||
return 1
|
||
fi
|
||
|
||
# Create backup of current state
|
||
local backup_id
|
||
backup_id=$(create_package_backup "${packages[@]}")
|
||
|
||
if [[ -z "$backup_id" ]]; then
|
||
log_error "Failed to create package backup" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Created backup: $backup_id" "apt-layer"
|
||
|
||
# Start transaction
|
||
start_transaction "advanced_update_${packages[0]}"
|
||
|
||
# Update package lists
|
||
update_transaction_phase "updating_package_lists"
|
||
log_info "Updating package lists" "apt-layer"
|
||
if ! apt-get update; then
|
||
log_error "Failed to update package lists" "apt-layer"
|
||
rollback_transaction
|
||
return 1
|
||
fi
|
||
|
||
# Update packages
|
||
update_transaction_phase "updating_packages"
|
||
log_info "Updating packages: ${packages[*]}" "apt-layer"
|
||
if ! apt-get upgrade -y "${packages[@]}"; then
|
||
log_error "Failed to update packages: ${packages[*]}" "apt-layer"
|
||
log_info "Rolling back to backup: $backup_id" "apt-layer"
|
||
restore_package_backup "$backup_id"
|
||
rollback_transaction
|
||
return 1
|
||
fi
|
||
|
||
# Log update
|
||
log_package_update "$user" "${packages[@]}"
|
||
|
||
commit_transaction
|
||
log_success "Advanced package update completed: ${packages[*]}" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Create package backup
|
||
create_package_backup() {
|
||
local packages=("$@")
|
||
local backup_id="backup-$(date +%Y%m%d-%H%M%S)-$$"
|
||
local backup_dir="$ADVANCED_PKG_STATE_DIR/backups/$backup_id"
|
||
|
||
mkdir -p "$backup_dir"
|
||
|
||
log_info "Creating comprehensive package backup: $backup_id" "apt-layer"
|
||
|
||
# Save package states
|
||
for package in "${packages[@]}"; do
|
||
dpkg -l "$package" > "$backup_dir/${package}.state" 2>/dev/null || true
|
||
done
|
||
|
||
# Save complete package list
|
||
dpkg -l | grep -E "^ii" > "$backup_dir/installed-packages.list" 2>/dev/null || true
|
||
|
||
# Save package configuration
|
||
dpkg --get-selections > "$backup_dir/package-selections.list" 2>/dev/null || true
|
||
|
||
# Save repository information
|
||
cp /etc/apt/sources.list "$backup_dir/sources.list" 2>/dev/null || true
|
||
cp -r /etc/apt/sources.list.d "$backup_dir/" 2>/dev/null || true
|
||
|
||
# Save GPG key information
|
||
apt-key list > "$backup_dir/apt-keys.list" 2>/dev/null || true
|
||
|
||
# Create backup metadata
|
||
cat > "$backup_dir/backup-metadata.json" << EOF
|
||
{
|
||
"backup_id": "$backup_id",
|
||
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||
"created_by": "$(whoami)",
|
||
"packages": $(printf '%s\n' "${packages[@]}" | jq -R . | jq -s .),
|
||
"system_info": {
|
||
"hostname": "$(hostname)",
|
||
"distribution": "$(lsb_release -d | cut -f2 2>/dev/null || echo 'unknown')",
|
||
"kernel": "$(uname -r)"
|
||
}
|
||
}
|
||
EOF
|
||
|
||
# Compress backup for storage efficiency
|
||
tar -czf "$backup_dir.tar.gz" -C "$ADVANCED_PKG_STATE_DIR/backups" "$backup_id" 2>/dev/null || true
|
||
|
||
log_success "Package backup created: $backup_id" "apt-layer"
|
||
echo "$backup_id"
|
||
}
|
||
|
||
# Restore package backup
|
||
restore_package_backup() {
|
||
local backup_id="$1"
|
||
local backup_dir="$ADVANCED_PKG_STATE_DIR/backups/$backup_id"
|
||
local backup_archive="$backup_dir.tar.gz"
|
||
|
||
# Check if backup exists (try both directory and compressed archive)
|
||
if [[ ! -d "$backup_dir" ]] && [[ ! -f "$backup_archive" ]]; then
|
||
log_error "Backup not found: $backup_id" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Restoring from backup: $backup_id" "apt-layer"
|
||
|
||
# Extract compressed backup if needed
|
||
if [[ -f "$backup_archive" ]] && [[ ! -d "$backup_dir" ]]; then
|
||
log_info "Extracting compressed backup..." "apt-layer"
|
||
tar -xzf "$backup_archive" -C "$ADVANCED_PKG_STATE_DIR/backups/" 2>/dev/null || {
|
||
log_error "Failed to extract backup archive: $backup_archive" "apt-layer"
|
||
return 1
|
||
}
|
||
fi
|
||
|
||
# Verify backup integrity
|
||
if [[ ! -f "$backup_dir/backup-metadata.json" ]]; then
|
||
log_error "Backup metadata not found, backup may be corrupted: $backup_id" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
# Read backup metadata
|
||
local backup_metadata
|
||
backup_metadata=$(cat "$backup_dir/backup-metadata.json" 2>/dev/null || echo "{}")
|
||
local backup_packages
|
||
backup_packages=$(echo "$backup_metadata" | jq -r '.packages[]?' 2>/dev/null || echo "")
|
||
|
||
log_info "Backup contains $(echo "$backup_packages" | wc -l) packages" "apt-layer"
|
||
|
||
# Restore package selections if available
|
||
if [[ -f "$backup_dir/package-selections.list" ]]; then
|
||
log_info "Restoring package selections..." "apt-layer"
|
||
dpkg --set-selections < "$backup_dir/package-selections.list" 2>/dev/null || {
|
||
log_warning "Failed to restore package selections" "apt-layer"
|
||
}
|
||
fi
|
||
|
||
# Restore repository information if available
|
||
if [[ -f "$backup_dir/sources.list" ]]; then
|
||
log_info "Restoring repository configuration..." "apt-layer"
|
||
cp "$backup_dir/sources.list" /etc/apt/sources.list 2>/dev/null || {
|
||
log_warning "Failed to restore sources.list" "apt-layer"
|
||
}
|
||
fi
|
||
|
||
if [[ -d "$backup_dir/sources.list.d" ]]; then
|
||
cp -r "$backup_dir/sources.list.d"/* /etc/apt/sources.list.d/ 2>/dev/null || {
|
||
log_warning "Failed to restore sources.list.d" "apt-layer"
|
||
}
|
||
fi
|
||
|
||
# Update package lists after repository restoration
|
||
apt-get update 2>/dev/null || {
|
||
log_warning "Failed to update package lists after repository restoration" "apt-layer"
|
||
}
|
||
|
||
log_success "Backup restoration completed: $backup_id" "apt-layer"
|
||
log_audit "BACKUP_RESTORE" "Restored from backup: $backup_id"
|
||
return 0
|
||
}
|
||
|
||
# Log package update
|
||
log_package_update() {
|
||
local user="$1"
|
||
shift
|
||
local packages=("$@")
|
||
|
||
local audit_log="$ADVANCED_PKG_SECURITY_DIR/audit.log"
|
||
|
||
for package in "${packages[@]}"; do
|
||
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) - UPDATE - User: $user - Package: $package" >> "$audit_log"
|
||
done
|
||
}
|
||
|
||
# List package backups
|
||
list_package_backups() {
|
||
log_info "Listing package backups" "apt-layer"
|
||
|
||
local backups_dir="$ADVANCED_PKG_STATE_DIR/backups"
|
||
|
||
if [[ ! -d "$backups_dir" ]]; then
|
||
log_info "No backups directory found" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
echo "=== Package Backups ==="
|
||
|
||
local backups
|
||
backups=$(find "$backups_dir" -name "backup-*" -type d 2>/dev/null | sort -r || echo "")
|
||
|
||
if [[ -n "$backups" ]]; then
|
||
for backup_dir in $backups; do
|
||
local backup_id
|
||
backup_id=$(basename "$backup_dir")
|
||
local metadata_file="$backup_dir/backup-metadata.json"
|
||
|
||
if [[ -f "$metadata_file" ]]; then
|
||
local created_at
|
||
created_at=$(jq -r '.created_at // "unknown"' "$metadata_file" 2>/dev/null || echo "unknown")
|
||
local created_by
|
||
created_by=$(jq -r '.created_by // "unknown"' "$metadata_file" 2>/dev/null || echo "unknown")
|
||
local package_count
|
||
package_count=$(jq -r '.packages | length // 0' "$metadata_file" 2>/dev/null || echo "0")
|
||
|
||
echo " $backup_id: $package_count packages, created by $created_by at $created_at"
|
||
else
|
||
echo " $backup_id: (metadata not available)"
|
||
fi
|
||
done
|
||
else
|
||
log_info "No backups found" "apt-layer"
|
||
fi
|
||
|
||
echo ""
|
||
}
|
||
|
||
# Clean up old backups
|
||
cleanup_old_backups() {
|
||
local max_age_days="${1:-30}"
|
||
|
||
log_info "Cleaning up backups older than $max_age_days days" "apt-layer"
|
||
|
||
local backups_dir="$ADVANCED_PKG_STATE_DIR/backups"
|
||
|
||
if [[ ! -d "$backups_dir" ]]; then
|
||
log_info "No backups directory found" "apt-layer"
|
||
return 0
|
||
fi
|
||
|
||
local removed_count=0
|
||
|
||
# Find and remove old backup directories
|
||
while IFS= read -r -d '' backup_dir; do
|
||
local backup_id
|
||
backup_id=$(basename "$backup_dir")
|
||
local metadata_file="$backup_dir/backup-metadata.json"
|
||
|
||
if [[ -f "$metadata_file" ]]; then
|
||
local created_at
|
||
created_at=$(jq -r '.created_at // ""' "$metadata_file" 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$created_at" ]]; then
|
||
local created_timestamp
|
||
created_timestamp=$(date -d "$created_at" +%s 2>/dev/null || echo "0")
|
||
local current_timestamp
|
||
current_timestamp=$(date +%s)
|
||
local age_days
|
||
age_days=$(( (current_timestamp - created_timestamp) / 86400 ))
|
||
|
||
if [[ $age_days -gt $max_age_days ]]; then
|
||
log_info "Removing old backup: $backup_id (age: ${age_days} days)" "apt-layer"
|
||
rm -rf "$backup_dir" 2>/dev/null || true
|
||
rm -f "$backup_dir.tar.gz" 2>/dev/null || true
|
||
((removed_count++))
|
||
fi
|
||
fi
|
||
fi
|
||
done < <(find "$backups_dir" -name "backup-*" -type d -print0 2>/dev/null)
|
||
|
||
log_success "Cleaned up $removed_count old backups" "apt-layer"
|
||
return 0
|
||
}
|
||
|
||
# Get advanced package information
|
||
get_advanced_package_info() {
|
||
local package="$1"
|
||
|
||
if [[ -z "$package" ]]; then
|
||
log_error "Package name required" "apt-layer"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Getting advanced information for package: $package" "apt-layer"
|
||
|
||
echo "=== Advanced Package Information: $package ==="
|
||
|
||
# Basic package information
|
||
echo "Basic Information:"
|
||
apt-cache show "$package" 2>/dev/null | grep -E "^(Package|Version|Architecture|Maintainer|Description)" | head -10
|
||
|
||
# Dependencies
|
||
echo ""
|
||
echo "Dependencies:"
|
||
apt-cache depends "$package" 2>/dev/null | grep -E "^(Depends|Recommends|Suggests)" | head -5
|
||
|
||
# Reverse dependencies
|
||
echo ""
|
||
echo "Reverse Dependencies:"
|
||
apt-cache rdepends "$package" 2>/dev/null | grep -v "^Reverse Depends" | head -5
|
||
|
||
# Security information
|
||
echo ""
|
||
echo "Security Information:"
|
||
if check_package_signature "$package"; then
|
||
echo " <20> Package is signed"
|
||
else
|
||
echo " <20> Package is not signed"
|
||
fi
|
||
|
||
# Size information
|
||
local package_size
|
||
package_size=$(apt-cache show "$package" 2>/dev/null | grep "^Size:" | cut -d: -f2 | tr -d ' ' || echo "unknown")
|
||
echo " Size: $package_size bytes"
|
||
|
||
echo ""
|
||
}
|
||
|
||
# List advanced package management status
|
||
list_advanced_package_status() {
|
||
log_info "Listing advanced package management status" "apt-layer"
|
||
|
||
echo "=== Advanced Package Management Status ==="
|
||
|
||
# User management status
|
||
echo "User Management:"
|
||
local user_count
|
||
user_count=$(jq '.users | length' "$ADVANCED_PKG_USERS_DIR/users.json" 2>/dev/null || echo "0")
|
||
echo " Active users: $user_count"
|
||
|
||
# Security policy status
|
||
echo ""
|
||
echo "Security Policies:"
|
||
local security_policy="$ADVANCED_PKG_SECURITY_DIR/security-policy.json"
|
||
if [[ -f "$security_policy" ]]; then
|
||
local gpg_check
|
||
gpg_check=$(jq -r '.package_verification.gpg_check' "$security_policy" 2>/dev/null || echo "unknown")
|
||
echo " GPG verification: $gpg_check"
|
||
|
||
local audit_logging
|
||
audit_logging=$(jq -r '.audit_logging.enabled' "$security_policy" 2>/dev/null || echo "unknown")
|
||
echo " Audit logging: $audit_logging"
|
||
else
|
||
echo " Security policies: not configured"
|
||
fi
|
||
|
||
# Dependency cache status
|
||
echo ""
|
||
echo "Dependency Cache:"
|
||
local dep_cache="$ADVANCED_PKG_DEPENDENCIES_DIR/dependency-cache.json"
|
||
if [[ -f "$dep_cache" ]]; then
|
||
local last_updated
|
||
last_updated=$(jq -r '.last_updated' "$dep_cache" 2>/dev/null || echo "unknown")
|
||
echo " Last updated: $last_updated"
|
||
else
|
||
echo " Dependency cache: not initialized"
|
||
fi
|
||
|
||
# Audit log status
|
||
echo ""
|
||
echo "Audit Log:"
|
||
local audit_log="$ADVANCED_PKG_SECURITY_DIR/audit.log"
|
||
if [[ -f "$audit_log" ]]; then
|
||
local log_entries
|
||
log_entries=$(wc -l < "$audit_log" 2>/dev/null || echo "0")
|
||
echo " Total entries: $log_entries"
|
||
|
||
local recent_entries
|
||
recent_entries=$(tail -10 "$audit_log" 2>/dev/null | wc -l || echo "0")
|
||
echo " Recent entries: $recent_entries"
|
||
else
|
||
echo " Audit log: not available"
|
||
fi
|
||
|
||
echo ""
|
||
}
|
||
|
||
# =============================================================================
|
||
# INTEGRATION FUNCTIONS
|
||
# =============================================================================
|
||
|
||
# Initialize advanced package management on script startup
|
||
init_advanced_package_management_on_startup() {
|
||
# Only initialize if not already done
|
||
if [[ ! -d "$ADVANCED_PKG_STATE_DIR" ]]; then
|
||
init_advanced_package_management
|
||
fi
|
||
}
|
||
|
||
# Cleanup advanced package management on script exit
|
||
cleanup_advanced_package_management_on_exit() {
|
||
# Clean up temporary files
|
||
rm -f "$ADVANCED_PKG_CACHE_DIR"/temp-* 2>/dev/null || true
|
||
rm -f "$ADVANCED_PKG_CACHE_DIR"/resolved-* 2>/dev/null || true
|
||
}
|
||
|
||
# Register cleanup function
|
||
trap cleanup_advanced_package_management_on_exit EXIT
|