├── version.txt
├── pytest.ini
├── terraform-build-user
├── .terraform-docs.yml
├── caller_identity.tf
├── main.tf
├── versions.tf
├── backend.tf
├── variables.tf
├── .terraform.lock.hcl
├── remote_states.tf
├── README.md
└── providers.tf
├── terraform-post-packer
├── .terraform-docs.yml
├── versions.tf
├── outputs.tf
├── providers.tf
├── backend.tf
├── .terraform.lock.hcl
├── variables.tf
├── README.md
└── main.tf
├── requirements-test.txt
├── locals.pkr.hcl
├── .github
├── lineage.yml
├── CODEOWNERS
├── dependabot.yml
├── labeler.yml
├── labels.yml
└── workflows
│ ├── label-prs.yml
│ ├── sync-labels.yml
│ ├── dependency-review.yml
│ ├── codeql-analysis.yml
│ ├── prerelease.yml
│ ├── release.yml
│ └── build.yml
├── requirements-dev.txt
├── .prettierignore
├── .lgtm.yml
├── ansible
├── upgrade.yml
├── playbook.yml
├── python.yml
├── aws.yml
├── install-prerequisites-for-netplan-configuration-fix.yml
├── base.yml
├── requirements.yml
└── guacamole.yml
├── .terraform-docs.yml
├── .isort.cfg
├── .gitignore
├── versions.pkr.hcl
├── .bandit.yml
├── base_amis.pkr.hcl
├── tests
├── test_version.py
└── conftest.py
├── requirements.txt
├── .ansible-lint
├── .flake8
├── .mdl_config.yaml
├── ami_arm64.pkr.hcl
├── ami_x86_64.pkr.hcl
├── .yamllint
├── variables.pkr.hcl
├── bump-version
├── LICENSE
├── CONTRIBUTING.md
├── .pre-commit-config.yaml
├── setup-env
└── README.md
/version.txt:
--------------------------------------------------------------------------------
1 | 2.0.1
2 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts = -v -ra
3 |
--------------------------------------------------------------------------------
/terraform-build-user/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | ../.terraform-docs.yml
--------------------------------------------------------------------------------
/terraform-post-packer/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | ../.terraform-docs.yml
--------------------------------------------------------------------------------
/requirements-test.txt:
--------------------------------------------------------------------------------
1 | --requirement requirements.txt
2 | pre-commit
3 | pytest
4 |
--------------------------------------------------------------------------------
/locals.pkr.hcl:
--------------------------------------------------------------------------------
1 | locals {
2 | timestamp = regex_replace(timestamp(), "[- TZ:]", "")
3 | }
4 |
--------------------------------------------------------------------------------
/.github/lineage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | lineage:
3 | skeleton:
4 | remote-url: https://github.com/cisagov/skeleton-packer.git
5 | version: "1"
6 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | --requirement requirements-test.txt
2 | ipython
3 | # The bump-version script requires at least version 3 of semver.
4 | semver>=3
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Already being linted by pretty-format-json
2 | *.json
3 | # Already being linted by mdl
4 | *.md
5 | # Already being linted by yamllint
6 | *.yaml
7 | *.yml
8 |
--------------------------------------------------------------------------------
/.lgtm.yml:
--------------------------------------------------------------------------------
1 | ---
2 | extraction:
3 | python:
4 | python_setup:
5 | version: 3
6 | requirements_files:
7 | - requirements-test.txt
8 | setup_py: false
9 |
--------------------------------------------------------------------------------
/ansible/upgrade.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Upgrade base image
3 | hosts: all
4 | become: true
5 | become_method: ansible.builtin.sudo
6 | tasks:
7 | - name: Upgrade all packages
8 | ansible.builtin.include_role:
9 | name: upgrade
10 |
--------------------------------------------------------------------------------
/terraform-build-user/caller_identity.tf:
--------------------------------------------------------------------------------
1 | # Retrieve the effective Account ID, User ID, and ARN in which Terraform is
2 | # authorized. This is used to calculate the session names for assumed roles.
3 | data "aws_caller_identity" "terraform_backend" {
4 | provider = aws.cool-terraform-backend
5 | }
6 |
--------------------------------------------------------------------------------
/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | ---
2 | formatter: markdown table
3 | output:
4 | file: README.md
5 | mode: inject
6 | template: |-
7 |
8 | {{ .Content }}
9 |
10 | settings:
11 | anchor: false
12 | atx-closed: true
13 | html: false
14 | lockfile: false
15 |
--------------------------------------------------------------------------------
/.isort.cfg:
--------------------------------------------------------------------------------
1 | [settings]
2 | combine_star=true
3 | force_sort_within_sections=true
4 |
5 | import_heading_stdlib=Standard Python Libraries
6 | import_heading_thirdparty=Third-Party Libraries
7 | import_heading_firstparty=cisagov Libraries
8 |
9 | # Run isort under the black profile to align with our other Python linting
10 | profile=black
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file specifies intentionally untracked files that Git should ignore.
2 | # Files already tracked by Git are not affected.
3 | # See: https://git-scm.com/docs/gitignore
4 |
5 | ## Packer ##
6 | packer_cache
7 |
8 | ## Python ##
9 | __pycache__
10 | .coverage
11 | .mypy_cache
12 | .pytest_cache
13 | .python-version
14 | *.egg-info
15 | dist
16 |
17 | ## Terraform ##
18 | .terraform
19 | terraform.tfstate
20 | terraform.tfstate.backup
21 | *.tfconfig
22 | *.tfvars
23 |
--------------------------------------------------------------------------------
/versions.pkr.hcl:
--------------------------------------------------------------------------------
1 | packer {
2 | required_plugins {
3 | amazon = {
4 | source = "github.com/hashicorp/amazon"
5 | version = "~> 1.2"
6 | }
7 | ansible = {
8 | source = "github.com/hashicorp/ansible"
9 | version = "~> 1.1"
10 | }
11 | }
12 | # The required_plugins section is only supported in Packer 1.7.0 and
13 | # later. We also want to avoid jumping to Packer v2 until we are
14 | # ready.
15 | required_version = "~> 1.7"
16 | }
17 |
--------------------------------------------------------------------------------
/.bandit.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Configuration file for the Bandit python security scanner
3 | # https://bandit.readthedocs.io/en/latest/config.html
4 | # This config is applied to bandit when scanning the "tests" tree
5 |
6 | # Tests are first included by `tests`, and then excluded by `skips`.
7 | # If `tests` is empty, all tests are considered included.
8 |
9 | tests:
10 | # - B101
11 | # - B102
12 |
13 | skips:
14 | - B101 # skip "assert used" check since assertions are required in pytests
15 |
--------------------------------------------------------------------------------
/ansible/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Import base image playbook
3 | ansible.builtin.import_playbook: base.yml
4 |
5 | # TODO: Remove this when and if that becomes possible. See
6 | # cisagov/skeleton-packer#301.
7 | - name: >-
8 | Import playbook to install prerequisites for the Netplan
9 | configuration fix
10 | ansible.builtin.import_playbook: install-prerequisites-for-netplan-configuration-fix.yml
11 |
12 | - name: Import guacamole playbook
13 | ansible.builtin.import_playbook: guacamole.yml
14 |
15 | - name: Import AWS playbook
16 | ansible.builtin.import_playbook: aws.yml
17 |
--------------------------------------------------------------------------------
/terraform-build-user/main.tf:
--------------------------------------------------------------------------------
1 | module "iam_user" {
2 | source = "github.com/cisagov/ami-build-iam-user-tf-module"
3 |
4 | providers = {
5 | aws = aws
6 | aws.images-ami = aws.images-ami
7 | aws.images-ssm = aws.images-ssm
8 | }
9 |
10 | ssm_parameters = [
11 | "/guacamole/postgres_username",
12 | "/guacamole/postgres_password",
13 | "/rdp/username",
14 | "/rdp/password",
15 | "/vnc/ssh/ed25519_private_key",
16 | "/vnc/username",
17 | "/vnc/password",
18 | "/vnc/sftp/windows_base_directory",
19 | ]
20 | user_name = "build-guacamole-packer"
21 | }
22 |
--------------------------------------------------------------------------------
/terraform-build-user/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Version 1.1 of Terraform is the first version to support the
3 | # nullable key in variable definitions.
4 | required_version = "~> 1.1"
5 |
6 | # If you use any other providers you should also pin them to the
7 | # major version currently being used. This practice will help us
8 | # avoid unwelcome surprises.
9 | required_providers {
10 | # We have verified that our code works with version 6.7 of this
11 | # Terraform provider.
12 | aws = {
13 | source = "hashicorp/aws"
14 | version = "~> 6.7"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/terraform-post-packer/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Version 1.1 of Terraform is the first version to support the
3 | # nullable key in variable definitions.
4 | required_version = "~> 1.1"
5 |
6 | # If you use any other providers you should also pin them to the
7 | # major version currently being used. This practice will help us
8 | # avoid unwelcome surprises.
9 | required_providers {
10 | # We have verified that our code works with version 6.7 of this
11 | # Terraform provider.
12 | aws = {
13 | source = "hashicorp/aws"
14 | version = "~> 6.7"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/terraform-post-packer/outputs.tf:
--------------------------------------------------------------------------------
1 | # cisagov/ansible-role-guacamole cannot currently support ARM64
2 | # because the official Guacamole Docker images do not.
3 | # output "launch_permissions_arm64" {
4 | # value = module.ami_launch_permission_arm64
5 | # description = "The cisagov/ami-launch-permission-tf-module for each ARM64 AMI to which launch permission is being granted."
6 | # }
7 |
8 | output "launch_permissions_x86_64" {
9 | value = module.ami_launch_permission_x86_64
10 | description = "The cisagov/ami-launch-permission-tf-module for each x86_64 AMI to which launch permission is being granted."
11 | }
12 |
--------------------------------------------------------------------------------
/terraform-post-packer/providers.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | tags = {
3 | Application = "guacamole-packer"
4 | Team = "CISA - Development"
5 | }
6 | }
7 |
8 | # Default AWS provider (EC2AMICreate role in the Images account)
9 | provider "aws" {
10 | default_tags {
11 | tags = local.tags
12 | }
13 | profile = "cool-images-ec2amicreate"
14 | region = "us-east-1"
15 | }
16 |
17 | # AWS provider for the Master account (OrganizationsReadOnly role)
18 | provider "aws" {
19 | alias = "master"
20 | default_tags {
21 | tags = local.tags
22 | }
23 | profile = "cool-master-organizationsreadonly"
24 | region = "us-east-1"
25 | }
26 |
--------------------------------------------------------------------------------
/terraform-build-user/backend.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | backend "s3" {
3 | # Use a partial configuration to avoid hardcoding the bucket name. This
4 | # allows the bucket name to be set on a per-environment basis via the
5 | # -backend-config command line option or other methods. For details, see:
6 | # https://developer.hashicorp.com/terraform/language/backend#partial-configuration
7 | bucket = ""
8 | dynamodb_table = "terraform-state-lock"
9 | encrypt = true
10 | key = "guacamole-packer/terraform-build-user.tfstate"
11 | profile = "cool-terraform-backend"
12 | region = "us-east-1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/terraform-post-packer/backend.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | backend "s3" {
3 | # Use a partial configuration to avoid hardcoding the bucket name. This
4 | # allows the bucket name to be set on a per-environment basis via the
5 | # -backend-config command line option or other methods. For details, see:
6 | # https://developer.hashicorp.com/terraform/language/backend#partial-configuration
7 | bucket = ""
8 | dynamodb_table = "terraform-state-lock"
9 | encrypt = true
10 | key = "guacamole-packer/terraform-post-packer.tfstate"
11 | profile = "cool-terraform-backend"
12 | region = "us-east-1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/base_amis.pkr.hcl:
--------------------------------------------------------------------------------
1 | data "amazon-ami" "debian_trixie_arm64" {
2 | filters = {
3 | architecture = "arm64"
4 | name = "debian-13-arm64-*"
5 | root-device-type = "ebs"
6 | virtualization-type = "hvm"
7 | }
8 | most_recent = true
9 | owners = ["136693071363"]
10 | region = var.build_region
11 | }
12 |
13 | data "amazon-ami" "debian_trixie_x86_64" {
14 | filters = {
15 | architecture = "x86_64"
16 | name = "debian-13-amd64-*"
17 | root-device-type = "ebs"
18 | virtualization-type = "hvm"
19 | }
20 | most_recent = true
21 | owners = ["136693071363"]
22 | region = var.build_region
23 | }
24 |
--------------------------------------------------------------------------------
/ansible/python.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install pip3/python3 and remove pip2/python2
3 | hosts: all
4 | become: true
5 | become_method: ansible.builtin.sudo
6 | tasks:
7 | # If pip were to be installed first, then the OS _could_ pull
8 | # different Python packages than what would be installed via the
9 | # cisagov/ansible-role-python role; hence, the ordering below is
10 | # more controlled.
11 | - name: Install Python 3
12 | ansible.builtin.include_role:
13 | name: python
14 | - name: Install pip3
15 | ansible.builtin.include_role:
16 | name: pip
17 | - name: Uninstall Python 2
18 | ansible.builtin.include_role:
19 | name: remove_python2
20 |
--------------------------------------------------------------------------------
/terraform-build-user/variables.tf:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Required parameters
3 | #
4 | # You must provide a value for each of these parameters.
5 | # ------------------------------------------------------------------------------
6 |
7 | variable "terraform_state_bucket" {
8 | description = "The name of the S3 bucket where Terraform state is stored."
9 | nullable = false
10 | type = string
11 | }
12 |
13 | # ------------------------------------------------------------------------------
14 | # Optional parameters
15 | #
16 | # These parameters have reasonable defaults.
17 | # ------------------------------------------------------------------------------
18 |
--------------------------------------------------------------------------------
/tests/test_version.py:
--------------------------------------------------------------------------------
1 | """Version tests for packer skeleton project."""
2 |
3 | # Standard Python Libraries
4 | import os
5 |
6 | # Third-Party Libraries
7 | import pytest
8 |
9 | GITHUB_RELEASE_TAG = os.getenv("GITHUB_RELEASE_TAG")
10 | VERSION_FILE = "version.txt"
11 |
12 |
13 | @pytest.mark.skipif(
14 | GITHUB_RELEASE_TAG in [None, ""],
15 | reason="this is not a release (GITHUB_RELEASE_TAG not set)",
16 | )
17 | def test_release_version():
18 | """Verify that release tag version agrees with the module version."""
19 | with open(VERSION_FILE) as f:
20 | project_version = f.read().strip()
21 | assert (
22 | GITHUB_RELEASE_TAG == f"v{project_version}"
23 | ), "GITHUB_RELEASE_TAG does not match the project version"
24 |
--------------------------------------------------------------------------------
/ansible/aws.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: AWS-specific roles
3 | hosts: all
4 | become: true
5 | become_method: ansible.builtin.sudo
6 | tasks:
7 | - name: Install Amazon SSM Agent
8 | ansible.builtin.include_role:
9 | name: amazon_ssm_agent
10 | - name: Install chrony and configure it for use within AWS
11 | ansible.builtin.include_role:
12 | name: chrony_aws
13 | - name: Install and configure Amazon CloudWatch Agent
14 | ansible.builtin.include_role:
15 | name: cloudwatch_agent
16 | # The instance types used for almost all the instances expose EBS
17 | # volumes as NVMe block devices, so that's why we need nvme here.
18 | - name: Install prerequisites for working with NVMe block devices
19 | ansible.builtin.include_role:
20 | name: nvme
21 |
--------------------------------------------------------------------------------
/ansible/install-prerequisites-for-netplan-configuration-fix.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Install the prerequisites for the user script that fixes the Netplan
3 | # configuration generated by cloud-init from the instance metadata.
4 | #
5 | # See these issues for more details:
6 | # - cisagov/skeleton-packer#300
7 | # - canonical/cloud-init#4764
8 | #
9 | # TODO: Remove this playbook when and if that becomes possible. See
10 | # cisagov/skeleton-packer#301 for more details.
11 | - name: >-
12 | Install prerequisites for the script that fixes the Netplan
13 | configuration generated by cloud-init from the instance metadata
14 | hosts: all
15 | become: true
16 | become_method: ansible.builtin.sudo
17 | tasks:
18 | - name: Install python3-pyyaml
19 | ansible.builtin.package:
20 | name:
21 | - python3-yaml
22 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | """pytest plugin configuration.
2 |
3 | https://docs.pytest.org/en/latest/writing_plugins.html#conftest-py-plugins
4 | """
5 |
6 | # Third-Party Libraries
7 | import pytest
8 |
9 |
10 | def pytest_addoption(parser):
11 | """Add new commandline options to pytest."""
12 | parser.addoption(
13 | "--runslow", action="store_true", default=False, help="run slow tests"
14 | )
15 |
16 |
17 | def pytest_collection_modifyitems(config, items):
18 | """Modify collected tests based on custom marks and commandline options."""
19 | if config.getoption("--runslow"):
20 | # --runslow given in cli: do not skip slow tests
21 | return
22 | skip_slow = pytest.mark.skip(reason="need --runslow option to run")
23 | for item in items:
24 | if "slow" in item.keywords:
25 | item.add_marker(skip_slow)
26 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # Version 10 is required because the pip-audit pre-commit hook
2 | # identifies a vulnerability in ansible-core 2.16.13, but all versions
3 | # of ansible 9 have a dependency on ~=2.16.X.
4 | #
5 | # We have tested against version 10. We want to avoid automatically
6 | # jumping to another major version without testing, since there are
7 | # often breaking changes across major versions. This is the reason
8 | # for the upper bound.
9 | ansible>=10,<11
10 | # ansible-core<2.17.7 suffers from GHSA-99w6-3xph-cx78.
11 | #
12 | # Note that any changes made to this dependency must also be made in
13 | # requirements-test.txt in cisagov/skeleton-ansible-role and
14 | # .pre-commit-config.yaml in cisagov/skeleton-generic.
15 | ansible-core>=2.17.7
16 | boto3
17 | docopt
18 | # The bump-version script requires at least version 3 of semver.
19 | semver>=3
20 | setuptools
21 | wheel
22 |
--------------------------------------------------------------------------------
/.ansible-lint:
--------------------------------------------------------------------------------
1 | ---
2 | # See https://ansible-lint.readthedocs.io/configuring/ for a list of
3 | # the configuration elements that can exist in this file.
4 | enable_list:
5 | # Useful checks that one must opt-into. See here for more details:
6 | # https://ansible-lint.readthedocs.io/rules/
7 | - fcqn-builtins
8 | - no-log-password
9 | - no-same-owner
10 | exclude_paths:
11 | # This exclusion is implicit, unless exclude_paths is defined
12 | - .cache
13 | # Seems wise to ignore this too
14 | - .github
15 | kinds:
16 | # This will force our systemd specific molecule configurations to be treated
17 | # as plain yaml files by ansible-lint. This mirrors the default kind
18 | # configuration in ansible-lint for molecule configurations:
19 | # yaml: "**/molecule/*/{base,molecule}.{yaml,yml}"
20 | - yaml: "**/molecule/*/molecule-{no,with}-systemd.yml"
21 | use_default_rules: true
22 |
--------------------------------------------------------------------------------
/ansible/base.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Setup base image
3 | hosts: all
4 | become: true
5 | become_method: ansible.builtin.sudo
6 | tasks:
7 | - name: Install and configure automated security updates
8 | ansible.builtin.include_role:
9 | name: automated_security_updates
10 | - name: Install and configure login banner
11 | ansible.builtin.include_role:
12 | name: banner
13 | - name: Install and configure ClamAV
14 | ansible.builtin.include_role:
15 | name: clamav
16 | - name: Create the devops user
17 | ansible.builtin.include_role:
18 | name: devops
19 | - name: Install and configure FreeIPA client
20 | ansible.builtin.include_role:
21 | name: freeipa_client
22 | - name: Install and configure htop
23 | ansible.builtin.include_role:
24 | name: htop
25 | - name: Create the joiner user
26 | ansible.builtin.include_role:
27 | name: joiner
28 | - name: Configure JournalD to preserve logs across reboots
29 | ansible.builtin.include_role:
30 | name: persist_journald
31 | - name: Install and configure systemd-resolved
32 | ansible.builtin.include_role:
33 | name: systemd_resolved
34 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Each line is a file pattern followed by one or more owners.
2 |
3 | # These owners will be the default owners for everything in the
4 | # repo. Unless a later match takes precedence, these owners will be
5 | # requested for review when someone opens a pull request.
6 | * @dav3r @felddy @jsf9k @mcdonnnj
7 |
8 | # These folks own any files in the .github directory at the root of
9 | # the repository and any of its subdirectories.
10 | /.github/ @dav3r @felddy @jsf9k @mcdonnnj
11 |
12 | # Let jsf9k own the sometimes-touchy AWS and Python playbooks, as well
13 | # as the Packer template.
14 | /*.pkr.hcl @jsf9k
15 | /ansible/aws.yml @jsf9k
16 | /ansible/python.yml @jsf9k
17 |
18 | # These folks own all linting configuration files.
19 | /.ansible-lint @dav3r @felddy @jsf9k @mcdonnnj
20 | /.bandit.yml @dav3r @felddy @jsf9k @mcdonnnj
21 | /.flake8 @dav3r @felddy @jsf9k @mcdonnnj
22 | /.isort.cfg @dav3r @felddy @jsf9k @mcdonnnj
23 | /.mdl_config.yaml @dav3r @felddy @jsf9k @mcdonnnj
24 | /.pre-commit-config.yaml @dav3r @felddy @jsf9k @mcdonnnj
25 | /.prettierignore @dav3r @felddy @jsf9k @mcdonnnj
26 | /.yamllint @dav3r @felddy @jsf9k @mcdonnnj
27 | /requirements.txt @dav3r @felddy @jsf9k @mcdonnnj
28 | /requirements-dev.txt @dav3r @felddy @jsf9k @mcdonnnj
29 | /requirements-test.txt @dav3r @felddy @jsf9k @mcdonnnj
30 | /setup-env @dav3r @felddy @jsf9k @mcdonnnj
31 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 80
3 | # Select (turn on)
4 | # * Complexity violations reported by mccabe (C) -
5 | # http://flake8.pycqa.org/en/latest/user/error-codes.html#error-violation-codes
6 | # * Documentation conventions compliance reported by pydocstyle (D) -
7 | # http://www.pydocstyle.org/en/stable/error_codes.html
8 | # * Default errors and warnings reported by pycodestyle (E and W) -
9 | # https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes
10 | # * Default errors reported by pyflakes (F) -
11 | # http://flake8.pycqa.org/en/latest/glossary.html#term-pyflakes
12 | # * Default warnings reported by flake8-bugbear (B) -
13 | # https://github.com/PyCQA/flake8-bugbear#list-of-warnings
14 | # * The B950 flake8-bugbear opinionated warning -
15 | # https://github.com/PyCQA/flake8-bugbear#opinionated-warnings
16 | select = C,D,E,F,W,B,B950
17 | # Ignore flake8's default warning about maximum line length, which has
18 | # a hard stop at the configured value. Instead we use
19 | # flake8-bugbear's B950, which allows up to 10% overage.
20 | #
21 | # Also ignore flake8's warning about line breaks before binary
22 | # operators. It no longer agrees with PEP8. See, for example, here:
23 | # https://github.com/ambv/black/issues/21. Guido agrees here:
24 | # https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b.
25 | ignore = E501,W503
26 |
--------------------------------------------------------------------------------
/terraform-build-user/.terraform.lock.hcl:
--------------------------------------------------------------------------------
1 | # This file is maintained automatically by "terraform init".
2 | # Manual edits may be lost in future updates.
3 |
4 | provider "registry.terraform.io/hashicorp/aws" {
5 | version = "6.25.0"
6 | constraints = ">= 4.9.0, ~> 6.7"
7 | hashes = [
8 | "h1:0XEc9eHELD/BtPNybqkzzaS3bYp2HSv9LwAfaGyCpOU=",
9 | "zh:0f9621f719ec2051eabb94ca59aa4f13574487fbc1517b183293431c9d388e38",
10 | "zh:2ffbedb2e3afcd82da8bfc540bd74e9611527bdafd00d6d1885f62e7d13bac74",
11 | "zh:30fb4ab8b4af19da7b9ce95cb41fa9399f81383e1adc91801b770e7eeab651c3",
12 | "zh:377cbaffe3ec8aa5bb594071df0e91f17ac9292a325ed73cebd69fe78c51f7ec",
13 | "zh:3b65f5c98e03f1bfc5b71fa69521e785552ff9656860b25e211287910874037d",
14 | "zh:4478fab7b111c40a9a2a9db6ec5331618cc8e5a8b591f651095c77b87e9f22b1",
15 | "zh:4fdaa559c57aed5d24fa3d5cb59fed59e1e689c21d038fd336a3ba93b258803f",
16 | "zh:7a751ecd0f2654746dd4041d0f6d894c3a1876a152ba4bb7805ec2c715259065",
17 | "zh:866725b83f8d5587dab0559ac208ee6c181746871faa99ce551b535e19c7bb6a",
18 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
19 | "zh:b16e3e2a8ccba4ceeeee961c708ef572c4a65e0001eaf09d08fa14cef01ab179",
20 | "zh:dc897b2037bbb7f8d6456a4aa1ed82cbd4daddb173a184efdfe8c03a57557771",
21 | "zh:de2344f23c980093a46dda3185f9052cda950d1b8ca9cf3c6e16b8c45fa23779",
22 | "zh:ef538ec8a917715a1804c6735d44b756c32972d4fab71e15df87a59eb75dd57c",
23 | "zh:f25cdfdac6798e7de4a1d3dd577a97c1ca200a12317a1fd5a4b9ea54cb05e868",
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/terraform-post-packer/.terraform.lock.hcl:
--------------------------------------------------------------------------------
1 | # This file is maintained automatically by "terraform init".
2 | # Manual edits may be lost in future updates.
3 |
4 | provider "registry.terraform.io/hashicorp/aws" {
5 | version = "6.25.0"
6 | constraints = ">= 4.9.0, ~> 6.7"
7 | hashes = [
8 | "h1:0XEc9eHELD/BtPNybqkzzaS3bYp2HSv9LwAfaGyCpOU=",
9 | "zh:0f9621f719ec2051eabb94ca59aa4f13574487fbc1517b183293431c9d388e38",
10 | "zh:2ffbedb2e3afcd82da8bfc540bd74e9611527bdafd00d6d1885f62e7d13bac74",
11 | "zh:30fb4ab8b4af19da7b9ce95cb41fa9399f81383e1adc91801b770e7eeab651c3",
12 | "zh:377cbaffe3ec8aa5bb594071df0e91f17ac9292a325ed73cebd69fe78c51f7ec",
13 | "zh:3b65f5c98e03f1bfc5b71fa69521e785552ff9656860b25e211287910874037d",
14 | "zh:4478fab7b111c40a9a2a9db6ec5331618cc8e5a8b591f651095c77b87e9f22b1",
15 | "zh:4fdaa559c57aed5d24fa3d5cb59fed59e1e689c21d038fd336a3ba93b258803f",
16 | "zh:7a751ecd0f2654746dd4041d0f6d894c3a1876a152ba4bb7805ec2c715259065",
17 | "zh:866725b83f8d5587dab0559ac208ee6c181746871faa99ce551b535e19c7bb6a",
18 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
19 | "zh:b16e3e2a8ccba4ceeeee961c708ef572c4a65e0001eaf09d08fa14cef01ab179",
20 | "zh:dc897b2037bbb7f8d6456a4aa1ed82cbd4daddb173a184efdfe8c03a57557771",
21 | "zh:de2344f23c980093a46dda3185f9052cda950d1b8ca9cf3c6e16b8c45fa23779",
22 | "zh:ef538ec8a917715a1804c6735d44b756c32972d4fab71e15df87a59eb75dd57c",
23 | "zh:f25cdfdac6798e7de4a1d3dd577a97c1ca200a12317a1fd5a4b9ea54cb05e868",
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/terraform-post-packer/variables.tf:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # OPTIONAL PARAMETERS
3 | #
4 | # These parameters have reasonable defaults.
5 | # ------------------------------------------------------------------------------
6 |
7 | variable "ami_share_account_name_regex" {
8 | default = "^env[[:digit:]]+"
9 | description = "A regular expression that matches the names of AWS accounts with which to share the AMIs created by this repository. This variable is used to share the AMIs with accounts that are members of the same AWS Organization as the account that owns the AMIs."
10 | nullable = false
11 | type = string
12 | }
13 |
14 | variable "extraorg_account_ids" {
15 | default = []
16 | description = "A list of AWS account IDs corresponding to \"extra\" accounts with which you want to share this AMI (e.g. [\"123456789012\"]). Normally this variable is used to share an AMI with accounts that are not a member of the same AWS Organization as the account that owns the AMI."
17 | nullable = false
18 | type = list(string)
19 | }
20 |
21 | variable "recent_ami_count" {
22 | default = 12
23 | description = "The number of most-recent AMIs (per architecture) for which to grant launch permission (e.g. \"3\"). If this variable is set to three, for example, then accounts will be granted permission to launch the three most recent AMIs (or all most recent AMIs, if there are only one or two of them in existence)."
24 | nullable = false
25 | type = number
26 | }
27 |
--------------------------------------------------------------------------------
/ansible/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: amazon_ssm_agent
3 | src: https://github.com/cisagov/ansible-role-amazon-ssm-agent
4 | - name: automated_security_updates
5 | src: https://github.com/cisagov/ansible-role-automated-security-updates
6 | - name: banner
7 | src: https://github.com/cisagov/ansible-role-banner
8 | - name: chrony_aws
9 | src: https://github.com/cisagov/ansible-role-chrony-aws
10 | - name: clamav
11 | src: https://github.com/cisagov/ansible-role-clamav
12 | - name: cloudwatch_agent
13 | src: https://github.com/cisagov/ansible-role-cloudwatch-agent
14 | - name: devops
15 | src: https://github.com/cisagov/ansible-role-devops-user
16 | - name: freeipa_client
17 | src: https://github.com/cisagov/ansible-role-freeipa-client
18 | - name: guacamole
19 | src: https://github.com/cisagov/ansible-role-guacamole
20 | - name: htop
21 | src: https://github.com/cisagov/ansible-role-htop
22 | - name: joiner
23 | src: https://github.com/cisagov/ansible-role-joiner-user
24 | - name: nvme
25 | src: https://github.com/cisagov/ansible-role-nvme
26 | - name: persist_journald
27 | src: https://github.com/cisagov/ansible-role-persist-journald
28 | - name: pip
29 | src: https://github.com/cisagov/ansible-role-pip
30 | - name: python
31 | src: https://github.com/cisagov/ansible-role-python
32 | - name: remove_python2
33 | src: https://github.com/cisagov/ansible-role-remove-python2
34 | - name: systemd_resolved
35 | src: https://github.com/cisagov/ansible-role-systemd-resolved
36 | - name: upgrade
37 | src: https://github.com/cisagov/ansible-role-upgrade
38 |
--------------------------------------------------------------------------------
/.mdl_config.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | # Default state for all rules
4 | default: true
5 |
6 | # MD003/heading-style/header-style - Heading style
7 | MD003:
8 | # Enforce the ATX-closed style of header
9 | style: atx_closed
10 |
11 | # MD004/ul-style - Unordered list style
12 | MD004:
13 | # Enforce dashes for unordered lists
14 | style: dash
15 |
16 | # MD013/line-length - Line length
17 | MD013:
18 | # Do not enforce for code blocks
19 | code_blocks: false
20 | # Do not enforce for tables
21 | tables: false
22 |
23 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the
24 | # same content
25 | MD024:
26 | # Allow headers with the same content as long as they are not in the same
27 | # parent heading
28 | allow_different_nesting: true
29 |
30 | # MD029/ol-prefix - Ordered list item prefix
31 | MD029:
32 | # Enforce the `1.` style for ordered lists
33 | style: one
34 |
35 | # MD033/no-inline-html - Inline HTML
36 | MD033:
37 | # The h1 and img elements are allowed to permit header images
38 | allowed_elements:
39 | - h1
40 | - img
41 |
42 | # MD035/hr-style - Horizontal rule style
43 | MD035:
44 | # Enforce dashes for horizontal rules
45 | style: ---
46 |
47 | # MD046/code-block-style - Code block style
48 | MD046:
49 | # Enforce the fenced style for code blocks
50 | style: fenced
51 |
52 | # MD049/emphasis-style - Emphasis style should be consistent
53 | MD049:
54 | # Enforce asterisks as the style to use for emphasis
55 | style: asterisk
56 |
57 | # MD050/strong-style - Strong style should be consistent
58 | MD050:
59 | # Enforce asterisks as the style to use for strong
60 | style: asterisk
61 |
--------------------------------------------------------------------------------
/terraform-build-user/remote_states.tf:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Retrieves state data from Terraform backends. This allows use of the
3 | # root-level outputs of one or more Terraform configurations as input data
4 | # for this configuration.
5 | # ------------------------------------------------------------------------------
6 |
7 | data "terraform_remote_state" "images_parameterstore" {
8 | backend = "s3"
9 |
10 | config = {
11 | bucket = var.terraform_state_bucket
12 | dynamodb_table = "terraform-state-lock"
13 | encrypt = true
14 | key = "cool-images-parameterstore/terraform.tfstate"
15 | profile = "cool-terraform-backend"
16 | region = "us-east-1"
17 | }
18 |
19 | workspace = terraform.workspace
20 | }
21 |
22 | data "terraform_remote_state" "images" {
23 | backend = "s3"
24 |
25 | config = {
26 | bucket = var.terraform_state_bucket
27 | dynamodb_table = "terraform-state-lock"
28 | encrypt = true
29 | key = "cool-accounts/images.tfstate"
30 | profile = "cool-terraform-backend"
31 | region = "us-east-1"
32 | }
33 |
34 | workspace = terraform.workspace
35 | }
36 |
37 | data "terraform_remote_state" "users" {
38 | backend = "s3"
39 |
40 | config = {
41 | bucket = var.terraform_state_bucket
42 | dynamodb_table = "terraform-state-lock"
43 | encrypt = true
44 | key = "cool-accounts/users.tfstate"
45 | profile = "cool-terraform-backend"
46 | region = "us-east-1"
47 | }
48 |
49 | workspace = terraform.workspace
50 | }
51 |
--------------------------------------------------------------------------------
/ami_arm64.pkr.hcl:
--------------------------------------------------------------------------------
1 | source "amazon-ebs" "arm64" {
2 | ami_name = "guacamole-hvm-${local.timestamp}-arm64-ebs"
3 | ami_regions = var.ami_regions
4 | associate_public_ip_address = true
5 | encrypt_boot = true
6 | instance_type = "t4g.small"
7 | kms_key_id = var.build_region_kms
8 | launch_block_device_mappings {
9 | delete_on_termination = true
10 | device_name = "/dev/xvda"
11 | encrypted = true
12 | volume_size = 8
13 | volume_type = "gp3"
14 | }
15 | region = var.build_region
16 | region_kms_key_ids = var.region_kms_keys
17 | skip_create_ami = var.skip_create_ami
18 | source_ami = data.amazon-ami.debian_trixie_arm64.id
19 | ssh_username = "admin"
20 | subnet_filter {
21 | filters = {
22 | "tag:Name" = "AMI Build"
23 | }
24 | }
25 | tags = {
26 | Application = "Guacamole"
27 | Architecture = "arm64"
28 | Base_AMI_Name = data.amazon-ami.debian_trixie_arm64.name
29 | GitHub_Ref_Name = var.github_ref_name
30 | GitHub_Release_URL = var.release_url
31 | GitHub_SHA = var.github_sha
32 | OS_Version = "Debian Trixie"
33 | Pre_Release = var.is_prerelease
34 | Release = var.release_tag
35 | Team = "VM Fusion - Development"
36 | }
37 | # Many Linux distributions are now disallowing the use of RSA keys,
38 | # so it makes sense to use an ED25519 key instead.
39 | temporary_key_pair_type = "ed25519"
40 | vpc_filter {
41 | filters = {
42 | "tag:Name" = "AMI Build"
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ami_x86_64.pkr.hcl:
--------------------------------------------------------------------------------
1 | source "amazon-ebs" "x86_64" {
2 | ami_name = "guacamole-hvm-${local.timestamp}-x86_64-ebs"
3 | ami_regions = var.ami_regions
4 | associate_public_ip_address = true
5 | encrypt_boot = true
6 | instance_type = "t3.small"
7 | kms_key_id = var.build_region_kms
8 | launch_block_device_mappings {
9 | delete_on_termination = true
10 | device_name = "/dev/xvda"
11 | encrypted = true
12 | volume_size = 8
13 | volume_type = "gp3"
14 | }
15 | region = var.build_region
16 | region_kms_key_ids = var.region_kms_keys
17 | skip_create_ami = var.skip_create_ami
18 | source_ami = data.amazon-ami.debian_trixie_x86_64.id
19 | ssh_username = "admin"
20 | subnet_filter {
21 | filters = {
22 | "tag:Name" = "AMI Build"
23 | }
24 | }
25 | tags = {
26 | Application = "Guacamole"
27 | Architecture = "x86_64"
28 | Base_AMI_Name = data.amazon-ami.debian_trixie_x86_64.name
29 | GitHub_Ref_Name = var.github_ref_name
30 | GitHub_Release_URL = var.release_url
31 | GitHub_SHA = var.github_sha
32 | OS_Version = "Debian Trixie"
33 | Pre_Release = var.is_prerelease
34 | Release = var.release_tag
35 | Team = "VM Fusion - Development"
36 | }
37 | # Many Linux distributions are now disallowing the use of RSA keys,
38 | # so it makes sense to use an ED25519 key instead.
39 | temporary_key_pair_type = "ed25519"
40 | vpc_filter {
41 | filters = {
42 | "tag:Name" = "AMI Build"
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ansible/guacamole.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install Guacamole Docker composition
3 | hosts: all
4 | become: true
5 | become_method: ansible.builtin.sudo
6 | tasks:
7 | - name: Install Guacamole Docker composition
8 | ansible.builtin.include_role:
9 | name: guacamole
10 | vars:
11 | # The ">-" combination is critical here. Without the minus
12 | # character these values would have a line return appended
13 | # onto the end of them.
14 | #
15 | # The postgres database username and password for guacamole
16 | guacamole_postgres_username: >-
17 | {{ lookup('aws_ssm', '/guacamole/postgres_username') }}
18 | guacamole_postgres_password: >-
19 | {{ lookup('aws_ssm', '/guacamole/postgres_password') }}
20 | guacamole_private_ssh_key: >-
21 | {{ lookup('aws_ssm', '/vnc/ssh/ed25519_private_key') }}
22 | guacamole_rdp_username: >-
23 | {{ lookup('aws_ssm', '/rdp/username') }}
24 | guacamole_rdp_password: >-
25 | {{ lookup('aws_ssm', '/rdp/password') }}
26 | guacamole_vnc_username: >-
27 | {{ lookup('aws_ssm', '/vnc/username') }}
28 | guacamole_vnc_password: >-
29 | {{ lookup('aws_ssm', '/vnc/password') }}
30 | guacamole_windows_sftp_base: >-
31 | {{ lookup('aws_ssm', '/vnc/sftp/windows_base_directory') }}
32 | # boto3 is needed during deployment to fetch SSL certificates.
33 | # pystache may be used during deployment as part of the process
34 | # that automatically defines Guacamole connections to other
35 | # instances.
36 | - name: Install python3-boto3 and python3-pystache
37 | ansible.builtin.package:
38 | name:
39 | - python3-boto3
40 | - python3-pystache
41 |
--------------------------------------------------------------------------------
/terraform-build-user/README.md:
--------------------------------------------------------------------------------
1 | # Build User #
2 |
3 | This directory consists of [Terraform](https://www.terraform.io/) code
4 | that is used to create a build user. This build user is in turn used
5 | by our CI/CD pipeline to create AMIs via [Packer](https://packer.io)
6 | for our various COOL environments.
7 |
8 | See [the overall project documentation](../README.md) for a detailed
9 | description of how this code is intended to be used.
10 |
11 |
12 | ## Requirements ##
13 |
14 | | Name | Version |
15 | |------|---------|
16 | | terraform | ~> 1.1 |
17 | | aws | ~> 6.7 |
18 |
19 | ## Providers ##
20 |
21 | | Name | Version |
22 | |------|---------|
23 | | aws.cool-terraform-backend | ~> 6.7 |
24 | | terraform | n/a |
25 |
26 | ## Modules ##
27 |
28 | | Name | Source | Version |
29 | |------|--------|---------|
30 | | iam\_user | github.com/cisagov/ami-build-iam-user-tf-module | n/a |
31 |
32 | ## Resources ##
33 |
34 | | Name | Type |
35 | |------|------|
36 | | [aws_caller_identity.terraform_backend](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
37 | | [terraform_remote_state.images](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source |
38 | | [terraform_remote_state.images_parameterstore](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source |
39 | | [terraform_remote_state.users](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source |
40 |
41 | ## Inputs ##
42 |
43 | | Name | Description | Type | Default | Required |
44 | |------|-------------|------|---------|:--------:|
45 | | terraform\_state\_bucket | The name of the S3 bucket where Terraform state is stored. | `string` | n/a | yes |
46 |
47 | ## Outputs ##
48 |
49 | No outputs.
50 |
51 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | # Any ignore directives should be uncommented in downstream projects to disable
4 | # Dependabot updates for the given dependency. Downstream projects will get
5 | # these updates when the pull request(s) in the appropriate skeleton are merged
6 | # and Lineage processes these changes.
7 |
8 | updates:
9 | - directory: /
10 | ignore:
11 | # Managed by cisagov/skeleton-generic
12 | - dependency-name: actions/cache
13 | - dependency-name: actions/checkout
14 | - dependency-name: actions/dependency-review-action
15 | - dependency-name: actions/labeler
16 | - dependency-name: actions/setup-go
17 | - dependency-name: actions/setup-python
18 | - dependency-name: cisagov/action-job-preamble
19 | - dependency-name: cisagov/setup-env-github-action
20 | - dependency-name: crazy-max/ghaction-github-labeler
21 | - dependency-name: github/codeql-action
22 | - dependency-name: hashicorp/setup-packer
23 | - dependency-name: hashicorp/setup-terraform
24 | - dependency-name: mxschmitt/action-tmate
25 | # Managed by cisagov/skeleton-packer
26 | - dependency-name: aws-actions/configure-aws-credentials
27 | labels:
28 | # dependabot default we need to replicate
29 | - dependencies
30 | # This matches our label definition in .github/labels.yml as opposed to
31 | # dependabot's default of `github_actions`.
32 | - github-actions
33 | package-ecosystem: github-actions
34 | schedule:
35 | interval: weekly
36 |
37 | - directory: /
38 | ignore:
39 | # Managed by cisagov/skeleton-packer
40 | - dependency-name: ansible
41 | - dependency-name: ansible-core
42 | package-ecosystem: pip
43 | schedule:
44 | interval: weekly
45 |
46 | - directory: /terraform-build-user
47 | ignore:
48 | # Managed by cisagov/skeleton-packer
49 | - dependency-name: hashicorp/aws
50 | package-ecosystem: terraform
51 | schedule:
52 | interval: weekly
53 |
54 | - directory: /terraform-post-packer
55 | ignore:
56 | # Managed by cisagov/skeleton-packer
57 | - dependency-name: hashicorp/aws
58 | package-ecosystem: terraform
59 | schedule:
60 | interval: weekly
61 | version: 2
62 |
--------------------------------------------------------------------------------
/.yamllint:
--------------------------------------------------------------------------------
1 | ---
2 | extends: default
3 |
4 | rules:
5 | braces:
6 | # Do not allow non-empty flow mappings
7 | forbid: non-empty
8 | # Allow up to one space inside braces. This is required for Ansible compatibility.
9 | max-spaces-inside: 1
10 |
11 | brackets:
12 | # Do not allow non-empty flow sequences
13 | forbid: non-empty
14 |
15 | comments:
16 | # Ensure that inline comments have at least one space before the preceding content.
17 | # This is required for Ansible compatibility.
18 | min-spaces-from-content: 1
19 |
20 | # yamllint does not like it when you comment out different parts of
21 | # dictionaries in a list. You can see
22 | # https://github.com/adrienverge/yamllint/issues/384 for some examples of
23 | # this behavior.
24 | comments-indentation: disable
25 |
26 | indentation:
27 | # Ensure that block sequences inside of a mapping are indented
28 | indent-sequences: true
29 | # Enforce a specific number of spaces
30 | spaces: 2
31 |
32 | # yamllint does not allow inline mappings that exceed the line length by
33 | # default. There are many scenarios where the inline mapping may be a key,
34 | # hash, or other long value that would exceed the line length but cannot
35 | # reasonably be broken across lines.
36 | line-length:
37 | # This rule implies the allow-non-breakable-words rule
38 | allow-non-breakable-inline-mappings: true
39 | # Allows a 10% overage from the default limit of 80
40 | max: 88
41 |
42 | # Using anything other than strings to express octal values can lead to unexpected
43 | # and potentially unsafe behavior. Ansible strongly recommends against such practices
44 | # and these rules are needed for Ansible compatibility. Please see the following for
45 | # more information:
46 | # https://ansible.readthedocs.io/projects/lint/rules/risky-octal/
47 | octal-values:
48 | # Do not allow explicit octal values (those beginning with a leading 0o).
49 | forbid-explicit-octal: true
50 | # Do not allow implicit octal values (those beginning with a leading 0).
51 | forbid-implicit-octal: true
52 |
53 | quoted-strings:
54 | # Allow disallowed quotes (single quotes) for strings that contain allowed quotes
55 | # (double quotes).
56 | allow-quoted-quotes: true
57 | # Apply these rules to keys in mappings as well
58 | check-keys: true
59 | # We prefer double quotes for strings when they are needed
60 | quote-type: double
61 | # Only require quotes when they are necessary for proper processing
62 | required: only-when-needed
63 |
--------------------------------------------------------------------------------
/terraform-post-packer/README.md:
--------------------------------------------------------------------------------
1 | # Post-Packer #
2 |
3 | This directory consists of [Terraform](https://www.terraform.io/) code
4 | that is used to share AMIs with the appropriate AWS accounts. This
5 | code is intended to be run after the CI/CD pipeline has created a new
6 | AMI for a COOL environment.
7 |
8 | See [the overall project documentation](../README.md) for more
9 | details.
10 |
11 |
12 | ## Requirements ##
13 |
14 | | Name | Version |
15 | |------|---------|
16 | | terraform | ~> 1.1 |
17 | | aws | ~> 6.7 |
18 |
19 | ## Providers ##
20 |
21 | | Name | Version |
22 | |------|---------|
23 | | aws | ~> 6.7 |
24 |
25 | ## Modules ##
26 |
27 | | Name | Source | Version |
28 | |------|--------|---------|
29 | | ami\_launch\_permission\_x86\_64 | github.com/cisagov/ami-launch-permission-tf-module | n/a |
30 |
31 | ## Resources ##
32 |
33 | | Name | Type |
34 | |------|------|
35 | | [aws_ami_ids.historical_amis_x86_64](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami_ids) | data source |
36 | | [aws_caller_identity.images](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
37 |
38 | ## Inputs ##
39 |
40 | | Name | Description | Type | Default | Required |
41 | |------|-------------|------|---------|:--------:|
42 | | ami\_share\_account\_name\_regex | A regular expression that matches the names of AWS accounts with which to share the AMIs created by this repository. This variable is used to share the AMIs with accounts that are members of the same AWS Organization as the account that owns the AMIs. | `string` | `"^env[[:digit:]]+"` | no |
43 | | extraorg\_account\_ids | A list of AWS account IDs corresponding to "extra" accounts with which you want to share this AMI (e.g. ["123456789012"]). Normally this variable is used to share an AMI with accounts that are not a member of the same AWS Organization as the account that owns the AMI. | `list(string)` | `[]` | no |
44 | | recent\_ami\_count | The number of most-recent AMIs (per architecture) for which to grant launch permission (e.g. "3"). If this variable is set to three, for example, then accounts will be granted permission to launch the three most recent AMIs (or all most recent AMIs, if there are only one or two of them in existence). | `number` | `12` | no |
45 |
46 | ## Outputs ##
47 |
48 | | Name | Description |
49 | |------|-------------|
50 | | launch\_permissions\_x86\_64 | The cisagov/ami-launch-permission-tf-module for each x86\_64 AMI to which launch permission is being granted. |
51 |
52 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Each entry in this file is a label that will be applied to pull requests
3 | # if there is a match based on the matching rules for the entry. Please see
4 | # the actions/labeler documentation for more information:
5 | # https://github.com/actions/labeler#match-object
6 | #
7 | # Note: Verify that the label you want to use is defined in the
8 | # crazy-max/ghaction-github-labeler configuration file located at
9 | # .github/labels.yml.
10 |
11 | ansible:
12 | - changed-files:
13 | - any-glob-to-any-file:
14 | - ansible/**/*.yml
15 | dependencies:
16 | - changed-files:
17 | - any-glob-to-any-file:
18 | # Add any dependency files used.
19 | - .pre-commit-config.yaml
20 | - "**/.terraform.lock.hcl"
21 | - "**/versions.tf"
22 | - ansible/requirements.yml
23 | - requirements*.txt
24 | - versions.pkr.hcl
25 | docker:
26 | - changed-files:
27 | - any-glob-to-any-file:
28 | - "**/compose*.yml"
29 | - "**/docker-compose*.yml"
30 | - "**/Dockerfile*"
31 | documentation:
32 | - changed-files:
33 | - any-glob-to-any-file:
34 | - "**/*.md"
35 | github-actions:
36 | - changed-files:
37 | - any-glob-to-any-file:
38 | - .github/workflows/**
39 | javascript:
40 | - changed-files:
41 | - any-glob-to-any-file:
42 | - "**/*.js"
43 | packer:
44 | - changed-files:
45 | - any-glob-to-any-file:
46 | - "**/*.pkr.hcl"
47 | python:
48 | - changed-files:
49 | - any-glob-to-any-file:
50 | - "**/*.py"
51 | shell script:
52 | - changed-files:
53 | - any-glob-to-any-file:
54 | # If this project has any shell scripts that do not end in the ".sh"
55 | # extension, add them below.
56 | - "**/*.sh"
57 | - bump-version
58 | - setup-env
59 | terraform:
60 | - changed-files:
61 | - any-glob-to-any-file:
62 | - "**/*.tf"
63 | test:
64 | - changed-files:
65 | - any-glob-to-any-file:
66 | # Add any test-related files or paths.
67 | - .ansible-lint
68 | - .bandit.yml
69 | - .flake8
70 | - .isort.cfg
71 | - .mdl_config.yaml
72 | - .yamllint
73 | - tests/**
74 | typescript:
75 | - changed-files:
76 | - any-glob-to-any-file:
77 | - "**/*.ts"
78 | upstream update:
79 | - head-branch:
80 | # Any Lineage pull requests should use this branch.
81 | - lineage/skeleton
82 | version bump:
83 | - changed-files:
84 | - any-glob-to-any-file:
85 | # Ensure this matches your version tracking file(s).
86 | - version.txt
87 |
--------------------------------------------------------------------------------
/terraform-build-user/providers.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | # Extract the user name of the caller for use in assumed role session names.
3 | # When using the value of `user_id` we were unable to assume roles using
4 | # `caller_user_name` as their `session_name`. The error message returned by AWS
5 | # did not indicate that the `session_name` was the issue, however when the
6 | # semicolon was removed there were no issues. Please see the documentation at
7 | # https://docs.aws.amazon.com/cli/latest/reference/sts/assume-role.html
8 | # for information about acceptable characters for the session name.
9 | caller_user_name = replace(data.aws_caller_identity.terraform_backend.user_id, ":", ".")
10 |
11 | tags = {
12 | Application = "guacamole-packer"
13 | Team = "CISA - Development"
14 | }
15 | }
16 |
17 | # Provider that is only used for obtaining the caller identity.
18 | # Note that we cannot use a provider that assumes a role via an ARN from a
19 | # Terraform remote state for this purpose (like we do for all of the other
20 | # providers below). This is because we derive the session name (in the
21 | # assume_role block within the provider) from the caller identity of this
22 | # provider; if we try to do that, it results in a Terraform "Cycle" error.
23 | # Hence, for our caller identity, we use a provider based on a profile that
24 | # must exist for the Terraform backend to work ("cool-terraform-backend").
25 | provider "aws" {
26 | alias = "cool-terraform-backend"
27 | default_tags {
28 | tags = local.tags
29 | }
30 | profile = "cool-terraform-backend"
31 | region = "us-east-1"
32 | }
33 |
34 | # Default AWS provider (ProvisionAccount for the Users account)
35 | provider "aws" {
36 | assume_role {
37 | role_arn = data.terraform_remote_state.users.outputs.provisionaccount_role.arn
38 | session_name = local.caller_user_name
39 | }
40 | default_tags {
41 | tags = local.tags
42 | }
43 | region = "us-east-1"
44 | }
45 |
46 | # ProvisionEC2AMICreateRoles AWS provider for the Images account
47 | provider "aws" {
48 | alias = "images-ami"
49 | assume_role {
50 | role_arn = data.terraform_remote_state.images.outputs.provisionec2amicreateroles_role.arn
51 | session_name = local.caller_user_name
52 | }
53 | default_tags {
54 | tags = local.tags
55 | }
56 | region = "us-east-1"
57 | }
58 |
59 | # ProvisionParameterStoreReadRoles AWS provider for the Images account
60 | provider "aws" {
61 | alias = "images-ssm"
62 | assume_role {
63 | role_arn = data.terraform_remote_state.images_parameterstore.outputs.provisionparameterstorereadroles_role.arn
64 | session_name = local.caller_user_name
65 | }
66 | default_tags {
67 | tags = local.tags
68 | }
69 | region = "us-east-1"
70 | }
71 |
--------------------------------------------------------------------------------
/variables.pkr.hcl:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Required parameters
3 | #
4 | # You must provide a value for each of these parameters.
5 | # ------------------------------------------------------------------------------
6 |
7 | # ------------------------------------------------------------------------------
8 | # Optional parameters
9 | #
10 | # These parameters have reasonable defaults.
11 | # ------------------------------------------------------------------------------
12 |
13 | variable "ami_regions" {
14 | default = []
15 | description = "The list of AWS regions to copy the AMI to once it has been created. Example: [\"us-east-1\"]"
16 | type = list(string)
17 | }
18 |
19 | variable "build_region" {
20 | default = "us-east-1"
21 | description = "The region in which to retrieve the base AMI from and build the new AMI."
22 | type = string
23 | }
24 |
25 | variable "build_region_kms" {
26 | default = "alias/cool-amis"
27 | description = "The ID or ARN of the KMS key to use for AMI encryption."
28 | type = string
29 | }
30 |
31 | variable "force_install_ansible_requirements" {
32 | default = false
33 | description = "Indicate if the Ansible requirements should be force installed."
34 | type = bool
35 | }
36 |
37 | variable "force_install_ansible_requirements_with_dependencies" {
38 | default = false
39 | description = "Indicate if the Ansible requirements *and* their dependencies should be force installed."
40 | type = bool
41 | }
42 |
43 | variable "github_ref_name" {
44 | default = ""
45 | description = "The GitHub short ref name to use for the tags applied to the created AMI."
46 | type = string
47 | }
48 |
49 | variable "github_sha" {
50 | default = ""
51 | description = "The GitHub commit SHA to use for the tags applied to the created AMI."
52 | type = string
53 | }
54 | variable "is_prerelease" {
55 | default = false
56 | description = "The pre-release status to use for the tags applied to the created AMI."
57 | type = bool
58 | }
59 |
60 | variable "region_kms_keys" {
61 | default = {}
62 | 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\"}"
63 | type = map(string)
64 | }
65 |
66 | variable "release_tag" {
67 | default = ""
68 | description = "The GitHub release tag to use for the tags applied to the created AMI."
69 | type = string
70 | }
71 |
72 | variable "release_url" {
73 | default = ""
74 | description = "The GitHub release URL to use for the tags applied to the created AMI."
75 | type = string
76 | }
77 |
78 | variable "skip_create_ami" {
79 | default = false
80 | description = "Indicate if Packer should not create the AMI."
81 | type = bool
82 | }
83 |
--------------------------------------------------------------------------------
/.github/labels.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Rather than breaking up descriptions into multiline strings we disable that
3 | # specific rule in yamllint for this file.
4 | # yamllint disable rule:line-length
5 | - color: ff5850
6 | description: Pull requests that update Ansible code
7 | name: ansible
8 | - color: eb6420
9 | description: This issue or pull request is awaiting the outcome of another issue or pull request
10 | name: blocked
11 | - color: "000000"
12 | description: This issue or pull request involves changes to existing functionality
13 | name: breaking change
14 | - color: d73a4a
15 | description: This issue or pull request addresses broken functionality
16 | name: bug
17 | - color: 07648d
18 | description: This issue will be advertised on code.gov's Open Tasks page (https://code.gov/open-tasks)
19 | name: code.gov
20 | - color: 0366d6
21 | description: Pull requests that update a dependency file
22 | name: dependencies
23 | - color: 1d63ed
24 | description: Pull requests that update Docker code
25 | name: docker
26 | - color: 5319e7
27 | description: This issue or pull request improves or adds to documentation
28 | name: documentation
29 | - color: cfd3d7
30 | description: This issue or pull request already exists or is covered in another issue or pull request
31 | name: duplicate
32 | - color: b005bc
33 | description: A high-level objective issue encompassing multiple issues instead of a specific unit of work
34 | name: epic
35 | - color: "000000"
36 | description: Pull requests that update GitHub Actions code
37 | name: github-actions
38 | - color: 0e8a16
39 | description: This issue or pull request is well-defined and good for newcomers
40 | name: good first issue
41 | - color: ff7518
42 | description: Pull request that should count toward Hacktoberfest participation
43 | name: hacktoberfest-accepted
44 | - color: a2eeef
45 | description: This issue or pull request will add or improve functionality, maintainability, or ease of use
46 | name: improvement
47 | - color: fef2c0
48 | description: This issue or pull request is not applicable, incorrect, or obsolete
49 | name: invalid
50 | - color: f0db4f
51 | description: Pull requests that update JavaScript code
52 | name: javascript
53 | - color: ce099a
54 | description: This pull request is ready to merge during the next Lineage Kraken release
55 | name: kraken 🐙
56 | - color: a4fc5d
57 | description: This issue or pull request requires further information
58 | name: need info
59 | - color: fcdb45
60 | description: This pull request is awaiting an action or decision to move forward
61 | name: on hold
62 | - color: 02a8ef
63 | description: Pull requests that update Packer code
64 | name: packer
65 | - color: 3776ab
66 | description: Pull requests that update Python code
67 | name: python
68 | - color: ef476c
69 | description: This issue is a request for information or needs discussion
70 | name: question
71 | - color: d73a4a
72 | description: This issue or pull request addresses a security issue
73 | name: security
74 | - color: 4eaa25
75 | description: Pull requests that update shell scripts
76 | name: shell script
77 | - color: 7b42bc
78 | description: Pull requests that update Terraform code
79 | name: terraform
80 | - color: 00008b
81 | description: This issue or pull request adds or otherwise modifies test code
82 | name: test
83 | - color: 2678c5
84 | description: Pull requests that update TypeScript code
85 | name: typescript
86 | - color: 1d76db
87 | description: This issue or pull request pulls in upstream updates
88 | name: upstream update
89 | - color: d4c5f9
90 | description: This issue or pull request increments the version number
91 | name: version bump
92 | - color: ffffff
93 | description: This issue will not be incorporated
94 | name: wontfix
95 |
--------------------------------------------------------------------------------
/.github/workflows/label-prs.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Label pull requests
3 |
4 | on: # yamllint disable-line rule:truthy
5 | pull_request:
6 | types:
7 | - edited
8 | - opened
9 | - synchronize
10 |
11 | # Set a default shell for any run steps. The `-Eueo pipefail` sets errtrace,
12 | # nounset, errexit, and pipefail. The `-x` will print all commands as they are
13 | # run. Please see the GitHub Actions documentation for more information:
14 | # https://docs.github.com/en/actions/using-jobs/setting-default-values-for-jobs
15 | defaults:
16 | run:
17 | shell: bash -Eueo pipefail -x {0}
18 |
19 | jobs:
20 | diagnostics:
21 | name: Run diagnostics
22 | # This job does not need any permissions
23 | permissions: {}
24 | runs-on: ubuntu-latest
25 | steps:
26 | # Note that a duplicate of this step must be added at the top of
27 | # each job.
28 | - name: Apply standard cisagov job preamble
29 | uses: cisagov/action-job-preamble@v1
30 | with:
31 | check_github_status: "true"
32 | # This functionality is poorly implemented and has been
33 | # causing problems due to the MITM implementation hogging or
34 | # leaking memory. As a result we disable it by default. If
35 | # you want to temporarily enable it, simply set
36 | # monitor_permissions equal to "true".
37 | #
38 | # TODO: Re-enable this functionality when practical. See
39 | # cisagov/skeleton-generic#207 for more details.
40 | monitor_permissions: "false"
41 | output_workflow_context: "true"
42 | # Use a variable to specify the permissions monitoring
43 | # configuration. By default this will yield the
44 | # configuration stored in the cisagov organization-level
45 | # variable, but if you want to use a different configuration
46 | # then simply:
47 | # 1. Create a repository-level variable with the name
48 | # ACTIONS_PERMISSIONS_CONFIG.
49 | # 2. Set this new variable's value to the configuration you
50 | # want to use for this repository.
51 | #
52 | # Note in particular that changing the permissions
53 | # monitoring configuration *does not* require you to modify
54 | # this workflow.
55 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
56 | label:
57 | needs:
58 | - diagnostics
59 | permissions:
60 | # Permissions required by actions/labeler
61 | contents: read
62 | pull-requests: write
63 | runs-on: ubuntu-latest
64 | steps:
65 | - name: Apply standard cisagov job preamble
66 | uses: cisagov/action-job-preamble@v1
67 | with:
68 | # This functionality is poorly implemented and has been
69 | # causing problems due to the MITM implementation hogging or
70 | # leaking memory. As a result we disable it by default. If
71 | # you want to temporarily enable it, simply set
72 | # monitor_permissions equal to "true".
73 | #
74 | # TODO: Re-enable this functionality when practical. See
75 | # cisagov/skeleton-generic#207 for more details.
76 | monitor_permissions: "false"
77 | # Use a variable to specify the permissions monitoring
78 | # configuration. By default this will yield the
79 | # configuration stored in the cisagov organization-level
80 | # variable, but if you want to use a different configuration
81 | # then simply:
82 | # 1. Create a repository-level variable with the name
83 | # ACTIONS_PERMISSIONS_CONFIG.
84 | # 2. Set this new variable's value to the configuration you
85 | # want to use for this repository.
86 | #
87 | # Note in particular that changing the permissions
88 | # monitoring configuration *does not* require you to modify
89 | # this workflow.
90 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
91 | - name: Apply suitable labels to a pull request
92 | uses: actions/labeler@v6
93 |
--------------------------------------------------------------------------------
/terraform-post-packer/main.tf:
--------------------------------------------------------------------------------
1 | # Use aws_caller_identity with the default provider (Images account)
2 | # so we can provide the Images account ID below
3 | data "aws_caller_identity" "images" {
4 | }
5 |
6 | # cisagov/ansible-role-guacamole cannot currently support ARM64
7 | # because the official Guacamole Docker images do not.
8 | # The IDs of all ARM64 cisagov/guacamole-packer AMIs
9 | # data "aws_ami_ids" "historical_amis_arm64" {
10 | # owners = [data.aws_caller_identity.images.account_id]
11 | #
12 | # filter {
13 | # name = "architecture"
14 | # values = ["arm64"]
15 | # }
16 | #
17 | # filter {
18 | # name = "name"
19 | # values = ["guacamole-hvm-*-arm64-ebs"]
20 | # }
21 | #
22 | # filter {
23 | # name = "root-device-type"
24 | # values = ["ebs"]
25 | # }
26 | #
27 | # filter {
28 | # name = "virtualization-type"
29 | # values = ["hvm"]
30 | # }
31 | # }
32 |
33 | # cisagov/ansible-role-guacamole cannot currently support ARM64
34 | # because the official Guacamole Docker images do not.
35 | # Assign launch permissions to the ARM64 AMIs
36 | # module "ami_launch_permission_arm64" {
37 | # # Really we only want the var.recent_ami_count most recent AMIs, but
38 | # # we have to cover the case where there are fewer than that many
39 | # # AMIs in existence. Hence the min()/length() tomfoolery.
40 | # for_each = toset(slice(data.aws_ami_ids.historical_amis_arm64.ids, 0, min(var.recent_ami_count, length(data.aws_ami_ids.historical_amis_arm64.ids))))
41 | #
42 | # source = "github.com/cisagov/ami-launch-permission-tf-module"
43 | #
44 | # providers = {
45 | # aws = aws
46 | # aws.master = aws.master
47 | # }
48 | #
49 | # account_name_regex = var.ami_share_account_name_regex
50 | # ami_id = each.value
51 | # extraorg_account_ids = var.extraorg_account_ids
52 | # }
53 |
54 | # The IDs of all x86-64 cisagov/guacamole-packer AMIs
55 | data "aws_ami_ids" "historical_amis_x86_64" {
56 | owners = [data.aws_caller_identity.images.account_id]
57 |
58 | filter {
59 | name = "architecture"
60 | values = ["x86_64"]
61 | }
62 |
63 | filter {
64 | name = "name"
65 | values = ["guacamole-hvm-*-x86_64-ebs"]
66 | }
67 |
68 | filter {
69 | name = "root-device-type"
70 | values = ["ebs"]
71 | }
72 |
73 | filter {
74 | name = "virtualization-type"
75 | values = ["hvm"]
76 | }
77 | }
78 |
79 | # This moved block allows us to rename the resources at
80 | # aws_ami_ids.historical_amis to aws_ami_ids.historical_amis_x86_64
81 | # instead of destroying and recreating them with a new name.
82 | #
83 | # TODO: Consider removing this moved block when it is no longer
84 | # needed. See cisagov/skeleton-packer#369 for more details.
85 | moved {
86 | from = aws_ami_ids.historical_amis
87 | to = aws_ami_ids.historical_amis_x86_64
88 | }
89 |
90 | # Assign launch permissions to the x86-64 AMIs
91 | module "ami_launch_permission_x86_64" {
92 | # Really we only want the var.recent_ami_count most recent AMIs, but
93 | # we have to cover the case where there are fewer than that many
94 | # AMIs in existence. Hence the min()/length() tomfoolery.
95 | for_each = toset(slice(data.aws_ami_ids.historical_amis_x86_64.ids, 0, min(var.recent_ami_count, length(data.aws_ami_ids.historical_amis_x86_64.ids))))
96 |
97 | source = "github.com/cisagov/ami-launch-permission-tf-module"
98 |
99 | providers = {
100 | aws = aws
101 | aws.master = aws.master
102 | }
103 |
104 | account_name_regex = var.ami_share_account_name_regex
105 | ami_id = each.value
106 | extraorg_account_ids = var.extraorg_account_ids
107 | }
108 |
109 | # This moved block allows us to rename the resources at
110 | # module.ami_launch_permission to module.ami_launch_permission_x86_64
111 | # instead of destroying and recreating them with a new name.
112 | #
113 | # TODO: Consider removing this moved block when it is no longer
114 | # needed. See cisagov/skeleton-packer#369 for more details.
115 | moved {
116 | from = module.ami_launch_permission
117 | to = module.ami_launch_permission_x86_64
118 | }
119 |
--------------------------------------------------------------------------------
/.github/workflows/sync-labels.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: sync-labels
3 |
4 | on: # yamllint disable-line rule:truthy
5 | push:
6 | paths:
7 | - .github/labels.yml
8 | - .github/workflows/sync-labels.yml
9 | workflow_dispatch:
10 |
11 | permissions:
12 | contents: read
13 |
14 | jobs:
15 | diagnostics:
16 | name: Run diagnostics
17 | # This job does not need any permissions
18 | permissions: {}
19 | runs-on: ubuntu-latest
20 | steps:
21 | # Note that a duplicate of this step must be added at the top of
22 | # each job.
23 | - name: Apply standard cisagov job preamble
24 | uses: cisagov/action-job-preamble@v1
25 | with:
26 | check_github_status: "true"
27 | # This functionality is poorly implemented and has been
28 | # causing problems due to the MITM implementation hogging or
29 | # leaking memory. As a result we disable it by default. If
30 | # you want to temporarily enable it, simply set
31 | # monitor_permissions equal to "true".
32 | #
33 | # TODO: Re-enable this functionality when practical. See
34 | # cisagov/skeleton-generic#207 for more details.
35 | monitor_permissions: "false"
36 | output_workflow_context: "true"
37 | # Use a variable to specify the permissions monitoring
38 | # configuration. By default this will yield the
39 | # configuration stored in the cisagov organization-level
40 | # variable, but if you want to use a different configuration
41 | # then simply:
42 | # 1. Create a repository-level variable with the name
43 | # ACTIONS_PERMISSIONS_CONFIG.
44 | # 2. Set this new variable's value to the configuration you
45 | # want to use for this repository.
46 | #
47 | # Note in particular that changing the permissions
48 | # monitoring configuration *does not* require you to modify
49 | # this workflow.
50 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
51 | labeler:
52 | needs:
53 | - diagnostics
54 | permissions:
55 | # actions/checkout needs this to fetch code
56 | contents: read
57 | # crazy-max/ghaction-github-labeler needs this to manage repository labels
58 | issues: write
59 | runs-on: ubuntu-latest
60 | steps:
61 | - name: Apply standard cisagov job preamble
62 | uses: cisagov/action-job-preamble@v1
63 | with:
64 | # This functionality is poorly implemented and has been
65 | # causing problems due to the MITM implementation hogging or
66 | # leaking memory. As a result we disable it by default. If
67 | # you want to temporarily enable it, simply set
68 | # monitor_permissions equal to "true".
69 | #
70 | # TODO: Re-enable this functionality when practical. See
71 | # cisagov/skeleton-generic#207 for more details.
72 | monitor_permissions: "false"
73 | # Use a variable to specify the permissions monitoring
74 | # configuration. By default this will yield the
75 | # configuration stored in the cisagov organization-level
76 | # variable, but if you want to use a different configuration
77 | # then simply:
78 | # 1. Create a repository-level variable with the name
79 | # ACTIONS_PERMISSIONS_CONFIG.
80 | # 2. Set this new variable's value to the configuration you
81 | # want to use for this repository.
82 | #
83 | # Note in particular that changing the permissions
84 | # monitoring configuration *does not* require you to modify
85 | # this workflow.
86 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
87 | - uses: actions/checkout@v6
88 | - name: Sync repository labels
89 | if: success()
90 | uses: crazy-max/ghaction-github-labeler@v5
91 | with:
92 | # This is a hideous ternary equivalent so we only do a dry run unless
93 | # this workflow is triggered by the develop branch.
94 | dry-run: ${{ github.ref_name == 'develop' && 'false' || 'true' }}
95 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Dependency review
3 |
4 | on: # yamllint disable-line rule:truthy
5 | merge_group:
6 | types:
7 | - checks_requested
8 | pull_request:
9 |
10 | # Set a default shell for any run steps. The `-Eueo pipefail` sets errtrace,
11 | # nounset, errexit, and pipefail. The `-x` will print all commands as they are
12 | # run. Please see the GitHub Actions documentation for more information:
13 | # https://docs.github.com/en/actions/using-jobs/setting-default-values-for-jobs
14 | defaults:
15 | run:
16 | shell: bash -Eueo pipefail -x {0}
17 |
18 | jobs:
19 | diagnostics:
20 | name: Run diagnostics
21 | # This job does not need any permissions
22 | permissions: {}
23 | runs-on: ubuntu-latest
24 | steps:
25 | # Note that a duplicate of this step must be added at the top of
26 | # each job.
27 | - name: Apply standard cisagov job preamble
28 | uses: cisagov/action-job-preamble@v1
29 | with:
30 | check_github_status: "true"
31 | # This functionality is poorly implemented and has been
32 | # causing problems due to the MITM implementation hogging or
33 | # leaking memory. As a result we disable it by default. If
34 | # you want to temporarily enable it, simply set
35 | # monitor_permissions equal to "true".
36 | #
37 | # TODO: Re-enable this functionality when practical. See
38 | # cisagov/skeleton-generic#207 for more details.
39 | monitor_permissions: "false"
40 | output_workflow_context: "true"
41 | # Use a variable to specify the permissions monitoring
42 | # configuration. By default this will yield the
43 | # configuration stored in the cisagov organization-level
44 | # variable, but if you want to use a different configuration
45 | # then simply:
46 | # 1. Create a repository-level variable with the name
47 | # ACTIONS_PERMISSIONS_CONFIG.
48 | # 2. Set this new variable's value to the configuration you
49 | # want to use for this repository.
50 | #
51 | # Note in particular that changing the permissions
52 | # monitoring configuration *does not* require you to modify
53 | # this workflow.
54 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
55 | dependency-review:
56 | name: Dependency review
57 | needs:
58 | - diagnostics
59 | permissions:
60 | # actions/checkout needs this to fetch code
61 | contents: read
62 | runs-on: ubuntu-latest
63 | steps:
64 | - name: Apply standard cisagov job preamble
65 | uses: cisagov/action-job-preamble@v1
66 | with:
67 | # This functionality is poorly implemented and has been
68 | # causing problems due to the MITM implementation hogging or
69 | # leaking memory. As a result we disable it by default. If
70 | # you want to temporarily enable it, simply set
71 | # monitor_permissions equal to "true".
72 | #
73 | # TODO: Re-enable this functionality when practical. See
74 | # cisagov/skeleton-generic#207 for more details.
75 | monitor_permissions: "false"
76 | # Use a variable to specify the permissions monitoring
77 | # configuration. By default this will yield the
78 | # configuration stored in the cisagov organization-level
79 | # variable, but if you want to use a different configuration
80 | # then simply:
81 | # 1. Create a repository-level variable with the name
82 | # ACTIONS_PERMISSIONS_CONFIG.
83 | # 2. Set this new variable's value to the configuration you
84 | # want to use for this repository.
85 | #
86 | # Note in particular that changing the permissions
87 | # monitoring configuration *does not* require you to modify
88 | # this workflow.
89 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
90 | - id: checkout-repo
91 | name: Checkout the repository
92 | uses: actions/checkout@v6
93 | - id: dependency-review
94 | name: Review dependency changes for vulnerabilities and license changes
95 | uses: actions/dependency-review-action@v4
96 |
--------------------------------------------------------------------------------
/bump-version:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # bump-version [--push] [--label LABEL] (major | minor | patch | prerelease | build | finalize | show)
4 | # bump-version --list-files
5 |
6 | set -o nounset
7 | set -o errexit
8 | set -o pipefail
9 |
10 | # Stores the canonical version for the project.
11 | VERSION_FILE=version.txt
12 | # Files that should be updated with the new version.
13 | VERSION_FILES=("$VERSION_FILE")
14 |
15 | USAGE=$(
16 | cat << END_OF_LINE
17 | Update the version of the project.
18 |
19 | Usage:
20 | ${0##*/} [--push] [--label LABEL] (major | minor | patch | prerelease | build | finalize | show)
21 | ${0##*/} --list-files
22 | ${0##*/} (-h | --help)
23 |
24 | Options:
25 | -h | --help Show this message.
26 | --push Perform a \`git push\` after updating the version.
27 | --label LABEL Specify the label to use when updating the build or prerelease version.
28 | --list-files List the files that will be updated when the version is bumped.
29 | END_OF_LINE
30 | )
31 |
32 | old_version=$(< "$VERSION_FILE")
33 | # Comment out periods so they are interpreted as periods and don't
34 | # just match any character
35 | old_version_regex=${old_version//\./\\\.}
36 | new_version="$old_version"
37 |
38 | bump_part=""
39 | label=""
40 | commit_prefix="Bump"
41 | with_push=false
42 | commands_with_label=("build" "prerelease")
43 | commands_with_prerelease=("major" "minor" "patch")
44 | with_prerelease=false
45 |
46 | #######################################
47 | # Display an error message, the help information, and exit with a non-zero status.
48 | # Arguments:
49 | # Error message.
50 | #######################################
51 | function invalid_option() {
52 | echo "$1"
53 | echo "$USAGE"
54 | exit 1
55 | }
56 |
57 | #######################################
58 | # Bump the version using the provided command.
59 | # Arguments:
60 | # The version to bump.
61 | # The command to bump the version.
62 | # Returns:
63 | # The new version.
64 | #######################################
65 | function bump_version() {
66 | local temp_version
67 | temp_version=$(python -c "import semver; print(semver.parse_version_info('$1').${2})")
68 | echo "$temp_version"
69 | }
70 |
71 | if [ $# -eq 0 ]; then
72 | echo "$USAGE"
73 | exit 1
74 | else
75 | while [ $# -gt 0 ]; do
76 | case $1 in
77 | --push)
78 | if [ "$with_push" = true ]; then
79 | invalid_option "Push has already been set."
80 | fi
81 |
82 | with_push=true
83 | shift
84 | ;;
85 | --label)
86 | if [ -n "$label" ]; then
87 | invalid_option "Label has already been set."
88 | fi
89 |
90 | label="$2"
91 | shift 2
92 | ;;
93 | build | finalize | major | minor | patch)
94 | if [ -n "$bump_part" ]; then
95 | invalid_option "Only one version part should be bumped at a time."
96 | fi
97 |
98 | bump_part="$1"
99 | shift
100 | ;;
101 | prerelease)
102 | with_prerelease=true
103 | shift
104 | ;;
105 | show)
106 | echo "$old_version"
107 | exit 0
108 | ;;
109 | -h | --help)
110 | echo "$USAGE"
111 | exit 0
112 | ;;
113 | --list-files)
114 | printf '%s\n' "${VERSION_FILES[@]}"
115 | exit 0
116 | ;;
117 | *)
118 | invalid_option "Invalid option: $1"
119 | ;;
120 | esac
121 | done
122 | fi
123 |
124 | if [ -n "$label" ] && [ "$with_prerelease" = false ] && [[ ! " ${commands_with_label[*]} " =~ [[:space:]]${bump_part}[[:space:]] ]]; then
125 | invalid_option "Setting the label is only allowed for the following commands: ${commands_with_label[*]}"
126 | fi
127 |
128 | if [ "$with_prerelease" = true ] && [ -n "$bump_part" ] && [[ ! " ${commands_with_prerelease[*]} " =~ [[:space:]]${bump_part}[[:space:]] ]]; then
129 | invalid_option "Changing the prerelease is only allowed in conjunction with the following commands: ${commands_with_prerelease[*]}"
130 | fi
131 |
132 | label_option=""
133 | if [ -n "$label" ]; then
134 | label_option="token='$label'"
135 | fi
136 |
137 | if [ -n "$bump_part" ]; then
138 | if [ "$bump_part" = "finalize" ]; then
139 | commit_prefix="Finalize"
140 | bump_command="finalize_version()"
141 | elif [ "$bump_part" = "build" ]; then
142 | bump_command="bump_${bump_part}($label_option)"
143 | else
144 | bump_command="bump_${bump_part}()"
145 | fi
146 | new_version=$(bump_version "$old_version" "$bump_command")
147 | echo Changing version from "$old_version" to "$new_version"
148 | fi
149 |
150 | if [ "$with_prerelease" = true ]; then
151 | bump_command="bump_prerelease($label_option)"
152 | temp_version=$(bump_version "$new_version" "$bump_command")
153 | echo Changing version from "$new_version" to "$temp_version"
154 | new_version="$temp_version"
155 | fi
156 |
157 | tmp_file=/tmp/version.$$
158 | for version_file in "${VERSION_FILES[@]}"; do
159 | if [ ! -f "$version_file" ]; then
160 | echo Missing expected file: "$version_file"
161 | exit 1
162 | fi
163 | sed "s/$old_version_regex/$new_version/" "$version_file" > $tmp_file
164 | mv $tmp_file "$version_file"
165 | done
166 |
167 | git add "${VERSION_FILES[@]}"
168 | git commit --message "$commit_prefix version from $old_version to $new_version"
169 |
170 | if [ "$with_push" = true ]; then
171 | git push
172 | fi
173 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # For most projects, this workflow file will not need changing; you simply need
3 | # to commit it to your repository.
4 | #
5 | # You may wish to alter this file to override the set of languages analyzed,
6 | # or to provide custom queries or build logic.
7 | name: CodeQL
8 |
9 | # The use of on here as a key is part of the GitHub actions syntax.
10 | # yamllint disable-line rule:truthy
11 | on:
12 | merge_group:
13 | types:
14 | - checks_requested
15 | pull_request:
16 | # The branches here must be a subset of the ones in the push key
17 | branches:
18 | - develop
19 | push:
20 | # Dependabot-triggered push events have read-only access, but uploading code
21 | # scanning requires write access.
22 | branches-ignore:
23 | - dependabot/**
24 | schedule:
25 | - cron: 0 2 * * 6
26 |
27 | jobs:
28 | diagnostics:
29 | name: Run diagnostics
30 | # This job does not need any permissions
31 | permissions: {}
32 | runs-on: ubuntu-latest
33 | steps:
34 | # Note that a duplicate of this step must be added at the top of
35 | # each job.
36 | - name: Apply standard cisagov job preamble
37 | uses: cisagov/action-job-preamble@v1
38 | with:
39 | check_github_status: "true"
40 | # This functionality is poorly implemented and has been
41 | # causing problems due to the MITM implementation hogging or
42 | # leaking memory. As a result we disable it by default. If
43 | # you want to temporarily enable it, simply set
44 | # monitor_permissions equal to "true".
45 | #
46 | # TODO: Re-enable this functionality when practical. See
47 | # cisagov/skeleton-generic#207 for more details.
48 | monitor_permissions: "false"
49 | output_workflow_context: "true"
50 | # Use a variable to specify the permissions monitoring
51 | # configuration. By default this will yield the
52 | # configuration stored in the cisagov organization-level
53 | # variable, but if you want to use a different configuration
54 | # then simply:
55 | # 1. Create a repository-level variable with the name
56 | # ACTIONS_PERMISSIONS_CONFIG.
57 | # 2. Set this new variable's value to the configuration you
58 | # want to use for this repository.
59 | #
60 | # Note in particular that changing the permissions
61 | # monitoring configuration *does not* require you to modify
62 | # this workflow.
63 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
64 | analyze:
65 | name: Analyze
66 | needs:
67 | - diagnostics
68 | runs-on: ubuntu-latest
69 | permissions:
70 | # actions/checkout needs this to fetch code
71 | contents: read
72 | # required for all workflows
73 | security-events: write
74 | strategy:
75 | fail-fast: false
76 | matrix:
77 | # Override automatic language detection by changing the below
78 | # list
79 | #
80 | # Supported options are actions, c-cpp, csharp, go,
81 | # java-kotlin, javascript-typescript, python, ruby, and swift.
82 | language:
83 | - actions
84 | # Learn more...
85 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
86 |
87 | steps:
88 | - name: Apply standard cisagov job preamble
89 | uses: cisagov/action-job-preamble@v1
90 | with:
91 | # This functionality is poorly implemented and has been
92 | # causing problems due to the MITM implementation hogging or
93 | # leaking memory. As a result we disable it by default. If
94 | # you want to temporarily enable it, simply set
95 | # monitor_permissions equal to "true".
96 | #
97 | # TODO: Re-enable this functionality when practical. See
98 | # cisagov/skeleton-generic#207 for more details.
99 | monitor_permissions: "false"
100 | # Use a variable to specify the permissions monitoring
101 | # configuration. By default this will yield the
102 | # configuration stored in the cisagov organization-level
103 | # variable, but if you want to use a different configuration
104 | # then simply:
105 | # 1. Create a repository-level variable with the name
106 | # ACTIONS_PERMISSIONS_CONFIG.
107 | # 2. Set this new variable's value to the configuration you
108 | # want to use for this repository.
109 | #
110 | # Note in particular that changing the permissions
111 | # monitoring configuration *does not* require you to modify
112 | # this workflow.
113 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
114 |
115 | - name: Checkout repository
116 | uses: actions/checkout@v6
117 |
118 | # Initializes the CodeQL tools for scanning.
119 | - name: Initialize CodeQL
120 | uses: github/codeql-action/init@v4
121 | with:
122 | languages: ${{ matrix.language }}
123 |
124 | # Autobuild attempts to build any compiled languages (C/C++, C#, or
125 | # Java). If this step fails, then you should remove it and run the build
126 | # manually (see below).
127 | - name: Autobuild
128 | uses: github/codeql-action/autobuild@v4
129 |
130 | # ℹ️ Command-line programs to run using the OS shell.
131 | # 📚 https://git.io/JvXDl
132 |
133 | # ✏️ If the Autobuild fails above, remove it and uncomment the following
134 | # three lines and modify them (or add more) to build your code if your
135 | # project uses a compiled language
136 |
137 | # - run: |
138 | # make bootstrap
139 | # make release
140 |
141 | - name: Perform CodeQL Analysis
142 | uses: github/codeql-action/analyze@v4
143 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Welcome #
2 |
3 | We're so glad you're thinking about contributing to this open source
4 | project! If you're unsure or afraid of anything, just ask or submit
5 | the issue or pull request anyway. The worst that can happen is that
6 | you'll be politely asked to change something. We appreciate any sort
7 | of contribution, and don't want a wall of rules to get in the way of
8 | that.
9 |
10 | Before contributing, we encourage you to read our CONTRIBUTING policy
11 | (you are here), our [LICENSE](LICENSE), and our [README](README.md),
12 | all of which should be in this repository.
13 |
14 | ## Issues ##
15 |
16 | If you want to report a bug or request a new feature, the most direct
17 | method is to [create an
18 | issue](https://github.com/cisagov/guacamole-packer/issues) in this
19 | repository. We recommend that you first search through existing
20 | issues (both open and closed) to check if your particular issue has
21 | already been reported. If it has then you might want to add a comment
22 | to the existing issue. If it hasn't then feel free to create a new
23 | one.
24 |
25 | ## Pull requests ##
26 |
27 | If you choose to [submit a pull
28 | request](https://github.com/cisagov/guacamole-packer/pulls), you will
29 | notice that our continuous integration (CI) system runs a fairly
30 | extensive set of linters and syntax checkers. Your pull request may
31 | fail these checks, and that's OK. If you want you can stop there and
32 | wait for us to make the necessary corrections to ensure your code
33 | passes the CI checks.
34 |
35 | If you want to make the changes yourself, or if you want to become a
36 | regular contributor, then you will want to set up
37 | [pre-commit](https://pre-commit.com/) on your local machine. Once you
38 | do that, the CI checks will run locally before you even write your
39 | commit message. This speeds up your development cycle considerably.
40 |
41 | ### Setting up pre-commit ###
42 |
43 | There are a few ways to do this, but we prefer to use
44 | [`pyenv`](https://github.com/pyenv/pyenv) and
45 | [`pyenv-virtualenv`](https://github.com/pyenv/pyenv-virtualenv) to
46 | create and manage a Python virtual environment specific to this
47 | project.
48 |
49 | We recommend using the `setup-env` script located in this repository,
50 | as it automates the entire environment configuration process. The
51 | dependencies required to run this script are
52 | [GNU `getopt`](https://github.com/util-linux/util-linux/blob/master/misc-utils/getopt.1.adoc),
53 | [`pyenv`](https://github.com/pyenv/pyenv), and [`pyenv-virtualenv`](https://github.com/pyenv/pyenv-virtualenv).
54 | If these tools are already configured on your system, you can simply run the
55 | following command:
56 |
57 | ```console
58 | ./setup-env
59 | ```
60 |
61 | Otherwise, follow the steps below to manually configure your
62 | environment.
63 |
64 | #### Installing and using GNU `getopt`, `pyenv`, and `pyenv-virtualenv` ####
65 |
66 | On macOS, we recommend installing [brew](https://brew.sh/). Then
67 | installation is as simple as `brew install gnu-getopt pyenv pyenv-virtualenv` and
68 | adding this to your profile:
69 |
70 | ```bash
71 | # GNU getopt must be explicitly added to the path since it is
72 | # keg-only (https://docs.brew.sh/FAQ#what-does-keg-only-mean)
73 | export PATH="$(brew --prefix)/opt/gnu-getopt/bin:$PATH"
74 |
75 | # Setup pyenv
76 | export PYENV_ROOT="$HOME/.pyenv"
77 | export PATH="$PYENV_ROOT/bin:$PATH"
78 | eval "$(pyenv init --path)"
79 | eval "$(pyenv init -)"
80 | eval "$(pyenv virtualenv-init -)"
81 | ```
82 |
83 | For Linux, Windows Subsystem for Linux (WSL), or macOS (if you
84 | don't want to use `brew`) you can use
85 | [pyenv/pyenv-installer](https://github.com/pyenv/pyenv-installer) to
86 | install the necessary tools. Before running this ensure that you have
87 | installed the prerequisites for your platform according to the
88 | [`pyenv` wiki
89 | page](https://github.com/pyenv/pyenv/wiki/common-build-problems).
90 | GNU `getopt` is included in most Linux distributions as part of the
91 | [`util-linux`](https://github.com/util-linux/util-linux) package.
92 |
93 | On WSL you should treat your platform as whatever Linux distribution
94 | you've chosen to install.
95 |
96 | Once you have installed `pyenv` you will need to add the following
97 | lines to your `.bash_profile` (or `.profile`):
98 |
99 | ```bash
100 | export PYENV_ROOT="$HOME/.pyenv"
101 | export PATH="$PYENV_ROOT/bin:$PATH"
102 | eval "$(pyenv init --path)"
103 | ```
104 |
105 | and then add the following lines to your `.bashrc`:
106 |
107 | ```bash
108 | eval "$(pyenv init -)"
109 | eval "$(pyenv virtualenv-init -)"
110 | ```
111 |
112 | If you want more information about setting up `pyenv` once installed, please run
113 |
114 | ```console
115 | pyenv init
116 | ```
117 |
118 | and
119 |
120 | ```console
121 | pyenv virtualenv-init
122 | ```
123 |
124 | for the current configuration instructions.
125 |
126 | If you are using a shell other than `bash` you should follow the
127 | instructions that the `pyenv-installer` script outputs.
128 |
129 | You will need to reload your shell for these changes to take effect so
130 | you can begin to use `pyenv`.
131 |
132 | For a list of Python versions that are already installed and ready to
133 | use with `pyenv`, use the command `pyenv versions`. To see a list of
134 | the Python versions available to be installed and used with `pyenv`
135 | use the command `pyenv install --list`. You can read more about
136 | the [many things that `pyenv` can do](https://github.com/pyenv/pyenv/blob/master/COMMANDS.md).
137 | See the [usage information](https://github.com/pyenv/pyenv-virtualenv#usage)
138 | for the additional capabilities that pyenv-virtualenv adds to the `pyenv`
139 | command.
140 |
141 | #### Creating the Python virtual environment ####
142 |
143 | Once `pyenv` and `pyenv-virtualenv` are installed on your system, you
144 | can create and configure the Python virtual environment with these
145 | commands:
146 |
147 | ```console
148 | cd guacamole-packer
149 | pyenv virtualenv guacamole-packer
150 | pyenv local guacamole-packer
151 | pip install --requirement requirements-dev.txt
152 | ```
153 |
154 | #### Installing the pre-commit hook ####
155 |
156 | Now setting up pre-commit is as simple as:
157 |
158 | ```console
159 | pre-commit install
160 | ```
161 |
162 | At this point the pre-commit checks will run against any files that
163 | you attempt to commit. If you want to run the checks against the
164 | entire repo, just execute `pre-commit run --all-files`.
165 |
166 | ### Running unit and system tests ###
167 |
168 | In addition to the pre-commit checks the CI system will run the suite
169 | of unit and system tests that are included with this project. To run
170 | these tests locally execute `pytest` from the root of the project.
171 |
172 | We encourage any updates to these tests to improve the overall code
173 | coverage. If your pull request adds new functionality we would
174 | appreciate it if you extend existing test cases, or add new ones to
175 | exercise the newly added code.
176 |
177 | ## Public domain ##
178 |
179 | This project is in the public domain within the United States, and
180 | copyright and related rights in the work worldwide are waived through
181 | the [CC0 1.0 Universal public domain
182 | dedication](https://creativecommons.org/publicdomain/zero/1.0/).
183 |
184 | All contributions to this project will be released under the CC0
185 | dedication. By submitting a pull request, you are agreeing to comply
186 | with this waiver of copyright interest.
187 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: prerelease
3 |
4 | on: # yamllint disable-line rule:truthy
5 | release:
6 | types:
7 | - prereleased
8 |
9 | env:
10 | AWS_DEFAULT_REGION: us-east-1
11 | # We have seen some failures of packer init in GitHub Actions due to
12 | # rate limiting when pulling Packer plugins from GitHub. Having
13 | # Packer use a GitHub API token should reduce this. The rate
14 | # limiting for unauthenticated requests is 60 requests/hour while
15 | # the rate limiting for authenticated requests is 5000
16 | # requests/hour.
17 | PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 | PIP_CACHE_DIR: ~/.cache/pip
19 | RUN_TMATE: ${{ secrets.RUN_TMATE }}
20 |
21 | jobs:
22 | diagnostics:
23 | name: Run diagnostics
24 | # This job does not need any permissions
25 | permissions: {}
26 | runs-on: ubuntu-latest
27 | steps:
28 | # Note that a duplicate of this step must be added at the top of
29 | # each job.
30 | - name: Apply standard cisagov job preamble
31 | uses: cisagov/action-job-preamble@v1
32 | with:
33 | check_github_status: "true"
34 | # This functionality is poorly implemented and has been
35 | # causing problems due to the MITM implementation hogging or
36 | # leaking memory. As a result we disable it by default. If
37 | # you want to temporarily enable it, simply set
38 | # monitor_permissions equal to "true".
39 | #
40 | # TODO: Re-enable this functionality when practical. See
41 | # cisagov/skeleton-packer#411 for more details.
42 | monitor_permissions: "false"
43 | output_workflow_context: "true"
44 | # Use a variable to specify the permissions monitoring
45 | # configuration. By default this will yield the
46 | # configuration stored in the cisagov organization-level
47 | # variable, but if you want to use a different configuration
48 | # then simply:
49 | # 1. Create a repository-level variable with the name
50 | # ACTIONS_PERMISSIONS_CONFIG.
51 | # 2. Set this new variable's value to the configuration you
52 | # want to use for this repository.
53 | #
54 | # Note in particular that changing the permissions
55 | # monitoring configuration *does not* require you to modify
56 | # this workflow.
57 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
58 | prerelease:
59 | environment: staging-a
60 | needs:
61 | - diagnostics
62 | permissions:
63 | # actions/checkout needs this to fetch code
64 | contents: read
65 | runs-on: ubuntu-latest
66 | strategy:
67 | fail-fast: false
68 | matrix:
69 | architecture:
70 | # cisagov/ansible-role-guacamole cannot currently support
71 | # ARM64 because the official Guacamole Docker images do not.
72 | # - arm64
73 | - x86_64
74 | steps:
75 | - name: Apply standard cisagov job preamble
76 | uses: cisagov/action-job-preamble@v1
77 | with:
78 | # This functionality is poorly implemented and has been
79 | # causing problems due to the MITM implementation hogging or
80 | # leaking memory. As a result we disable it by default. If
81 | # you want to temporarily enable it, simply set
82 | # monitor_permissions equal to "true".
83 | #
84 | # TODO: Re-enable this functionality when practical. See
85 | # cisagov/skeleton-packer#411 for more details.
86 | monitor_permissions: "false"
87 | # Use a variable to specify the permissions monitoring
88 | # configuration. By default this will yield the
89 | # configuration stored in the cisagov organization-level
90 | # variable, but if you want to use a different configuration
91 | # then simply:
92 | # 1. Create a repository-level variable with the name
93 | # ACTIONS_PERMISSIONS_CONFIG.
94 | # 2. Set this new variable's value to the configuration you
95 | # want to use for this repository.
96 | #
97 | # Note in particular that changing the permissions
98 | # monitoring configuration *does not* require you to modify
99 | # this workflow.
100 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
101 | - id: setup-env
102 | uses: cisagov/setup-env-github-action@v1
103 | - uses: actions/checkout@v6
104 | - id: setup-python
105 | uses: actions/setup-python@v6
106 | with:
107 | python-version: ${{ steps.setup-env.outputs.python-version }}
108 | - uses: actions/cache@v4
109 | env:
110 | BASE_CACHE_KEY: ${{ github.job }}-${{ runner.os }}-\
111 | py${{ steps.setup-python.outputs.python-version }}-\
112 | packer${{ steps.setup-env.outputs.packer-version }}-\
113 | tf-${{ steps.setup-env.outputs.terraform-version }}-
114 | with:
115 | path: |
116 | ${{ env.PIP_CACHE_DIR }}
117 | key: ${{ env.BASE_CACHE_KEY }}\
118 | ${{ hashFiles('**/requirements.txt') }}
119 | restore-keys: |
120 | ${{ env.BASE_CACHE_KEY }}
121 | - uses: hashicorp/setup-packer@v3
122 | with:
123 | version: ${{ steps.setup-env.outputs.packer-version }}
124 | - uses: hashicorp/setup-terraform@v3
125 | with:
126 | terraform_version: ${{ steps.setup-env.outputs.terraform-version }}
127 | - name: Install dependencies
128 | run: |
129 | python -m pip install --upgrade pip
130 | pip install --upgrade \
131 | --requirement requirements.txt
132 | - name: Assume AWS build role
133 | uses: aws-actions/configure-aws-credentials@v5
134 | with:
135 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
136 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
137 | aws-region: ${{ env.AWS_DEFAULT_REGION }}
138 | role-to-assume: ${{ secrets.BUILD_ROLE_TO_ASSUME }}
139 | role-duration-seconds: 3600
140 | # When called by Packer, Ansible will find /usr/bin/python3 and
141 | # use it; therefore, we must ensure that /usr/bin/python3 points
142 | # to the version of Python that we installed in the
143 | # actions/setup-python step above. This can hose other tasks
144 | # that are expecting to find the system Python at that location,
145 | # though, so we undo this change after running Packer.
146 | - name: Create a /usr/bin/python3 symlink to the installed python
147 | run: |
148 | sudo mv /usr/bin/python3 /usr/bin/python3-default
149 | sudo ln -s ${{ env.pythonLocation }}/bin/python3 \
150 | /usr/bin/python3
151 | - name: Install Packer plugins
152 | run: packer init .
153 | - name: Create machine image
154 | run: |
155 | packer build -only amazon-ebs.${{ matrix.architecture }} \
156 | -timestamp-ui \
157 | -var github_ref_name=${{ github.ref_name }} \
158 | -var github_sha=${{ github.sha }} \
159 | -var is_prerelease=${{ github.event.release.prerelease }} \
160 | -var release_tag=${{ github.event.release.tag_name }} \
161 | -var release_url=${{ github.event.release.html_url }} \
162 | .
163 | - name: Remove /usr/bin/python3 symlink to the installed python
164 | run: |
165 | sudo mv /usr/bin/python3-default /usr/bin/python3
166 | - name: Setup tmate debug session
167 | uses: mxschmitt/action-tmate@v3
168 | if: env.RUN_TMATE
169 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | ci:
3 | # Do not commit changes from running pre-commit for pull requests.
4 | autofix_prs: false
5 | # Autoupdate hooks weekly (this is the default).
6 | autoupdate_schedule: weekly
7 |
8 | default_language_version:
9 | # force all unspecified python hooks to run python3
10 | python: python3
11 |
12 | repos:
13 | # Check the pre-commit configuration
14 | - repo: meta
15 | hooks:
16 | - id: check-useless-excludes
17 |
18 | - repo: https://github.com/pre-commit/pre-commit-hooks
19 | rev: v6.0.0
20 | hooks:
21 | - id: check-case-conflict
22 | - id: check-executables-have-shebangs
23 | - id: check-json
24 | - id: check-merge-conflict
25 | - id: check-shebang-scripts-are-executable
26 | - id: check-symlinks
27 | - id: check-toml
28 | - id: check-vcs-permalinks
29 | - id: check-xml
30 | - id: debug-statements
31 | - id: destroyed-symlinks
32 | - id: detect-aws-credentials
33 | args:
34 | - --allow-missing-credentials
35 | - id: detect-private-key
36 | - id: end-of-file-fixer
37 | - id: mixed-line-ending
38 | args:
39 | - --fix=lf
40 | - id: pretty-format-json
41 | args:
42 | - --autofix
43 | - id: requirements-txt-fixer
44 | - id: trailing-whitespace
45 |
46 | # Text file hooks
47 | - repo: https://github.com/igorshubovych/markdownlint-cli
48 | rev: v0.45.0
49 | hooks:
50 | - id: markdownlint
51 | args:
52 | - --config=.mdl_config.yaml
53 | - repo: https://github.com/rbubley/mirrors-prettier
54 | rev: v3.6.2
55 | hooks:
56 | - id: prettier
57 | - repo: https://github.com/adrienverge/yamllint
58 | rev: v1.37.1
59 | hooks:
60 | - id: yamllint
61 | args:
62 | - --strict
63 |
64 | # GitHub Actions hooks
65 | - repo: https://github.com/python-jsonschema/check-jsonschema
66 | rev: 0.35.0
67 | hooks:
68 | - id: check-github-actions
69 | - id: check-github-workflows
70 |
71 | # pre-commit hooks
72 | - repo: https://github.com/pre-commit/pre-commit
73 | rev: v4.4.0
74 | hooks:
75 | - id: validate_manifest
76 |
77 | # Go hooks
78 | - repo: https://github.com/TekWizely/pre-commit-golang
79 | rev: v1.0.0-rc.4
80 | hooks:
81 | # Go Build
82 | - id: go-build-repo-mod
83 | # Style Checkers
84 | - id: go-critic
85 | # goimports
86 | - id: go-imports-repo
87 | args:
88 | # Write changes to files
89 | - -w
90 | # Go Mod Tidy
91 | - id: go-mod-tidy-repo
92 | # GoSec
93 | - id: go-sec-repo-mod
94 | # StaticCheck
95 | - id: go-staticcheck-repo-mod
96 | # Go Test
97 | - id: go-test-repo-mod
98 | # Go Vet
99 | - id: go-vet-repo-mod
100 | # Nix hooks
101 | - repo: https://github.com/nix-community/nixpkgs-fmt
102 | rev: v1.3.0
103 | hooks:
104 | - id: nixpkgs-fmt
105 |
106 | # Shell script hooks
107 | - repo: https://github.com/scop/pre-commit-shfmt
108 | rev: v3.12.0-2
109 | hooks:
110 | - id: shfmt
111 | args:
112 | # List files that will be formatted
113 | - --list
114 | # Write result to file instead of stdout
115 | - --write
116 | # Indent by two spaces
117 | - --indent
118 | - "2"
119 | # Binary operators may start a line
120 | - --binary-next-line
121 | # Switch cases are indented
122 | - --case-indent
123 | # Redirect operators are followed by a space
124 | - --space-redirects
125 | - repo: https://github.com/shellcheck-py/shellcheck-py
126 | rev: v0.11.0.1
127 | hooks:
128 | - id: shellcheck
129 |
130 | # Python hooks
131 | # Run bandit on the "tests" tree with a configuration
132 | - repo: https://github.com/PyCQA/bandit
133 | rev: 1.9.1
134 | hooks:
135 | - id: bandit
136 | name: bandit (tests tree)
137 | files: tests
138 | args:
139 | - --config=.bandit.yml
140 | # Run bandit on everything except the "tests" tree
141 | - repo: https://github.com/PyCQA/bandit
142 | rev: 1.9.1
143 | hooks:
144 | - id: bandit
145 | name: bandit (everything else)
146 | exclude: tests
147 | - repo: https://github.com/psf/black-pre-commit-mirror
148 | rev: 25.11.0
149 | hooks:
150 | - id: black
151 | - repo: https://github.com/PyCQA/flake8
152 | rev: 7.3.0
153 | hooks:
154 | - id: flake8
155 | additional_dependencies:
156 | - flake8-docstrings==1.7.0
157 | - repo: https://github.com/PyCQA/isort
158 | rev: 7.0.0
159 | hooks:
160 | - id: isort
161 | - repo: https://github.com/pre-commit/mirrors-mypy
162 | rev: v1.18.2
163 | hooks:
164 | - id: mypy
165 | - repo: https://github.com/pypa/pip-audit
166 | rev: v2.9.0
167 | hooks:
168 | - id: pip-audit
169 | args:
170 | # We have to ignore this vulnerability since we need to pin
171 | # to ansible 10 for now to support our CyHy code that must
172 | # still run on Debian Buster. This vulnerability is fixed
173 | # in ansible>=12.
174 | #
175 | # This isn't a big deal since the vulnerability only impacts
176 | # users of the Keycloak modules in
177 | # ansible.community.general, and we don't use these modules.
178 | #
179 | # TODO: Remove this when it becomes possible. See
180 | # cisagov/skeleton-packer#486 for more details.
181 | - --ignore-vuln
182 | - GHSA-8ggh-xwr9-3373
183 | # Add any pip requirements files to scan
184 | - --requirement
185 | - requirements-dev.txt
186 | - --requirement
187 | - requirements-test.txt
188 | - --requirement
189 | - requirements.txt
190 | - repo: https://github.com/asottile/pyupgrade
191 | rev: v3.21.1
192 | hooks:
193 | - id: pyupgrade
194 | args:
195 | # Python 3.10 is currently the oldest non-EOL version of
196 | # Python, so we want to apply all rules that apply to this
197 | # version or later. See here for more details:
198 | # https://www.gyford.com/phil/writing/2025/08/26/how-to-use-pyupgrade/
199 | - --py310-plus
200 |
201 | # Ansible hooks
202 | - repo: https://github.com/ansible/ansible-lint
203 | rev: v25.11.1
204 | hooks:
205 | - id: ansible-lint
206 | additional_dependencies:
207 | # On its own ansible-lint does not pull in ansible, only
208 | # ansible-core. Therefore, if an Ansible module lives in
209 | # ansible instead of ansible-core, the linter will complain
210 | # that the module is unknown. In these cases it is
211 | # necessary to add the ansible package itself as an
212 | # additional dependency, with the same pinning as is done in
213 | # requirements-test.txt of cisagov/skeleton-ansible-role.
214 | #
215 | # Version 10 is required because the pip-audit pre-commit
216 | # hook identifies a vulnerability in ansible-core 2.16.13,
217 | # but all versions of ansible 9 have a dependency on
218 | # ~=2.16.X.
219 | # - ansible>=10,<11
220 | # ansible-core<2.17.7 suffers from GHSA-99w6-3xph-cx78.
221 | #
222 | # Note that any changes made to this dependency must also be
223 | # made in requirements.txt in cisagov/skeleton-packer and
224 | # requirements-test.txt in cisagov/skeleton-ansible-role.
225 | - ansible-core>=2.17.7
226 |
227 | # Terraform hooks
228 | - repo: https://github.com/antonbabenko/pre-commit-terraform
229 | rev: v1.103.0
230 | hooks:
231 | - id: terraform_fmt
232 | - id: terraform_validate
233 |
234 | # Docker hooks
235 | - repo: https://github.com/IamTheFij/docker-pre-commit
236 | rev: v3.0.1
237 | hooks:
238 | - id: docker-compose-check
239 |
240 | # Packer hooks
241 | - repo: https://github.com/cisagov/pre-commit-packer
242 | rev: v0.3.1
243 | hooks:
244 | - id: packer_fmt
245 | - id: packer_validate
246 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: release
3 |
4 | on: # yamllint disable-line rule:truthy
5 | release:
6 | types:
7 | - released
8 |
9 | env:
10 | AWS_DEFAULT_REGION: us-east-1
11 | # We have seen some failures of packer init in GitHub Actions due to
12 | # rate limiting when pulling Packer plugins from GitHub. Having
13 | # Packer use a GitHub API token should reduce this. The rate
14 | # limiting for unauthenticated requests is 60 requests/hour while
15 | # the rate limiting for authenticated requests is 5000
16 | # requests/hour.
17 | PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 | # Do not copy the AMI to other regions until we have figured out a
19 | # workable mechanism for creating and managing AMI KMS keys in other
20 | # regions.
21 | # See https://github.com/cisagov/cool-system/issues/18 for details.
22 | # COPY_REGIONS_KMS_MAP: "us-east-2:alias/cool-amis,
23 | # us-west-1:alias/cool-amis,
24 | # us-west-2:alias/cool-amis"
25 | PIP_CACHE_DIR: ~/.cache/pip
26 | RUN_TMATE: ${{ secrets.RUN_TMATE }}
27 |
28 | jobs:
29 | diagnostics:
30 | name: Run diagnostics
31 | # This job does not need any permissions
32 | permissions: {}
33 | runs-on: ubuntu-latest
34 | steps:
35 | # Note that a duplicate of this step must be added at the top of
36 | # each job.
37 | - name: Apply standard cisagov job preamble
38 | uses: cisagov/action-job-preamble@v1
39 | with:
40 | check_github_status: "true"
41 | # This functionality is poorly implemented and has been
42 | # causing problems due to the MITM implementation hogging or
43 | # leaking memory. As a result we disable it by default. If
44 | # you want to temporarily enable it, simply set
45 | # monitor_permissions equal to "true".
46 | #
47 | # TODO: Re-enable this functionality when practical. See
48 | # cisagov/skeleton-packer#411 for more details.
49 | monitor_permissions: "false"
50 | output_workflow_context: "true"
51 | # Use a variable to specify the permissions monitoring
52 | # configuration. By default this will yield the
53 | # configuration stored in the cisagov organization-level
54 | # variable, but if you want to use a different configuration
55 | # then simply:
56 | # 1. Create a repository-level variable with the name
57 | # ACTIONS_PERMISSIONS_CONFIG.
58 | # 2. Set this new variable's value to the configuration you
59 | # want to use for this repository.
60 | #
61 | # Note in particular that changing the permissions
62 | # monitoring configuration *does not* require you to modify
63 | # this workflow.
64 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
65 | release:
66 | environment: production
67 | needs:
68 | - diagnostics
69 | permissions:
70 | # actions/checkout needs this to fetch code
71 | contents: read
72 | runs-on: ubuntu-latest
73 | strategy:
74 | fail-fast: false
75 | matrix:
76 | architecture:
77 | # cisagov/ansible-role-guacamole cannot currently support
78 | # ARM64 because the official Guacamole Docker images do not.
79 | # - arm64
80 | - x86_64
81 | steps:
82 | - name: Apply standard cisagov job preamble
83 | uses: cisagov/action-job-preamble@v1
84 | with:
85 | # This functionality is poorly implemented and has been
86 | # causing problems due to the MITM implementation hogging or
87 | # leaking memory. As a result we disable it by default. If
88 | # you want to temporarily enable it, simply set
89 | # monitor_permissions equal to "true".
90 | #
91 | # TODO: Re-enable this functionality when practical. See
92 | # cisagov/skeleton-packer#411 for more details.
93 | monitor_permissions: "false"
94 | # Use a variable to specify the permissions monitoring
95 | # configuration. By default this will yield the
96 | # configuration stored in the cisagov organization-level
97 | # variable, but if you want to use a different configuration
98 | # then simply:
99 | # 1. Create a repository-level variable with the name
100 | # ACTIONS_PERMISSIONS_CONFIG.
101 | # 2. Set this new variable's value to the configuration you
102 | # want to use for this repository.
103 | #
104 | # Note in particular that changing the permissions
105 | # monitoring configuration *does not* require you to modify
106 | # this workflow.
107 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
108 | - id: setup-env
109 | uses: cisagov/setup-env-github-action@v1
110 | - uses: actions/checkout@v6
111 | - id: setup-python
112 | uses: actions/setup-python@v6
113 | with:
114 | python-version: ${{ steps.setup-env.outputs.python-version }}
115 | - uses: actions/cache@v4
116 | env:
117 | BASE_CACHE_KEY: ${{ github.job }}-${{ runner.os }}-\
118 | py${{ steps.setup-python.outputs.python-version }}-\
119 | packer${{ steps.setup-env.outputs.packer-version }}-\
120 | tf-${{ steps.setup-env.outputs.terraform-version }}-
121 | with:
122 | path: |
123 | ${{ env.PIP_CACHE_DIR }}
124 | key: ${{ env.BASE_CACHE_KEY }}\
125 | ${{ hashFiles('**/requirements.txt') }}
126 | restore-keys: |
127 | ${{ env.BASE_CACHE_KEY }}
128 | - uses: hashicorp/setup-packer@v3
129 | with:
130 | version: ${{ steps.setup-env.outputs.packer-version }}
131 | - uses: hashicorp/setup-terraform@v3
132 | with:
133 | terraform_version: ${{ steps.setup-env.outputs.terraform-version }}
134 | - name: Install dependencies
135 | run: |
136 | python -m pip install --upgrade pip
137 | pip install --upgrade \
138 | --requirement requirements.txt
139 | # Do not copy the AMI to other regions until we have figured out a
140 | # workable mechanism for creating and managing AMI KMS keys in other
141 | # regions.
142 | # See https://github.com/cisagov/cool-system/issues/18 for details.
143 | # - name: Add copy regions to packer configuration
144 | # run: |
145 | # echo $COPY_REGIONS_KMS_MAP | \
146 | # ./patch_packer_config.py variables.pkr.hcl
147 | - name: Assume AWS build role
148 | uses: aws-actions/configure-aws-credentials@v5
149 | with:
150 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
151 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
152 | aws-region: ${{ env.AWS_DEFAULT_REGION }}
153 | role-to-assume: ${{ secrets.BUILD_ROLE_TO_ASSUME }}
154 | role-duration-seconds: 3600
155 | # When called by Packer, Ansible will find /usr/bin/python3 and
156 | # use it; therefore, we must ensure that /usr/bin/python3 points
157 | # to the version of Python that we installed in the
158 | # actions/setup-python step above. This can hose other tasks
159 | # that are expecting to find the system Python at that location,
160 | # though, so we undo this change after running Packer.
161 | - name: Create a /usr/bin/python3 symlink to the installed python
162 | run: |
163 | sudo mv /usr/bin/python3 /usr/bin/python3-default
164 | sudo ln -s ${{ env.pythonLocation }}/bin/python3 \
165 | /usr/bin/python3
166 | - name: Install Packer plugins
167 | run: packer init .
168 | - name: Create machine image
169 | run: |
170 | packer build -only amazon-ebs.${{ matrix.architecture }} \
171 | -timestamp-ui \
172 | -var github_ref_name=${{ github.ref_name }} \
173 | -var github_sha=${{ github.sha }} \
174 | -var is_prerelease=${{ github.event.release.prerelease }} \
175 | -var release_tag=${{ github.event.release.tag_name }} \
176 | -var release_url=${{ github.event.release.html_url }} \
177 | .
178 | - name: Remove /usr/bin/python3 symlink to the installed python
179 | run: |
180 | sudo mv /usr/bin/python3-default /usr/bin/python3
181 | - name: Setup tmate debug session
182 | uses: mxschmitt/action-tmate@v3
183 | if: env.RUN_TMATE
184 |
--------------------------------------------------------------------------------
/setup-env:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -o nounset
4 | set -o errexit
5 | set -o pipefail
6 |
7 | USAGE=$(
8 | cat << 'END_OF_LINE'
9 | Configure a development environment for this repository.
10 |
11 | It does the following:
12 | - Allows the user to specify the Python version to use for the virtual environment.
13 | - Allows the user to specify a name for the virtual environment.
14 | - Verifies pyenv and pyenv-virtualenv are installed.
15 | - Creates the Python virtual environment.
16 | - Configures the activation of the virtual enviroment for the repo directory.
17 | - Installs the requirements needed for development.
18 | - Installs git pre-commit hooks.
19 | - Configures git remotes for upstream "lineage" repositories.
20 |
21 | Usage:
22 | setup-env [--venv-name venv_name] [--python-version python_version]
23 | setup-env (-h | --help)
24 |
25 | Options:
26 | -f | --force Delete virtual enviroment if it already exists.
27 | -h | --help Show this message.
28 | -i | --install-hooks Install hook environments for all environments in the
29 | pre-commit config file.
30 | -l | --list-versions List available Python versions and select one interactively.
31 | -v | --venv-name Specify the name of the virtual environment.
32 | -p | --python-version Specify the Python version for the virtual environment.
33 |
34 | END_OF_LINE
35 | )
36 |
37 | # Display pyenv's installed Python versions
38 | python_versions() {
39 | pyenv versions --bare --skip-aliases --skip-envs
40 | }
41 |
42 | check_python_version() {
43 | local version=$1
44 |
45 | # This is a valid regex for semantically correct Python version strings.
46 | # For more information see here:
47 | # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
48 | # Break down the regex into readable parts major.minor.patch
49 | local major="0|[1-9]\d*"
50 | local minor="0|[1-9]\d*"
51 | local patch="0|[1-9]\d*"
52 |
53 | # Splitting the prerelease part for readability
54 | # Start of the prerelease
55 | local prerelease="(?:-"
56 | # Numeric or alphanumeric identifiers
57 | local prerelease+="(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)"
58 | # Additional dot-separated identifiers
59 | local prerelease+="(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*"
60 | # End of the prerelease, making it optional
61 | local prerelease+=")?"
62 | # Optional build metadata
63 | local build="(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?"
64 |
65 | # Final regex composed of parts
66 | local regex="^($major)\.($minor)\.($patch)$prerelease$build$"
67 |
68 | # This checks if the Python version does not match the regex pattern specified in $regex,
69 | # using Perl for regex matching. If the pattern is not found, then prompt the user with
70 | # the invalid version message.
71 | if ! echo "$version" | perl -ne "exit(!/$regex/)"; then
72 | echo "Invalid version of Python: Python follows semantic versioning," \
73 | "so any version string that is not a valid semantic version is an" \
74 | "invalid version of Python."
75 | exit 1
76 | # Else if the Python version isn't installed then notify the user.
77 | # grep -E is used for searching through text lines that match the
78 | # specific version.
79 | elif ! python_versions | grep -E "^${version}$" > /dev/null; then
80 | echo "Error: Python version $version is not installed."
81 | echo "Installed Python versions are:"
82 | python_versions
83 | exit 1
84 | else
85 | echo "Using Python version $version"
86 | fi
87 | }
88 |
89 | # Flag to force deletion and creation of virtual environment
90 | FORCE=0
91 |
92 | # Initialize the other flags
93 | INSTALL_HOOKS=0
94 | LIST_VERSIONS=0
95 | PYTHON_VERSION=""
96 | VENV_NAME=""
97 |
98 | # Define long options
99 | LONGOPTS="force,help,install-hooks,list-versions,python-version:,venv-name:"
100 |
101 | # Define short options for getopt
102 | SHORTOPTS="fhilp:v:"
103 |
104 | # Check for GNU getopt by testing for long option support. GNU getopt supports
105 | # the "--test" option and will return exit code 4 while POSIX/BSD getopt does
106 | # not and will return exit code 0.
107 | if getopt --test > /dev/null 2>&1; then
108 | cat << 'END_OF_LINE'
109 |
110 | Please note, this script requires GNU getopt due to its enhanced
111 | functionality and compatibility with certain script features that
112 | are not supported by the POSIX getopt found in some systems, particularly
113 | those with a non-GNU version of getopt. This distinction is crucial
114 | as a system might have a non-GNU version of getopt installed by default,
115 | which could lead to unexpected behavior.
116 |
117 | On macOS, we recommend installing brew (https://brew.sh/). Then installation
118 | is as simple as `brew install gnu-getopt` and adding this to your
119 | profile:
120 |
121 | export PATH="$(brew --prefix)/opt/gnu-getopt/bin:$PATH"
122 |
123 | GNU getopt must be explicitly added to the PATH since it
124 | is keg-only (https://docs.brew.sh/FAQ#what-does-keg-only-mean).
125 |
126 | END_OF_LINE
127 | exit 1
128 | fi
129 |
130 | # Check to see if pyenv is installed
131 | if [ -z "$(command -v pyenv)" ] || { [ -z "$(command -v pyenv-virtualenv)" ] && [ ! -f "$(pyenv root)/plugins/pyenv-virtualenv/bin/pyenv-virtualenv" ]; }; then
132 | echo "pyenv and pyenv-virtualenv are required."
133 | if [[ "$OSTYPE" == "darwin"* ]]; then
134 | cat << 'END_OF_LINE'
135 |
136 | On macOS, we recommend installing brew, https://brew.sh/. Then installation
137 | is as simple as `brew install pyenv pyenv-virtualenv` and adding this to your
138 | profile:
139 |
140 | eval "$(pyenv init -)"
141 | eval "$(pyenv virtualenv-init -)"
142 |
143 | END_OF_LINE
144 |
145 | fi
146 | cat << 'END_OF_LINE'
147 | For Linux, Windows Subsystem for Linux (WSL), or macOS (if you don't want
148 | to use "brew") you can use https://github.com/pyenv/pyenv-installer to install
149 | the necessary tools. Before running this ensure that you have installed the
150 | prerequisites for your platform according to the pyenv wiki page,
151 | https://github.com/pyenv/pyenv/wiki/common-build-problems.
152 |
153 | On WSL you should treat your platform as whatever Linux distribution you've
154 | chosen to install.
155 |
156 | Once you have installed "pyenv" you will need to add the following lines to
157 | your ".bashrc":
158 |
159 | export PATH="$PATH:$HOME/.pyenv/bin"
160 | eval "$(pyenv init -)"
161 | eval "$(pyenv virtualenv-init -)"
162 | END_OF_LINE
163 | exit 1
164 | fi
165 |
166 | # Use GNU getopt to parse options
167 | if ! PARSED=$(getopt --options $SHORTOPTS --longoptions $LONGOPTS --name "$0" -- "$@"); then
168 | echo "Error parsing options"
169 | exit 1
170 | fi
171 | eval set -- "$PARSED"
172 |
173 | while true; do
174 | case "$1" in
175 | -f | --force)
176 | FORCE=1
177 | shift
178 | ;;
179 | -h | --help)
180 | echo "$USAGE"
181 | exit 0
182 | ;;
183 | -i | --install-hooks)
184 | INSTALL_HOOKS=1
185 | shift
186 | ;;
187 | -l | --list-versions)
188 | LIST_VERSIONS=1
189 | shift
190 | ;;
191 | -p | --python-version)
192 | PYTHON_VERSION="$2"
193 | shift 2
194 | # Check the Python version being passed in.
195 | check_python_version "$PYTHON_VERSION"
196 | ;;
197 | -v | --venv-name)
198 | VENV_NAME="$2"
199 | shift 2
200 | ;;
201 | --)
202 | shift
203 | break
204 | ;;
205 | *)
206 | # Unreachable due to GNU getopt handling all options
207 | echo "Programming error"
208 | exit 64
209 | ;;
210 | esac
211 | done
212 |
213 | # Determine the virtual environment name
214 | if [ -n "$VENV_NAME" ]; then
215 | # Use the user-provided environment name
216 | env_name="$VENV_NAME"
217 | else
218 | # Set the environment name to the last part of the working directory.
219 | env_name=${PWD##*/}
220 | fi
221 |
222 | # List Python versions and select one interactively.
223 | if [ $LIST_VERSIONS -ne 0 ]; then
224 | echo Available Python versions:
225 | python_versions
226 | # Read the user's desired Python version.
227 | # -r: treat backslashes as literal, -p: display prompt before input.
228 | read -r -p "Enter the desired Python version: " PYTHON_VERSION
229 | # Check the Python version being passed in.
230 | check_python_version "$PYTHON_VERSION"
231 | fi
232 |
233 | # Remove any lingering local configuration.
234 | if [ $FORCE -ne 0 ]; then
235 | rm -f .python-version
236 | pyenv virtualenv-delete --force "${env_name}" || true
237 | elif [[ -f .python-version ]]; then
238 | cat << 'END_OF_LINE'
239 | An existing .python-version file was found. Either remove this file yourself
240 | or re-run with the --force option to have it deleted along with the associated
241 | virtual environment.
242 |
243 | rm .python-version
244 |
245 | END_OF_LINE
246 | exit 1
247 | fi
248 |
249 | # Create a new virtual environment for this project
250 | #
251 | # If $PYTHON_VERSION is undefined then the current pyenv Python version will be used.
252 | #
253 | # We can't quote ${PYTHON_VERSION:=} below since if the variable is
254 | # undefined then we want nothing to appear; this is the reason for the
255 | # "shellcheck disable" line below.
256 | #
257 | # shellcheck disable=SC2086
258 | if ! pyenv virtualenv ${PYTHON_VERSION:=} "${env_name}"; then
259 | cat << END_OF_LINE
260 | An existing virtual environment named $env_name was found. Either delete this
261 | environment yourself or re-run with the --force option to have it deleted.
262 |
263 | pyenv virtualenv-delete ${env_name}
264 |
265 | END_OF_LINE
266 | exit 1
267 | fi
268 |
269 | # Set the local application-specific Python version(s) by writing the
270 | # version name to a file named `.python-version'.
271 | pyenv local "${env_name}"
272 |
273 | # Upgrade pip and friends
274 | python3 -m pip install --upgrade pip setuptools wheel
275 |
276 | # Find a requirements file (if possible) and install
277 | for req_file in "requirements-dev.txt" "requirements-test.txt" "requirements.txt"; do
278 | if [[ -f $req_file ]]; then
279 | pip install --requirement $req_file
280 | break
281 | fi
282 | done
283 |
284 | # Install Packer plugin dependencies
285 | packer init -upgrade .
286 |
287 | # Install git pre-commit hooks now or later.
288 | pre-commit install ${INSTALL_HOOKS:+"--install-hooks"}
289 |
290 | # Setup git remotes from lineage configuration
291 | # This could fail if the remotes are already setup, but that is ok.
292 | set +o errexit
293 |
294 | eval "$(
295 | python3 << 'END_OF_LINE'
296 | from pathlib import Path
297 | import yaml
298 | import sys
299 |
300 | LINEAGE_CONFIG = Path(".github/lineage.yml")
301 |
302 | if not LINEAGE_CONFIG.exists():
303 | print("No lineage configuration found.", file=sys.stderr)
304 | sys.exit(0)
305 |
306 | with LINEAGE_CONFIG.open("r") as f:
307 | lineage = yaml.safe_load(stream=f)
308 |
309 | if lineage["version"] == "1":
310 | for parent_name, v in lineage["lineage"].items():
311 | remote_url = v["remote-url"]
312 | print(f"git remote add {parent_name} {remote_url};")
313 | print(f"git remote set-url --push {parent_name} no_push;")
314 | else:
315 | print(f'Unsupported lineage version: {lineage["version"]}', file=sys.stderr)
316 | END_OF_LINE
317 | )"
318 |
319 | # Qapla'
320 | echo "Success!"
321 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # guacamole-packer 🥑📦 #
2 |
3 | [](https://github.com/cisagov/guacamole-packer/actions)
4 | [](https://spdx.org/licenses/)
5 | [](https://github.com/cisagov/guacamole-packer/actions/workflows/codeql-analysis.yml)
6 |
7 | This project can be used to create machine images that include
8 | [Apache Guacamole](https://guacamole.apache.org/), a clientless
9 | remote desktop gateway.
10 |
11 | ## Pre-requisites ##
12 |
13 | This project requires a build user to exist in AWS. The accompanying Terraform
14 | code will create the user with the appropriate name and permissions. This only
15 | needs to be run once per project, per AWS account. This user will also be used
16 | by GitHub Actions.
17 |
18 | Before the build user can be created, the following profile must exist in
19 | your AWS credentials file:
20 |
21 | - `cool-terraform-backend`
22 |
23 | The easiest way to set up that profile is to use our
24 | [`aws-profile-sync`](https://github.com/cisagov/aws-profile-sync) utility.
25 | Follow the usage instructions in that repository before continuing with the
26 | next steps. Note that you will need to know where your team stores their
27 | remote profile data in order to use
28 | [`aws-profile-sync`](https://github.com/cisagov/aws-profile-sync).
29 |
30 | ### Creating a build user ###
31 |
32 | You will need to create a build user for each environment that you use. The
33 | following steps show how to create a build user for an environment named "dev".
34 | You will need to repeat this process for any additional environments.
35 |
36 | 1. Change into the `terraform-build-user` directory:
37 |
38 | ```console
39 | cd terraform-build-user
40 | ```
41 |
42 | 1. Create a backend configuration file named `dev.tfconfig` containing the
43 | name of the bucket where "dev" environment Terraform state is stored - this file
44 | is required to initialize the Terraform backend in each environment:
45 |
46 | ```hcl
47 | bucket = "my-dev-terraform-state-bucket"
48 | ```
49 |
50 | 1. Initialize the Terraform backend for the "dev" environment using your backend
51 | configuration file:
52 |
53 | ```console
54 | terraform init -backend-config=dev.tfconfig
55 | ```
56 |
57 | > [!NOTE]
58 | > When performing this step for additional environments (i.e. not your first
59 | > environment), use the `-reconfigure` flag:
60 | >
61 | > ```console
62 | > terraform init -backend-config=other-env.tfconfig -reconfigure
63 | > ```
64 |
65 | 1. Create a Terraform variables file named `dev.tfvars` containing all
66 | required variables (currently only `terraform_state_bucket`):
67 |
68 | ```hcl
69 | terraform_state_bucket = "my-dev-terraform-state-bucket"
70 | ```
71 |
72 | 1. Create a Terraform workspace for the "dev" environment:
73 |
74 | ```console
75 | terraform workspace new dev
76 | ```
77 |
78 | 1. Initialize and upgrade the Terraform workspace, then apply the configuration
79 | to create the build user in the "dev" environment:
80 |
81 | ```console
82 | terraform init -upgrade=true
83 | terraform apply -var-file=dev.tfvars
84 | ```
85 |
86 | Once the build user is created you will need to update the
87 | [repository's secrets](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets)
88 | with the new encrypted environment variables. This should be done using the
89 | [`terraform-to-secrets`](https://github.com/cisagov/development-guide/tree/develop/project_setup#terraform-iam-credentials-to-github-secrets-)
90 | tool available in the
91 | [development guide](https://github.com/cisagov/development-guide). Instructions
92 | for how to use this tool can be found in the
93 | ["Terraform IAM Credentials to GitHub Secrets" section](https://github.com/cisagov/development-guide/tree/develop/project_setup#terraform-iam-credentials-to-github-secrets-).
94 | of the Project Setup README.
95 |
96 | If you have appropriate permissions for the repository you can view existing
97 | secrets on the
98 | [appropriate page](https://github.com/cisagov/guacamole-packer/settings/secrets)
99 | in the repository's settings.
100 |
101 | This project also requires the following data to exist in your [AWS Systems
102 | Manager parameter store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html):
103 |
104 | - `/guacamole/postgres_username`: The non-admin postgres username used by
105 | Guacamole
106 | - `/guacamole/postgres_password`: The non-admin postgres password used by
107 | Guacamole
108 | - `/rdp/username`: The username for Guacamole to use when connecting to an
109 | instance via RDP
110 | - `/rdp/password`: The password for Guacamole to use when connecting to an
111 | instance via RDP
112 | - `/vnc/ssh/ed25519_private_key`: The private SSH key to use for SFTP
113 | file transfer in Guacamole
114 | - `/vnc/username`: The username for Guacamole to use when connecting to an
115 | instance via VNC
116 | - `/vnc/password`: The password for Guacamole to use when connecting to an
117 | instance via VNC
118 | - `/vnc/sftp/windows_base_directory`: The base path for the SFTP directories
119 | that Guacamole will use when connecting to a Windows instance via VNC
120 |
121 | IMPORTANT: The account where your images will be built must have a VPC and
122 | a public subnet both tagged with the name "AMI Build", otherwise `packer`
123 | will not be able to build images.
124 |
125 | ## Building the image ##
126 |
127 | ### Using GitHub Actions ###
128 |
129 | 1. Create a [new release](https://help.github.com/en/articles/creating-releases)
130 | in GitHub.
131 | 1. There is no step 2!
132 |
133 | GitHub Actions can build this project in three different modes depending on
134 | how the build was triggered from GitHub.
135 |
136 | 1. **Development release**: After a normal commit and also on a pull request,
137 | GitHub Actions will run tests and validation on the Packer template, and then
138 | build the project. An image will be built and deployed using the
139 | [`build`](.github/workflows/build.yml) workflow. This should be configured
140 | to deploy the image to a single region using a development account.
141 | 1. **Pre-release**: Publish a GitHub release with the "This is a pre-release"
142 | checkbox checked. An image will be built and deployed using the
143 | [`prerelease`](.github/workflows/prerelease.yml) workflow. This should be
144 | configured to deploy the image to a single region using a non-production
145 | account (e.g. "staging").
146 | 1. **Production release**: Publish a GitHub release with the "This is a
147 | pre-release" checkbox unchecked. An image will be built and deployed using
148 | the [`release`](.github/workflows/release.yml) workflow. This should be
149 | configured to deploy the image to multiple regions using a production
150 | account.
151 |
152 | ### Using your local environment ###
153 |
154 | Packer will use your
155 | [standard AWS environment](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)
156 | to build the image, however you will need to set up one profile for the
157 | previously-created build user and another profile to assume the associated
158 | `EC2AMICreate` role. You will need the `aws_access_key_id` and
159 | `aws_secret_access_key` that you set as GitHub secrets earlier.
160 |
161 | Add the following blocks to your AWS credentials file (be sure to replace the
162 | dummy account ID in the `role_arn` with your own):
163 |
164 | ```console
165 | [build-guacamole-packer]
166 | aws_access_key_id = AKIAXXXXXXXXXXXXXXXX
167 | aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
168 |
169 | [cool-images-ec2amicreate-guacamole-packer]
170 | role_arn = arn:aws:iam::111111111111:role/EC2AMICreate-build-guacamole-packer
171 | source_profile = build-guacamole-packer
172 | role_session_name = example
173 | ```
174 |
175 | This Packer template defines a number of variables whose defaults can be changed
176 | through a `.pkrvars.hcl` file:
177 |
178 | ```hcl
179 | build_region = "us-east-2"
180 | build_region_kms = "alias/example-kms"
181 | is_prerelease = "true"
182 | ```
183 |
184 | Here is an example of how to kick off a pre-release build:
185 |
186 | ```console
187 | pip install --requirement requirements-dev.txt
188 | ansible-galaxy install --force --force-with-deps --role-file ansible/requirements.yml
189 | AWS_PROFILE=cool-images-ec2amicreate-guacamole-packer packer build --timestamp-ui -var release_tag=$(./bump-version show) -var is_prerelease=true .
190 | ```
191 |
192 | If you are satisfied with your pre-release image, you can easily create a release
193 | that deploys to all regions by adding additional regions to the Packer template.
194 | This can be done by using a `.pkrvars.hcl` for example with `release.pkrvars.hcl`:
195 |
196 | ```hcl
197 | ami_regions = ["us-east-2", "us-west-1", "us-west-2"]
198 | region_kms_keys = {
199 | "us-east-2": "alias/cool-amis",
200 | "us-west-1": "alias/cool-amis",
201 | "us-west-2": "alias/cool-amis",
202 | }
203 | ```
204 |
205 | ```console
206 | AWS_PROFILE=cool-images-ec2amicreate-guacamole-packer packer build --timestamp-ui -var-file release.pkrvars.hcl .
207 | ```
208 |
209 | ### Giving other AWS accounts permission to launch the image ###
210 |
211 | After the AMI has been successfully created, you may want to allow other
212 | accounts in your AWS organization permission to launch it. The following steps
213 | show how to do this for an environment named "dev". You will need to repeat this
214 | process for any additional environments.
215 |
216 | > [!NOTE]
217 | > Refer to the `ami_share_account_name_regex` variable if you want to customize
218 | > which accounts in your AWS organization to share your AMI with.
219 |
220 | 1. Change into the `terraform-post-packer` directory:
221 |
222 | ```console
223 | cd terraform-post-packer
224 | ```
225 |
226 | 1. Create a backend configuration file named `dev.tfconfig` containing the
227 | name of the bucket where "dev" environment Terraform state is stored - this file
228 | is required to initialize the Terraform backend in each environment:
229 |
230 | ```hcl
231 | bucket = "my-dev-terraform-state-bucket"
232 | ```
233 |
234 | 1. Initialize the Terraform backend for the "dev" environment using your backend
235 | configuration file:
236 |
237 | ```console
238 | terraform init -backend-config=dev.tfconfig
239 | ```
240 |
241 | > [!NOTE]
242 | > When performing this step for additional environments (i.e. not your first
243 | > environment), use the `-reconfigure` flag:
244 | >
245 | > ```console
246 | > terraform init -backend-config=other-env.tfconfig -reconfigure
247 | > ```
248 |
249 | 1. If not already created, create a Terraform workspace for the "dev" environment:
250 |
251 | ```console
252 | terraform workspace new dev
253 | ```
254 |
255 | Otherwise, switch to the existing "dev" workspace:
256 |
257 | ```console
258 | terraform workspace select dev
259 | ```
260 |
261 | 1. Initialize and upgrade the Terraform workspace, then apply the configuration
262 | to share the AMI with accounts in the "dev" environment:
263 |
264 | ```console
265 | terraform init -upgrade=true
266 | terraform apply
267 | ```
268 |
269 |
270 | ## Requirements ##
271 |
272 | No requirements.
273 |
274 | ## Providers ##
275 |
276 | | Name | Version |
277 | |------|---------|
278 | | amazon-ami | n/a |
279 |
280 | ## Modules ##
281 |
282 | No modules.
283 |
284 | ## Resources ##
285 |
286 | | Name | Type |
287 | |------|------|
288 | | [amazon-ami_amazon-ami.debian_trixie_arm64](https://registry.terraform.io/providers/hashicorp/amazon-ami/latest/docs/data-sources/amazon-ami) | data source |
289 | | [amazon-ami_amazon-ami.debian_trixie_x86_64](https://registry.terraform.io/providers/hashicorp/amazon-ami/latest/docs/data-sources/amazon-ami) | data source |
290 |
291 | ## Inputs ##
292 |
293 | | Name | Description | Type | Default | Required |
294 | |------|-------------|------|---------|:--------:|
295 | | ami\_regions | The list of AWS regions to copy the AMI to once it has been created. Example: ["us-east-1"] | `list(string)` | `[]` | no |
296 | | build\_region | The region in which to retrieve the base AMI from and build the new AMI. | `string` | `"us-east-1"` | no |
297 | | build\_region\_kms | The ID or ARN of the KMS key to use for AMI encryption. | `string` | `"alias/cool-amis"` | no |
298 | | force\_install\_ansible\_requirements | Indicate if the Ansible requirements should be force installed. | `bool` | `false` | no |
299 | | force\_install\_ansible\_requirements\_with\_dependencies | Indicate if the Ansible requirements *and* their dependencies should be force installed. | `bool` | `false` | no |
300 | | github\_ref\_name | The GitHub short ref name to use for the tags applied to the created AMI. | `string` | `""` | no |
301 | | github\_sha | The GitHub commit SHA to use for the tags applied to the created AMI. | `string` | `""` | no |
302 | | is\_prerelease | The pre-release status to use for the tags applied to the created AMI. | `bool` | `false` | no |
303 | | 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 |
304 | | release\_tag | The GitHub release tag to use for the tags applied to the created AMI. | `string` | `""` | no |
305 | | release\_url | The GitHub release URL to use for the tags applied to the created AMI. | `string` | `""` | no |
306 | | skip\_create\_ami | Indicate if Packer should not create the AMI. | `bool` | `false` | no |
307 |
308 | ## Outputs ##
309 |
310 | No outputs.
311 |
312 |
313 | ## Contributing ##
314 |
315 | We welcome contributions! Please see [`CONTRIBUTING.md`](CONTRIBUTING.md) for
316 | details.
317 |
318 | ## License ##
319 |
320 | This project is in the worldwide [public domain](LICENSE).
321 |
322 | This project is in the public domain within the United States, and
323 | copyright and related rights in the work worldwide are waived through
324 | the [CC0 1.0 Universal public domain
325 | dedication](https://creativecommons.org/publicdomain/zero/1.0/).
326 |
327 | All contributions to this project will be released under the CC0
328 | dedication. By submitting a pull request, you are agreeing to comply
329 | with this waiver of copyright interest.
330 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: build
3 |
4 | on: # yamllint disable-line rule:truthy
5 | merge_group:
6 | types:
7 | - checks_requested
8 | pull_request:
9 | push:
10 | repository_dispatch:
11 | types:
12 | - apb
13 |
14 | # Set a default shell for any run steps. The `-Eueo pipefail` sets errtrace,
15 | # nounset, errexit, and pipefail. The `-x` will print all commands as they are
16 | # run. Please see the GitHub Actions documentation for more information:
17 | # https://docs.github.com/en/actions/using-jobs/setting-default-values-for-jobs
18 | defaults:
19 | run:
20 | shell: bash -Eueo pipefail -x {0}
21 |
22 | env:
23 | AWS_DEFAULT_REGION: us-east-1
24 | # We have seen some failures of packer init in GitHub Actions due to
25 | # rate limiting when pulling Packer plugins from GitHub. Having
26 | # Packer use a GitHub API token should reduce this. The rate
27 | # limiting for unauthenticated requests is 60 requests/hour while
28 | # the rate limiting for authenticated requests is 5000
29 | # requests/hour.
30 | PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31 | PIP_CACHE_DIR: ~/.cache/pip
32 | PRE_COMMIT_CACHE_DIR: ~/.cache/pre-commit
33 | RUN_TMATE: ${{ secrets.RUN_TMATE }}
34 | TERRAFORM_DOCS_REPO_BRANCH_NAME: improvement/support_atx_closed_markdown_headers
35 | TERRAFORM_DOCS_REPO_DEPTH: 1
36 | TERRAFORM_DOCS_REPO_URL: https://github.com/mcdonnnj/terraform-docs.git
37 |
38 | jobs:
39 | diagnostics:
40 | name: Run diagnostics
41 | # This job does not need any permissions
42 | permissions: {}
43 | runs-on: ubuntu-latest
44 | steps:
45 | # Note that a duplicate of this step must be added at the top of
46 | # each job.
47 | - name: Apply standard cisagov job preamble
48 | uses: cisagov/action-job-preamble@v1
49 | with:
50 | check_github_status: "true"
51 | # This functionality is poorly implemented and has been
52 | # causing problems due to the MITM implementation hogging or
53 | # leaking memory. As a result we disable it by default. If
54 | # you want to temporarily enable it, simply set
55 | # monitor_permissions equal to "true".
56 | #
57 | # TODO: Re-enable this functionality when practical. See
58 | # cisagov/skeleton-generic#207 for more details.
59 | monitor_permissions: "false"
60 | output_workflow_context: "true"
61 | # Use a variable to specify the permissions monitoring
62 | # configuration. By default this will yield the
63 | # configuration stored in the cisagov organization-level
64 | # variable, but if you want to use a different configuration
65 | # then simply:
66 | # 1. Create a repository-level variable with the name
67 | # ACTIONS_PERMISSIONS_CONFIG.
68 | # 2. Set this new variable's value to the configuration you
69 | # want to use for this repository.
70 | #
71 | # Note in particular that changing the permissions
72 | # monitoring configuration *does not* require you to modify
73 | # this workflow.
74 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
75 | lint:
76 | needs:
77 | - diagnostics
78 | permissions:
79 | # actions/checkout needs this to fetch code
80 | contents: read
81 | runs-on: ubuntu-latest
82 | steps:
83 | - name: Apply standard cisagov job preamble
84 | uses: cisagov/action-job-preamble@v1
85 | with:
86 | # This functionality is poorly implemented and has been
87 | # causing problems due to the MITM implementation hogging or
88 | # leaking memory. As a result we disable it by default. If
89 | # you want to temporarily enable it, simply set
90 | # monitor_permissions equal to "true".
91 | #
92 | # TODO: Re-enable this functionality when practical. See
93 | # cisagov/skeleton-generic#207 for more details.
94 | monitor_permissions: "false"
95 | # Use a variable to specify the permissions monitoring
96 | # configuration. By default this will yield the
97 | # configuration stored in the cisagov organization-level
98 | # variable, but if you want to use a different configuration
99 | # then simply:
100 | # 1. Create a repository-level variable with the name
101 | # ACTIONS_PERMISSIONS_CONFIG.
102 | # 2. Set this new variable's value to the configuration you
103 | # want to use for this repository.
104 | #
105 | # Note in particular that changing the permissions
106 | # monitoring configuration *does not* require you to modify
107 | # this workflow.
108 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
109 | - id: setup-env
110 | uses: cisagov/setup-env-github-action@v1
111 | - uses: actions/checkout@v6
112 | - id: setup-python
113 | uses: actions/setup-python@v6
114 | with:
115 | python-version: ${{ steps.setup-env.outputs.python-version }}
116 | # We need the Go version and Go cache location for the actions/cache step,
117 | # so the Go installation must happen before that.
118 | - id: setup-go
119 | uses: actions/setup-go@v6
120 | with:
121 | # There is no expectation for actual Go code so we disable caching as
122 | # it relies on the existence of a go.sum file.
123 | cache: false
124 | go-version: ${{ steps.setup-env.outputs.go-version }}
125 | - id: go-cache
126 | name: Lookup Go cache directory
127 | run: |
128 | echo "dir=$(go env GOCACHE)" >> $GITHUB_OUTPUT
129 | - uses: actions/cache@v4
130 | env:
131 | BASE_CACHE_KEY: ${{ github.job }}-${{ runner.os }}-\
132 | py${{ steps.setup-python.outputs.python-version }}-\
133 | go${{ steps.setup-go.outputs.go-version }}-\
134 | packer${{ steps.setup-env.outputs.packer-version }}-\
135 | tf${{ steps.setup-env.outputs.terraform-version }}-
136 | with:
137 | key: ${{ env.BASE_CACHE_KEY }}\
138 | ${{ hashFiles('**/requirements-test.txt') }}-\
139 | ${{ hashFiles('**/requirements.txt') }}-\
140 | ${{ hashFiles('**/.pre-commit-config.yaml') }}
141 | # Note that the .terraform directory IS NOT included in the
142 | # cache because if we were caching, then we would need to use
143 | # the `-upgrade=true` option. This option blindly pulls down the
144 | # latest modules and providers instead of checking to see if an
145 | # update is required. That behavior defeats the benefits of caching.
146 | # so there is no point in doing it for the .terraform directory.
147 | path: |
148 | ${{ env.PIP_CACHE_DIR }}
149 | ${{ env.PRE_COMMIT_CACHE_DIR }}
150 | ${{ steps.go-cache.outputs.dir }}
151 | restore-keys: |
152 | ${{ env.BASE_CACHE_KEY }}
153 | - uses: hashicorp/setup-packer@v3
154 | with:
155 | version: ${{ steps.setup-env.outputs.packer-version }}
156 | - uses: hashicorp/setup-terraform@v3
157 | with:
158 | terraform_version: ${{ steps.setup-env.outputs.terraform-version }}
159 | - name: Install go-critic
160 | env:
161 | PACKAGE_URL: github.com/go-critic/go-critic/cmd/gocritic
162 | PACKAGE_VERSION: ${{ steps.setup-env.outputs.go-critic-version }}
163 | run: go install ${PACKAGE_URL}@${PACKAGE_VERSION}
164 | - name: Install goimports
165 | env:
166 | PACKAGE_URL: golang.org/x/tools/cmd/goimports
167 | PACKAGE_VERSION: ${{ steps.setup-env.outputs.goimports-version }}
168 | run: go install ${PACKAGE_URL}@${PACKAGE_VERSION}
169 | - name: Install gosec
170 | env:
171 | PACKAGE_URL: github.com/securego/gosec/v2/cmd/gosec
172 | PACKAGE_VERSION: ${{ steps.setup-env.outputs.gosec-version }}
173 | run: go install ${PACKAGE_URL}@${PACKAGE_VERSION}
174 | - name: Install staticcheck
175 | env:
176 | PACKAGE_URL: honnef.co/go/tools/cmd/staticcheck
177 | PACKAGE_VERSION: ${{ steps.setup-env.outputs.staticcheck-version }}
178 | run: go install ${PACKAGE_URL}@${PACKAGE_VERSION}
179 | # TODO: https://github.com/cisagov/skeleton-generic/issues/165
180 | # We are temporarily using @mcdonnnj's forked branch of terraform-docs
181 | # until his PR: https://github.com/terraform-docs/terraform-docs/pull/745
182 | # is approved. This temporary fix will allow for ATX header support when
183 | # terraform-docs is run during linting.
184 | - name: Clone ATX headers branch from terraform-docs fork
185 | run: |
186 | git clone \
187 | --branch $TERRAFORM_DOCS_REPO_BRANCH_NAME \
188 | --depth $TERRAFORM_DOCS_REPO_DEPTH \
189 | --single-branch \
190 | $TERRAFORM_DOCS_REPO_URL /tmp/terraform-docs
191 | - name: Build and install terraform-docs binary
192 | run: |
193 | go build \
194 | -C /tmp/terraform-docs \
195 | -o $(go env GOPATH)/bin/terraform-docs
196 | - name: Install dependencies
197 | run: |
198 | python -m pip install --upgrade pip setuptools wheel
199 | pip install --upgrade --requirement requirements-test.txt
200 | - name: Install Ansible roles
201 | run: ansible-galaxy install --force --role-file ansible/requirements.yml
202 | # This must happen before pre-commit is run or the Packer format
203 | # linter will throw an error.
204 | - name: Install Packer plugins
205 | run: packer init .
206 | - name: Set up pre-commit hook environments
207 | run: pre-commit install-hooks
208 | - name: Run pre-commit on all files
209 | run: pre-commit run --all-files
210 | - name: Setup tmate debug session
211 | uses: mxschmitt/action-tmate@v3
212 | if: env.RUN_TMATE
213 | test:
214 | needs:
215 | - diagnostics
216 | permissions:
217 | # actions/checkout needs this to fetch code
218 | contents: read
219 | runs-on: ubuntu-latest
220 | steps:
221 | - name: Apply standard cisagov job preamble
222 | uses: cisagov/action-job-preamble@v1
223 | with:
224 | # This functionality is poorly implemented and has been
225 | # causing problems due to the MITM implementation hogging or
226 | # leaking memory. As a result we disable it by default. If
227 | # you want to temporarily enable it, simply set
228 | # monitor_permissions equal to "true".
229 | #
230 | # TODO: Re-enable this functionality when practical. See
231 | # cisagov/skeleton-packer#411 for more details.
232 | monitor_permissions: "false"
233 | # Use a variable to specify the permissions monitoring
234 | # configuration. By default this will yield the
235 | # configuration stored in the cisagov organization-level
236 | # variable, but if you want to use a different configuration
237 | # then simply:
238 | # 1. Create a repository-level variable with the name
239 | # ACTIONS_PERMISSIONS_CONFIG.
240 | # 2. Set this new variable's value to the configuration you
241 | # want to use for this repository.
242 | #
243 | # Note in particular that changing the permissions
244 | # monitoring configuration *does not* require you to modify
245 | # this workflow.
246 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
247 | - id: setup-env
248 | uses: cisagov/setup-env-github-action@v1
249 | - uses: actions/checkout@v6
250 | - id: setup-python
251 | uses: actions/setup-python@v6
252 | with:
253 | python-version: ${{ steps.setup-env.outputs.python-version }}
254 | - uses: actions/cache@v4
255 | env:
256 | BASE_CACHE_KEY: ${{ github.job }}-${{ runner.os }}-\
257 | py${{ steps.setup-python.outputs.python-version }}-\
258 | packer${{ steps.setup-env.outputs.packer-version }}-
259 | with:
260 | path: |
261 | ${{ env.PIP_CACHE_DIR }}
262 | key: ${{ env.BASE_CACHE_KEY }}\
263 | ${{ hashFiles('**/requirements-test.txt') }}-\
264 | ${{ hashFiles('**/requirements.txt') }}
265 | restore-keys: |
266 | ${{ env.BASE_CACHE_KEY }}
267 | - uses: hashicorp/setup-packer@v3
268 | with:
269 | version: ${{ steps.setup-env.outputs.packer-version }}
270 | - name: Install dependencies
271 | run: |
272 | python -m pip install --upgrade pip
273 | pip install --upgrade --requirement requirements-test.txt
274 | - name: Run tests
275 | env:
276 | GITHUB_RELEASE_TAG: ${{ github.event.release.tag_name }}
277 | run: pytest
278 | - name: Setup tmate debug session
279 | uses: mxschmitt/action-tmate@v3
280 | if: env.RUN_TMATE
281 | build:
282 | environment: dev-a
283 | # The AMI build process is an expensive test (in terms of time) so
284 | # let's not run it unless the other jobs succeed.
285 | needs:
286 | - lint
287 | - test
288 | permissions:
289 | # actions/checkout needs this to fetch code
290 | contents: read
291 | runs-on: ubuntu-latest
292 | strategy:
293 | fail-fast: false
294 | matrix:
295 | architecture:
296 | # cisagov/ansible-role-guacamole cannot currently support
297 | # ARM64 because the official Guacamole Docker images do not.
298 | # - arm64
299 | - x86_64
300 | steps:
301 | - name: Apply standard cisagov job preamble
302 | uses: cisagov/action-job-preamble@v1
303 | with:
304 | # This functionality is poorly implemented and has been
305 | # causing problems due to the MITM implementation hogging or
306 | # leaking memory. As a result we disable it by default. If
307 | # you want to temporarily enable it, simply set
308 | # monitor_permissions equal to "true".
309 | #
310 | # TODO: Re-enable this functionality when practical. See
311 | # cisagov/skeleton-packer#411 for more details.
312 | monitor_permissions: "false"
313 | # Use a variable to specify the permissions monitoring
314 | # configuration. By default this will yield the
315 | # configuration stored in the cisagov organization-level
316 | # variable, but if you want to use a different configuration
317 | # then simply:
318 | # 1. Create a repository-level variable with the name
319 | # ACTIONS_PERMISSIONS_CONFIG.
320 | # 2. Set this new variable's value to the configuration you
321 | # want to use for this repository.
322 | #
323 | # Note in particular that changing the permissions
324 | # monitoring configuration *does not* require you to modify
325 | # this workflow.
326 | permissions_monitoring_config: ${{ vars.ACTIONS_PERMISSIONS_CONFIG }}
327 | - id: setup-env
328 | uses: cisagov/setup-env-github-action@v1
329 | - uses: actions/checkout@v6
330 | - id: setup-python
331 | uses: actions/setup-python@v6
332 | with:
333 | python-version: ${{ steps.setup-env.outputs.python-version }}
334 | - uses: actions/cache@v4
335 | env:
336 | BASE_CACHE_KEY: ${{ github.job }}-${{ runner.os }}-\
337 | py${{ steps.setup-python.outputs.python-version }}-\
338 | packer${{ steps.setup-env.outputs.packer-version }}-\
339 | tf-${{ steps.setup-env.outputs.terraform-version }}-
340 | with:
341 | path: |
342 | ${{ env.PIP_CACHE_DIR }}
343 | key: ${{ env.BASE_CACHE_KEY }}\
344 | ${{ hashFiles('**/requirements.txt') }}
345 | restore-keys: |
346 | ${{ env.BASE_CACHE_KEY }}
347 | - uses: hashicorp/setup-packer@v3
348 | with:
349 | version: ${{ steps.setup-env.outputs.packer-version }}
350 | - uses: hashicorp/setup-terraform@v3
351 | with:
352 | terraform_version: ${{ steps.setup-env.outputs.terraform-version }}
353 | - name: Install dependencies
354 | run: |
355 | python -m pip install --upgrade pip
356 | pip install --upgrade \
357 | --requirement requirements.txt
358 | - name: Assume AWS build role
359 | uses: aws-actions/configure-aws-credentials@v5
360 | with:
361 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
362 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
363 | aws-region: ${{ env.AWS_DEFAULT_REGION }}
364 | role-to-assume: ${{ secrets.BUILD_ROLE_TO_ASSUME }}
365 | role-duration-seconds: 3600
366 | # When called by Packer, Ansible will find /usr/bin/python3 and
367 | # use it; therefore, we must ensure that /usr/bin/python3 points
368 | # to the version of Python that we installed in the
369 | # actions/setup-python step above. This can hose other tasks
370 | # that are expecting to find the system Python at that location,
371 | # though, so we undo this change after running Packer.
372 | - name: Create a /usr/bin/python3 symlink to the installed Python
373 | run: |
374 | sudo mv /usr/bin/python3 /usr/bin/python3-default
375 | sudo ln -s ${{ env.pythonLocation }}/bin/python3 \
376 | /usr/bin/python3
377 | - name: Install Packer plugins
378 | run: packer init .
379 | - name: Create machine image
380 | run: |
381 | packer build -only amazon-ebs.${{ matrix.architecture }} \
382 | -timestamp-ui \
383 | -var github_ref_name=${{ github.ref_name }} \
384 | -var github_sha=${{ github.sha }} \
385 | .
386 | - name: Remove /usr/bin/python3 symlink to the installed Python
387 | run: |
388 | sudo mv /usr/bin/python3-default /usr/bin/python3
389 | - name: Setup tmate debug session
390 | uses: mxschmitt/action-tmate@v3
391 | if: env.RUN_TMATE
392 |
--------------------------------------------------------------------------------