diff --git a/.github/scripts/update-nginx-checksums.sh b/.github/scripts/update-nginx-checksums.sh index 9b8b8a9..ead1efd 100755 --- a/.github/scripts/update-nginx-checksums.sh +++ b/.github/scripts/update-nginx-checksums.sh @@ -9,18 +9,90 @@ set -euo pipefail -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly BLUE='\033[0;34m' -readonly NC='\033[0m' - -log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } -log_success() { echo -e "${GREEN}[✓]${NC} $1"; } -log_error() { echo -e "${RED}[✗]${NC} $1" >&2; } -log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } - -usage() { +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} + +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} + +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} + +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} + +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} + +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} + +Show-Usage() { cat <<'EOF' Usage: update-nginx-checksums.sh [--apply] @@ -38,12 +110,12 @@ while [[ $# -gt 0 ]]; do shift ;; -h|--help) - usage + Show-Usage exit 0 ;; *) - log_error "Unknown argument: $1" - usage + Write-Log ERROR "Unknown argument: $1" + Show-Usage exit 1 ;; esac @@ -56,36 +128,36 @@ readonly PS_INSTALLER="$REPO_ROOT/nginx/nginx_installer.ps1" cd "$REPO_ROOT" -read_sh_var() { +Get-BashVar() { local key=$1 sed -n "s/^${key}=\"\\([^\"]*\\)\"$/\\1/p" "$BASH_INSTALLER" | head -n1 } -update_bash_var() { +Set-BashVar() { local key=$1 local value=$2 sed -i "s/^${key}=\"[^\"]*\"$/${key}=\"${value}\"/" "$BASH_INSTALLER" } -update_ps_var() { +Set-PsVar() { local key=$1 local value=$2 sed -i "s#^\\(\\\$Script:${key}[[:space:]]*=[[:space:]]*'\\)[^']*'#\\1${value}'#" "$PS_INSTALLER" } -download_and_hash() { +Get-UrlHash() { local url=$1 local file=$2 curl -fsSL "$url" -o "$file" sha256sum "$file" | awk '{print $1}' } -NGINX_VERSION="$(read_sh_var NGINX_VERSION)" -PCRE2_VERSION="$(read_sh_var PCRE2_VERSION)" -ZLIB_VERSION="$(read_sh_var ZLIB_VERSION)" -HEADERS_MORE_VERSION="$(read_sh_var HEADERS_MORE_VERSION)" -ZSTD_MODULE_VERSION="$(read_sh_var ZSTD_MODULE_VERSION)" -ACME_MODULE_VERSION="$(read_sh_var ACME_MODULE_VERSION)" +NGINX_VERSION="$(Get-BashVar NGINX_VERSION)" +PCRE2_VERSION="$(Get-BashVar PCRE2_VERSION)" +ZLIB_VERSION="$(Get-BashVar ZLIB_VERSION)" +HEADERS_MORE_VERSION="$(Get-BashVar HEADERS_MORE_VERSION)" +ZSTD_MODULE_VERSION="$(Get-BashVar ZSTD_MODULE_VERSION)" +ACME_MODULE_VERSION="$(Get-BashVar ACME_MODULE_VERSION)" required_values=( "$NGINX_VERSION" @@ -96,10 +168,10 @@ required_values=( "$ACME_MODULE_VERSION" ) for value in "${required_values[@]}"; do - [[ -n "$value" ]] || { log_error "Failed to read one or more versions from $BASH_INSTALLER"; exit 1; } + [[ -n "$value" ]] || { Write-Log ERROR "Failed to read one or more versions from $BASH_INSTALLER"; exit 1; } done -log_info "Versions to recalculate:" +Write-Log INFO "Versions to recalculate:" echo " NGINX: $NGINX_VERSION" echo " PCRE2: $PCRE2_VERSION" echo " Zlib: $ZLIB_VERSION" @@ -112,28 +184,28 @@ TEMP_DIR=$(mktemp -d) trap 'rm -rf -- "$TEMP_DIR"' EXIT cd "$TEMP_DIR" -log_info "Downloading and hashing release tarballs..." +Write-Log INFO "Downloading and hashing release tarballs..." -NGINX_SHA256="$(download_and_hash "https://github.com/nginx/nginx/releases/download/release-${NGINX_VERSION}/nginx-${NGINX_VERSION}.tar.gz" "nginx.tar.gz")" -log_success "NGINX_SHA256: $NGINX_SHA256" +NGINX_SHA256="$(Get-UrlHash "https://github.com/nginx/nginx/releases/download/release-${NGINX_VERSION}/nginx-${NGINX_VERSION}.tar.gz" "nginx.tar.gz")" +Write-Log SUCCESS "NGINX_SHA256: $NGINX_SHA256" -PCRE2_SHA256="$(download_and_hash "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VERSION}/pcre2-${PCRE2_VERSION}.tar.gz" "pcre2.tar.gz")" -log_success "PCRE2_SHA256: $PCRE2_SHA256" +PCRE2_SHA256="$(Get-UrlHash "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VERSION}/pcre2-${PCRE2_VERSION}.tar.gz" "pcre2.tar.gz")" +Write-Log SUCCESS "PCRE2_SHA256: $PCRE2_SHA256" -ZLIB_SHA256="$(download_and_hash "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" "zlib.tar.gz")" -log_success "ZLIB_SHA256: $ZLIB_SHA256" +ZLIB_SHA256="$(Get-UrlHash "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" "zlib.tar.gz")" +Write-Log SUCCESS "ZLIB_SHA256: $ZLIB_SHA256" -HEADERS_MORE_SHA256="$(download_and_hash "https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v${HEADERS_MORE_VERSION}.tar.gz" "headers-more.tar.gz")" -log_success "HEADERS_MORE_SHA256: $HEADERS_MORE_SHA256" +HEADERS_MORE_SHA256="$(Get-UrlHash "https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v${HEADERS_MORE_VERSION}.tar.gz" "headers-more.tar.gz")" +Write-Log SUCCESS "HEADERS_MORE_SHA256: $HEADERS_MORE_SHA256" -ZSTD_MODULE_SHA256="$(download_and_hash "https://github.com/tokers/zstd-nginx-module/archive/refs/tags/${ZSTD_MODULE_VERSION}.tar.gz" "zstd-module.tar.gz")" -log_success "ZSTD_MODULE_SHA256: $ZSTD_MODULE_SHA256" +ZSTD_MODULE_SHA256="$(Get-UrlHash "https://github.com/tokers/zstd-nginx-module/archive/refs/tags/${ZSTD_MODULE_VERSION}.tar.gz" "zstd-module.tar.gz")" +Write-Log SUCCESS "ZSTD_MODULE_SHA256: $ZSTD_MODULE_SHA256" -ACME_MODULE_SHA256="$(download_and_hash "https://github.com/nginx/nginx-acme/releases/download/v${ACME_MODULE_VERSION}/nginx-acme-${ACME_MODULE_VERSION}.tar.gz" "nginx-acme.tar.gz")" -log_success "ACME_MODULE_SHA256: $ACME_MODULE_SHA256" +ACME_MODULE_SHA256="$(Get-UrlHash "https://github.com/nginx/nginx-acme/releases/download/v${ACME_MODULE_VERSION}/nginx-acme-${ACME_MODULE_VERSION}.tar.gz" "nginx-acme.tar.gz")" +Write-Log SUCCESS "ACME_MODULE_SHA256: $ACME_MODULE_SHA256" echo -log_info "Calculated checksums:" +Write-Log INFO "Calculated checksums:" echo " NGINX_SHA256: $NGINX_SHA256" echo " PCRE2_SHA256: $PCRE2_SHA256" echo " ZLIB_SHA256: $ZLIB_SHA256" @@ -145,27 +217,27 @@ echo if [[ "$APPLY" != true ]]; then read -rp "Apply these checksums to installer files? [y/N] " response if [[ ! "$response" =~ ^[Yy]$ ]]; then - log_info "No changes made" + Write-Log INFO "No changes made" exit 0 fi fi cd "$REPO_ROOT" -update_bash_var NGINX_SHA256 "$NGINX_SHA256" -update_bash_var PCRE2_SHA256 "$PCRE2_SHA256" -update_bash_var ZLIB_SHA256 "$ZLIB_SHA256" -update_bash_var HEADERS_MORE_SHA256 "$HEADERS_MORE_SHA256" -update_bash_var ZSTD_MODULE_SHA256 "$ZSTD_MODULE_SHA256" -update_bash_var ACME_MODULE_SHA256 "$ACME_MODULE_SHA256" - -update_ps_var NGINX_SHA256 "$NGINX_SHA256" -update_ps_var PCRE2_SHA256 "$PCRE2_SHA256" -update_ps_var ZLIB_SHA256 "$ZLIB_SHA256" -update_ps_var HEADERS_MORE_SHA256 "$HEADERS_MORE_SHA256" -update_ps_var ZSTD_MODULE_SHA256 "$ZSTD_MODULE_SHA256" -update_ps_var ACME_MODULE_SHA256 "$ACME_MODULE_SHA256" - -log_success "Updated checksums in:" +Set-BashVar NGINX_SHA256 "$NGINX_SHA256" +Set-BashVar PCRE2_SHA256 "$PCRE2_SHA256" +Set-BashVar ZLIB_SHA256 "$ZLIB_SHA256" +Set-BashVar HEADERS_MORE_SHA256 "$HEADERS_MORE_SHA256" +Set-BashVar ZSTD_MODULE_SHA256 "$ZSTD_MODULE_SHA256" +Set-BashVar ACME_MODULE_SHA256 "$ACME_MODULE_SHA256" + +Set-PsVar NGINX_SHA256 "$NGINX_SHA256" +Set-PsVar PCRE2_SHA256 "$PCRE2_SHA256" +Set-PsVar ZLIB_SHA256 "$ZLIB_SHA256" +Set-PsVar HEADERS_MORE_SHA256 "$HEADERS_MORE_SHA256" +Set-PsVar ZSTD_MODULE_SHA256 "$ZSTD_MODULE_SHA256" +Set-PsVar ACME_MODULE_SHA256 "$ACME_MODULE_SHA256" + +Write-Log SUCCESS "Updated checksums in:" echo " - nginx/nginx_installer.sh" echo " - nginx/nginx_installer.ps1" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bb60e2..4f5c099 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,19 +4,23 @@ Thank you for your interest in contributing to this repository! This guide will ## Automated Dependency Management -This repository uses several automated tools to help keep dependencies up-to-date: +This repository uses Renovate plus a few GitHub Actions workflows to keep dependencies up-to-date: -### 1. Dependabot (`.github/dependabot.yml`) +### 1. Renovate (`renovate.json`) Automatically monitors and creates PRs for: - GitHub Actions updates -- Docker image updates +- The `TLS-tools/testssl.sh` git submodule +- Hardcoded versions in installer scripts (NGINX and its modules, Ansible, kubectl, minikube, Vagrant) via custom regex managers -**Action required:** Review and merge Dependabot PRs after testing. +**Important:** the custom regex managers match exact variable formats such as +`NGINX_VERSION="1.31.1"` (bash) and `$Script:NGINX_VERSION = '1.31.1'` +(PowerShell). Do not reformat these lines or rename these variables/files, or +Renovate will silently stop updating them. -### 2. Dependency Version Checker (`.github/workflows/check-dependencies.yml`) -Runs weekly to check for updates to hardcoded versions in installer scripts. - -**Action required:** When issues are created, follow the update instructions to manually update version numbers and SHA256 checksums. +### 2. NGINX Checksum Updater (`.github/workflows/update-nginx-checksums.yml`) +Runs automatically on `renovate/*` PR branches: when Renovate bumps an NGINX +dependency, this workflow recalculates the SHA256 checksums via +`.github/scripts/update-nginx-checksums.sh` and commits them to the PR. ### 3. Script Validator (`.github/workflows/validate-scripts.yml`) Runs on all PRs and commits to validate: @@ -26,29 +30,84 @@ Runs on all PRs and commits to validate: **Action required:** Fix any validation errors before merging. +## Script Structure Guidelines + +### Shared helpers (bash) + +Every bash script is standalone but carries the same helper functions +(colors, `Write-Log`, `Stop-Script`, `Test-Root`, `Get-PkgMgr`, `Get-OsId`, +`Invoke-Cmd`). When you improve a helper, apply the same change in the other +scripts so they stay aligned. + +### Naming convention + +Function names follow the PowerShell Verb-Noun convention **everywhere**, +including bash: `Write-Log`, `Get-PkgMgr`, `Install-Podman`, `Remove-Nginx`. +Hyphenated function names are bash-only syntax, so scripts must keep the +`#!/usr/bin/env bash` shebang. + +### Distro support (Linux) + +All Linux installers support apt (Debian/Ubuntu), dnf (Fedora/RHEL) and +pacman (Arch). New installers should cover all three; use the shared +`Get-PkgMgr` helper and add a clear `Stop-Script` for unsupported systems. + +### Version Configuration + +Always keep version numbers at the top of scripts in clearly marked sections: + +```bash +# ============================================================================ +# Version Configuration +# ============================================================================ + +NGINX_VERSION="1.31.1" +NGINX_SHA256="9fcaaeb8f22544b09a19a761f3412c4112215422401634bebdd1296a403cc4bc" +``` + +### Error Handling + +Every script starts with: + +```bash +#!/usr/bin/env bash +set -euo pipefail # Exit on error, undefined vars, pipe failures +``` + +### Logging + +Use the shared helper functions instead of raw `echo`: + +```bash +Write-Log INFO "Installing build dependencies" +Write-Log WARN "Cargo not found. Installing rustup..." +Stop-Script "Unsupported package manager." +Invoke-Cmd apt-get install -y podman # logs + aborts on failure +``` + +Set `LOG_FILE` (after the helper functions) to also append plain-text logs +to a file. + ## Updating Installer Scripts ### General Process -1. **Check for updates**: Automated workflows will create issues when updates are available -2. **Update version numbers**: Edit the relevant `*_VERSION` variables in the script -3. **Calculate new checksums**: Download the new version and calculate SHA256 -4. **Update checksums**: Replace old SHA256 values with new ones -5. **Test**: Run the script on a clean system to verify it works -6. **Commit**: Push changes and ensure CI passes -7. **Close issue**: Close the automated dependency issue +Renovate normally handles version bumps. For manual updates: -### Example: Updating NGINX +1. **Update version numbers**: Edit the relevant `*_VERSION` variables in the script (keep the exact line format) +2. **Update checksums**: For NGINX, run `.github/scripts/update-nginx-checksums.sh` +3. **Test**: Run the script on a clean system to verify it works +4. **Commit**: Push changes and ensure CI passes + +### Example: Updating NGINX manually ```bash -# 1. Edit nginx/nginx_installer.sh -# Update these lines: -NGINX_VERSION="1.29.8" # Change version -NGINX_SHA256="new_sha256_here" # Update checksum +# 1. Edit nginx/nginx_installer.sh AND nginx/nginx_installer.ps1 +# Update these lines (same format!): +NGINX_VERSION="1.31.1" -# 2. Calculate new SHA256 (if needed) -wget https://github.com/nginx/nginx/releases/download/release-1.29.8/nginx-1.29.8.tar.gz -sha256sum nginx-1.29.8.tar.gz +# 2. Recalculate all checksums for both installers +.github/scripts/update-nginx-checksums.sh --apply # 3. Test the script sudo ./nginx/nginx_installer.sh install @@ -58,48 +117,43 @@ nginx -v nginx -V # Check modules # 5. Commit changes -git add nginx/nginx_installer.sh -git commit -m "deps: update NGINX to 1.29.8" +git add nginx/ +git commit -m "deps: update NGINX to 1.31.1" git push ``` -### Example: Updating Ansible/Python - -```bash -# 1. Edit ansible/ansible_installer.sh -# Update these variables: -BUILD_PYTHON_VERSION="${BUILD_PYTHON_VERSION:-3.14.3}" - -# 2. Also update pip install line if Ansible version changed -# Look for: pip install ansible==13.4.0 - -# 3. Test installation -sudo ./ansible/ansible_installer.sh +### Adding Renovate coverage for a new script -# 4. Verify -ansible --version +Add a custom regex manager to `renovate.json`: -# 5. Commit -git add ansible/ansible_installer.sh -git commit -m "deps: update Python to 3.14.3 and Ansible to 13.4.0" -git push +```json +{ + "customType": "regex", + "datasourceTemplate": "github-releases", + "depNameTemplate": "owner/repo", + "matchStrings": ["NEWTOOL_VERSION=\"(?[^\"]+)\""], + "managerFilePatterns": ["/^newtool/newtool_installer\\.sh$/"] +} ``` +Validate with `npx --yes --package renovate -- renovate-config-validator renovate.json`. + ## Testing Guidelines ### Before Committing 1. **Syntax check**: Ensure scripts pass syntax validation ```bash bash -n script_name.sh - shellcheck script_name.sh + shellcheck -S warning script_name.sh ``` 2. **Test installation**: Run on a clean system (VM/container recommended) ```bash - # Use a fresh Ubuntu/RHEL VM or container docker run -it --rm ubuntu:latest # Or docker run -it --rm fedora:latest + # Or + docker run -it --rm archlinux:latest ``` 3. **Verify functionality**: After installation, test that the software works @@ -115,71 +169,18 @@ git push - Review any warnings from ShellCheck or PSScriptAnalyzer - Ensure no security scan warnings -## Script Structure Guidelines - -### Version Configuration -Always keep version numbers at the top of scripts in clearly marked sections: - -```bash -# ============================================================================ -# Version Configuration -# ============================================================================ - -NGINX_VERSION="1.29.7" -NGINX_SHA256="673f8fb8c0961c44fbd9410d6161831453609b44063d3f2948253fc2b5692139" -``` - -### Error Handling -Use proper error handling: - -```bash -set -euo pipefail # Exit on error, undefined vars, pipe failures - -# Or -set -e -set -o pipefail -``` - -### Logging -Include informative logging: - -```bash -info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; } -success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; } -error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; exit 1; } -``` - ## Common Tasks ### Adding a New Installer Script 1. Create the script in an appropriate directory 2. Follow existing naming convention: `_installer.sh` -3. Include version configuration section at the top -4. Add error handling and logging -5. Test thoroughly on clean systems -6. Update README.md to mention the new script -7. Consider adding version checks to `.github/workflows/check-dependencies.yml` - -### Adding Dependency Checks for New Scripts - -Edit `.github/workflows/check-dependencies.yml`: - -```yaml -check-newtool-deps: - name: Check NewTool Dependencies - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Check NewTool version - id: newtool - run: | - CURRENT_VERSION=$(grep -oP 'NEWTOOL_VERSION="\K[^"]+' newtool/newtool_installer.sh) - # ... rest of check logic -``` +3. Copy the shared helper functions from an existing installer +4. Support apt, dnf and pacman +5. Include a version configuration section at the top (if versions are pinned) +6. Test thoroughly on clean systems +7. Update README.md (script table + distro matrix) +8. Add a Renovate custom manager for any pinned versions ## Security Considerations diff --git a/README.md b/README.md index 70f5aac..671f860 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,39 @@ pwsh ./_installer.ps1 | `TLS-tools/` | `testssl.sh` (submodule) | Linux | Comprehensive TLS/SSL scanner by Dirk Wetter — pinned at a specific version | | `windows/` | `Enable-WinRM.ps1` | Windows | Configures WinRM for remote management | | `windows/` | `Get-InstalledSoftware.ps1` | Windows | Lists installed software from registry | -| `windows/` | `configure-Windows-VM.ps1` | Windows | Disables unnecessary services for VMs | +| `windows/` | `Optimize-WindowsVM.ps1` | Windows | Disables unnecessary services for VMs | | `windows/` | `Install-VagrantVMware.ps1` | Windows | Installs Vagrant + VMware Workstation | -| `windows/` | `Install Dell-Command_Update.ps1` | Windows | Installs Dell Command Update via winget | -| `windows/` | `Install HPIA.ps1` | Windows | Installs HP Image Assistant via winget | +| `windows/` | `Install-DellCommandUpdate.ps1` | Windows | Installs Dell Command Update via winget | +| `windows/` | `Install-HPImageAssistant.ps1` | Windows | Installs HP Image Assistant via winget | + +## Linux distro support + +All Linux installers target the same three package-manager families: + +| Script | apt (Debian/Ubuntu) | dnf (Fedora/RHEL) | pacman (Arch) | +|--------|:---:|:---:|:---:| +| `ansible_installer.sh` | ✅ | ✅ | ✅ | +| `docker_installer.sh` | ✅ | ✅ | ✅ ¹ | +| `kubernetes_installer.sh` | ✅ | ✅ | ✅ ² | +| `nginx_installer.sh` | ✅ | ✅ | ✅ | +| `openssh_installer.sh` | ✅ | ✅ | ✅ | +| `podman_installer.sh` | ✅ | ✅ | ✅ | +| `terraform_installer.sh` | ✅ | ✅ | ✅ ¹ | + +¹ No vendor repo exists for Arch; installed from the community repos. +² No pkgs.k8s.io repo exists for Arch; kubectl is installed as a checksum-verified binary. + +openSUSE (zypper) is not supported. + +## Conventions + +- All bash scripts are standalone single-file downloads. They share the same + set of helper functions (`Write-Log`, `Stop-Script`, `Get-PkgMgr`, ...), + copied into each script — keep them aligned when changing one. +- Function names follow the PowerShell Verb-Noun convention everywhere — also + in bash (`Write-Log`, `Get-PkgMgr`, `Install-Podman`). Hyphenated function + names are bash-only syntax, so scripts must keep the bash shebang. +- Every script starts with `#!/usr/bin/env bash` and `set -euo pipefail`. ## Automation diff --git a/ansible/ansible_installer.sh b/ansible/ansible_installer.sh index cd0d27d..f43c05d 100755 --- a/ansible/ansible_installer.sh +++ b/ansible/ansible_installer.sh @@ -1,120 +1,180 @@ -#!/bin/bash +#!/usr/bin/env bash # # Ansible Installer Script # # Installs Ansible (with its bundled ansible-core dependency) in a Python virtual environment. # Note: 'ansible' is the community package; 'ansible-core' is the engine it ships with. -# Supports Debian/Ubuntu (apt) and RHEL/Fedora (dnf). +# Supports Debian/Ubuntu (apt), RHEL/Fedora (dnf) and Arch Linux (pacman). # Run as root. # -set -e -set -o pipefail +set -euo pipefail + +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} -# === Colors === -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} -# === Settings === -VENV_DIR="${VENV_DIR:-/opt/ansible-env}" +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} -LOG_FILE="/tmp/ansible_install_$(date +%Y%m%d_%H%M%S).log" +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} -# === Logging === -info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; } -success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; } -error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; exit 1; } +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} -run() { - info "Executing: $*" - "$@" >> "$LOG_FILE" 2>&1 || error "Command failed: '$*'. Check log: $LOG_FILE" +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi } +# === Settings === +VENV_DIR="${VENV_DIR:-/opt/ansible-env}" +LOG_FILE="/tmp/ansible_install_$(date +%Y%m%d_%H%M%S).log" + # === Root check === -[ "$EUID" -ne 0 ] && error "Run as root (sudo)." +Test-Root # === Package manager detection === -if command -v apt &>/dev/null; then - PKG_MANAGER="apt" -elif command -v dnf &>/dev/null; then - PKG_MANAGER="dnf" -else - error "Unsupported distro. Only Debian/Ubuntu (apt) and RHEL/Fedora (dnf) are supported." -fi -info "Package manager: ${PKG_MANAGER}" +PKG_MANAGER=$(Get-PkgMgr) +[[ "$PKG_MANAGER" != "unknown" ]] || Stop-Script "Unsupported distro. Only Debian/Ubuntu (apt), RHEL/Fedora (dnf) and Arch Linux (pacman) are supported." +Write-Log INFO "Package manager: ${PKG_MANAGER}" # === Update system packages === -info "Updating system packages..." +Write-Log INFO "Updating system packages..." case $PKG_MANAGER in - apt) run env DEBIAN_FRONTEND=noninteractive apt update -y ;; - dnf) run dnf upgrade -y ;; + apt) Invoke-Cmd env DEBIAN_FRONTEND=noninteractive apt-get update -y ;; + dnf) Invoke-Cmd dnf upgrade -y ;; + pacman) Invoke-Cmd pacman -Syu --noconfirm ;; esac # === Install Python and pip === -info "Installing Python and pip..." +Write-Log INFO "Installing Python and pip..." case $PKG_MANAGER in - apt) run env DEBIAN_FRONTEND=noninteractive apt install -y python3 python3-venv python3-pip ;; - dnf) run dnf install -y python3 python3-pip ;; + apt) Invoke-Cmd env DEBIAN_FRONTEND=noninteractive apt-get install -y python3 python3-venv python3-pip ;; + dnf) Invoke-Cmd dnf install -y python3 python3-pip ;; + pacman) Invoke-Cmd pacman -S --noconfirm --needed python python-pip ;; esac # === Find Python === PY_CMD=$(command -v python3 || true) -[ -z "$PY_CMD" ] && error "python3 not found after install." -info "Using Python: ${PY_CMD} ($("$PY_CMD" --version 2>&1))" +[[ -z "$PY_CMD" ]] && Stop-Script "python3 not found after install." +Write-Log INFO "Using Python: ${PY_CMD} ($("$PY_CMD" --version 2>&1))" # === Create virtual environment === -if [ -d "$VENV_DIR" ]; then - info "Virtual environment already exists at ${VENV_DIR}." +if [[ -d "$VENV_DIR" ]]; then + Write-Log INFO "Virtual environment already exists at ${VENV_DIR}." else - info "Creating virtual environment at ${VENV_DIR}..." + Write-Log INFO "Creating virtual environment at ${VENV_DIR}..." mkdir -p "$(dirname "$VENV_DIR")" - run "$PY_CMD" -m venv "$VENV_DIR" + Invoke-Cmd "$PY_CMD" -m venv "$VENV_DIR" fi # === Install Ansible === -info "Installing Ansible (community package, bundles ansible-core)..." +Write-Log INFO "Installing Ansible (community package, bundles ansible-core)..." ( source "$VENV_DIR/bin/activate" - command -v pip &>/dev/null || error "pip not found in venv." - run pip install --upgrade pip setuptools wheel - run pip install ansible==14.0.0 -) || error "Failed during venv operations." + command -v pip &>/dev/null || Stop-Script "pip not found in venv." + Invoke-Cmd pip install --upgrade pip setuptools wheel + Invoke-Cmd pip install ansible==14.0.0 +) || Stop-Script "Failed during venv operations." # === Global symlinks === -info "Creating symlinks in /usr/local/bin..." +Write-Log INFO "Creating symlinks in /usr/local/bin..." for tool in ansible ansible-playbook ansible-galaxy ansible-doc ansible-config \ ansible-console ansible-inventory ansible-vault; do - if [ -f "$VENV_DIR/bin/$tool" ]; then + if [[ -f "$VENV_DIR/bin/$tool" ]]; then ln -sf "$VENV_DIR/bin/$tool" "/usr/local/bin/$tool" else - warn "${tool} not found in venv, skipping symlink." + Write-Log WARN "${tool} not found in venv, skipping symlink." fi done # === Global ansible.cfg === -info "Writing /etc/ansible/ansible.cfg..." +Write-Log INFO "Writing /etc/ansible/ansible.cfg..." mkdir -p /etc/ansible VENV_SITE_PACKAGES=$("$VENV_DIR/bin/python" -c "import site; print(site.getsitepackages()[0])") -[ -z "$VENV_SITE_PACKAGES" ] && error "Could not determine venv site-packages." +[[ -z "$VENV_SITE_PACKAGES" ]] && Stop-Script "Could not determine venv site-packages." cat > /etc/ansible/ansible.cfg <&1) -ANSIBLE_PKG_VER=$("$VENV_DIR/bin/pip" show ansible 2>/dev/null | grep '^Version:' | cut -d' ' -f2) -[ -n "$ANSIBLE_PKG_VER" ] && ANSIBLE_PKG_VER="ansible ${ANSIBLE_PKG_VER}" || ANSIBLE_PKG_VER="N/A" -ANSIBLE_CORE_VER=$("$VENV_DIR/bin/pip" show ansible-core 2>/dev/null | grep '^Version:' | cut -d' ' -f2) -[ -n "$ANSIBLE_CORE_VER" ] && ANSIBLE_CORE_VER="ansible-core ${ANSIBLE_CORE_VER}" || ANSIBLE_CORE_VER="N/A" +ANSIBLE_PKG_VER=$("$VENV_DIR/bin/pip" show ansible 2>/dev/null | grep '^Version:' | cut -d' ' -f2 || true) +[[ -n "$ANSIBLE_PKG_VER" ]] && ANSIBLE_PKG_VER="ansible ${ANSIBLE_PKG_VER}" || ANSIBLE_PKG_VER="N/A" +ANSIBLE_CORE_VER=$("$VENV_DIR/bin/pip" show ansible-core 2>/dev/null | grep '^Version:' | cut -d' ' -f2 || true) +[[ -n "$ANSIBLE_CORE_VER" ]] && ANSIBLE_CORE_VER="ansible-core ${ANSIBLE_CORE_VER}" || ANSIBLE_CORE_VER="N/A" echo -e "\n${GREEN}==============================================================${NC}" -success "Ansible installation complete!" +Write-Log SUCCESS "Ansible installation complete!" echo -e "${GREEN}==============================================================${NC}\n" echo -e "${BLUE}Python:${NC} ${GREEN}${PY_VER}${NC}" echo -e "${BLUE}Ansible:${NC} ${GREEN}${ANSIBLE_PKG_VER}${NC} (community package)" diff --git a/docker/docker_installer.sh b/docker/docker_installer.sh index faf43a6..e903e50 100644 --- a/docker/docker_installer.sh +++ b/docker/docker_installer.sh @@ -1,128 +1,205 @@ -#!/bin/bash +#!/usr/bin/env bash # # Docker Installer Script # -# Installs Docker CE on Debian/Ubuntu (apt) and RHEL/Fedora/CentOS (dnf). -# Falls back to get.docker.com on failure. +# Installs Docker CE on Debian/Ubuntu (apt) and RHEL/Fedora/CentOS (dnf), +# and Docker from the community repos on Arch Linux (pacman). +# Falls back to get.docker.com on failure (disable with DOCKER_FALLBACK=0). # Run as root. # -set -e -set -o pipefail +set -euo pipefail + +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} -# === Colors === -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} -LOG_FILE="/tmp/docker_install_$(date +%Y%m%d_%H%M%S).log" +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} -# === Logging === -info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; } -success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; } -error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; exit 1; } +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} -run() { - info "Executing: $*" - "$@" >> "$LOG_FILE" 2>&1 || error "Command failed: '$*'. Check log: $LOG_FILE" +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi } +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} + +# === Settings === +LOG_FILE="/tmp/docker_install_$(date +%Y%m%d_%H%M%S).log" +CODENAME="" + # === Fallback === -fallback_installer() { - warn "Installation failed. Falling back to get.docker.com..." - curl -fsSL https://get.docker.com | bash || error "Fallback installer also failed." +Invoke-FallbackInstaller() { + if [[ "${DOCKER_FALLBACK:-1}" != "1" ]]; then + Stop-Script "Installation failed and fallback is disabled (DOCKER_FALLBACK=0)." + fi + Write-Log WARN "Installation failed. Falling back to get.docker.com..." + Write-Log WARN "This runs an unverified convenience script from get.docker.com." + curl -fsSL https://get.docker.com | bash || Stop-Script "Fallback installer also failed." exit 0 } -trap 'fallback_installer' ERR +trap 'Invoke-FallbackInstaller' ERR # === Root check === -[ "$EUID" -ne 0 ] && error "Run as root (sudo)." +Test-Root # === Docker already installed? === if command -v docker &>/dev/null; then - warn "Docker is already installed. Skipping." + Write-Log WARN "Docker is already installed. Skipping." docker --version exit 0 fi # === Detect distro === -[ -r /etc/os-release ] || error "Cannot read /etc/os-release. Unsupported system." -. /etc/os-release -DISTRO="${ID,,}" -info "Detected distribution: ${DISTRO}" +DISTRO=$(Get-OsId) +[[ "$DISTRO" != "unknown" ]] || Stop-Script "Cannot read /etc/os-release. Unsupported system." +Write-Log INFO "Detected distribution: ${DISTRO}" # === Install === case "$DISTRO" in ubuntu|debian) - info "APT-based system: ${DISTRO}" + Write-Log INFO "APT-based system: ${DISTRO}" if command -v lsb_release &>/dev/null; then CODENAME=$(lsb_release -cs) else CODENAME=$(grep VERSION_CODENAME /etc/os-release | cut -d'=' -f2) fi - info "Codename: ${CODENAME}" + Write-Log INFO "Codename: ${CODENAME}" - # Verwijder eventuele kapotte/oude repo entry en GPG key van eerdere pogingen - info "Cleaning up stale Docker repo entries..." + # Remove any broken/stale repo entry and GPG key from earlier attempts + Write-Log INFO "Cleaning up stale Docker repo entries..." rm -f /etc/apt/sources.list.d/docker.list rm -f /etc/apt/keyrings/docker.gpg - run apt-get update -y - run apt-get install -y ca-certificates curl gnupg + Invoke-Cmd apt-get update -y + Invoke-Cmd apt-get install -y ca-certificates curl gnupg mkdir -p /etc/apt/keyrings curl -fsSL "https://download.docker.com/linux/${DISTRO}/gpg" | \ gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>>"$LOG_FILE" || \ - error "Failed to add Docker GPG key." + Stop-Script "Failed to add Docker GPG key." chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/${DISTRO} ${CODENAME} stable" \ > /etc/apt/sources.list.d/docker.list - run apt-get update -y - run apt-get install -y docker-ce docker-ce-cli containerd.io \ + Invoke-Cmd apt-get update -y + Invoke-Cmd apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin ;; fedora|rhel|centos) - info "DNF-based system: ${DISTRO}" - command -v dnf &>/dev/null || error "dnf not found. Only dnf is supported for ${DISTRO}." + Write-Log INFO "DNF-based system: ${DISTRO}" + command -v dnf &>/dev/null || Stop-Script "dnf not found. Only dnf is supported for ${DISTRO}." - run dnf install -y dnf-plugins-core + Invoke-Cmd dnf install -y dnf-plugins-core rm -f /etc/yum.repos.d/docker-ce.repo - run dnf config-manager addrepo \ + Invoke-Cmd dnf config-manager addrepo \ --from-repofile="https://download.docker.com/linux/${DISTRO}/docker-ce.repo" - run dnf install -y docker-ce docker-ce-cli containerd.io \ + Invoke-Cmd dnf install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin ;; + arch) + Write-Log INFO "Pacman-based system: ${DISTRO}" + # Docker has no vendor repo for Arch; these are the community packages. + Invoke-Cmd pacman -Syu --noconfirm docker docker-buildx docker-compose + ;; + *) - error "Unsupported distribution: ${DISTRO}. Only Debian/Ubuntu and RHEL/Fedora/CentOS are supported." + Stop-Script "Unsupported distribution: ${DISTRO}. Only Debian/Ubuntu (apt), RHEL/Fedora/CentOS (dnf) and Arch Linux (pacman) are supported." ;; esac # === Enable & start Docker === -info "Enabling and starting Docker service..." -run systemctl enable docker -run systemctl start docker +Write-Log INFO "Enabling and starting Docker service..." +Invoke-Cmd systemctl enable docker +Invoke-Cmd systemctl start docker # === Summary === DOCKER_VER=$(docker --version 2>/dev/null) || DOCKER_VER="N/A" echo -e "\n${GREEN}==============================================================${NC}" -success "Docker installation complete!" +Write-Log SUCCESS "Docker installation complete!" echo -e "${GREEN}==============================================================${NC}\n" echo -e "${BLUE}Docker:${NC} ${GREEN}${DOCKER_VER}${NC}" -echo -e "${BLUE}Distro:${NC} ${GREEN}${DISTRO} (${CODENAME})${NC}" +echo -e "${BLUE}Distro:${NC} ${GREEN}${DISTRO} (${CODENAME:-n/a})${NC}" echo -e "${BLUE}Log:${NC} ${GREEN}${LOG_FILE}${NC}" echo "" echo -e "${BLUE}Rootless:${NC} dockerd-rootless-setuptool.sh install" echo -e "${BLUE}Uninstall:${NC} sudo apt-get remove docker-ce docker-ce-cli containerd.io # apt" echo -e " sudo dnf remove docker-ce docker-ce-cli containerd.io # dnf" +echo -e " sudo pacman -Rns docker docker-buildx docker-compose # pacman" diff --git a/kubernetes/kubernetes_installer.sh b/kubernetes/kubernetes_installer.sh index c6251e2..68ac9be 100644 --- a/kubernetes/kubernetes_installer.sh +++ b/kubernetes/kubernetes_installer.sh @@ -1,76 +1,146 @@ -#!/bin/bash +#!/usr/bin/env bash # # Kubernetes (kubectl) Installer Script # -# Installs kubectl on Debian/Ubuntu (apt) and RHEL/Fedora/CentOS (dnf). +# Installs kubectl on Debian/Ubuntu (apt), RHEL/Fedora/CentOS (dnf) and +# Arch Linux (pacman, via a checksum-verified binary download). # Optionally installs minikube. # Run as root. # -set -e -set -o pipefail +set -euo pipefail + +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} -# === Colors === -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} -LOG_FILE="/tmp/kubernetes_install_$(date +%Y%m%d_%H%M%S).log" +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} -# === Logging === -info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; } -success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; } -error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; exit 1; } +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} -run() { - info "Executing: $*" - "$@" >> "$LOG_FILE" 2>&1 || error "Command failed: '$*'. Check log: $LOG_FILE" +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi } -# === Root check === -[ "$EUID" -ne 0 ] && error "Run as root (sudo)." +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} # === Settings === +LOG_FILE="/tmp/kubernetes_install_$(date +%Y%m%d_%H%M%S).log" + K8S_VERSION="${K8S_VERSION:-v1.36.2}" MINIKUBE_VERSION="${MINIKUBE_VERSION:-v1.38.1}" -APT_BASE_URL="https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/" -RPM_BASE_URL="https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/rpm/" +# pkgs.k8s.io repos are per minor version (v1.36), not per patch release. +K8S_CHANNEL="${K8S_VERSION%.*}" +APT_BASE_URL="https://pkgs.k8s.io/core:/stable:/${K8S_CHANNEL}/deb/" +RPM_BASE_URL="https://pkgs.k8s.io/core:/stable:/${K8S_CHANNEL}/rpm/" + +# === Root check === +Test-Root # === kubectl already installed? === if command -v kubectl &>/dev/null; then - warn "kubectl is already installed: $(kubectl version --client --short 2>/dev/null || kubectl version --client)" + Write-Log WARN "kubectl is already installed: $(kubectl version --client --short 2>/dev/null || kubectl version --client)" exit 0 fi -info "Installing kubectl ${K8S_VERSION}..." +Write-Log INFO "Installing kubectl ${K8S_VERSION} (channel ${K8S_CHANNEL})..." # === Install kubectl === -if command -v apt-get &>/dev/null; then - info "APT-based system detected." +PKG_MANAGER=$(Get-PkgMgr) +case $PKG_MANAGER in + apt) + Write-Log INFO "APT-based system detected." - run apt-get update -y - run apt-get install -y apt-transport-https ca-certificates curl gnupg + Invoke-Cmd apt-get update -y + Invoke-Cmd apt-get install -y apt-transport-https ca-certificates curl gnupg - mkdir -p -m 755 /etc/apt/keyrings - curl -fsSL "${APT_BASE_URL}Release.key" | \ - gpg --dearmour -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg 2>>"$LOG_FILE" || \ - error "Failed to download Kubernetes signing key." - chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg + install -d -m 755 /etc/apt/keyrings + curl -fsSL "${APT_BASE_URL}Release.key" | \ + gpg --dearmour -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg 2>>"$LOG_FILE" || \ + Stop-Script "Failed to download Kubernetes signing key." + chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg - echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] ${APT_BASE_URL} /" \ - > /etc/apt/sources.list.d/kubernetes.list - chmod 644 /etc/apt/sources.list.d/kubernetes.list + echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] ${APT_BASE_URL} /" \ + > /etc/apt/sources.list.d/kubernetes.list + chmod 644 /etc/apt/sources.list.d/kubernetes.list - run apt-get update -y - run apt-get install -y kubectl + Invoke-Cmd apt-get update -y + Invoke-Cmd apt-get install -y kubectl + ;; -elif command -v dnf &>/dev/null; then - info "DNF-based system detected." + dnf) + Write-Log INFO "DNF-based system detected." - cat > /etc/yum.repos.d/kubernetes.repo < /etc/yum.repos.d/kubernetes.repo <>"$LOG_FILE" || Stop-Script "Failed to download kubectl." + curl -fSL "${KUBECTL_URL}.sha256" -o kubectl.sha256 2>>"$LOG_FILE" || Stop-Script "Failed to download kubectl checksum." + echo "$(cat kubectl.sha256) kubectl" | sha256sum -c - >>"$LOG_FILE" 2>&1 || Stop-Script "kubectl checksum verification failed." + install kubectl /usr/local/bin/kubectl + rm -f kubectl kubectl.sha256 + ;; + + *) + Stop-Script "Unsupported system. Only Debian/Ubuntu (apt), RHEL/Fedora/CentOS (dnf) and Arch Linux (pacman) are supported." + ;; +esac -success "kubectl installed: $(kubectl version --client --short 2>/dev/null || kubectl version --client)" +Write-Log SUCCESS "kubectl installed: $(kubectl version --client --short 2>/dev/null || kubectl version --client)" # === Minikube (optional) === echo "" read -rp "Install minikube as well? (y/n): " install_minikube if [[ "$install_minikube" == "y" ]]; then - info "Installing minikube..." + Write-Log INFO "Installing minikube..." ARCH=$(uname -m) case "$ARCH" in x86_64) MINIKUBE_BIN="minikube-linux-amd64" ;; aarch64) MINIKUBE_BIN="minikube-linux-arm64" ;; - *) error "Unsupported architecture for minikube: ${ARCH}" ;; + *) Stop-Script "Unsupported architecture for minikube: ${ARCH}" ;; esac MINIKUBE_URL="https://github.com/kubernetes/minikube/releases/download/${MINIKUBE_VERSION}/${MINIKUBE_BIN}" - curl -fSL "${MINIKUBE_URL}" -o "${MINIKUBE_BIN}" 2>>"$LOG_FILE" || error "Failed to download minikube." - curl -fSL "${MINIKUBE_URL}.sha256" -o "${MINIKUBE_BIN}.sha256" 2>>"$LOG_FILE" || error "Failed to download minikube checksum." - echo "$(cat "${MINIKUBE_BIN}.sha256") ${MINIKUBE_BIN}" | sha256sum -c - >>"$LOG_FILE" 2>&1 || error "Minikube checksum verification failed." + curl -fSL "${MINIKUBE_URL}" -o "${MINIKUBE_BIN}" 2>>"$LOG_FILE" || Stop-Script "Failed to download minikube." + curl -fSL "${MINIKUBE_URL}.sha256" -o "${MINIKUBE_BIN}.sha256" 2>>"$LOG_FILE" || Stop-Script "Failed to download minikube checksum." + echo "$(cat "${MINIKUBE_BIN}.sha256") ${MINIKUBE_BIN}" | sha256sum -c - >>"$LOG_FILE" 2>&1 || Stop-Script "Minikube checksum verification failed." install "${MINIKUBE_BIN}" /usr/local/bin/minikube rm -f "${MINIKUBE_BIN}" "${MINIKUBE_BIN}.sha256" - success "minikube installed: $(minikube version --short 2>/dev/null)" + Write-Log SUCCESS "minikube installed: $(minikube version --short 2>/dev/null)" fi # === Summary === KUBECTL_VER=$(kubectl version --client --short 2>/dev/null || kubectl version --client) echo -e "\n${GREEN}==============================================================${NC}" -success "Kubernetes installation complete!" +Write-Log SUCCESS "Kubernetes installation complete!" echo -e "${GREEN}==============================================================${NC}\n" echo -e "${BLUE}kubectl:${NC} ${GREEN}${KUBECTL_VER}${NC}" echo -e "${BLUE}Log:${NC} ${GREEN}${LOG_FILE}${NC}" diff --git a/nginx/nginx_installer.sh b/nginx/nginx_installer.sh index d82d513..c58d8af 100755 --- a/nginx/nginx_installer.sh +++ b/nginx/nginx_installer.sh @@ -15,11 +15,88 @@ set -euo pipefail # # ============================================================================ -# Early syntax check -if ! bash -n "$0" >/dev/null 2>&1; then - echo "ERROR: Syntax check failed for $0" >&2 +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} + +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" exit 1 -fi +} + +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} + +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} + +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} + +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} # ============================================================================ # Version Configuration @@ -75,24 +152,11 @@ ACME_MODULE_URL="https://github.com/nginx/nginx-acme/releases/download/v${ACME_M # Initialize logging mkdir -p "$(dirname "$LOG_FILE")" "$BUILD_DIR" -exec > >(tee -a "$LOG_FILE") -exec 2>&1 # ============================================================================ # Helper Functions # ============================================================================ -Write-Log() { - local level=$1 - local msg=$2 - echo "[$level] $msg" >&2 -} - -Stop-Script() { - Write-Log ERROR "$1" - exit 1 -} - Test-Hash() { local file=$1 local expected=$2 @@ -116,16 +180,29 @@ Get-File() { Test-Hash "$file" "$sha" } -Detect-PkgMgr() { - if command -v apt-get >/dev/null 2>&1; then - echo "apt" - elif command -v dnf >/dev/null 2>&1; then - echo "dnf" - elif command -v pacman >/dev/null 2>&1; then - echo "pacman" - else - echo "unknown" - fi +# Downloads rustup-init, verifies its published SHA256, then installs the +# Rust toolchain. Replaces the old unverified `curl | sh` pattern. +Install-Rustup() { + Write-Log INFO "Installing Rust toolchain via rustup-init" + local rustup_arch + rustup_arch="$(uname -m)-unknown-linux-gnu" + local rustup_url="https://static.rust-lang.org/rustup/dist/${rustup_arch}/rustup-init" + local tmp_dir + tmp_dir=$(mktemp -d) + + curl -fsSL "$rustup_url" -o "$tmp_dir/rustup-init" || Stop-Script "Failed to download rustup-init" + curl -fsSL "${rustup_url}.sha256" -o "$tmp_dir/rustup-init.sha256" || Stop-Script "Failed to download rustup-init checksum" + + local expected actual + expected=$(cut -d' ' -f1 "$tmp_dir/rustup-init.sha256") + actual=$(sha256sum "$tmp_dir/rustup-init" | awk '{print $1}') + [[ -n "$expected" && "$actual" == "$expected" ]] || Stop-Script "rustup-init checksum verification failed" + + chmod +x "$tmp_dir/rustup-init" + "$tmp_dir/rustup-init" -y >/dev/null 2>&1 || Stop-Script "rustup installation failed" + rm -rf "$tmp_dir" + # shellcheck disable=SC1091 + source "$HOME/.cargo/env" } # ============================================================================ @@ -133,14 +210,14 @@ Detect-PkgMgr() { # ============================================================================ Install-Dependencies() { - [[ $EUID -eq 0 ]] || Stop-Script "Run as root" + Test-Root command -v curl >/dev/null 2>&1 || Stop-Script "curl required" - + Write-Log INFO "Installing build dependencies" - + local mgr - mgr=$(Detect-PkgMgr) - + mgr=$(Get-PkgMgr) + case $mgr in apt) export DEBIAN_FRONTEND=noninteractive @@ -163,20 +240,19 @@ Install-Dependencies() { # Verify cargo availability if ! command -v cargo >/dev/null 2>&1; then Write-Log WARN "Cargo not found. Installing rustup..." - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - source "$HOME/.cargo/env" + Install-Rustup fi Write-Log INFO "Dependencies installed" } Update-SystemPackages() { - [[ $EUID -eq 0 ]] || Stop-Script "Run as root" + Test-Root Write-Log INFO "Updating system packages" local mgr - mgr=$(Detect-PkgMgr) + mgr=$(Get-PkgMgr) case $mgr in apt) @@ -309,6 +385,7 @@ Build-Nginx() { --add-dynamic-module="$BUILD_DIR/headers-more" \ --add-dynamic-module="$BUILD_DIR/zstd-module" \ 2>&1); then + printf '%s\n' "$output" >> "$LOG_FILE" Write-Log ERROR "Configure output: $(echo "$output" | tail -20)" Stop-Script "Configure failed" fi @@ -320,6 +397,7 @@ Build-Nginx() { fi if ! output=$(make -j"$(nproc)" 2>&1); then + printf '%s\n' "$output" >> "$LOG_FILE" Write-Log ERROR "Make output: $(echo "$output" | tail -20)" Stop-Script "Build failed" fi @@ -338,12 +416,12 @@ Build-Nginx() { # Verify Rust toolchain if ! command -v rustc >/dev/null 2>&1; then Write-Log WARN "rustc not found, installing rustup" - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - source "$HOME/.cargo/env" + Install-Rustup fi local cargo_output if ! cargo_output=$(cargo build --release 2>&1); then + printf '%s\n' "$cargo_output" >> "$LOG_FILE" Write-Log ERROR "ACME build failed: $(echo "$cargo_output" | tail -20)" Stop-Script "ACME module build failed" fi @@ -559,6 +637,7 @@ Install-Nginx() { cd "$BUILD_DIR/nginx" || Stop-Script "Cannot cd to $BUILD_DIR/nginx" local output if ! output=$(make install 2>&1); then + printf '%s\n' "$output" >> "$LOG_FILE" Write-Log ERROR "Install output: $(echo "$output" | tail -10)" Stop-Script "Nginx install failed" fi diff --git a/openssh/openssh_installer.sh b/openssh/openssh_installer.sh index 39a1f31..8a38a41 100644 --- a/openssh/openssh_installer.sh +++ b/openssh/openssh_installer.sh @@ -22,48 +22,119 @@ set -euo pipefail -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly BLUE='\033[0;34m' -readonly PURPLE='\033[0;35m' -readonly NC='\033[0m' -readonly BOLD='\033[1m' - -readonly BACKUP_DIR="/root/ssh-backup-$(date +%Y%m%d-%H%M%S)" +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} + +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} + +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} + +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} + +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} + +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} + +BACKUP_DIR="/root/ssh-backup-$(date +%Y%m%d-%H%M%S)" +readonly BACKUP_DIR readonly CONFIG_FILE="/etc/ssh/sshd_config" readonly ORIGINAL_CONFIG="${CONFIG_FILE}.original" readonly LOG_DIR="/tmp/openssh-logs-$$" -detect_ssh_service() { +# The systemd unit is "ssh" on Debian/Ubuntu and "sshd" on RHEL/Fedora/Arch. +# Prefer the installed unit; fall back to the package manager convention +# (the unit only exists once openssh-server is installed). +Get-SshService() { if systemctl list-unit-files | grep -q "^ssh\.service"; then echo "ssh" elif systemctl list-unit-files | grep -q "^sshd\.service"; then echo "sshd" + elif [[ $(Get-PkgMgr) == "apt" ]]; then + echo "ssh" else - command -v apt-get &>/dev/null && echo "ssh" || echo "sshd" + echo "sshd" fi } -readonly SSH_SERVICE=$(detect_ssh_service) +SSH_SERVICE=$(Get-SshService) mkdir -p "$LOG_DIR" -log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } -log_success() { echo -e "${GREEN}[✓]${NC} $1"; } -log_error() { echo -e "${RED}[✗]${NC} $1" >&2; } -log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } -log_step() { echo -e "${PURPLE}[→]${NC} ${BOLD}$1${NC}"; } - -cleanup() { +Remove-TempDir() { [ -n "$LOG_DIR" ] && [ -d "$LOG_DIR" ] && rm -rf "$LOG_DIR" } -trap cleanup EXIT INT TERM +trap Remove-TempDir EXIT INT TERM -check_root() { - [ "$EUID" -eq 0 ] || { log_error "Run as root: sudo $0"; exit 1; } -} - -print_header() { +Show-Header() { echo echo -e "${BOLD}OpenSSH Hardened Configuration Installer${NC}" echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @@ -72,43 +143,47 @@ print_header() { echo } -install_openssh() { - log_step "Installing OpenSSH server" - if command -v apt-get &>/dev/null; then - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq &>"$LOG_DIR/apt-update.log" - apt-get install -y openssh-server &>"$LOG_DIR/apt-install.log" - elif command -v dnf &>/dev/null; then - dnf install -y openssh-server &>"$LOG_DIR/dnf-install.log" - elif command -v yum &>/dev/null; then - yum install -y openssh-server &>"$LOG_DIR/yum-install.log" - else - log_error "Unsupported package manager (requires apt, dnf, or yum)" - exit 1 - fi - log_success "OpenSSH server installed" +Install-OpenSsh() { + Write-Log STEP "Installing OpenSSH server" + case $(Get-PkgMgr) in + apt) + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq &>"$LOG_DIR/apt-update.log" + apt-get install -y openssh-server &>"$LOG_DIR/apt-install.log" + ;; + dnf) + dnf install -y openssh-server &>"$LOG_DIR/dnf-install.log" + ;; + pacman) + pacman -Sy --noconfirm openssh &>"$LOG_DIR/pacman-install.log" + ;; + *) + Stop-Script "Unsupported package manager (requires apt, dnf, or pacman)" + ;; + esac + Write-Log SUCCESS "OpenSSH server installed" } -backup_config() { - log_step "Backing up existing configuration" +Backup-SshConfig() { + Write-Log STEP "Backing up existing configuration" mkdir -p "$BACKUP_DIR" [ -d "/etc/ssh" ] && cp -a /etc/ssh "$BACKUP_DIR/" [ -f "$CONFIG_FILE" ] && [ ! -f "$ORIGINAL_CONFIG" ] && cp "$CONFIG_FILE" "$ORIGINAL_CONFIG" systemctl is-active "$SSH_SERVICE" &>/dev/null \ && echo "active" > "$BACKUP_DIR/service_status.txt" \ || echo "inactive" > "$BACKUP_DIR/service_status.txt" - log_success "Backup saved to $BACKUP_DIR" + Write-Log SUCCESS "Backup saved to $BACKUP_DIR" } -generate_host_keys() { - log_step "Generating Ed25519 host key" +New-HostKeys() { + Write-Log STEP "Generating Ed25519 host key" # Ed25519 — the only host key we need if [ ! -f "/etc/ssh/ssh_host_ed25519_key" ]; then ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N '' -q - log_info "Generated Ed25519 host key" + Write-Log INFO "Generated Ed25519 host key" else - log_info "Ed25519 host key already exists" + Write-Log INFO "Ed25519 host key already exists" fi # Remove weak legacy keys (RSA, ECDSA, DSA) @@ -116,17 +191,17 @@ generate_host_keys() { if [ -f "/etc/ssh/ssh_host_${key_type}_key" ]; then rm -f "/etc/ssh/ssh_host_${key_type}_key" \ "/etc/ssh/ssh_host_${key_type}_key.pub" - log_info "Removed legacy $key_type host key" + Write-Log INFO "Removed legacy $key_type host key" fi done chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*_key.pub - log_success "Host keys configured (Ed25519 only)" + Write-Log SUCCESS "Host keys configured (Ed25519 only)" } -configure_ssh() { - log_step "Writing hardened SSH configuration" +Set-SshConfig() { + Write-Log STEP "Writing hardened SSH configuration" # Write new configuration to a temporary file first, so we can validate it local tmp_config @@ -279,7 +354,7 @@ EOF # Validate the new config before replacing the live one local validation_output if ! validation_output=$(sshd -t -f "$tmp_config" 2>&1); then - log_error "New configuration failed validation; original config left intact" + Write-Log ERROR "New configuration failed validation; original config left intact" echo "$validation_output" >&2 rm -f "$tmp_config" return 1 @@ -291,36 +366,45 @@ EOF mkdir -p /run/sshd chmod 755 /run/sshd - log_success "SSH configuration written" + Write-Log SUCCESS "SSH configuration written" } -configure_firewall() { - log_step "Configuring firewall" - if command -v firewall-cmd &>/dev/null; then +Set-Firewall() { + Write-Log STEP "Configuring firewall" + # Check which firewall is actually ACTIVE, not merely installed: a Debian + # box can have firewalld installed while ufw is the one doing the work. + if systemctl is-active --quiet firewalld 2>/dev/null && command -v firewall-cmd &>/dev/null; then + firewall-cmd --permanent --add-service=ssh &>/dev/null || true + firewall-cmd --reload &>/dev/null || true + Write-Log INFO "firewalld configured for SSH" + elif command -v ufw &>/dev/null && ufw status 2>/dev/null | grep -q "Status: active"; then + ufw allow ssh &>/dev/null || true + Write-Log INFO "ufw configured for SSH" + elif command -v firewall-cmd &>/dev/null; then firewall-cmd --permanent --add-service=ssh &>/dev/null || true firewall-cmd --reload &>/dev/null || true - log_info "firewalld configured for SSH" + Write-Log INFO "firewalld configured for SSH (inactive)" elif command -v ufw &>/dev/null; then ufw allow ssh &>/dev/null || true - log_info "ufw configured for SSH" + Write-Log INFO "ufw configured for SSH (inactive)" else - log_warn "No firewall detected — ensure port 22 is accessible" + Write-Log WARN "No firewall detected — ensure port 22 is accessible" fi - log_success "Firewall done" + Write-Log SUCCESS "Firewall done" } -test_configuration() { - log_step "Testing SSH configuration" +Test-SshConfig() { + Write-Log STEP "Testing SSH configuration" if sshd -t 2>/dev/null; then - log_success "Configuration syntax valid" + Write-Log SUCCESS "Configuration syntax valid" else - log_error "Configuration has syntax errors:" + Write-Log ERROR "Configuration has syntax errors:" sshd -t return 1 fi } -show_summary() { +Show-Summary() { local ssh_version ssh_version=$(ssh -V 2>&1 | awk '{print $1}' | tr -d ',') @@ -358,123 +442,129 @@ show_summary() { echo } -install() { +Install-HardenedOpenSsh() { if [[ -n "${SSH_CONNECTION:-}" ]] && [[ "${FORCE_SSH_INSTALL:-}" != "1" ]]; then - log_error "Running in SSH session — this will modify SSH config." - log_warn "Use tmux/screen, or: FORCE_SSH_INSTALL=1 $0 install" + Write-Log ERROR "Running in SSH session — this will modify SSH config." + Write-Log WARN "Use tmux/screen, or: FORCE_SSH_INSTALL=1 $0 install" exit 1 fi if [[ "${CONFIRM:-}" != "yes" ]]; then if [[ -t 0 ]]; then read -rp "Install hardened OpenSSH? This disables password auth. [y/N] " answer - [[ "${answer,,}" != "y" ]] && { log_error "Cancelled"; exit 0; } + [[ "${answer,,}" != "y" ]] && { Write-Log ERROR "Cancelled"; exit 0; } else - log_error "Non-interactive: use CONFIRM=yes $0 install" + Write-Log ERROR "Non-interactive: use CONFIRM=yes $0 install" exit 0 fi fi - check_root - print_header - backup_config - install_openssh - generate_host_keys - configure_ssh - configure_firewall - test_configuration + Test-Root + Show-Header + Backup-SshConfig + Install-OpenSsh + # Re-detect now that the package (and thus the systemd unit) exists + SSH_SERVICE=$(Get-SshService) + New-HostKeys + Set-SshConfig + Set-Firewall + Test-SshConfig systemctl enable "$SSH_SERVICE" systemctl restart "$SSH_SERVICE" - show_summary - log_success "Done!" + Show-Summary + Write-Log SUCCESS "Done!" } -remove() { - check_root +Remove-OpenSsh() { + Test-Root if [[ "${CONFIRM:-}" != "yes" ]]; then if [[ -t 0 ]]; then read -rp "Remove OpenSSH server? [y/N] " answer - [[ "${answer,,}" != "y" ]] && { log_error "Cancelled"; exit 0; } + [[ "${answer,,}" != "y" ]] && { Write-Log ERROR "Cancelled"; exit 0; } else - log_error "Non-interactive: use CONFIRM=yes $0 remove" + Write-Log ERROR "Non-interactive: use CONFIRM=yes $0 remove" exit 0 fi fi systemctl is-active --quiet "$SSH_SERVICE" && systemctl stop "$SSH_SERVICE" || true systemctl is-enabled --quiet "$SSH_SERVICE" && systemctl disable "$SSH_SERVICE" || true - [ -f "$ORIGINAL_CONFIG" ] && cp "$ORIGINAL_CONFIG" "$CONFIG_FILE" && log_info "Original config restored" + [ -f "$ORIGINAL_CONFIG" ] && cp "$ORIGINAL_CONFIG" "$CONFIG_FILE" && Write-Log INFO "Original config restored" local remove_failed=0 - if command -v apt-get &>/dev/null; then - apt-get remove -y openssh-server &>/dev/null || { log_error "apt-get remove failed"; remove_failed=1; } - elif command -v dnf &>/dev/null; then - dnf remove -y openssh-server &>/dev/null || { log_error "dnf remove failed"; remove_failed=1; } - elif command -v yum &>/dev/null; then - yum remove -y openssh-server &>/dev/null || { log_error "yum remove failed"; remove_failed=1; } - fi + case $(Get-PkgMgr) in + apt) + apt-get remove -y openssh-server &>/dev/null || { Write-Log ERROR "apt-get remove failed"; remove_failed=1; } + ;; + dnf) + dnf remove -y openssh-server &>/dev/null || { Write-Log ERROR "dnf remove failed"; remove_failed=1; } + ;; + pacman) + pacman -Rns --noconfirm openssh &>/dev/null || { Write-Log ERROR "pacman remove failed"; remove_failed=1; } + ;; + esac if [ "$remove_failed" -eq 0 ]; then - log_success "OpenSSH removed. Backup: $BACKUP_DIR" + Write-Log SUCCESS "OpenSSH removed. Backup: $BACKUP_DIR" else - log_warn "OpenSSH removal encountered errors. Backup: $BACKUP_DIR" + Write-Log WARN "OpenSSH removal encountered errors. Backup: $BACKUP_DIR" return 1 fi } -verify() { +Test-OpenSshInstallation() { local issues=0 command -v sshd &>/dev/null \ - && log_success "sshd found: $ssh_version=$(ssh -V 2>&1 | awk '{print $1}' | tr -d ',')" \ - || { log_error "sshd not found"; ((issues++)); } + && Write-Log SUCCESS "sshd found: $(ssh -V 2>&1 | awk '{print $1}' | tr -d ',')" \ + || { Write-Log ERROR "sshd not found"; issues=$((issues + 1)); } [ -f "$CONFIG_FILE" ] && sshd -t 2>/dev/null \ - && log_success "Config syntax valid" \ - || { log_error "Config invalid or missing"; ((issues++)); } + && Write-Log SUCCESS "Config syntax valid" \ + || { Write-Log ERROR "Config invalid or missing"; issues=$((issues + 1)); } - systemctl is-active --quiet "$SSH_SERVICE" && log_success "sshd running" || log_warn "sshd not running" - systemctl is-enabled --quiet "$SSH_SERVICE" && log_success "sshd enabled" || log_warn "sshd not enabled" + systemctl is-active --quiet "$SSH_SERVICE" && Write-Log SUCCESS "sshd running" || Write-Log WARN "sshd not running" + systemctl is-enabled --quiet "$SSH_SERVICE" && Write-Log SUCCESS "sshd enabled" || Write-Log WARN "sshd not enabled" for key in /etc/ssh/ssh_host_*_key; do - [ -f "$key" ] && log_success "Host key: $(ssh-keygen -lf "$key" 2>/dev/null)" + [ -f "$key" ] && Write-Log SUCCESS "Host key: $(ssh-keygen -lf "$key" 2>/dev/null)" done if [ ! -f "/etc/ssh/ssh_host_ed25519_key" ]; then - log_error "Ed25519 host key not found" - ((issues++)) + Write-Log ERROR "Ed25519 host key not found" + issues=$((issues + 1)) fi if command -v ss &>/dev/null; then if ss -tlnp | grep -q :22; then - log_success "Listening on :22" + Write-Log SUCCESS "Listening on :22" else - log_warn "Not listening on :22" + Write-Log WARN "Not listening on :22" fi elif command -v netstat &>/dev/null; then if netstat -tlnp 2>/dev/null | grep -q ':22'; then - log_success "Listening on :22" + Write-Log SUCCESS "Listening on :22" else - log_warn "Not listening on :22" + Write-Log WARN "Not listening on :22" fi elif command -v lsof &>/dev/null; then if lsof -iTCP:22 -sTCP:LISTEN -nP &>/dev/null; then - log_success "Listening on :22" + Write-Log SUCCESS "Listening on :22" else - log_warn "Not listening on :22" + Write-Log WARN "Not listening on :22" fi else - log_warn "Cannot verify listening port :22 (no ss/netstat/lsof available)" + Write-Log WARN "Cannot verify listening port :22 (no ss/netstat/lsof available)" fi - [ $issues -eq 0 ] && log_success "Verification passed" || { log_error "$issues issue(s) found"; return 1; } + [ $issues -eq 0 ] && Write-Log SUCCESS "Verification passed" || { Write-Log ERROR "$issues issue(s) found"; return 1; } } -main() { +Invoke-Main() { case "${1:-help}" in - install) install ;; - remove) remove ;; - verify) verify ;; + install) Install-HardenedOpenSsh ;; + remove) Remove-OpenSsh ;; + verify) Test-OpenSshInstallation ;; *) echo echo -e "${BOLD}OpenSSH Hardened Configuration Installer${NC}" @@ -493,4 +583,4 @@ main() { esac } -main "$@" +Invoke-Main "$@" diff --git a/podman/podman_installer.sh b/podman/podman_installer.sh index 34d21b9..657284a 100755 --- a/podman/podman_installer.sh +++ b/podman/podman_installer.sh @@ -1,109 +1,165 @@ #!/usr/bin/env bash +# # Podman Installer Script (standalone) +# +# Installs or removes Podman from the distro repos on Debian/Ubuntu (apt), +# RHEL/Fedora (dnf) and Arch Linux (pacman). # https://podman.io/docs/installation +# Run as root. +# set -euo pipefail -# Color definitions -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly BLUE='\033[0;34m' -readonly NC='\033[0m' -readonly BOLD='\033[1m' - -# Logging functions -log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } -log_success() { echo -e "${GREEN}[✓]${NC} $1"; } -log_error() { echo -e "${RED}[✗]${NC} $1" >&2; } -log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } -log_step() { echo -e "${BOLD}$1${NC}"; } -die() { log_error "$1"; exit 1; } +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} + +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} + +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} + +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} + +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} + +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} # === Root check === -[ "$EUID" -ne 0 ] && die "Run as root (sudo)." +Test-Root -usage() { - echo "Usage: $0 [install|remove]" - exit 1 +Show-Usage() { + echo "Usage: $0 [install|remove]" + exit 1 } -install_podman() { - log_step "Installing Podman..." - if command -v podman &>/dev/null; then - log_success "Podman is already installed." - return 0 - fi - if [ -f /etc/os-release ]; then - . /etc/os-release - case "$ID" in - ubuntu|debian) - log_info "Detected $ID. Installing via apt." - apt-get update - apt-get install -y podman - ;; - fedora) - log_info "Detected Fedora. Installing via dnf." - dnf -y install podman - ;; - centos|rhel) - log_info "Detected $ID. Installing via dnf." - dnf -y install podman - ;; - arch) - log_info "Detected Arch Linux. Installing via pacman." - pacman -Sy --noconfirm podman - ;; - *) - die "Unsupported OS: $ID. Please install Podman manually." - ;; - esac - else - die "Cannot detect OS. Please install Podman manually." - fi - log_success "Podman installation complete." +Install-Podman() { + Write-Log STEP "Installing Podman..." + if command -v podman &>/dev/null; then + Write-Log SUCCESS "Podman is already installed." + return 0 + fi + case $(Get-PkgMgr) in + apt) + Write-Log INFO "APT-based system detected. Installing via apt." + Invoke-Cmd apt-get update + Invoke-Cmd apt-get install -y podman + ;; + dnf) + Write-Log INFO "DNF-based system detected. Installing via dnf." + Invoke-Cmd dnf -y install podman + ;; + pacman) + Write-Log INFO "Pacman-based system detected. Installing via pacman." + Invoke-Cmd pacman -Sy --noconfirm podman + ;; + *) + Stop-Script "Unsupported system. Please install Podman manually." + ;; + esac + Write-Log SUCCESS "Podman installation complete." } -remove_podman() { - log_step "Removing Podman..." - if ! command -v podman &>/dev/null; then - log_warn "Podman is not installed." - return 0 - fi - if [ -f /etc/os-release ]; then - . /etc/os-release - case "$ID" in - ubuntu|debian) - apt-get remove -y podman - ;; - fedora) - dnf -y remove podman - ;; - centos|rhel) - dnf -y remove podman - ;; - arch) - pacman -Rns --noconfirm podman - ;; - *) - die "Unsupported OS: $ID. Please remove Podman manually." - ;; - esac - else - die "Cannot detect OS. Please remove Podman manually." - fi - log_success "Podman removal complete." +Remove-Podman() { + Write-Log STEP "Removing Podman..." + if ! command -v podman &>/dev/null; then + Write-Log WARN "Podman is not installed." + return 0 + fi + case $(Get-PkgMgr) in + apt) + Invoke-Cmd apt-get remove -y podman + ;; + dnf) + Invoke-Cmd dnf -y remove podman + ;; + pacman) + Invoke-Cmd pacman -Rns --noconfirm podman + ;; + *) + Stop-Script "Unsupported system. Please remove Podman manually." + ;; + esac + Write-Log SUCCESS "Podman removal complete." } -main() { - if [ $# -ne 1 ]; then - usage - fi - case "$1" in - install) install_podman ;; - remove) remove_podman ;; - *) usage ;; - esac +Invoke-Main() { + if [[ $# -ne 1 ]]; then + Show-Usage + fi + case "$1" in + install) Install-Podman ;; + remove) Remove-Podman ;; + *) Show-Usage ;; + esac } -main "$@" +Invoke-Main "$@" diff --git a/renovate.json b/renovate.json index a63005f..5133bd6 100644 --- a/renovate.json +++ b/renovate.json @@ -1,291 +1,291 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ], - "timezone": "Europe/Amsterdam", - "forkProcessing": "enabled", - "pinDigests": true, - "assigneesFromCodeOwners": true, - "reviewersFromCodeOwners": true, - "enabledManagers": [ - "github-actions", - "dockerfile", - "docker-compose", - "custom.regex" - ], - "labels": [ - "dependencies" - ], - "rebaseWhen": "behind-base-branch", - "gitIgnoredAuthors": [ - "github-actions[bot]@users.noreply.github.com", - "41898282+github-actions[bot]@users.noreply.github.com" - ], - "vulnerabilityAlerts": { - "labels": [ - "security" - ], - "enabled": true - }, - "packageRules": [ - { - "matchManagers": [ - "dockerfile", - "docker-compose" - ], - "groupName": "Docker images", - "addLabels": [ - "docker" - ] - }, - { - "matchManagers": [ - "github-actions" - ], - "groupName": "GitHub Actions", - "addLabels": [ - "github-actions" - ] - }, - { - "semanticCommitType": "deps", - "groupName": "nginx installer deps", - "matchFileNames": [ - "nginx/**" - ], - "description": "Groepeer alle nginx installer-afhankelijkheden in een PR", - "matchManagers": [ - "custom.regex" - ] - }, - { - "semanticCommitType": "deps", - "groupName": "ansible installer deps", - "matchFileNames": [ - "ansible/**" - ], - "matchManagers": [ - "custom.regex" - ] - }, - { - "semanticCommitType": "deps", - "groupName": "kubernetes installer deps", - "matchFileNames": [ - "kubernetes/**" - ], - "matchManagers": [ - "custom.regex" - ] - } - ], - "customManagers": [ - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "NGINX_VERSION=\"(?[^\"]+)\"" - ], - "description": "NGINX versie in bash installer", - "customType": "regex", - "depNameTemplate": "nginx/nginx", - "extractVersionTemplate": "^release-(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:NGINX_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "NGINX versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "nginx/nginx", - "extractVersionTemplate": "^release-(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "PCRE2_VERSION=\"(?[^\"]+)\"" - ], - "description": "PCRE2 versie in bash installer", - "customType": "regex", - "depNameTemplate": "PCRE2Project/pcre2", - "extractVersionTemplate": "^pcre2-(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:PCRE2_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "PCRE2 versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "PCRE2Project/pcre2", - "extractVersionTemplate": "^pcre2-(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "ZLIB_VERSION=\"(?[^\"]+)\"" - ], - "description": "Zlib versie in bash installer", - "customType": "regex", - "depNameTemplate": "madler/zlib", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:ZLIB_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "Zlib versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "madler/zlib", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "HEADERS_MORE_VERSION=\"(?[^\"]+)\"" - ], - "description": "headers-more-nginx-module versie in bash installer", - "customType": "regex", - "depNameTemplate": "openresty/headers-more-nginx-module", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:HEADERS_MORE_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "headers-more-nginx-module versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "openresty/headers-more-nginx-module", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "ZSTD_MODULE_VERSION=\"(?[^\"]+)\"" - ], - "description": "zstd-nginx-module versie in bash installer", - "customType": "regex", - "depNameTemplate": "tokers/zstd-nginx-module", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:ZSTD_MODULE_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "zstd-nginx-module versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "tokers/zstd-nginx-module", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "ACME_MODULE_VERSION=\"(?[^\"]+)\"" - ], - "description": "nginx-acme versie in bash installer", - "customType": "regex", - "depNameTemplate": "nginx/nginx-acme", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$Script:ACME_MODULE_VERSION\\s*=\\s*'(?[^']+)'" - ], - "description": "nginx-acme versie in PowerShell installer", - "customType": "regex", - "depNameTemplate": "nginx/nginx-acme", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^nginx/nginx_installer\\.ps1$/" - ] - }, - { - "datasourceTemplate": "pypi", - "matchStrings": [ - "pip install ansible==(?[0-9.]+)" - ], - "description": "Ansible versie in bash installer", - "customType": "regex", - "depNameTemplate": "ansible", - "managerFilePatterns": [ - "/^ansible/ansible_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "K8S_VERSION:-(?v[0-9.]+)" - ], - "description": "Kubernetes versie in bash installer", - "customType": "regex", - "depNameTemplate": "kubernetes/kubernetes", - "managerFilePatterns": [ - "/^kubernetes/kubernetes_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "MINIKUBE_VERSION:-(?v[0-9.]+)" - ], - "description": "Minikube versie in bash installer", - "customType": "regex", - "depNameTemplate": "kubernetes/minikube", - "managerFilePatterns": [ - "/^kubernetes/kubernetes_installer\\.sh$/" - ] - }, - { - "datasourceTemplate": "github-releases", - "matchStrings": [ - "\\$VAGRANT_VERSION\\s*=\\s*\"(?[^\"]+)\"" - ], - "description": "Vagrant versie in Windows installer", - "customType": "regex", - "depNameTemplate": "hashicorp/vagrant", - "extractVersionTemplate": "^v(?.+)$", - "managerFilePatterns": [ - "/^windows/Install-VagrantVMware\\.ps1$/" - ] - } - ], - "automerge": true, - "automergeType": "pr", - "semanticCommits": "enabled" -} +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "timezone": "Europe/Amsterdam", + "forkProcessing": "enabled", + "pinDigests": true, + "assigneesFromCodeOwners": true, + "reviewersFromCodeOwners": true, + "enabledManagers": [ + "github-actions", + "git-submodules", + "custom.regex" + ], + "labels": [ + "dependencies" + ], + "rebaseWhen": "behind-base-branch", + "gitIgnoredAuthors": [ + "github-actions[bot]@users.noreply.github.com", + "41898282+github-actions[bot]@users.noreply.github.com" + ], + "vulnerabilityAlerts": { + "labels": [ + "security" + ], + "enabled": true + }, + "packageRules": [ + { + "matchManagers": [ + "github-actions" + ], + "groupName": "GitHub Actions", + "addLabels": [ + "github-actions" + ] + }, + { + "matchManagers": [ + "git-submodules" + ], + "groupName": "testssl.sh submodule", + "addLabels": [ + "submodules" + ], + "semanticCommitType": "deps", + "description": "Track the testssl.sh submodule (follows the branch configured in .gitmodules)" + }, + { + "semanticCommitType": "deps", + "groupName": "nginx installer deps", + "matchFileNames": [ + "nginx/**" + ], + "description": "Group all nginx installer dependencies into one PR", + "matchManagers": [ + "custom.regex" + ] + }, + { + "semanticCommitType": "deps", + "groupName": "ansible installer deps", + "matchFileNames": [ + "ansible/**" + ], + "matchManagers": [ + "custom.regex" + ] + }, + { + "semanticCommitType": "deps", + "groupName": "kubernetes installer deps", + "matchFileNames": [ + "kubernetes/**" + ], + "matchManagers": [ + "custom.regex" + ] + } + ], + "customManagers": [ + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "NGINX_VERSION=\"(?[^\"]+)\"" + ], + "description": "NGINX version in the bash installer", + "customType": "regex", + "depNameTemplate": "nginx/nginx", + "extractVersionTemplate": "^release-(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:NGINX_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "NGINX version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "nginx/nginx", + "extractVersionTemplate": "^release-(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "PCRE2_VERSION=\"(?[^\"]+)\"" + ], + "description": "PCRE2 version in the bash installer", + "customType": "regex", + "depNameTemplate": "PCRE2Project/pcre2", + "extractVersionTemplate": "^pcre2-(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:PCRE2_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "PCRE2 version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "PCRE2Project/pcre2", + "extractVersionTemplate": "^pcre2-(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "ZLIB_VERSION=\"(?[^\"]+)\"" + ], + "description": "Zlib version in the bash installer", + "customType": "regex", + "depNameTemplate": "madler/zlib", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:ZLIB_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "Zlib version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "madler/zlib", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "HEADERS_MORE_VERSION=\"(?[^\"]+)\"" + ], + "description": "headers-more-nginx-module version in the bash installer", + "customType": "regex", + "depNameTemplate": "openresty/headers-more-nginx-module", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:HEADERS_MORE_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "headers-more-nginx-module version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "openresty/headers-more-nginx-module", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "ZSTD_MODULE_VERSION=\"(?[^\"]+)\"" + ], + "description": "zstd-nginx-module version in the bash installer", + "customType": "regex", + "depNameTemplate": "tokers/zstd-nginx-module", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:ZSTD_MODULE_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "zstd-nginx-module version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "tokers/zstd-nginx-module", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "ACME_MODULE_VERSION=\"(?[^\"]+)\"" + ], + "description": "nginx-acme version in the bash installer", + "customType": "regex", + "depNameTemplate": "nginx/nginx-acme", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$Script:ACME_MODULE_VERSION\\s*=\\s*'(?[^']+)'" + ], + "description": "nginx-acme version in the PowerShell installer", + "customType": "regex", + "depNameTemplate": "nginx/nginx-acme", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^nginx/nginx_installer\\.ps1$/" + ] + }, + { + "datasourceTemplate": "pypi", + "matchStrings": [ + "pip install ansible==(?[0-9.]+)" + ], + "description": "Ansible version in the bash installer", + "customType": "regex", + "depNameTemplate": "ansible", + "managerFilePatterns": [ + "/^ansible/ansible_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "K8S_VERSION:-(?v[0-9.]+)" + ], + "description": "Kubernetes version in the bash installer", + "customType": "regex", + "depNameTemplate": "kubernetes/kubernetes", + "managerFilePatterns": [ + "/^kubernetes/kubernetes_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "MINIKUBE_VERSION:-(?v[0-9.]+)" + ], + "description": "Minikube version in the bash installer", + "customType": "regex", + "depNameTemplate": "kubernetes/minikube", + "managerFilePatterns": [ + "/^kubernetes/kubernetes_installer\\.sh$/" + ] + }, + { + "datasourceTemplate": "github-releases", + "matchStrings": [ + "\\$VAGRANT_VERSION\\s*=\\s*\"(?[^\"]+)\"" + ], + "description": "Vagrant version in the Windows installer", + "customType": "regex", + "depNameTemplate": "hashicorp/vagrant", + "extractVersionTemplate": "^v(?.+)$", + "managerFilePatterns": [ + "/^windows/Install-VagrantVMware\\.ps1$/" + ] + } + ], + "automerge": true, + "automergeType": "pr", + "semanticCommits": "enabled" +} diff --git a/terraform/terraform_installer.sh b/terraform/terraform_installer.sh index fbc01f5..3c44682 100644 --- a/terraform/terraform_installer.sh +++ b/terraform/terraform_installer.sh @@ -1,68 +1,165 @@ -#!/bin/bash -set -e - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } -print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } -print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } -print_error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } +#!/usr/bin/env bash +# +# Terraform Installer Script +# +# Installs Terraform from the HashiCorp repos on Debian/Ubuntu (apt) and +# RHEL/Fedora (dnf), and from the community repos on Arch Linux (pacman). +# Run as root. +# + +set -euo pipefail + +# ============================================================================ +# Common Helper Functions +# The same helpers are used in every bash script in this repo, so the +# scripts stay consistent while remaining standalone single-file downloads. +# Function names follow the PowerShell Verb-Noun convention. +# ============================================================================ + +# shellcheck disable=SC2034 # not every script uses every color +readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' \ + BLUE='\033[0;34m' PURPLE='\033[0;35m' BOLD='\033[1m' NC='\033[0m' + +# Optional plain-text logfile; set LOG_FILE after this block to enable. +LOG_FILE="${LOG_FILE:-}" + +# Usage: Write-Log "message" +Write-Log() { + local level=$1; shift + local color=$NC + case $level in + INFO) color=$BLUE ;; + SUCCESS) color=$GREEN ;; + WARN) color=$YELLOW ;; + ERROR) color=$RED ;; + STEP) color=$PURPLE ;; + esac + if [[ $level == ERROR ]]; then + echo -e "${color}[$level]${NC} $*" >&2 + else + echo -e "${color}[$level]${NC} $*" + fi + if [[ -n "$LOG_FILE" ]]; then + echo "[$level] $*" >> "$LOG_FILE" + fi +} + +# Usage: Stop-Script "fatal message" +Stop-Script() { + Write-Log ERROR "$1" + exit 1 +} + +# Usage: Test-Root (exits unless running as root) +Test-Root() { + [[ $EUID -eq 0 ]] || Stop-Script "Run as root (sudo)." +} + +# Usage: mgr=$(Get-PkgMgr) -> apt | dnf | pacman | unknown +Get-PkgMgr() { + if command -v apt-get >/dev/null 2>&1; then + echo "apt" + elif command -v dnf >/dev/null 2>&1; then + echo "dnf" + elif command -v pacman >/dev/null 2>&1; then + echo "pacman" + else + echo "unknown" + fi +} + +# Usage: os_id=$(Get-OsId) -> lowercase /etc/os-release ID (ubuntu, debian, +# fedora, arch, ...) or "unknown". Call in $(...) so sourcing stays contained. +Get-OsId() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + local os_id="${ID:-unknown}" + echo "${os_id,,}" + else + echo "unknown" + fi +} + +# Usage: Invoke-Cmd command [args...] +# Logs the command, sends its output to LOG_FILE when set, aborts on failure. +Invoke-Cmd() { + Write-Log INFO "Executing: $*" + if [[ -n "$LOG_FILE" ]]; then + "$@" >> "$LOG_FILE" 2>&1 || Stop-Script "Command failed: '$*'. Check log: $LOG_FILE" + else + "$@" || Stop-Script "Command failed: '$*'" + fi +} # === Root check === -[ "$EUID" -ne 0 ] && print_error "This script must be run as root or with sudo privileges." +Test-Root # === Already installed? === if command -v terraform >/dev/null 2>&1; then - print_warning "Terraform is already installed. Skipping installation." + Write-Log WARN "Terraform is already installed. Skipping installation." exit 0 fi -# === Detect package manager (dnf before yum) === -if command -v apt-get >/dev/null 2>&1; then - print_info "APT-based system detected." - - print_info "Updating package lists..." - apt-get update -y - - print_info "Installing prerequisites..." - apt-get install -y gnupg software-properties-common curl || print_error "Failed to install prerequisites." - - print_info "Adding HashiCorp GPG key..." - curl -fsSL https://apt.releases.hashicorp.com/gpg | \ - gpg --dearmor | tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null || \ - print_error "Failed to add HashiCorp GPG key." - - DISTRO_CODENAME=$(lsb_release -cs 2>/dev/null || echo "bookworm") - print_info "Adding HashiCorp repository for '${DISTRO_CODENAME}'..." - echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com ${DISTRO_CODENAME} main" | \ - tee /etc/apt/sources.list.d/hashicorp.list || print_error "Failed to add HashiCorp repository." - - apt-get update -y - apt-get install -y terraform || print_error "Failed to install Terraform." - -elif command -v dnf >/dev/null 2>&1; then - print_info "DNF-based system detected." - - dnf install -y dnf-plugins-core || print_error "Failed to install dnf-plugins-core." - dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo || \ - print_error "Failed to add HashiCorp repository." - dnf install -y terraform || print_error "Failed to install Terraform." - -elif command -v yum >/dev/null 2>&1; then - print_info "YUM-based system detected." - - yum install -y yum-utils || print_error "Failed to install yum-utils." - yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo || \ - print_error "Failed to add HashiCorp repository." - yum install -y terraform || print_error "Failed to install Terraform." - -else - print_error "Unsupported package manager." -fi - -print_success "Terraform installation completed successfully!" +# === Install === +PKG_MANAGER=$(Get-PkgMgr) +case $PKG_MANAGER in + apt) + Write-Log INFO "APT-based system detected." + + Write-Log INFO "Updating package lists..." + Invoke-Cmd apt-get update -y + + Write-Log INFO "Installing prerequisites..." + Invoke-Cmd apt-get install -y gnupg software-properties-common curl + + Write-Log INFO "Adding HashiCorp GPG key..." + curl -fsSL https://apt.releases.hashicorp.com/gpg | \ + gpg --dearmor | tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null || \ + Stop-Script "Failed to add HashiCorp GPG key." + + DISTRO_CODENAME="" + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + DISTRO_CODENAME=$(. /etc/os-release && echo "${VERSION_CODENAME:-}") + fi + [[ -n "$DISTRO_CODENAME" ]] || DISTRO_CODENAME=$(lsb_release -cs 2>/dev/null || true) + [[ -n "$DISTRO_CODENAME" ]] || Stop-Script "Could not determine the distribution codename." + Write-Log INFO "Adding HashiCorp repository for '${DISTRO_CODENAME}'..." + echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com ${DISTRO_CODENAME} main" | \ + tee /etc/apt/sources.list.d/hashicorp.list || Stop-Script "Failed to add HashiCorp repository." + + Invoke-Cmd apt-get update -y + Invoke-Cmd apt-get install -y terraform + ;; + + dnf) + Write-Log INFO "DNF-based system detected." + + Invoke-Cmd dnf install -y dnf-plugins-core + + # HashiCorp publishes separate repos for Fedora and RHEL-compatible distros + case $(Get-OsId) in + fedora) HASHICORP_REPO="https://rpm.releases.hashicorp.com/fedora/hashicorp.repo" ;; + *) HASHICORP_REPO="https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo" ;; + esac + + # dnf5 (Fedora 41+) and dnf4 (RHEL) use different config-manager syntax + dnf config-manager addrepo --overwrite --from-repofile="$HASHICORP_REPO" 2>/dev/null || \ + dnf config-manager --add-repo "$HASHICORP_REPO" || \ + Stop-Script "Failed to add HashiCorp repository." + Invoke-Cmd dnf install -y terraform + ;; + + pacman) + Write-Log INFO "Pacman-based system detected." + # HashiCorp has no vendor repo for Arch; this is the community package. + Invoke-Cmd pacman -Syu --noconfirm terraform + ;; + + *) + Stop-Script "Unsupported package manager. Only apt, dnf and pacman are supported." + ;; +esac + +Write-Log SUCCESS "Terraform installation completed successfully!" diff --git a/windows/Enable-WinRM.ps1 b/windows/Enable-WinRM.ps1 index aa6b9c8..25e54c7 100644 --- a/windows/Enable-WinRM.ps1 +++ b/windows/Enable-WinRM.ps1 @@ -1,5 +1,19 @@ -# WinRM configuration script -# Run as Administrator +<# +.SYNOPSIS + Configures WinRM for remote management. +.DESCRIPTION + Enables PowerShell Remoting, creates an HTTP listener on port 5985 if + one does not exist yet, and adds the matching inbound firewall rule. + Warns when a network connection is set to Public. +.NOTES + Run as Administrator. +#> + +#Requires -RunAsAdministrator + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + Clear-Host # --- Step 1: Check Network Profiles and Warn for Public Networks --- diff --git a/windows/Get-InstalledSoftware.ps1 b/windows/Get-InstalledSoftware.ps1 index 92caacf..9ce36ba 100644 --- a/windows/Get-InstalledSoftware.ps1 +++ b/windows/Get-InstalledSoftware.ps1 @@ -1,3 +1,14 @@ +<# +.SYNOPSIS + Lists installed software from the Windows registry. +.DESCRIPTION + Reads the Uninstall registry keys (machine-wide 64-bit, machine-wide + 32-bit and current user) and prints a table of installed applications. +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + $registryPaths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", @@ -5,11 +16,9 @@ $registryPaths = @( ) $installedApps = foreach ($path in $registryPaths) { - Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | - Select-Object @{Name="Naam";Expression={$_.DisplayName}}, - @{Name="Versie";Expression={$_.DisplayVersion}}, - @{Name="Publisher";Expression={$_.Publisher}}, - @{Name="InstallatieDatum";Expression={$_.InstallDate}} + Get-ItemProperty $path -ErrorAction SilentlyContinue | + Where-Object { $_.PSObject.Properties['DisplayName'] -and $_.DisplayName } | + Select-Object DisplayName, DisplayVersion, Publisher, InstallDate } -$installedApps | Sort-Object Naam | Format-Table -AutoSize +$installedApps | Sort-Object DisplayName | Format-Table -AutoSize diff --git a/windows/Install Dell-Command_Update.ps1 b/windows/Install Dell-Command_Update.ps1 deleted file mode 100644 index 314687c..0000000 --- a/windows/Install Dell-Command_Update.ps1 +++ /dev/null @@ -1 +0,0 @@ -winget install --accept-source-agreements --accept-package-agreements Dell.CommandUpdate \ No newline at end of file diff --git a/windows/Install HPIA.ps1 b/windows/Install HPIA.ps1 deleted file mode 100644 index 29cb2d1..0000000 --- a/windows/Install HPIA.ps1 +++ /dev/null @@ -1 +0,0 @@ -winget install --accept-source-agreements --accept-package-agreements HP.ImageAssistant \ No newline at end of file diff --git a/windows/Install-DellCommandUpdate.ps1 b/windows/Install-DellCommandUpdate.ps1 new file mode 100644 index 0000000..f22d6f0 --- /dev/null +++ b/windows/Install-DellCommandUpdate.ps1 @@ -0,0 +1,17 @@ +<# +.SYNOPSIS + Installs Dell Command | Update via winget. +.DESCRIPTION + Thin wrapper around winget for unattended installation of Dell Command | + Update on Dell machines. +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +winget install --accept-source-agreements --accept-package-agreements Dell.CommandUpdate +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to install Dell Command | Update (winget exit code $LASTEXITCODE)." -ForegroundColor Red + Exit 1 +} +Write-Host "Dell Command | Update installed." -ForegroundColor Green diff --git a/windows/Install-HPImageAssistant.ps1 b/windows/Install-HPImageAssistant.ps1 new file mode 100644 index 0000000..3fc4d48 --- /dev/null +++ b/windows/Install-HPImageAssistant.ps1 @@ -0,0 +1,17 @@ +<# +.SYNOPSIS + Installs HP Image Assistant via winget. +.DESCRIPTION + Thin wrapper around winget for unattended installation of HP Image + Assistant (HPIA) on HP machines. +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +winget install --accept-source-agreements --accept-package-agreements HP.ImageAssistant +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to install HP Image Assistant (winget exit code $LASTEXITCODE)." -ForegroundColor Red + Exit 1 +} +Write-Host "HP Image Assistant installed." -ForegroundColor Green diff --git a/windows/Install-VagrantVMware.ps1 b/windows/Install-VagrantVMware.ps1 index c2be627..1b7428c 100644 --- a/windows/Install-VagrantVMware.ps1 +++ b/windows/Install-VagrantVMware.ps1 @@ -1,9 +1,20 @@ -# Install-VagrantVMware.ps1 -# Automates the installation of VMware Workstation with Vagrant on Windows. -# Note: vagrant-vmware-utility is bundled with Vagrant since v2.x -# Credits: https://github.com/1eedaegon -# Run as Administrator. +<# +.SYNOPSIS + Automates the installation of VMware Workstation with Vagrant on Windows. +.DESCRIPTION + Installs Go, VMware Workstation Player and Vagrant via winget, installs + the vagrant-vmware-desktop plugin, then initialises and starts a test VM. + Note: vagrant-vmware-utility is bundled with Vagrant since v2.x. + Credits: https://github.com/1eedaegon +.NOTES + Run as Administrator (the script self-elevates if needed). +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Self-elevate when not running as Administrator $principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "Restarting the script with administrator rights..." -ForegroundColor Yellow diff --git a/windows/Optimize-WindowsVM.ps1 b/windows/Optimize-WindowsVM.ps1 new file mode 100644 index 0000000..b8cd8a1 --- /dev/null +++ b/windows/Optimize-WindowsVM.ps1 @@ -0,0 +1,68 @@ +<# +.SYNOPSIS + Disables unnecessary services and features for Windows VMs. +.DESCRIPTION + Optimizes a Windows VM by disabling Windows Update, Windows Search, + SysMain, diagnostics tracking, error reporting, OneDrive, hibernation + and Fast Startup, and sets the High Performance power plan. + + Tested on Windows 11 and Windows Server 2025. It may work on other + versions, but some services may differ. +.NOTES + Run as Administrator (the script self-elevates if needed). +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Self-elevate when not running as Administrator +$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) +if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Host "Restarting the script with administrator rights..." -ForegroundColor Yellow + # Restart PowerShell as admin with the same parameters + Start-Process -FilePath "PowerShell" ` + -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" ` + -Verb RunAs + Exit +} +Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force + +# Disables a service if it exists; absent services (Server vs 11) are skipped. +function Disable-WindowsService { + param([string]$Name, [string]$DisplayName) + Write-Host "Disabling $DisplayName..." + Stop-Service $Name -Force -ErrorAction SilentlyContinue + Set-Service $Name -StartupType Disabled -ErrorAction SilentlyContinue +} + +Disable-WindowsService -Name wuauserv -DisplayName "Windows Update" +Disable-WindowsService -Name WSearch -DisplayName "Windows Search" +Disable-WindowsService -Name SysMain -DisplayName "SysMain (Superfetch)" +Disable-WindowsService -Name DiagTrack -DisplayName "Diagnostics Tracking" +Disable-WindowsService -Name WerSvc -DisplayName "Windows Error Reporting" + +# Disable OneDrive (if present) +Write-Host "Disabling OneDrive (if installed)..." +$onedrive = Get-Process OneDrive -ErrorAction SilentlyContinue +if ($onedrive) { + Stop-Process -Name OneDrive -Force + $onedrivePath = "$env:SystemRoot\System32\OneDriveSetup.exe" + if (Test-Path $onedrivePath) { + Start-Process $onedrivePath "/uninstall" -NoNewWindow -Wait + } +} + +# Set power plan to high performance +Write-Host "Setting power plan to High Performance..." +powercfg -setactive SCHEME_MIN + +# Disable hibernation +Write-Host "Disabling hibernation..." +powercfg -h off + +# Disable Fast Startup +Write-Host "Disabling Fast Startup..." +$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" +Set-ItemProperty -Path $regPath -Name HiberbootEnabled -Value 0 + +Write-Host "VM optimization is done!" -ForegroundColor Green diff --git a/windows/configure-Windows-VM.ps1 b/windows/configure-Windows-VM.ps1 deleted file mode 100644 index 5511eb3..0000000 --- a/windows/configure-Windows-VM.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -# configure-Windows-VM.ps1 -# Script to disable unnecessary services and features for Windows VMs -# This script is tested for a Windows 11 VM and Windows Server 2025. It may work on other versions, but some services may be different. - -# Run as Administrator -$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { - Write-Host "Restarting the script with administrator rights..." -ForegroundColor Yellow - # Herstart PowerShell als admin, met dezelfde parameters - Start-Process -FilePath "PowerShell" ` - -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" ` - -Verb RunAs - Exit -} -Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - -# Disable Windows Update -Write-Host "Disabling Windows Update..." -Stop-Service wuauserv -Force -Set-Service wuauserv -StartupType Disabled - -# Disable Windows Search -Write-Host "Disabling Windows Search..." -Stop-Service WSearch -Force -Set-Service WSearch -StartupType Disabled - -# Disable Superfetch/SysMain -Write-Host "Disabling SysMain (Superfetch)..." -Stop-Service SysMain -Force -Set-Service SysMain -StartupType Disabled - -# Disable Diagnostics Tracking -Write-Host "Disabling Diagnostics Tracking..." -Stop-Service DiagTrack -Force -Set-Service DiagTrack -StartupType Disabled - -# Disable Windows Error Reporting -Write-Host "Disabling Windows Error Reporting..." -Stop-Service WerSvc -Force -Set-Service WerSvc -StartupType Disabled - -# Disable OneDrive (if present) -Write-Host "Disabling OneDrive (if installed)..." -$onedrive = Get-Process OneDrive -ErrorAction SilentlyContinue -if ($onedrive) { - Stop-Process -Name OneDrive -Force - $onedrivePath = "$env:SystemRoot\System32\OneDriveSetup.exe" - if (Test-Path $onedrivePath) { - Start-Process $onedrivePath "/uninstall" -NoNewWindow -Wait - } -} - -# Set power plan to high performance -Write-Host "Setting power plan to High Performance..." -powercfg -setactive SCHEME_MIN - -# Disable hibernation -Write-Host "Disabling hibernation..." -powercfg -h off - -# Disable Fast Startup -Write-Host "Disabling Fast Startup..." -$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" -Set-ItemProperty -Path $regPath -Name HiberbootEnabled -Value 0 - -Write-Host "VM optimization is done!." -ForegroundColor Green \ No newline at end of file