From 50a538c78ec3f71a37bfc9416565b11aeb105c54 Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Tue, 20 Sep 2022 04:01:45 +0000 Subject: [PATCH 1/4] start generator work --- flake.lock | 37 +++++++++++++++++++++++++++++++++++++ flake.nix | 20 +++++++++++++++++--- generators/aws.nix | 19 +++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 generators/aws.nix diff --git a/flake.lock b/flake.lock index a3a0bac..202ceab 100644 --- a/flake.lock +++ b/flake.lock @@ -74,6 +74,42 @@ "type": "github" } }, + "nixlib": { + "locked": { + "lastModified": 1636849918, + "narHash": "sha256-nzUK6dPcTmNVrgTAC1EOybSMsrcx+QrVPyqRdyKLkjA=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "28a5b0557f14124608db68d3ee1f77e9329e9dd5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixos-generators": { + "inputs": { + "nixlib": "nixlib", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660727616, + "narHash": "sha256-zYTIvdPMYMx/EYqXODAwIIU30RiEHqNHdgarIHuEYZc=", + "owner": "nix-community", + "repo": "nixos-generators", + "rev": "adccd191a0e83039d537e021f19495b7bad546a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixos-generators", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1663357389, @@ -124,6 +160,7 @@ "inputs": { "darwin": "darwin", "home-manager": "home-manager", + "nixos-generators": "nixos-generators", "nixpkgs": "nixpkgs", "nur": "nur", "wallpapers": "wallpapers", diff --git a/flake.nix b/flake.nix index 6ba3749..4b95a7c 100644 --- a/flake.nix +++ b/flake.nix @@ -32,9 +32,15 @@ flake = false; }; + # Used to generate NixOS images for other platforms + nixos-generators = { + url = "github:nix-community/nixos-generators"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; - outputs = { self, nixpkgs, darwin, wsl, home-manager, nur, wallpapers }: + outputs = { self, nixpkgs, ... }@inputs: let @@ -57,14 +63,14 @@ in { - nixosConfigurations = { + nixosConfigurations = with inputs; { desktop = import ./hosts/desktop { inherit nixpkgs home-manager nur globals wallpapers; }; wsl = import ./hosts/wsl { inherit nixpkgs wsl home-manager globals; }; }; - darwinConfigurations = { + darwinConfigurations = with inputs; { macbook = import ./hosts/macbook { inherit nixpkgs darwin home-manager nur globals; }; @@ -110,6 +116,14 @@ }); + # Package servers into images with a generator + packages.x86_64-linux = with inputs; { + aws = import ./generators/aws.nix { + inherit nixpkgs nixos-generators home-manager globals; + system = "x86_64-linux"; + }; + }; + # Templates for starting other projects quickly templates = rec { default = basic; diff --git a/generators/aws.nix b/generators/aws.nix new file mode 100644 index 0000000..f1f54cd --- /dev/null +++ b/generators/aws.nix @@ -0,0 +1,19 @@ +{ nixpkgs, system, nixos-generators, home-manager, globals, ... }: + +nixos-generators.nixoGenerate { + inherit system; + imports = [ + globals + home-manager.nixosModules.home-manager + { + networking.hostName = "sheep"; + gui.enable = false; + colorscheme = (import ../../modules/colorscheme/gruvbox); + passwordHash = + "$6$PZYiMGmJIIHAepTM$Wx5EqTQ5GApzXx58nvi8azh16pdxrN6Qrv1wunDlzveOgawitWzcIxuj76X9V868fsPi/NOIEO8yVXqwzS9UF."; + } + ../hosts/common.nix + ../modules/nixos + ]; + format = "aws"; +} From 9e3345ff9bfa7142eb11936fb794f3816a5ab8fa Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:50:45 +0000 Subject: [PATCH 2/4] add sshd for aws generators --- generators/aws.nix | 3 +++ modules/services/sshd.nix | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 modules/services/sshd.nix diff --git a/generators/aws.nix b/generators/aws.nix index f1f54cd..0c7997c 100644 --- a/generators/aws.nix +++ b/generators/aws.nix @@ -11,9 +11,12 @@ nixos-generators.nixoGenerate { colorscheme = (import ../../modules/colorscheme/gruvbox); passwordHash = "$6$PZYiMGmJIIHAepTM$Wx5EqTQ5GApzXx58nvi8azh16pdxrN6Qrv1wunDlzveOgawitWzcIxuj76X9V868fsPi/NOIEO8yVXqwzS9UF."; + publicKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s"; } ../hosts/common.nix ../modules/nixos + ../modules/services/sshd.nix ]; format = "aws"; } diff --git a/modules/services/sshd.nix b/modules/services/sshd.nix new file mode 100644 index 0000000..8be395b --- /dev/null +++ b/modules/services/sshd.nix @@ -0,0 +1,24 @@ +{ config, pkgs, lib, ... }: { + + options = { + publicKey = lib.mkOption { + type = lib.types.str; + description = "Public SSH key authorized for this system."; + }; + }; + + config = { + services.openssh = { + enable = true; + ports = [ 22 ]; + passwordAuthentication = false; + gatewayPorts = "no"; + forwardX11 = false; + allowSFTP = true; + permitRootLogin = "no"; + }; + + users.users.${config.user}.authorizedKeys.keys = [ config.publicKey ]; + }; + +} From 720a3cc409ee16185be14c69709ab5e10c803086 Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:50:04 +0000 Subject: [PATCH 3/4] additional options needed to build aws image --- generators/aws.nix | 19 ++++++++++++++----- modules/services/sshd.nix | 10 ++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/generators/aws.nix b/generators/aws.nix index 0c7997c..f648c18 100644 --- a/generators/aws.nix +++ b/generators/aws.nix @@ -1,22 +1,31 @@ { nixpkgs, system, nixos-generators, home-manager, globals, ... }: -nixos-generators.nixoGenerate { +nixos-generators.nixosGenerate { inherit system; - imports = [ - globals + format = "amazon"; + modules = [ home-manager.nixosModules.home-manager { + user = globals.user; + fullName = globals.fullName; + dotfilesRepo = globals.dotfilesRepo; + gitName = globals.gitName; + gitEmail = globals.gitEmail; networking.hostName = "sheep"; gui.enable = false; - colorscheme = (import ../../modules/colorscheme/gruvbox); + colorscheme = (import ../modules/colorscheme/gruvbox); passwordHash = "$6$PZYiMGmJIIHAepTM$Wx5EqTQ5GApzXx58nvi8azh16pdxrN6Qrv1wunDlzveOgawitWzcIxuj76X9V868fsPi/NOIEO8yVXqwzS9UF."; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s"; + # AWS settings require this + permitRootLogin = "prohibit-password"; } ../hosts/common.nix ../modules/nixos ../modules/services/sshd.nix + ] ++ [ + # Required to fix diskSize errors during build + ({ ... }: { amazonImage.sizeMB = 16 * 1024; }) ]; - format = "aws"; } diff --git a/modules/services/sshd.nix b/modules/services/sshd.nix index 8be395b..0161aba 100644 --- a/modules/services/sshd.nix +++ b/modules/services/sshd.nix @@ -5,6 +5,11 @@ type = lib.types.str; description = "Public SSH key authorized for this system."; }; + permitRootLogin = lib.mkOption { + type = lib.types.str; + description = "Root login settings."; + default = "no"; + }; }; config = { @@ -15,10 +20,11 @@ gatewayPorts = "no"; forwardX11 = false; allowSFTP = true; - permitRootLogin = "no"; + permitRootLogin = config.permitRootLogin; }; - users.users.${config.user}.authorizedKeys.keys = [ config.publicKey ]; + users.users.${config.user}.openssh.authorizedKeys.keys = + [ config.publicKey ]; }; } From f834cc20f4ae772d81ede8142d6f67603d6b0952 Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Wed, 21 Sep 2022 03:49:04 +0000 Subject: [PATCH 4/4] aws generator gh workflow and terraform upload --- flake.nix | 2 +- generators/{aws.nix => aws/default.nix} | 0 generators/aws/main.tf | 80 ++++++++ generators/aws/workflow.yml | 260 ++++++++++++++++++++++++ 4 files changed, 341 insertions(+), 1 deletion(-) rename generators/{aws.nix => aws/default.nix} (100%) create mode 100644 generators/aws/main.tf create mode 100644 generators/aws/workflow.yml diff --git a/flake.nix b/flake.nix index 4b95a7c..b995b50 100644 --- a/flake.nix +++ b/flake.nix @@ -118,7 +118,7 @@ # Package servers into images with a generator packages.x86_64-linux = with inputs; { - aws = import ./generators/aws.nix { + aws = import ./generators/aws { inherit nixpkgs nixos-generators home-manager globals; system = "x86_64-linux"; }; diff --git a/generators/aws.nix b/generators/aws/default.nix similarity index 100% rename from generators/aws.nix rename to generators/aws/default.nix diff --git a/generators/aws/main.tf b/generators/aws/main.tf new file mode 100644 index 0000000..4fbb2ca --- /dev/null +++ b/generators/aws/main.tf @@ -0,0 +1,80 @@ +locals { + image_file = one(fileset(path.root, "result/nixos-amazon-image-*.vhd")) +} + +# Upload to S3 +resource "aws_s3_object" "image" { + bucket = "your_bucket_name" + key = basename(local.image_file) + source = local.image_file + etag = filemd5(local.image_file) +} + +# Setup IAM access for the VM Importer +data "aws_iam_policy_document" "vmimport_trust_policy" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["vmie.amazonaws.com"] + } + } +} + +data "aws_iam_policy_document" "vmimport" { + statement { + actions = [ + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + ] + resources = [ + "arn:aws:s3:::${aws_s3_object.image.bucket}", + "arn:aws:s3:::${aws_s3_object.image.bucket}/*", + ] + } + statement { + actions = [ + "ec2:ModifySnapshotAttribute", + "ec2:CopySnapshot", + "ec2:RegisterImage", + "ec2:Describe*", + ] + resources = ["*"] + } +} + +resource "aws_iam_role" "vmimport" { + name = "vmimport" + assume_role_policy = data.aws_iam_policy_document.vmimport_trust_policy.json + inline_policy { + name = "vmimport" + policy = data.aws_iam_policy_document.vmimport.json + } +} + +# Import to EBS +resource "aws_ebs_snapshot_import" "image" { + disk_container { + format = "VHD" + user_bucket { + s3_bucket = aws_s3_object.image.bucket + s3_key = aws_s3_object.image.key + } + } + + role_name = aws_iam_role.vmimport.name +} + +# Convert to AMI +resource "aws_ami" "image" { + description = "Created with NixOS." + name = replace(basename(local.image_file), "/\\.vhd$/", "") + virtualization_type = "hvm" + + ebs_block_device { + device_name = "/dev/xvda" + snapshot_id = aws_ebs_snapshot_import.image.id + volume_size = 8 + } +} diff --git a/generators/aws/workflow.yml b/generators/aws/workflow.yml new file mode 100644 index 0000000..c0210e2 --- /dev/null +++ b/generators/aws/workflow.yml @@ -0,0 +1,260 @@ +name: 'Terraform' +env: + + + AWS_ACCOUNT_NUMBER: '' + AWS_PLAN_ROLE_NAME: github_actions_plan + AWS_APPLY_ROLE_NAME: github_actions_admin + + # Always required. Used for authenticating to AWS, but can also act as your + # default region if you don't want to specify in the provider configuration. + AWS_REGION: us-east-1 + + # You must change these to fit your project. + TF_VAR_project: change-me + TF_VAR_label: change-me + TF_VAR_owner: Your Name Here + + # If storing Terraform in a subdirectory, specify it here. + TERRAFORM_DIRECTORY: . + + # Pinned versions of tools to use. + # Check for new releases: + # - https://github.com/hashicorp/terraform/releases + # - https://github.com/fugue/regula/releases + # - https://github.com/terraform-linters/tflint/releases + TERRAFORM_VERSION: 1.2.6 + REGULA_VERSION: 2.9.0 + TFLINT_VERSION: 0.39.1 + + # Terraform configuration options + TERRAFORM_PARALLELISM: 10 + + # These variables are passed to Terraform based on GitHub information. + TF_VAR_repo: ${{ github.repository }} + +# This workflow is triggered in the following ways. +on: + + # Any push or merge to these branches. + push: + branches: + - dev + - prod + + # Any pull request targeting these branches (plan only). + pull_request: + branches: + - dev + - prod + + + # Any manual trigger on these branches. + workflow_dispatch: + branches: + - dev + - prod + +# ------------------------------------------------------------------- +# The rest of this workflow can operate without adjustments. Edit the +# below content at your own risk! +# ------------------------------------------------------------------- + +# Used to connect to AWS IAM +permissions: + id-token: write + contents: read + pull-requests: write + +# Only run one workflow at a time for each Terraform state. This prevents +# lockfile conflicts, especially during PR vs push. +concurrency: terraform-${{ github.base_ref || github.ref }} + +jobs: + terraform: + + name: 'Terraform' + + # Change this if you need to run your deployment on-prem. + runs-on: ubuntu-latest + + steps: + + # Downloads the current repo code to the runner. + - name: Checkout Repo Code + uses: actions/checkout@v2 + + # Install Nix + - name: Install Nix + uses: cachix/install-nix-action@v17 + + # Build the image + - name: Build Image + run: nix build .#aws + + # Login to AWS + - name: AWS Assume Role + uses: aws-actions/configure-aws-credentials@v1.6.1 + with: + role-to-assume: ${{ env.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + + # Exports all GitHub Secrets as environment variables prefixed by + # "TF_VAR_", which exposes them to Terraform. The name of each GitHub + # Secret must match its Terraform variable name exactly. + - name: Export Secrets to Terraform Variables + env: + ALL_SECRETS: ${{ toJson(secrets) }} + run: | + echo "$ALL_SECRETS" \ + | jq "to_entries | .[] | \"TF_VAR_\" + ( .key | ascii_downcase ) + \"=\" + .value" \ + | tr -d \" >> $GITHUB_ENV + + # Installs the Terraform binary and some other accessory functions. + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: ${{ env.TERRAFORM_VERSION }} + + # Checks whether Terraform is formatted properly. If this fails, you + # should install the pre-commit hook. + - name: Check Formatting + run: | + terraform fmt -no-color -check -diff -recursive + + # Downloads a Terraform code lint test. + - uses: terraform-linters/setup-tflint@v1 + name: Setup TFLint + with: + tflint_version: v${{ env.TFLINT_VERSION }} + + # Sets up linting with this codebase. + - name: Init TFLint + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: tflint --init + + # Lints the current code. + - name: Run TFLint + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: | + tflint -f compact + find ./modules/* -type d -maxdepth 0 | xargs -I __ tflint -f compact --disable-rule=terraform_required_providers --disable-rule=terraform_required_version __ + + # Connects to remote state backend and download providers. + - name: Terraform Init + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: | + terraform init \ + -backend-config="role_arn=${{ env.AWS_STATE_ROLE_ARN }}" \ + -backend-config="region=us-east-1" \ + -backend-config="workspace_key_prefix=accounts/${{ env.AWS_ACCOUNT_NUMBER }}/${{ github.repository }}" \ + -backend-config="key=state.tfstate" \ + -backend-config="dynamodb_table=global-tf-state-lock" + + # Set the Terraform Workspace to the current branch name. + - name: Set Terraform Workspace + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + shell: bash + run: | + export WORKSPACE=${{ github.base_ref || github.ref_name }} + terraform workspace select ${WORKSPACE} || terraform workspace new $WORKSPACE + echo "TF_WORKSPACE=$(echo ${WORKSPACE} | sed 's/\//_/g')" >> $GITHUB_ENV + + # Checks differences between current code and infrastructure state. + - name: Terraform Plan + id: plan + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: | + terraform plan \ + -input=false \ + -no-color \ + -out=tfplan \ + -parallelism=${TERRAFORM_PARALLELISM} \ + -var-file=variables-${TF_WORKSPACE}.tfvars + + # Gets the results of the plan for pull requests. + - name: Terraform Show Plan + id: show + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: terraform show -no-color tfplan + + # Adds the results of the plan to the pull request. + - name: Comment Plan + uses: actions/github-script@v6 + if: github.event_name == 'pull_request' + env: + STDOUT: "```terraform\n${{ steps.show.outputs.stdout }}```" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // 1. Retrieve existing bot comments for the PR + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }) + const botComment = comments.find(comment => { + return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style') + }) + + // 2. Prepare format of the comment + const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` +
Validation Output + + \`\`\`\n + ${{ steps.validate.outputs.stdout }} + \`\`\` + +
+ + #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` + +
Show Plan + + \`\`\`\n + ${process.env.PLAN} + \`\`\` + +
+ + *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`; + + // 3. If we have a comment, update it, otherwise create a new one + if (botComment) { + github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: output + }) + } else { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + } + + # Downloads Regula and checks whether the plan meets compliance requirements. + - name: Regula Compliance Check + shell: bash + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: | + REGULA_URL="https://github.com/fugue/regula/releases/download/v${REGULA_VERSION}/regula_${REGULA_VERSION}_Linux_x86_64.tar.gz" + curl -sL "$REGULA_URL" -o regula.tar.gz + tar xzf regula.tar.gz + terraform show -json tfplan | ./regula run + + # Deploys infrastructure or changes to infrastructure. + - name: Terraform Apply + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + working-directory: ${{ env.TERRAFORM_DIRECTORY }} + run: | + terraform apply \ + -auto-approve \ + -input=false \ + -parallelism=${TERRAFORM_PARALLELISM} \ + tfplan