diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 330d6f3..1e3437a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,29 +3,31 @@ # These owners will be the default owners for everything in the # repo. Unless a later match takes precedence, these owners will be # requested for review when someone opens a pull request. -* @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj +* @dav3r @felddy @jsf9k @mcdonnnj # These folks own any files in the .github directory at the root of # the repository and any of its subdirectories. -/.github/ @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj +/.github/ @dav3r @felddy @jsf9k @mcdonnnj -# Let jsf9k own the sometimes-touchy AWS and Python playbooks. -/src/aws.yml @jsf9k -/src/python.yml @jsf9k +# Let jsf9k own the sometimes-touchy AWS and Python playbooks, as well +# as the Packer template. +/*.pkr.hcl @jsf9k +/ansible/aws.yml @jsf9k +/ansible/python.yml @jsf9k -# Let dav3r, jasonodoom, jsf9k, and @mcdonnnj share ownership of packer.pkr.hcl. -/src/packer.pkr.hcl @dav3r @jasonodoom @jsf9k @mcdonnnj +# Let dav3r, jsf9k, and @mcdonnnj share ownership of packer.pkr.hcl. +/src/packer.pkr.hcl @dav3r @jsf9k @mcdonnnj # These folks own all linting configuration files. -/.ansible-lint @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.bandit.yml @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.flake8 @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.isort.cfg @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.mdl_config.yaml @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.pre-commit-config.yaml @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.prettierignore @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/.yamllint @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/requirements.txt @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/requirements-dev.txt @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/requirements-test.txt @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj -/setup-env @dav3r @felddy @jasonodoom @jsf9k @mcdonnnj +/.ansible-lint @dav3r @felddy @jsf9k @mcdonnnj +/.bandit.yml @dav3r @felddy @jsf9k @mcdonnnj +/.flake8 @dav3r @felddy @jsf9k @mcdonnnj +/.isort.cfg @dav3r @felddy @jsf9k @mcdonnnj +/.mdl_config.yaml @dav3r @felddy @jsf9k @mcdonnnj +/.pre-commit-config.yaml @dav3r @felddy @jsf9k @mcdonnnj +/.prettierignore @dav3r @felddy @jsf9k @mcdonnnj +/.yamllint @dav3r @felddy @jsf9k @mcdonnnj +/requirements.txt @dav3r @felddy @jsf9k @mcdonnnj +/requirements-dev.txt @dav3r @felddy @jsf9k @mcdonnnj +/requirements-test.txt @dav3r @felddy @jsf9k @mcdonnnj +/setup-env @dav3r @felddy @jsf9k @mcdonnnj diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 54e605c..8112cba 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,9 +13,12 @@ updates: - dependency-name: actions/checkout - dependency-name: actions/setup-go - dependency-name: actions/setup-python + - dependency-name: cisagov/setup-env-github-action - dependency-name: crazy-max/ghaction-dump-context - dependency-name: crazy-max/ghaction-github-labeler - dependency-name: crazy-max/ghaction-github-status + - dependency-name: GitHubSecurityLab/actions-permissions + - dependency-name: hashicorp/setup-packer - dependency-name: hashicorp/setup-terraform - dependency-name: mxschmitt/action-tmate - dependency-name: step-security/harden-runner diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e50bfa..b370991 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,6 @@ defaults: env: AWS_DEFAULT_REGION: us-east-1 - CURL_CACHE_DIR: ~/.cache/curl PIP_CACHE_DIR: ~/.cache/pip PRE_COMMIT_CACHE_DIR: ~/.cache/pre-commit RUN_TMATE: ${{ secrets.RUN_TMATE }} @@ -32,10 +31,18 @@ env: jobs: diagnostics: name: Run diagnostics + # This job does not need any permissions + permissions: {} runs-on: ubuntu-latest steps: # Note that a duplicate of this step must be added at the top of # each job. + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} + # Note that a duplicate of this step must be added at the top of + # each job. - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -50,8 +57,15 @@ jobs: lint: needs: - diagnostics + permissions: + # actions/checkout needs this to fetch code + contents: read runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -77,7 +91,7 @@ jobs: name: Lookup Go cache directory run: | echo "dir=$(go env GOCACHE)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\ py${{ steps.setup-python.outputs.python-version }}-\ @@ -98,25 +112,12 @@ jobs: path: | ${{ env.PIP_CACHE_DIR }} ${{ env.PRE_COMMIT_CACHE_DIR }} - ${{ env.CURL_CACHE_DIR }} ${{ steps.go-cache.outputs.dir }} restore-keys: | ${{ env.BASE_CACHE_KEY }} - - name: Setup curl cache - run: mkdir -p ${{ env.CURL_CACHE_DIR }} - - name: Install Packer - env: - PACKER_VERSION: ${{ steps.setup-env.outputs.packer-version }} - run: | - PACKER_ZIP="packer_${PACKER_VERSION}_linux_amd64.zip" - curl --output ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --time-cond ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --location \ - "https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_ZIP}" - sudo unzip -d /opt/packer \ - ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" - sudo mv /usr/local/bin/packer /usr/local/bin/packer-default - sudo ln -s /opt/packer/packer /usr/local/bin/packer + - uses: hashicorp/setup-packer@v3 + with: + version: ${{ steps.setup-env.outputs.packer-version }} - uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ steps.setup-env.outputs.terraform-version }} @@ -162,11 +163,11 @@ jobs: python -m pip install --upgrade pip setuptools wheel pip install --upgrade --requirement requirements-test.txt - name: Install Ansible roles - run: ansible-galaxy install --force --role-file src/requirements.yml + run: ansible-galaxy install --force --role-file ansible/requirements.yml # This must happen before pre-commit is run or the Packer format # linter will throw an error. - name: Install Packer plugins - run: packer init src + run: packer init . - name: Set up pre-commit hook environments run: pre-commit install-hooks - name: Run pre-commit on all files @@ -177,8 +178,15 @@ jobs: test: needs: - diagnostics + permissions: + # actions/checkout needs this to fetch code + contents: read runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -191,7 +199,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ steps.setup-env.outputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\ py${{ steps.setup-python.outputs.python-version }}-\ @@ -199,27 +207,14 @@ jobs: with: path: | ${{ env.PIP_CACHE_DIR }} - ${{ env.CURL_CACHE_DIR }} key: "${{ env.BASE_CACHE_KEY }}\ ${{ hashFiles('**/requirements-test.txt') }}-\ ${{ hashFiles('**/requirements.txt') }}" restore-keys: | ${{ env.BASE_CACHE_KEY }} - - name: Setup curl cache - run: mkdir -p ${{ env.CURL_CACHE_DIR }} - - name: Install Packer - env: - PACKER_VERSION: ${{ steps.setup-env.outputs.packer-version }} - run: | - PACKER_ZIP="packer_${PACKER_VERSION}_linux_amd64.zip" - curl --output ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --time-cond ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --location \ - "https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_ZIP}" - sudo unzip -d /opt/packer \ - ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" - sudo mv /usr/local/bin/packer /usr/local/bin/packer-default - sudo ln -s /opt/packer/packer /usr/local/bin/packer + - uses: hashicorp/setup-packer@v3 + with: + version: ${{ steps.setup-env.outputs.packer-version }} - name: Install dependencies run: | python -m pip install --upgrade pip @@ -237,6 +232,9 @@ jobs: needs: - lint - test + permissions: + # actions/checkout needs this to fetch code + contents: read runs-on: ubuntu-latest strategy: fail-fast: false @@ -246,6 +244,10 @@ jobs: # - arm64 - x86_64 steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -258,7 +260,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ steps.setup-env.outputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\ py${{ steps.setup-python.outputs.python-version }}-\ @@ -267,26 +269,13 @@ jobs: with: path: | ${{ env.PIP_CACHE_DIR }} - ${{ env.CURL_CACHE_DIR }} key: "${{ env.BASE_CACHE_KEY }}\ ${{ hashFiles('**/requirements.txt') }}" restore-keys: | ${{ env.BASE_CACHE_KEY }} - - name: Setup curl cache - run: mkdir -p ${{ env.CURL_CACHE_DIR }} - - name: Install Packer - env: - PACKER_VERSION: ${{ steps.setup-env.outputs.packer-version }} - run: | - PACKER_ZIP="packer_${PACKER_VERSION}_linux_amd64.zip" - curl --output ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --time-cond ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --location \ - "https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_ZIP}" - sudo unzip -d /opt/packer \ - ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" - sudo mv /usr/local/bin/packer /usr/local/bin/packer-default - sudo ln -s /opt/packer/packer /usr/local/bin/packer + - uses: hashicorp/setup-packer@v3 + with: + version: ${{ steps.setup-env.outputs.packer-version }} - uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ steps.setup-env.outputs.terraform-version }} @@ -296,7 +285,7 @@ jobs: pip install --upgrade \ --requirement requirements.txt - name: Install Ansible roles - run: ansible-galaxy install --force --role-file src/requirements.yml + run: ansible-galaxy install --force --role-file ansible/requirements.yml - name: Assume AWS build role uses: aws-actions/configure-aws-credentials@v4 with: @@ -317,7 +306,7 @@ jobs: sudo ln -s ${{ env.pythonLocation }}/bin/python3 \ /usr/bin/python3 - name: Install Packer plugins - run: packer init src + run: packer init . - name: Create machine image # This runs through the AMI creation process but does not # actually create an AMI @@ -325,7 +314,7 @@ jobs: packer build -only amazon-ebs.${{ matrix.architecture }} \ -timestamp-ui \ -var skip_create_ami=true \ - src/packer.pkr.hcl + . - name: Remove /usr/bin/python3 symlink to the installed Python run: | sudo mv /usr/bin/python3-default /usr/bin/python3 diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index ad268a5..1a1aa33 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -7,17 +7,24 @@ on: env: AWS_DEFAULT_REGION: us-east-1 - CURL_CACHE_DIR: ~/.cache/curl PIP_CACHE_DIR: ~/.cache/pip RUN_TMATE: ${{ secrets.RUN_TMATE }} jobs: diagnostics: name: Run diagnostics + # This job does not need any permissions + permissions: {} runs-on: ubuntu-latest steps: # Note that a duplicate of this step must be added at the top of # each job. + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} + # Note that a duplicate of this step must be added at the top of + # each job. - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -32,6 +39,9 @@ jobs: prerelease: needs: - diagnostics + permissions: + # actions/checkout needs this to fetch code + contents: read runs-on: ubuntu-latest strategy: fail-fast: false @@ -41,6 +51,10 @@ jobs: # - arm64 - x86_64 steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -53,7 +67,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ steps.setup-env.outputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\ py${{ steps.setup-python.outputs.python-version }}-\ @@ -62,26 +76,13 @@ jobs: with: path: | ${{ env.PIP_CACHE_DIR }} - ${{ env.CURL_CACHE_DIR }} key: "${{ env.BASE_CACHE_KEY }}\ ${{ hashFiles('**/requirements.txt') }}" restore-keys: | ${{ env.BASE_CACHE_KEY }} - - name: Setup curl cache - run: mkdir -p ${{ env.CURL_CACHE_DIR }} - - name: Install Packer - env: - PACKER_VERSION: ${{ steps.setup-env.outputs.packer-version }} - run: | - PACKER_ZIP="packer_${PACKER_VERSION}_linux_amd64.zip" - curl --output ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --time-cond ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --location \ - "https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_ZIP}" - sudo unzip -d /opt/packer \ - ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" - sudo mv /usr/local/bin/packer /usr/local/bin/packer-default - sudo ln -s /opt/packer/packer /usr/local/bin/packer + - uses: hashicorp/setup-packer@v3 + with: + version: ${{ steps.setup-env.outputs.packer-version }} - uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ steps.setup-env.outputs.terraform-version }} @@ -91,7 +92,7 @@ jobs: pip install --upgrade \ --requirement requirements.txt - name: Install ansible roles - run: ansible-galaxy install --force --role-file src/requirements.yml + run: ansible-galaxy install --force --role-file ansible/requirements.yml - name: Assume AWS build role uses: aws-actions/configure-aws-credentials@v4 with: @@ -112,7 +113,7 @@ jobs: sudo ln -s ${{ env.pythonLocation }}/bin/python3 \ /usr/bin/python3 - name: Install Packer plugins - run: packer init src + run: packer init . - name: Create machine image run: | packer build -only amazon-ebs.${{ matrix.architecture }} \ @@ -120,7 +121,7 @@ jobs: -var is_prerelease=${{ github.event.release.prerelease }} \ -var release_tag=${{ github.event.release.tag_name }} \ -var release_url=${{ github.event.release.html_url }} \ - src/packer.pkr.hcl + . - name: Remove /usr/bin/python3 symlink to the installed python run: | sudo mv /usr/bin/python3-default /usr/bin/python3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84c3661..9794cf1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,17 +14,24 @@ env: # COPY_REGIONS_KMS_MAP: "us-east-2:alias/cool-amis, # us-west-1:alias/cool-amis, # us-west-2:alias/cool-amis" - CURL_CACHE_DIR: ~/.cache/curl PIP_CACHE_DIR: ~/.cache/pip RUN_TMATE: ${{ secrets.RUN_TMATE }} jobs: diagnostics: name: Run diagnostics + # This job does not need any permissions + permissions: {} runs-on: ubuntu-latest steps: # Note that a duplicate of this step must be added at the top of # each job. + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} + # Note that a duplicate of this step must be added at the top of + # each job. - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -39,6 +46,9 @@ jobs: release: needs: - diagnostics + permissions: + # actions/checkout needs this to fetch code + contents: read runs-on: ubuntu-latest strategy: fail-fast: false @@ -48,6 +58,10 @@ jobs: # - arm64 - x86_64 steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -60,7 +74,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ steps.setup-env.outputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\ py${{ steps.setup-python.outputs.python-version }}-\ @@ -69,26 +83,13 @@ jobs: with: path: | ${{ env.PIP_CACHE_DIR }} - ${{ env.CURL_CACHE_DIR }} key: "${{ env.BASE_CACHE_KEY }}\ ${{ hashFiles('**/requirements.txt') }}" restore-keys: | ${{ env.BASE_CACHE_KEY }} - - name: Setup curl cache - run: mkdir -p ${{ env.CURL_CACHE_DIR }} - - name: Install Packer - env: - PACKER_VERSION: ${{ steps.setup-env.outputs.packer-version }} - run: | - PACKER_ZIP="packer_${PACKER_VERSION}_linux_amd64.zip" - curl --output ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --time-cond ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" \ - --location \ - "https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_ZIP}" - sudo unzip -d /opt/packer \ - ${{ env.CURL_CACHE_DIR }}/"${PACKER_ZIP}" - sudo mv /usr/local/bin/packer /usr/local/bin/packer-default - sudo ln -s /opt/packer/packer /usr/local/bin/packer + - uses: hashicorp/setup-packer@v3 + with: + version: ${{ steps.setup-env.outputs.packer-version }} - uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ steps.setup-env.outputs.terraform-version }} @@ -98,7 +99,7 @@ jobs: pip install --upgrade \ --requirement requirements.txt - name: Install ansible roles - run: ansible-galaxy install --force --role-file src/requirements.yml + run: ansible-galaxy install --force --role-file ansible/requirements.yml # Do not copy the AMI to other regions until we have figured out a # workable mechanism for creating and managing AMI KMS keys in other # regions. @@ -106,7 +107,7 @@ jobs: # - name: Add copy regions to packer configuration # run: | # echo $COPY_REGIONS_KMS_MAP | \ - # ./patch_packer_config.py src/packer.pkr.hcl + # ./patch_packer_config.py variables.pkr.hcl - name: Assume AWS build role uses: aws-actions/configure-aws-credentials@v4 with: @@ -127,7 +128,7 @@ jobs: sudo ln -s ${{ env.pythonLocation }}/bin/python3 \ /usr/bin/python3 - name: Install Packer plugins - run: packer init src + run: packer init . - name: Create machine image run: | packer build -only amazon-ebs.${{ matrix.architecture }} \ @@ -135,7 +136,7 @@ jobs: -var is_prerelease=${{ github.event.release.prerelease }} \ -var release_tag=${{ github.event.release.tag_name }} \ -var release_url=${{ github.event.release.html_url }} \ - src/packer.pkr.hcl + . - name: Remove /usr/bin/python3 symlink to the installed python run: | sudo mv /usr/bin/python3-default /usr/bin/python3 diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 5a20438..0005147 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -4,8 +4,9 @@ name: sync-labels on: push: paths: - - '.github/labels.yml' - - '.github/workflows/sync-labels.yml' + - .github/labels.yml + - .github/workflows/sync-labels.yml + workflow_dispatch: permissions: contents: read @@ -13,10 +14,18 @@ permissions: jobs: diagnostics: name: Run diagnostics + # This job does not need any permissions + permissions: {} runs-on: ubuntu-latest steps: # Note that a duplicate of this step must be added at the top of # each job. + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} + # Note that a duplicate of this step must be added at the top of + # each job. - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 @@ -24,7 +33,7 @@ jobs: egress-policy: audit - id: github-status name: Check GitHub status - uses: crazy-max/ghaction-github-status@v3 + uses: crazy-max/ghaction-github-status@v4 - id: dump-context name: Dump context uses: crazy-max/ghaction-dump-context@v2 @@ -38,6 +47,10 @@ jobs: issues: write runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + # Uses the organization variable unless overridden + config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }} - id: harden-runner name: Harden the runner uses: step-security/harden-runner@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a5000f..026f6b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,22 +4,30 @@ default_language_version: python: python3 repos: + # Check the pre-commit configuration + - repo: meta + hooks: + - id: check-useless-excludes + - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-case-conflict - id: check-executables-have-shebangs - id: check-json - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-symlinks - id: check-toml + - id: check-vcs-permalinks - id: check-xml - id: debug-statements + - id: destroyed-symlinks - id: detect-aws-credentials args: - --allow-missing-credentials - id: detect-private-key - id: end-of-file-fixer - exclude: files/(issue|motd) - id: mixed-line-ending args: - --fix=lf @@ -31,22 +39,15 @@ repos: # Text file hooks - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.41.0 + rev: v0.42.0 hooks: - id: markdownlint args: - --config=.mdl_config.yaml - - repo: https://github.com/pre-commit/mirrors-prettier - # This is the last version of v3 available from the mirror. We should hold - # here until v4, which is currently in alpha, is more stable. - rev: v3.1.0 + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.3.3 hooks: - id: prettier - # This is the latest version of v3 available from NPM. The pre-commit - # mirror does not pull tags for old major versions once a new major - # version tag is published. - additional_dependencies: - - prettier@3.3.1 - repo: https://github.com/adrienverge/yamllint rev: v1.35.1 hooks: @@ -56,14 +57,14 @@ repos: # GitHub Actions hooks - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.4 + rev: 0.29.4 hooks: - id: check-github-actions - id: check-github-workflows # pre-commit hooks - repo: https://github.com/pre-commit/pre-commit - rev: v3.7.1 + rev: v4.0.1 hooks: - id: validate_manifest @@ -71,25 +72,25 @@ repos: - repo: https://github.com/TekWizely/pre-commit-golang rev: v1.0.0-rc.1 hooks: - # Style Checkers - - id: go-critic - # StaticCheck - - id: go-staticcheck-repo-mod # Go Build - id: go-build-repo-mod + # Style Checkers + - id: go-critic + # goimports + - id: go-imports-repo + args: + # Write changes to files + - -w # Go Mod Tidy - id: go-mod-tidy-repo + # GoSec + - id: go-sec-repo-mod + # StaticCheck + - id: go-staticcheck-repo-mod # Go Test - id: go-test-repo-mod # Go Vet - id: go-vet-repo-mod - # GoSec - - id: go-sec-repo-mod - # goimports - - id: go-imports-repo - args: - # Write changes to files - - -w # Nix hooks - repo: https://github.com/nix-community/nixpkgs-fmt rev: v1.3.0 @@ -98,7 +99,7 @@ repos: # Shell script hooks - repo: https://github.com/scop/pre-commit-shfmt - rev: v3.8.0-1 + rev: v3.10.0-1 hooks: - id: shfmt args: @@ -123,7 +124,7 @@ repos: # Python hooks # Run bandit on the "tests" tree with a configuration - repo: https://github.com/PyCQA/bandit - rev: 1.7.8 + rev: 1.7.10 hooks: - id: bandit name: bandit (tests tree) @@ -132,37 +133,54 @@ repos: - --config=.bandit.yml # Run bandit on everything except the "tests" tree - repo: https://github.com/PyCQA/bandit - rev: 1.7.7 + rev: 1.7.10 hooks: - id: bandit name: bandit (everything else) exclude: tests - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.10.0 hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + rev: 7.1.1 hooks: - id: flake8 additional_dependencies: - - flake8-docstrings + - flake8-docstrings==1.7.0 - repo: https://github.com/PyCQA/isort rev: 5.13.2 hooks: - id: isort - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.0 + rev: v1.13.0 hooks: - id: mypy + - repo: https://github.com/pypa/pip-audit + rev: v2.7.3 + hooks: + - id: pip-audit + args: + # We have to ignore this particular vulnerability in + # ansible-core>=2.11 as there is currently no fix. See + # cisagov/skeleton-packer#380 for more details. + - --ignore-vuln + - GHSA-99w6-3xph-cx78 + # Add any pip requirements files to scan + - --requirement + - requirements-dev.txt + - --requirement + - requirements-test.txt + - --requirement + - requirements.txt - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.19.0 hooks: - id: pyupgrade # Ansible hooks - repo: https://github.com/ansible/ansible-lint - rev: v24.6.0 + rev: v24.10.0 hooks: - id: ansible-lint additional_dependencies: @@ -173,21 +191,40 @@ repos: # necessary to add the ansible package itself as an # additional dependency, with the same pinning as is done in # requirements-test.txt of cisagov/skeleton-ansible-role. - # - ansible>=9,<10 + # + # Version 10 is required because the pip-audit pre-commit + # hook identifies a vulnerability in ansible-core 2.16.13, + # but all versions of ansible 9 have a dependency on + # ~=2.16.X. + # + # It is also a good idea to go ahead and upgrade to version + # 10 since version 9 is going EOL at the end of November: + # https://endoflife.date/ansible + # - ansible>=10,<11 # ansible-core 2.16.3 through 2.16.6 suffer from the bug # discussed in ansible/ansible#82702, which breaks any # symlinked files in vars, tasks, etc. for any Ansible role # installed via ansible-galaxy. Hence we never want to # install those versions. # + # Note that the pip-audit pre-commit hook identifies a + # vulnerability in ansible-core 2.16.13. The pin of + # ansible-core to >=2.17 effectively also pins ansible to + # >=10. + # + # It is also a good idea to go ahead and upgrade to + # ansible-core 2.17 since security support for ansible-core + # 2.16 ends this month: + # https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix + # # Note that any changes made to this dependency must also be # made in requirements.txt in cisagov/skeleton-packer and # requirements-test.txt in cisagov/skeleton-ansible-role. - - ansible-core>=2.16.7 + - ansible-core>=2.17 # Terraform hooks - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.90.0 + rev: v1.96.1 hooks: - id: terraform_fmt - id: terraform_validate @@ -200,7 +237,7 @@ repos: # Packer hooks - repo: https://github.com/cisagov/pre-commit-packer - rev: v0.0.2 + rev: v0.3.0 hooks: - - id: packer_validate - id: packer_fmt + - id: packer_validate diff --git a/.terraform-docs.yml b/.terraform-docs.yml new file mode 100644 index 0000000..575b15d --- /dev/null +++ b/.terraform-docs.yml @@ -0,0 +1,14 @@ +--- +formatter: markdown table +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + +settings: + anchor: false + atx-closed: true + html: false + lockfile: false diff --git a/README.md b/README.md index 442c3ed..098f7b8 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ how the build was triggered from GitHub. 1. **Non-release test**: After a normal commit or pull request GitHub Actions will build the project, and run tests and validation on the - packer configuration. It will **not** build an image. + Packer template. It will **not** build an image. 1. **Pre-release deploy**: Publish a GitHub release with the "This is a pre-release" checkbox checked. An image will be built and deployed using the [`prerelease`](.github/workflows/prerelease.yml) @@ -117,20 +117,8 @@ source_profile = build-pca-gophish-composition role_session_name = example ``` -The [Packer template](src/packer.pkr.hcl) defines a number of variables: - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| ami\_regions | The list of AWS regions to copy the AMI to once it has been created. Example: ["us-east-1"] | `list(string)` | `[]` | no | -| build\_region | The region in which to retrieve the base AMI from and build the new AMI. | `string` | `"us-east-1"` | no | -| build\_region\_kms | The ID or ARN of the KMS key to use for AMI encryption. | `string` | `"alias/cool-amis"` | no | -| is\_prerelease | The pre-release status to use for the tags applied to the created AMI. | `bool` | `false` | no | -| region\_kms\_keys | A map of regions to copy the created AMI to and the KMS keys to use for encryption in that region. The keys for this map must match the values provided to the aws\_regions variable. Example: {"us-east-1": "alias/example-kms"} | `map(string)` | `{}` | no | -| release\_tag | The GitHub release tag to use for the tags applied to the created AMI. | `string` | `""` | no | -| release\_url | The GitHub release URL to use for the tags applied to the created AMI. | `string` | `""` | no | -| skip\_create\_ami | Indicate if Packer should not create the AMI. | `bool` | `false` | no | - -Changing these defaults can be done through a `.pkrvars.hcl` file: +This Packer template defines a number of variables whose defaults can be changed +through a `.pkrvars.hcl` file: ```hcl build_region = "us-east-2" @@ -142,12 +130,12 @@ Here is an example of how to kick off a pre-release build: ```console pip install --requirement requirements-dev.txt -ansible-galaxy install --force --force-with-deps --role-file src/requirements.yml -AWS_PROFILE=cool-images-ec2amicreate-pca-gophish-composition-packer packer build --timestamp-ui -var release_tag=$(./bump_version.sh show) -var is_prerelease=true src/packer.pkr.hcl +ansible-galaxy install --force --force-with-deps --role-file ansible/requirements.yml +AWS_PROFILE=cool-images-ec2amicreate-pca-gophish-composition-packer packer build --timestamp-ui -var release_tag=$(./bump-version show) -var is_prerelease=true . ``` If you are satisfied with your pre-release image, you can easily create a release -that deploys to all regions by adding additional regions to the packer configuration. +that deploys to all regions by adding additional regions to the Packer template. This can be done by using a `.pkrvars.hcl` for example with `release.pkrvars.hcl`: ```hcl @@ -160,7 +148,7 @@ region_kms_keys = { ``` ```console -AWS_PROFILE=cool-images-ec2amicreate-pca-gophish-composition-packer packer build --timestamp-ui -var-file release.pkrvars.hcl src/packer.pkr.hcl +AWS_PROFILE=cool-images-ec2amicreate-pca-gophish-composition-packer packer build --timestamp-ui -var-file release.pkrvars.hcl . ``` ### Giving Other AWS Accounts Permission to Launch the Image ### @@ -179,6 +167,46 @@ terraform init --upgrade=true terraform apply ``` + +## Requirements ## + +No requirements. + +## Providers ## + +| Name | Version | +|------|---------| +| amazon-ami | n/a | + +## Modules ## + +No modules. + +## Resources ## + +| Name | Type | +|------|------| +| [amazon-ami_amazon-ami.debian_bookworm_arm64](https://registry.terraform.io/providers/hashicorp/amazon-ami/latest/docs/data-sources/amazon-ami) | data source | +| [amazon-ami_amazon-ami.debian_bookworm_x86_64](https://registry.terraform.io/providers/hashicorp/amazon-ami/latest/docs/data-sources/amazon-ami) | data source | + +## Inputs ## + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| ami\_regions | The list of AWS regions to copy the AMI to once it has been created. Example: ["us-east-1"] | `list(string)` | `[]` | no | +| build\_region | The region in which to retrieve the base AMI from and build the new AMI. | `string` | `"us-east-1"` | no | +| build\_region\_kms | The ID or ARN of the KMS key to use for AMI encryption. | `string` | `"alias/cool-amis"` | no | +| is\_prerelease | The pre-release status to use for the tags applied to the created AMI. | `bool` | `false` | no | +| region\_kms\_keys | A map of regions to copy the created AMI to and the KMS keys to use for encryption in that region. The keys for this map must match the values provided to the aws\_regions variable. Example: {"us-east-1": "alias/example-kms"} | `map(string)` | `{}` | no | +| release\_tag | The GitHub release tag to use for the tags applied to the created AMI. | `string` | `""` | no | +| release\_url | The GitHub release URL to use for the tags applied to the created AMI. | `string` | `""` | no | +| skip\_create\_ami | Indicate if Packer should not create the AMI. | `bool` | `false` | no | + +## Outputs ## + +No outputs. + + ## Contributing ## We welcome contributions! Please see [`CONTRIBUTING.md`](CONTRIBUTING.md) for diff --git a/ami_arm64.pkr.hcl b/ami_arm64.pkr.hcl new file mode 100644 index 0000000..930e0e2 --- /dev/null +++ b/ami_arm64.pkr.hcl @@ -0,0 +1,43 @@ +source "amazon-ebs" "arm64" { + ami_name = "pca-gophish-hvm-${local.timestamp}-arm64-ebs" + ami_regions = var.ami_regions + associate_public_ip_address = true + encrypt_boot = true + instance_type = "t4g.small" + kms_key_id = var.build_region_kms + launch_block_device_mappings { + delete_on_termination = true + device_name = "/dev/xvda" + encrypted = true + volume_size = 8 + volume_type = "gp3" + } + region = var.build_region + region_kms_key_ids = var.region_kms_keys + skip_create_ami = var.skip_create_ami + source_ami = data.amazon-ami.debian_bookworm_arm64.id + ssh_username = "admin" + subnet_filter { + filters = { + "tag:Name" = "AMI Build" + } + } + tags = { + Application = "Phishing Campaign Assessment" + Architecture = "arm64" + Base_AMI_Name = data.amazon-ami.debian_bookworm_arm64.name + GitHub_Release_URL = var.release_url + OS_Version = "Debian Bookworm" + Pre_Release = var.is_prerelease + Release = var.release_tag + Team = "VM Fusion - Development" + } + # Many Linux distributions are now disallowing the use of RSA keys, + # so it makes sense to use an ED25519 key instead. + temporary_key_pair_type = "ed25519" + vpc_filter { + filters = { + "tag:Name" = "AMI Build" + } + } +} diff --git a/ami_x86_64.pkr.hcl b/ami_x86_64.pkr.hcl new file mode 100644 index 0000000..f520b97 --- /dev/null +++ b/ami_x86_64.pkr.hcl @@ -0,0 +1,43 @@ +source "amazon-ebs" "x86_64" { + ami_name = "pca-gophish-hvm-${local.timestamp}-x86_64-ebs" + ami_regions = var.ami_regions + associate_public_ip_address = true + encrypt_boot = true + instance_type = "t3.small" + kms_key_id = var.build_region_kms + launch_block_device_mappings { + delete_on_termination = true + device_name = "/dev/xvda" + encrypted = true + volume_size = 8 + volume_type = "gp3" + } + region = var.build_region + region_kms_key_ids = var.region_kms_keys + skip_create_ami = var.skip_create_ami + source_ami = data.amazon-ami.debian_bookworm_x86_64.id + ssh_username = "admin" + subnet_filter { + filters = { + "tag:Name" = "AMI Build" + } + } + tags = { + Application = "Phishing Campaign Assessment" + Architecture = "x86_64" + Base_AMI_Name = data.amazon-ami.debian_bookworm_x86_64.name + GitHub_Release_URL = var.release_url + OS_Version = "Debian Bookworm" + Pre_Release = var.is_prerelease + Release = var.release_tag + Team = "VM Fusion - Development" + } + # Many Linux distributions are now disallowing the use of RSA keys, + # so it makes sense to use an ED25519 key instead. + temporary_key_pair_type = "ed25519" + vpc_filter { + filters = { + "tag:Name" = "AMI Build" + } + } +} diff --git a/src/aws.yml b/ansible/aws.yml similarity index 100% rename from src/aws.yml rename to ansible/aws.yml diff --git a/src/base.yml b/ansible/base.yml similarity index 100% rename from src/base.yml rename to ansible/base.yml diff --git a/src/cloud_init.yml b/ansible/cloud_init.yml similarity index 100% rename from src/cloud_init.yml rename to ansible/cloud_init.yml diff --git a/src/docker-compose.production.yml b/ansible/docker-compose.production.yml similarity index 100% rename from src/docker-compose.production.yml rename to ansible/docker-compose.production.yml diff --git a/src/extras.yml b/ansible/extras.yml similarity index 100% rename from src/extras.yml rename to ansible/extras.yml diff --git a/src/gophish-shortcut.desktop b/ansible/gophish-shortcut.desktop similarity index 100% rename from src/gophish-shortcut.desktop rename to ansible/gophish-shortcut.desktop diff --git a/src/install-prerequisites-for-netplan-configuration-fix.yml b/ansible/install-prerequisites-for-netplan-configuration-fix.yml similarity index 100% rename from src/install-prerequisites-for-netplan-configuration-fix.yml rename to ansible/install-prerequisites-for-netplan-configuration-fix.yml diff --git a/src/pca-gophish-composition.service b/ansible/pca-gophish-composition.service similarity index 100% rename from src/pca-gophish-composition.service rename to ansible/pca-gophish-composition.service diff --git a/src/pca_gophish.yml b/ansible/pca_gophish.yml similarity index 100% rename from src/pca_gophish.yml rename to ansible/pca_gophish.yml diff --git a/src/playbook.yml b/ansible/playbook.yml similarity index 100% rename from src/playbook.yml rename to ansible/playbook.yml diff --git a/src/postfix-users.txt b/ansible/postfix-users.txt similarity index 100% rename from src/postfix-users.txt rename to ansible/postfix-users.txt diff --git a/src/python.yml b/ansible/python.yml similarity index 100% rename from src/python.yml rename to ansible/python.yml diff --git a/src/requirements.yml b/ansible/requirements.yml similarity index 100% rename from src/requirements.yml rename to ansible/requirements.yml diff --git a/src/upgrade.yml b/ansible/upgrade.yml similarity index 100% rename from src/upgrade.yml rename to ansible/upgrade.yml diff --git a/src/vnc.yml b/ansible/vnc.yml similarity index 100% rename from src/vnc.yml rename to ansible/vnc.yml diff --git a/src/xfce.yml b/ansible/xfce.yml similarity index 100% rename from src/xfce.yml rename to ansible/xfce.yml diff --git a/base_amis.pkr.hcl b/base_amis.pkr.hcl new file mode 100644 index 0000000..8f9f472 --- /dev/null +++ b/base_amis.pkr.hcl @@ -0,0 +1,23 @@ +data "amazon-ami" "debian_bookworm_arm64" { + filters = { + architecture = "arm64" + name = "debian-12-arm64-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["136693071363"] + region = var.build_region +} + +data "amazon-ami" "debian_bookworm_x86_64" { + filters = { + architecture = "x86_64" + name = "debian-12-amd64-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["136693071363"] + region = var.build_region +} diff --git a/build.pkr.hcl b/build.pkr.hcl new file mode 100644 index 0000000..cf98592 --- /dev/null +++ b/build.pkr.hcl @@ -0,0 +1,32 @@ +build { + sources = [ + # The gophish-tools Docker image only supports x86_64. + # "source.amazon-ebs.arm64", + "source.amazon-ebs.x86_64", + ] + + provisioner "ansible" { + playbook_file = "ansible/upgrade.yml" + use_proxy = false + use_sftp = true + } + + provisioner "ansible" { + playbook_file = "ansible/python.yml" + use_proxy = false + use_sftp = true + } + + provisioner "ansible" { + ansible_env_vars = ["AWS_DEFAULT_REGION=${var.build_region}"] + playbook_file = "ansible/playbook.yml" + use_proxy = false + use_sftp = true + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; sudo env {{ .Vars }} {{ .Path }} ; rm -f {{ .Path }}" + inline = ["sed -i '/^users:/ {N; s/users:.*/users: []/g}' /etc/cloud/cloud.cfg", "rm --force /etc/sudoers.d/90-cloud-init-users", "rm --force /root/.ssh/authorized_keys", "/usr/sbin/userdel --remove --force admin"] + skip_clean = true + } +} diff --git a/bump-version b/bump-version new file mode 100755 index 0000000..716613c --- /dev/null +++ b/bump-version @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +# bump-version [--push] [--label LABEL] (major | minor | patch | prerelease | build | finalize | show) +# bump-version --list-files + +set -o nounset +set -o errexit +set -o pipefail + +# Stores the canonical version for the project. +VERSION_FILE=version.txt +# Files that should be updated with the new version. +VERSION_FILES=("$VERSION_FILE") + +USAGE=$( + cat << END_OF_LINE +Update the version of the project. + +Usage: + ${0##*/} [--push] [--label LABEL] (major | minor | patch | prerelease | build | finalize | show) + ${0##*/} --list-files + ${0##*/} (-h | --help) + +Options: + -h | --help Show this message. + --push Perform a \`git push\` after updating the version. + --label LABEL Specify the label to use when updating the build or prerelease version. + --list-files List the files that will be updated when the version is bumped. +END_OF_LINE +) + +old_version=$(< "$VERSION_FILE") +# Comment out periods so they are interpreted as periods and don't +# just match any character +old_version_regex=${old_version//\./\\\.} +new_version="$old_version" + +bump_part="" +label="" +commit_prefix="Bump" +with_push=false +commands_with_label=("build" "prerelease") +commands_with_prerelease=("major" "minor" "patch") +with_prerelease=false + +####################################### +# Display an error message, the help information, and exit with a non-zero status. +# Arguments: +# Error message. +####################################### +function invalid_option() { + echo "$1" + echo "$USAGE" + exit 1 +} + +####################################### +# Bump the version using the provided command. +# Arguments: +# The version to bump. +# The command to bump the version. +# Returns: +# The new version. +####################################### +function bump_version() { + local temp_version + temp_version=$(python -c "import semver; print(semver.parse_version_info('$1').${2})") + echo "$temp_version" +} + +if [ $# -eq 0 ]; then + echo "$USAGE" + exit 1 +else + while [ $# -gt 0 ]; do + case $1 in + --push) + if [ "$with_push" = true ]; then + invalid_option "Push has already been set." + fi + + with_push=true + shift + ;; + --label) + if [ -n "$label" ]; then + invalid_option "Label has already been set." + fi + + label="$2" + shift 2 + ;; + build | finalize | major | minor | patch) + if [ -n "$bump_part" ]; then + invalid_option "Only one version part should be bumped at a time." + fi + + bump_part="$1" + shift + ;; + prerelease) + with_prerelease=true + shift + ;; + show) + echo "$old_version" + exit 0 + ;; + -h | --help) + echo "$USAGE" + exit 0 + ;; + --list-files) + printf '%s\n' "${VERSION_FILES[@]}" + exit 0 + ;; + *) + invalid_option "Invalid option: $1" + ;; + esac + done +fi + +if [ -n "$label" ] && [ "$with_prerelease" = false ] && [[ ! " ${commands_with_label[*]} " =~ [[:space:]]${bump_part}[[:space:]] ]]; then + invalid_option "Setting the label is only allowed for the following commands: ${commands_with_label[*]}" +fi + +if [ "$with_prerelease" = true ] && [[ ! " ${commands_with_prerelease[*]} " =~ [[:space:]]${bump_part}[[:space:]] ]]; then + invalid_option "Changing the prerelease is only allowed in conjunction with the following commands: ${commands_with_prerelease[*]}" +fi + +label_option="" +if [ -n "$label" ]; then + label_option="token='$label'" +fi + +if [ -n "$bump_part" ]; then + if [ "$bump_part" = "finalize" ]; then + commit_prefix="Finalize" + bump_command="finalize_version()" + elif [ "$bump_part" = "build" ]; then + bump_command="bump_${bump_part}($label_option)" + else + bump_command="bump_${bump_part}()" + fi + new_version=$(bump_version "$old_version" "$bump_command") + echo Changing version from "$old_version" to "$new_version" +fi + +if [ "$with_prerelease" = true ]; then + bump_command="bump_prerelease($label_option)" + temp_version=$(bump_version "$new_version" "$bump_command") + echo Changing version from "$new_version" to "$temp_version" + new_version="$temp_version" +fi + +tmp_file=/tmp/version.$$ +for version_file in "${VERSION_FILES[@]}"; do + if [ ! -f "$version_file" ]; then + echo Missing expected file: "$version_file" + exit 1 + fi + sed "s/$old_version_regex/$new_version/" "$version_file" > $tmp_file + mv $tmp_file "$version_file" +done + +git add "${VERSION_FILES[@]}" +git commit --message "$commit_prefix version from $old_version to $new_version" + +if [ "$with_push" = true ]; then + git push +fi diff --git a/bump_version.sh b/bump_version.sh deleted file mode 100755 index 49e6136..0000000 --- a/bump_version.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash - -# bump_version.sh (show|major|minor|patch|prerelease|build) - -set -o nounset -set -o errexit -set -o pipefail - -VERSION_FILE=src/version.txt - -HELP_INFORMATION="bump_version.sh (show|major|minor|patch|prerelease|build|finalize)" - -old_version=$(sed -n "s/^__version__ = \"\(.*\)\"$/\1/p" $VERSION_FILE) -# Comment out periods so they are interpreted as periods and don't -# just match any character -old_version_regex=${old_version//\./\\\.} - -if [ $# -ne 1 ]; then - echo "$HELP_INFORMATION" -else - case $1 in - major | minor | patch | prerelease | build) - new_version=$(python -c "import semver; print(semver.bump_$1('$old_version'))") - echo Changing version from "$old_version" to "$new_version" - tmp_file=/tmp/version.$$ - sed "s/$old_version_regex/$new_version/" $VERSION_FILE > $tmp_file - mv $tmp_file $VERSION_FILE - git add $VERSION_FILE - git commit -m"Bump version from $old_version to $new_version" - git push - ;; - finalize) - new_version=$(python -c "import semver; print(semver.finalize_version('$old_version'))") - echo Changing version from "$old_version" to "$new_version" - tmp_file=/tmp/version.$$ - sed "s/$old_version_regex/$new_version/" $VERSION_FILE > $tmp_file - mv $tmp_file $VERSION_FILE - git add $VERSION_FILE - git commit -m"Finalize version from $old_version to $new_version" - git push - ;; - show) - echo "$old_version" - ;; - *) - echo "$HELP_INFORMATION" - ;; - esac -fi diff --git a/locals.pkr.hcl b/locals.pkr.hcl new file mode 100644 index 0000000..90911c5 --- /dev/null +++ b/locals.pkr.hcl @@ -0,0 +1,3 @@ +locals { + timestamp = regex_replace(timestamp(), "[- TZ:]", "") +} diff --git a/requirements.txt b/requirements.txt index bdf26b2..d98d7ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,22 +8,39 @@ # as using the dnf package manager, and version 8 is currently the # oldest supported version. # -# We have tested against version 9. We want to avoid automatically +# Version 10 is required because the pip-audit pre-commit hook +# identifies a vulnerability in ansible-core 2.16.13, but all versions +# of ansible 9 have a dependency on ~=2.16.X. +# +# It is also a good idea to go ahead and upgrade to version 10 since +# version 9 is going EOL at the end of November: +# https://endoflife.date/ansible +# +# We have tested against version 10. We want to avoid automatically # jumping to another major version without testing, since there are # often breaking changes across major versions. This is the reason # for the upper bound. -ansible>=8,<10 +ansible>=10,<11 # ansible-core 2.16.3 through 2.16.6 suffer from the bug discussed in # ansible/ansible#82702, which breaks any symlinked files in vars, # tasks, etc. for any Ansible role installed via ansible-galaxy. # Hence we never want to install those versions. # +# Note that the pip-audit pre-commit hook identifies a vulnerability +# in ansible-core 2.16.13. Normally we would pin ansible-core +# accordingly (>2.16.13), but the above pin of ansible>=10 effectively +# pins ansible-core to >=2.17 anyway so that's what we use. +# +# It is also a good idea to go ahead and upgrade to ansible-core 2.17 +# since security support for ansible-core 2.16 ends this month: +# https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix +# # Note that any changes made to this dependency must also be made in # requirements-test.txt in cisagov/skeleton-ansible-role and # .pre-commit-config.yaml in cisagov/skeleton-generic. -ansible-core>=2.16.7 +ansible-core>=2.17 boto3 docopt -semver +semver>=3 setuptools wheel diff --git a/setup-env b/setup-env index 7d42d25..4004b77 100755 --- a/setup-env +++ b/setup-env @@ -39,6 +39,52 @@ python_versions() { pyenv versions --bare --skip-aliases --skip-envs } +check_python_version() { + local version=$1 + + # This is a valid regex for semantically correct Python version strings. + # For more information see here: + # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + # Break down the regex into readable parts major.minor.patch + local major="0|[1-9]\d*" + local minor="0|[1-9]\d*" + local patch="0|[1-9]\d*" + + # Splitting the prerelease part for readability + # Start of the prerelease + local prerelease="(?:-" + # Numeric or alphanumeric identifiers + local prerelease+="(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" + # Additional dot-separated identifiers + local prerelease+="(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*" + # End of the prerelease, making it optional + local prerelease+=")?" + # Optional build metadata + local build="(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?" + + # Final regex composed of parts + local regex="^($major)\.($minor)\.($patch)$prerelease$build$" + + # This checks if the Python version does not match the regex pattern specified in $regex, + # using Perl for regex matching. If the pattern is not found, then prompt the user with + # the invalid version message. + if ! echo "$version" | perl -ne "exit(!/$regex/)"; then + echo "Invalid version of Python: Python follows semantic versioning," \ + "so any version string that is not a valid semantic version is an" \ + "invalid version of Python." + exit 1 + # Else if the Python version isn't installed then notify the user. + # grep -E is used for searching through text lines that match the specific verison. + elif ! python_versions | grep -E "^${version}$" > /dev/null; then + echo "Error: Python version $version is not installed." + echo "Installed Python versions are:" + python_versions + exit 1 + else + echo "Using Python version $version" + fi +} + # Flag to force deletion and creation of virtual environment FORCE=0 @@ -144,17 +190,8 @@ while true; do -p | --python-version) PYTHON_VERSION="$2" shift 2 - # Check the Python versions being passed in. - if [ -n "${PYTHON_VERSION+x}" ]; then - if python_versions | grep -E "^${PYTHON_VERSION}$" > /dev/null; then - echo Using Python version "$PYTHON_VERSION" - else - echo Error: Python version "$PYTHON_VERSION" is not installed. - echo Installed Python versions are: - python_versions - exit 1 - fi - fi + # Check the Python version being passed in. + check_python_version "$PYTHON_VERSION" ;; -v | --venv-name) VENV_NAME="$2" @@ -188,15 +225,8 @@ if [ $LIST_VERSIONS -ne 0 ]; then # Read the user's desired Python version. # -r: treat backslashes as literal, -p: display prompt before input. read -r -p "Enter the desired Python version: " PYTHON_VERSION - # Check the Python versions being passed in. - if [ -n "${PYTHON_VERSION+x}" ]; then - if python_versions | grep -E "^${PYTHON_VERSION}$" > /dev/null; then - echo Using Python version "$PYTHON_VERSION" - else - echo Error: Python version "$PYTHON_VERSION" is not installed. - exit 1 - fi - fi + # Check the Python version being passed in. + check_python_version "$PYTHON_VERSION" fi # Remove any lingering local configuration. @@ -251,7 +281,7 @@ for req_file in "requirements-dev.txt" "requirements-test.txt" "requirements.txt done # Install Packer plugin dependencies -packer init -upgrade src +packer init -upgrade . # Install git pre-commit hooks now or later. pre-commit install ${INSTALL_HOOKS:+"--install-hooks"} diff --git a/src/packer.pkr.hcl b/src/packer.pkr.hcl deleted file mode 100644 index 8415db4..0000000 --- a/src/packer.pkr.hcl +++ /dev/null @@ -1,213 +0,0 @@ -packer { - required_plugins { - amazon = { - source = "github.com/hashicorp/amazon" - version = "~> 1.2" - } - ansible = { - source = "github.com/hashicorp/ansible" - version = "~> 1.1" - } - } - # The required_plugins section is only supported in Packer 1.7.0 and - # later. We also want to avoid jumping to Packer v2 until we are - # ready. - required_version = "~> 1.7" -} - -variable "ami_regions" { - default = [] - description = "The list of AWS regions to copy the AMI to once it has been created. Example: [\"us-east-1\"]" - type = list(string) -} - -variable "build_region" { - default = "us-east-1" - description = "The region in which to retrieve the base AMI from and build the new AMI." - type = string -} - -variable "build_region_kms" { - default = "alias/cool-amis" - description = "The ID or ARN of the KMS key to use for AMI encryption." - type = string -} - -variable "is_prerelease" { - default = false - description = "The pre-release status to use for the tags applied to the created AMI." - type = bool -} - -variable "region_kms_keys" { - default = {} - description = "A map of regions to copy the created AMI to and the KMS keys to use for encryption in that region. The keys for this map must match the values provided to the aws_regions variable. Example: {\"us-east-1\": \"alias/example-kms\"}" - type = map(string) -} - -variable "release_tag" { - default = "" - description = "The GitHub release tag to use for the tags applied to the created AMI." - type = string -} - -variable "release_url" { - default = "" - description = "The GitHub release URL to use for the tags applied to the created AMI." - type = string -} - -variable "skip_create_ami" { - default = false - description = "Indicate if Packer should not create the AMI." - type = bool -} - -# The gophish-tools Docker image only supports x86_64. -# data "amazon-ami" "debian_bookworm_arm64" { -# filters = { -# architecture = "arm64" -# name = "debian-12-arm64-*" -# root-device-type = "ebs" -# virtualization-type = "hvm" -# } -# most_recent = true -# owners = ["136693071363"] -# region = var.build_region -# } - -data "amazon-ami" "debian_bookworm_x86_64" { - filters = { - architecture = "x86_64" - name = "debian-12-amd64-*" - root-device-type = "ebs" - virtualization-type = "hvm" - } - most_recent = true - owners = ["136693071363"] - region = var.build_region -} - -locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } - -# The gophish-tools Docker image only supports x86_64. -# source "amazon-ebs" "arm64" { -# ami_name = "pca-gophish-hvm-${local.timestamp}-arm64-ebs" -# ami_regions = var.ami_regions -# associate_public_ip_address = true -# encrypt_boot = true -# instance_type = "t4g.small" -# kms_key_id = var.build_region_kms -# launch_block_device_mappings { -# delete_on_termination = true -# device_name = "/dev/xvda" -# encrypted = true -# volume_size = 8 -# volume_type = "gp3" -# } -# region = var.build_region -# region_kms_key_ids = var.region_kms_keys -# skip_create_ami = var.skip_create_ami -# source_ami = data.amazon-ami.debian_bookworm_arm64.id -# ssh_username = "admin" -# subnet_filter { -# filters = { -# "tag:Name" = "AMI Build" -# } -# } -# tags = { -# Application = "Phishing Campaign Assessment" -# Architecture = "arm64" -# Base_AMI_Name = data.amazon-ami.debian_bookworm_arm64.name -# GitHub_Release_URL = var.release_url -# OS_Version = "Debian Bookworm" -# Pre_Release = var.is_prerelease -# Release = var.release_tag -# Team = "VM Fusion - Development" -# } -# # Many Linux distributions are now disallowing the use of RSA keys, -# # so it makes sense to use an ED25519 key instead. -# temporary_key_pair_type = "ed25519" -# vpc_filter { -# filters = { -# "tag:Name" = "AMI Build" -# } -# } -# } - -source "amazon-ebs" "x86_64" { - ami_name = "pca-gophish-hvm-${local.timestamp}-x86_64-ebs" - ami_regions = var.ami_regions - associate_public_ip_address = true - encrypt_boot = true - instance_type = "t3.small" - kms_key_id = var.build_region_kms - launch_block_device_mappings { - delete_on_termination = true - device_name = "/dev/xvda" - encrypted = true - volume_size = 8 - volume_type = "gp3" - } - region = var.build_region - region_kms_key_ids = var.region_kms_keys - skip_create_ami = var.skip_create_ami - source_ami = data.amazon-ami.debian_bookworm_x86_64.id - ssh_username = "admin" - subnet_filter { - filters = { - "tag:Name" = "AMI Build" - } - } - tags = { - Application = "Phishing Campaign Assessment" - Architecture = "x86_64" - Base_AMI_Name = data.amazon-ami.debian_bookworm_x86_64.name - GitHub_Release_URL = var.release_url - OS_Version = "Debian Bookworm" - Pre_Release = var.is_prerelease - Release = var.release_tag - Team = "VM Fusion - Development" - } - # Many Linux distributions are now disallowing the use of RSA keys, - # so it makes sense to use an ED25519 key instead. - temporary_key_pair_type = "ed25519" - vpc_filter { - filters = { - "tag:Name" = "AMI Build" - } - } -} - -build { - sources = [ - # The gophish-tools Docker image only supports x86_64. - # "source.amazon-ebs.arm64", - "source.amazon-ebs.x86_64", - ] - - provisioner "ansible" { - playbook_file = "src/upgrade.yml" - use_proxy = false - use_sftp = true - } - - provisioner "ansible" { - playbook_file = "src/python.yml" - use_proxy = false - use_sftp = true - } - - provisioner "ansible" { - ansible_env_vars = ["AWS_DEFAULT_REGION=${var.build_region}"] - playbook_file = "src/playbook.yml" - use_proxy = false - use_sftp = true - } - - provisioner "shell" { - execute_command = "chmod +x {{ .Path }}; sudo env {{ .Vars }} {{ .Path }} ; rm -f {{ .Path }}" - inline = ["sed -i '/^users:/ {N; s/users:.*/users: []/g}' /etc/cloud/cloud.cfg", "rm --force /etc/sudoers.d/90-cloud-init-users", "rm --force /root/.ssh/authorized_keys", "/usr/sbin/userdel --remove --force admin"] - skip_clean = true - } -} diff --git a/src/version.txt b/src/version.txt deleted file mode 100644 index 7225152..0000000 --- a/src/version.txt +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.5.2" diff --git a/tests/test_version.py b/tests/test_version.py index f9f5184..cdf70cf 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,5 +1,4 @@ -#!/usr/bin/env pytest -vs -"""Version tests for packer project.""" +"""Version tests for pca-gophish-composition-packer project.""" # Standard Python Libraries import os @@ -8,7 +7,7 @@ import pytest GITHUB_RELEASE_TAG = os.getenv("GITHUB_RELEASE_TAG") -VERSION_FILE = "src/version.txt" +VERSION_FILE = "version.txt" @pytest.mark.skipif( @@ -17,10 +16,8 @@ ) def test_release_version(): """Verify that release tag version agrees with the module version.""" - pkg_vars = {} with open(VERSION_FILE) as f: - exec(f.read(), pkg_vars) # nosec - project_version = pkg_vars["__version__"] + project_version = f.read().strip() assert ( GITHUB_RELEASE_TAG == f"v{project_version}" ), "GITHUB_RELEASE_TAG does not match the project version" diff --git a/variables.pkr.hcl b/variables.pkr.hcl new file mode 100644 index 0000000..7df066f --- /dev/null +++ b/variables.pkr.hcl @@ -0,0 +1,59 @@ +# ------------------------------------------------------------------------------ +# Required parameters +# +# You must provide a value for each of these parameters. +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# Optional parameters +# +# These parameters have reasonable defaults. +# ------------------------------------------------------------------------------ + +variable "ami_regions" { + default = [] + description = "The list of AWS regions to copy the AMI to once it has been created. Example: [\"us-east-1\"]" + type = list(string) +} + +variable "build_region" { + default = "us-east-1" + description = "The region in which to retrieve the base AMI from and build the new AMI." + type = string +} + +variable "build_region_kms" { + default = "alias/cool-amis" + description = "The ID or ARN of the KMS key to use for AMI encryption." + type = string +} + +variable "is_prerelease" { + default = false + description = "The pre-release status to use for the tags applied to the created AMI." + type = bool +} + +variable "region_kms_keys" { + default = {} + description = "A map of regions to copy the created AMI to and the KMS keys to use for encryption in that region. The keys for this map must match the values provided to the aws_regions variable. Example: {\"us-east-1\": \"alias/example-kms\"}" + type = map(string) +} + +variable "release_tag" { + default = "" + description = "The GitHub release tag to use for the tags applied to the created AMI." + type = string +} + +variable "release_url" { + default = "" + description = "The GitHub release URL to use for the tags applied to the created AMI." + type = string +} + +variable "skip_create_ami" { + default = false + description = "Indicate if Packer should not create the AMI." + type = bool +} diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..cb0c939 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.5.2 diff --git a/versions.pkr.hcl b/versions.pkr.hcl new file mode 100644 index 0000000..9017550 --- /dev/null +++ b/versions.pkr.hcl @@ -0,0 +1,16 @@ +packer { + required_plugins { + amazon = { + source = "github.com/hashicorp/amazon" + version = "~> 1.2" + } + ansible = { + source = "github.com/hashicorp/ansible" + version = "~> 1.1" + } + } + # The required_plugins section is only supported in Packer 1.7.0 and + # later. We also want to avoid jumping to Packer v2 until we are + # ready. + required_version = "~> 1.7" +}