diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a4e608f..07d0b8d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,5 +18,7 @@ bitwarden-lite/ @bitwarden/dept-shot # Release workflows +.github/workflows/release-aws.yml @bitwarden/dept-bre +.github/workflows/release-azure.yml @bitwarden/dept-bre .github/workflows/release-digital-ocean.yml @bitwarden/dept-bre .github/workflows/release.yml @bitwarden/dept-bre diff --git a/.github/workflows/release-aws.yml b/.github/workflows/release-aws.yml new file mode 100644 index 0000000..12a50d7 --- /dev/null +++ b/.github/workflows/release-aws.yml @@ -0,0 +1,168 @@ +name: Release AWS Marketplace + +on: + release: + types: [published] + + push: + paths: + - "AWSMarketplace/**" + + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + build-image: + name: Build Image + runs-on: ubuntu-24.04 + timeout-minutes: 90 + permissions: + contents: read + id-token: write + steps: + - name: Checkout repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Log in to Azure + uses: bitwarden/gh-actions/azure-login@main + with: + subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant_id: ${{ secrets.AZURE_TENANT_ID }} + client_id: ${{ secrets.AZURE_CLIENT_ID }} + + - name: Retrieve secrets + id: retrieve-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "gh-self-host" + secrets: "aws-marketplace-access-key-id, + aws-marketplace-secret-access-key" + + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main + + - name: Set version from version.json + id: set-version + run: | + VERSION=$(jq -r '.versions.coreVersion' version.json) + if [[ -z "$VERSION" ]]; then + echo "ERROR: Failed to extract coreVersion from version.json" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Set up Hashicorp Packer + uses: hashicorp/setup-packer@1aa358be5cf73883762b302a3a03abd66e75b232 # v3.1.0 + + - name: Build AWS Image + env: + AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-marketplace-access-key-id }} + AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-marketplace-secret-access-key }} + AWS_DEFAULT_REGION: "us-east-1" + AWS_IMG_VERSION: ${{ steps.set-version.outputs.version }} + working-directory: ./AWSMarketplace + run: | + packer version + packer init -upgrade marketplace-image.pkr.hcl + packer build marketplace-image.pkr.hcl + + - name: Cleanup orphaned instances on cancellation or failure + if: cancelled() || failure() + env: + AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-marketplace-access-key-id }} + AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-marketplace-secret-access-key }} + AWS_DEFAULT_REGION: "us-east-1" + run: | + echo "Workflow cancelled - cleaning up any orphaned resources..." + + echo "## :warning: Workflow Cancelled - Resource Cleanup" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Searching for orphaned instances with tag: \`github-run-${{ github.run_id }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Find and terminate EC2 instances tagged with this run + INSTANCE_IDS=$(aws ec2 describe-instances \ + --filters "Name=tag:GitHub_Run,Values=github-run-${{ github.run_id }}" \ + "Name=instance-state-name,Values=pending,running,stopping,stopped" \ + --query "Reservations[].Instances[].InstanceId" \ + --output text) + + if [ -n "$INSTANCE_IDS" ]; then + echo "Found orphaned instances: $INSTANCE_IDS" + echo "### Orphaned Instances Found and Terminated" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Instance ID | Status |" >> $GITHUB_STEP_SUMMARY + echo "|-------------|--------|" >> $GITHUB_STEP_SUMMARY + + for INSTANCE_ID in $INSTANCE_IDS; do + echo "Terminating instance: $INSTANCE_ID" + if aws ec2 terminate-instances --instance-ids "$INSTANCE_ID"; then + echo "| $INSTANCE_ID | :white_check_mark: Terminated |" >> $GITHUB_STEP_SUMMARY + else + echo "| $INSTANCE_ID | :x: Failed to terminate |" >> $GITHUB_STEP_SUMMARY + fi + done + else + echo "No orphaned instances found" + echo ":white_check_mark: No orphaned resources found - nothing to clean up" >> $GITHUB_STEP_SUMMARY + fi + + - name: Add build summary + if: success() + env: + VERSION: ${{ steps.set-version.outputs.version }} + working-directory: ./AWSMarketplace + run: | + echo "## :rocket: AWS Marketplace Image Build Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${VERSION}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Get artifact details from manifest + if [ -f manifest.json ]; then + AMI_ID=$(jq -r '.builds[-1].artifact_id' manifest.json) + echo "**AMI ID:** \`$AMI_ID\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + fi + + echo ":white_check_mark: Packer build instance was automatically cleaned up" >> $GITHUB_STEP_SUMMARY + + - name: AWS Image Cleanup + working-directory: ./AWSMarketplace + if: ${{ github.event_name != 'release' && github.event_name != 'workflow_dispatch' }} + env: + AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-marketplace-access-key-id }} + AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-marketplace-secret-access-key }} + AWS_DEFAULT_REGION: "us-east-1" + run: | + # Get the AMI ID from the manifest (format: "us-east-1:ami-xxxxxxxxx") + AMI_ID=$(jq -r '.builds[-1].artifact_id' manifest.json | cut -d ":" -f2) + + if [ -n "$AMI_ID" ]; then + # Find associated snapshots before deregistering + SNAPSHOT_IDS=$(aws ec2 describe-images --image-ids "$AMI_ID" \ + --query "Images[].BlockDeviceMappings[].Ebs.SnapshotId" \ + --output text 2>/dev/null || true) + + # Deregister the AMI + aws ec2 deregister-image --image-id "$AMI_ID" 2>/dev/null || true + + # Delete associated snapshots + for SNAPSHOT_ID in $SNAPSHOT_IDS; do + aws ec2 delete-snapshot --snapshot-id "$SNAPSHOT_ID" 2>/dev/null || true + done + fi + + # Update summary for non-release builds + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo ":wastebasket: **Non-release build:** AMI \`$AMI_ID\` and snapshots were automatically cleaned up" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8a4905..7b32a84 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -529,3 +529,18 @@ jobs: ref: process.env.RELEASE_TAG, inputs: {} }); + + - name: Trigger release-aws workflow + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + RELEASE_TAG: v${{ inputs.release_version }} + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'bitwarden', + repo: 'self-host', + workflow_id: 'release-aws.yml', + ref: process.env.RELEASE_TAG, + inputs: {} + }); diff --git a/AWSMarketplace/files/etc/profile.d/bitwarden-install.sh b/AWSMarketplace/files/etc/profile.d/bitwarden-install.sh new file mode 100644 index 0000000..7c06165 --- /dev/null +++ b/AWSMarketplace/files/etc/profile.d/bitwarden-install.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# First-login trigger for Bitwarden installation. +# This script runs once on the first interactive login, then removes itself. +# Skip for the bitwarden service account (it doesn't have sudo). + +if [ "$(whoami)" = "bitwarden" ]; then + return 0 2>/dev/null || exit 0 +fi + +if [ -f /opt/bitwarden/install-bitwarden.sh ]; then + # Wait for cloud-init to finish (downloads bitwarden.sh on first boot) + echo "Waiting for cloud-init to complete..." + sudo cloud-init status --wait > /dev/null 2>&1 + sudo /opt/bitwarden/install-bitwarden.sh +fi diff --git a/AWSMarketplace/files/etc/ufw/applications.d/bitwarden b/AWSMarketplace/files/etc/ufw/applications.d/bitwarden new file mode 100644 index 0000000..c31cda1 --- /dev/null +++ b/AWSMarketplace/files/etc/ufw/applications.d/bitwarden @@ -0,0 +1,4 @@ +[Bitwarden] +title=Bitwarden server +description=Bitwarden is an open source password management tool that allows you to securely store, share, and sync passwords and other senitive data. +ports=80/tcp|443/tcp diff --git a/AWSMarketplace/files/etc/update-motd.d/99-bitwarden-welcome b/AWSMarketplace/files/etc/update-motd.d/99-bitwarden-welcome new file mode 100644 index 0000000..1baf5fd --- /dev/null +++ b/AWSMarketplace/files/etc/update-motd.d/99-bitwarden-welcome @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Configured as part of the AWS Marketplace Image build process + +myip=$(hostname -I | awk '{print$1}') +cat < /etc/cron.weekly/bitwardenupdate + +chmod +x /etc/cron.weekly/bitwardenupdate + +# +# Cleanup - remove the login trigger +# + +rm -f /etc/profile.d/bitwarden-install.sh diff --git a/AWSMarketplace/files/var/lib/cloud/scripts/per-instance/001_onboot b/AWSMarketplace/files/var/lib/cloud/scripts/per-instance/001_onboot new file mode 100644 index 0000000..18fcf95 --- /dev/null +++ b/AWSMarketplace/files/var/lib/cloud/scripts/per-instance/001_onboot @@ -0,0 +1,17 @@ +#!/bin/bash + +# Scripts in this directory will be executed by cloud-init on the first boot of instances +# created from your image. Things like generating passwords, configuration requiring IP address +# or other items that will be unique to each instance should be done in scripts here. + +# +# Setup Bitwarden Installer +# ref: https://help.bitwarden.com/article/install-on-premise/ +# + +docker pull ghcr.io/bitwarden/setup + +curl -L -s -o /home/bitwarden/bitwarden.sh "https://func.bitwarden.com/api/dl/?app=self-host&platform=linux" + +chmod +x /home/bitwarden/bitwarden.sh +chown bitwarden:bitwarden /home/bitwarden/bitwarden.sh diff --git a/AWSMarketplace/marketplace-image.pkr.hcl b/AWSMarketplace/marketplace-image.pkr.hcl new file mode 100644 index 0000000..7e525dc --- /dev/null +++ b/AWSMarketplace/marketplace-image.pkr.hcl @@ -0,0 +1,169 @@ +packer { + required_plugins { + amazon = { + version = ">= 1.2.0" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "application_name" { + type = string + default = "Bitwarden" +} + +variable "application_version" { + type = string + default = "${env("AWS_IMG_VERSION")}" +} + +variable "apt_packages" { + type = string + default = "fail2ban ca-certificates curl gnupg" +} + +variable "docker_packages" { + type = string + default = "docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" +} + +variable "aws_region" { + type = string + default = "us-east-1" +} + +variable "github_run_id" { + type = string + default = "${env("GITHUB_RUN_ID")}" +} + +# "timestamp" template function replacement +locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } + +locals { + image_name = "bitwarden-22-04-${local.timestamp}" +} + +source "amazon-ebs" "bitwarden_self_host" { + region = var.aws_region + instance_type = "t3.small" + ssh_username = "ubuntu" + + source_ami_filter { + filters = { + name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["099720109477"] # Canonical + } + + ami_name = local.image_name + ami_description = "Bitwarden Self-Host ${var.application_version}" + + tags = { + Name = local.image_name + Application = "bitwarden-packer-build" + Version = var.application_version + GitHub_Run = "github-run-${var.github_run_id}" + } + + run_tags = { + Name = "packer-bitwarden-${var.github_run_id}" + Application = "bitwarden-packer-build" + GitHub_Run = "github-run-${var.github_run_id}" + } +} + +build { + sources = ["source.amazon-ebs.bitwarden_self_host"] + + provisioner "shell" { + inline = ["cloud-init status --wait"] + } + + # Upload individual files to /tmp staging area (amazon-ebs connects as a non-root user) + provisioner "file" { + source = "files/etc/update-motd.d/99-bitwarden-welcome" + destination = "/tmp/99-bitwarden-welcome" + } + + provisioner "file" { + source = "files/etc/ufw/applications.d/bitwarden" + destination = "/tmp/bitwarden-ufw" + } + + provisioner "file" { + source = "files/opt/bitwarden/install-bitwarden.sh" + destination = "/tmp/install-bitwarden.sh" + } + + provisioner "file" { + source = "files/var/lib/cloud/scripts/per-instance/001_onboot" + destination = "/tmp/001_onboot" + } + + provisioner "file" { + source = "files/etc/profile.d/bitwarden-install.sh" + destination = "/tmp/bitwarden-install.sh" + } + + # Move staged files to their final system locations + provisioner "shell" { + inline = [ + "sudo mkdir -p /etc/update-motd.d /etc/ufw/applications.d /opt/bitwarden /var/lib/cloud/scripts/per-instance", + "sudo mv /tmp/99-bitwarden-welcome /etc/update-motd.d/99-bitwarden-welcome", + "sudo mv /tmp/bitwarden-ufw /etc/ufw/applications.d/bitwarden", + "sudo mv /tmp/install-bitwarden.sh /opt/bitwarden/install-bitwarden.sh", + "sudo mv /tmp/001_onboot /var/lib/cloud/scripts/per-instance/001_onboot", + "sudo mv /tmp/bitwarden-install.sh /etc/profile.d/bitwarden-install.sh", + "sudo chown root:root /etc/update-motd.d/99-bitwarden-welcome /etc/ufw/applications.d/bitwarden /opt/bitwarden/install-bitwarden.sh /var/lib/cloud/scripts/per-instance/001_onboot /etc/profile.d/bitwarden-install.sh", + "sudo chmod 644 /etc/ufw/applications.d/bitwarden /etc/profile.d/bitwarden-install.sh" + ] + } + + provisioner "shell" { + environment_vars = [ + "DEBIAN_FRONTEND=noninteractive", + "LC_ALL=C", + "LANG=en_US.UTF-8", + "LC_CTYPE=en_US.UTF-8" + ] + inline = [ + "sudo apt-get -qqy update", + "sudo apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' full-upgrade", + "sudo apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install ${var.apt_packages}", + "sudo install -m 0755 -d /etc/apt/keyrings", + "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg", + "sudo 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/ubuntu $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", + "sudo apt-get -qqy update", + "sudo apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install ${var.docker_packages}", + "sudo apt-get -qqy clean" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E bash '{{ .Path }}'" + environment_vars = [ + "application_name=${var.application_name}", + "application_version=${var.application_version}", + "DEBIAN_FRONTEND=noninteractive", + "LC_ALL=C", + "LANG=en_US.UTF-8", + "LC_CTYPE=en_US.UTF-8" + ] + scripts = [ + "scripts/01-setup-first-run.sh", + "scripts/02-ufw-bitwarden.sh", + "scripts/90-cleanup.sh", + "scripts/99-img-check.sh" + ] + } + + post-processor "manifest" { + output = "manifest.json" + strip_path = true + } +} diff --git a/AWSMarketplace/scripts/01-setup-first-run.sh b/AWSMarketplace/scripts/01-setup-first-run.sh new file mode 100644 index 0000000..55aef3c --- /dev/null +++ b/AWSMarketplace/scripts/01-setup-first-run.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Scripts in this directory are run during the build process. +# Each script will be uploaded to /tmp on your build instance, +# given execute permissions and run. The cleanup process will +# remove the scripts from your build system after they have run +# if you use the build_image task. +# + +# +# Create dedicated bitwarden user with Docker access +# + +useradd -m -s /bin/bash bitwarden +usermod -aG docker bitwarden + +# +# Make MOTD and boot script executable +# + +chmod +x /var/lib/cloud/scripts/per-instance/001_onboot + +chmod +x /etc/update-motd.d/99-bitwarden-welcome + +# +# Setup First Run Script +# + +chmod +x /opt/bitwarden/install-bitwarden.sh diff --git a/AWSMarketplace/scripts/02-ufw-bitwarden.sh b/AWSMarketplace/scripts/02-ufw-bitwarden.sh new file mode 100644 index 0000000..1f3f0bc --- /dev/null +++ b/AWSMarketplace/scripts/02-ufw-bitwarden.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +ufw allow ssh +ufw allow 'Bitwarden' + +ufw --force enable diff --git a/AWSMarketplace/scripts/90-cleanup.sh b/AWSMarketplace/scripts/90-cleanup.sh new file mode 100644 index 0000000..0363ff1 --- /dev/null +++ b/AWSMarketplace/scripts/90-cleanup.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# AWS Marketplace Image Cleanup + +set -o errexit + +# Ensure /tmp exists and has the proper permissions +if [ ! -d /tmp ]; then + mkdir /tmp +fi +chmod 1777 /tmp + +if [ -n "$(command -v apt-get)" ]; then + export DEBIAN_FRONTEND=noninteractive + apt-get -y update + apt-get -o Dpkg::Options::="--force-confold" upgrade -q -y + apt-get -y autoremove + apt-get -y autoclean +fi + +rm -rf /tmp/* /var/tmp/* +cat /dev/null > /root/.bash_history +unset HISTFILE +find /var/log -mtime -1 -type f -exec truncate -s 0 {} \; +rm -rf /var/log/*.gz /var/log/*.[0-9] /var/log/*-???????? +rm -rf /var/lib/cloud/instances/* +rm -f /root/.ssh/authorized_keys /etc/ssh/*key* +touch /etc/ssh/revoked_keys +chmod 600 /etc/ssh/revoked_keys diff --git a/AWSMarketplace/scripts/99-img-check.sh b/AWSMarketplace/scripts/99-img-check.sh new file mode 100644 index 0000000..e0f99d1 --- /dev/null +++ b/AWSMarketplace/scripts/99-img-check.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# AWS Marketplace Image Validation Tool + +VERSION="v. 1.0.0" +RUNDATE=$( date ) + +# Script should be run with SUDO +if [ "$EUID" -ne 0 ] + then echo "[Error] - This script must be run with sudo or as the root user." + exit 1 +fi + +STATUS=0 +PASS=0 +WARN=0 +FAIL=0 + +clear +echo "AWS Marketplace Image Validation Tool ${VERSION}" +echo "Executed on: ${RUNDATE}" +echo "Checking local system for Marketplace compatibility..." +echo "" + +# Check OS +if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$NAME + VER=$VERSION_ID +else + OS=$(uname -s) + VER=$(uname -r) +fi + +echo -en "Distribution: ${OS}\n" +echo -en "Version: ${VER}\n\n" + +if [[ $OS == "Ubuntu" ]] && [[ $VER == "22.04" ]]; then + echo -en "\e[32m[PASS]\e[0m Supported OS detected: ${OS} ${VER}\n" + ((PASS++)) +else + echo -en "\e[41m[FAIL]\e[0m ${OS} ${VER} is not the expected OS (Ubuntu 22.04)\n" + ((FAIL++)) + STATUS=2 +fi + +# Check cloud-init +if hash cloud-init 2>/dev/null; then + echo -en "\e[32m[PASS]\e[0m Cloud-init is installed.\n" + ((PASS++)) +else + echo -en "\e[41m[FAIL]\e[0m Cloud-init is not installed.\n" + ((FAIL++)) + STATUS=2 +fi + +# Check Docker +if hash docker 2>/dev/null; then + echo -en "\e[32m[PASS]\e[0m Docker is installed.\n" + ((PASS++)) +else + echo -en "\e[41m[FAIL]\e[0m Docker is not installed.\n" + ((FAIL++)) + STATUS=2 +fi + +# Check firewall +if [[ $OS == "Ubuntu" ]]; then + ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") + if [[ $ufwa == "active" ]]; then + echo -en "\e[32m[PASS]\e[0m Firewall (ufw) is active.\n" + ((PASS++)) + else + echo -en "\e[93m[WARN]\e[0m Firewall (ufw) is not active.\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then STATUS=1; fi + fi +fi + +# Check root password +SHADOW=$(cat /etc/shadow) +for usr in $SHADOW; do + IFS=':' read -r -a u <<< "$usr" + if [[ "${u[0]}" == "root" ]]; then + if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then + echo -en "\e[32m[PASS]\e[0m Root user has no password set.\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m Root user has a password set.\n" + ((FAIL++)) + STATUS=2 + fi + fi +done + +# Check SSH keys +if [ -f /root/.ssh/authorized_keys ] && [ "$(wc -c < /root/.ssh/authorized_keys)" -gt 50 ]; then + echo -en "\e[41m[FAIL]\e[0m Root has a populated authorized_keys file.\n" + ((FAIL++)) + STATUS=2 +else + echo -en "\e[32m[PASS]\e[0m No SSH keys found for root.\n" + ((PASS++)) +fi + +# Check bash history +if [ -f /root/.bash_history ]; then + BH_S=$(wc -c < /root/.bash_history) + if [[ $BH_S -lt 200 ]]; then + echo -en "\e[32m[PASS]\e[0m Root bash history appears cleared.\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m Root bash history should be cleared.\n" + ((FAIL++)) + STATUS=2 + fi +else + echo -en "\e[32m[PASS]\e[0m Root bash history is not present.\n" + ((PASS++)) +fi + +# Check for log files +echo -en "\nChecking for log files in /var/log\n" +for f in /var/log/*-????????; do + [[ -e $f ]] || break + echo -en "\e[93m[WARN]\e[0m Log archive ${f} found.\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then STATUS=1; fi +done +for f in /var/log/*.[0-9]; do + [[ -e $f ]] || break + echo -en "\e[93m[WARN]\e[0m Log archive ${f} found.\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then STATUS=1; fi +done + +# Summary +echo -en "\n---------------------------------------------------------------------------------------------------\n" + +if [[ $STATUS == 0 ]]; then + echo -en "Scan Complete.\n\e[32mAll Tests Passed!\e[0m\n" +elif [[ $STATUS == 1 ]]; then + echo -en "Scan Complete.\n\e[93mSome non-critical tests failed. Please review these items.\e[0m\n" +else + echo -en "Scan Complete.\n\e[41mOne or more tests failed. Please review these items and re-test.\e[0m\n" +fi +echo "---------------------------------------------------------------------------------------------------" +echo -en "\e[1m${PASS} Tests PASSED\e[0m\n" +echo -en "\e[1m${WARN} WARNINGS\e[0m\n" +echo -en "\e[1m${FAIL} Tests FAILED\e[0m\n" +echo -en "---------------------------------------------------------------------------------------------------\n" + +if [[ $STATUS == 0 ]]; then + echo -en "No issues detected. Ensure all software is functional, secure, and properly configured.\n\n" + exit 0 +elif [[ $STATUS == 1 ]]; then + echo -en "Please review all [WARN] items above and ensure they are intended or resolved.\n\n" + exit 0 +else + echo -en "Critical tests failed. These must be resolved before submitting to AWS Marketplace.\n\n" + exit 1 +fi