Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infrasys: create TUF infra in AWS using cargo make create-infra #1723

Merged
merged 2 commits into from
Sep 10, 2021

Conversation

aashnasheth
Copy link
Contributor

@aashnasheth aashnasheth commented Aug 24, 2021

Issue number:
N/A

Description of changes:

  • Infrasys is currently comprised of 2 subcommands: create-infra and check-infra-lock. This PR is the implementation of create-infra.
  • The main logic for creating the TUF infrastructure is in main.rs, which uses helper methods from other modules (keys.rs [creating keys], s3.rs [creating s3 bucket, adding bucket policy, uploading objects], shared.rs [helper methods used in all files], errors.rs [a single error file shared by all files], and root.rs [creating root, adding keys to it, signing it]).
  • main.rs reads from Infra.toml using pubsys-config (thus edits to a few files under pubsys-config and pubsys were required) and outputs Infra.lock in yaml (we used yaml to serialize the config because we were unable to serialize enums with structs within them in toml).
  • NOTE: a warning is generated because create-infra-lock has some unused variables (because it's unimplemented)

Testing done:

  • Added a unit test for for struct -> toml -> struct -> yaml -> struct conversion (main.rs), prefix format checking (s3.rs), and bucket policy insertion (s3.rs)

  • Created several test Infra.toml files with various combinations of configurations and ensured that errors were thrown / the resources showed up in the correct region, with the correct settings, in my account

    • Some cases are: available keys only, available keys + keys to be created, create keys only, s3 configs missing, alias missing, signing keys/root keys missing, empty prefix, etc. (and more in-progress!)
  • Example of a test where we're creating a root + signing keys

    Infra.toml

    # Check for:
    # key_id is new key in signing keys
    # available_keys is updated
    # s3 info set
    
    [repo.default] 
       file_hosting_config_name = "TUF-Repo-S3-Buck"
       signing_keys = { kms = { key_alias="signing-key-test", regions=["us-east-2"] } }
       root_keys = { kms = { key_alias="root-key-test", regions=["us-east-1"] } }
       root_key_threshold = 1
       pub_key_threshold = 1
       
    [aws] 
        [aws.s3.TUF-Repo-S3-Buck]
        region = "us-west-2"
        vpc_endpoint_id = "vpc-12345" 
        s3_prefix = "/my-bottlerocket-remix" 

    cargo make create-infra output

    [cargo-make] INFO - Running Task: check-infra-lock
    Successfully in check_infra_method!
    [cargo-make] INFO - Running Task: create-infra
    20:06:00 [INFO] Parsing Infra.toml...
    20:06:00 [INFO] Creating S3 bucket...
    20:06:01 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:06:21 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:06:42 [INFO] Creating KMS Keys...
    20:06:43 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:07:04 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:07:24 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:07:45 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:08:05 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:08:25 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:08:46 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:09:06 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:09:26 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:09:47 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:10:08 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:10:29 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:10:49 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:11:10 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:11:30 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:11:50 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:12:11 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:12:31 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:12:52 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:13:12 [INFO] Waiting for stack resources to be ready, current status is 'CREATE_IN_PROGRESS'...
    20:13:33 [INFO] Creating and signing root.json...
    f5b8391e577e7abd0354963177ecfc2b2aff6c6aa62b59df72c37a57020acb83
    fe6321b3c61d4f0cc7864e283e4dd007056d8e9ae1dd367166720225efa16681
    20:13:36 [INFO] Uploading root.json to S3 bucket...
    20:13:37 [INFO] Writing Infra.lock...
    20:13:37 [INFO] Complete!
    [cargo-make] INFO - Build Done in 460.56 seconds.

    Infra.lock

    ---
    repo:
      default:
        root_role_url: "http://tuf-repo-s3-buck-tufrepobucket-1xrapc0rkw7yx.s3-website-us-west-2.amazonaws.com/my-bottlerocket-remix/root.json"
        root_role_sha512: 2b2e33ff34c91c81ee06c0ad42a75d95dd5cb38e0b8eb04015147e6ce709eb086b5e3e03bf77249dab589e4e911cc89270a702c6e4a57f8b864717242ed41de8
        signing_keys:
          kms:
            key_id: "arn:aws:kms:us-east-2:965384286431:key/6928e220-d424-4818-aa1f-f4175e078269"
            available_keys:
              "arn:aws:kms:us-east-2:965384286431:key/6928e220-d424-4818-aa1f-f4175e078269": us-east-2
            key_alias: signing-key-test
            regions:
              - us-east-2
            key_stack_arns:
              "arn:aws:kms:us-east-2:965384286431:key/6928e220-d424-4818-aa1f-f4175e078269": "arn:aws:cloudformation:us-east-2:965384286431:stack/TUF-KMS-signing-key-test/cd84bad0-0516-11ec-9916-0a27ba133414"
        root_keys:
          kms:
            key_id: ~
            available_keys:
              "arn:aws:kms:us-east-1:965384286431:key/184567d9-cffd-4885-acff-0fd2754c504a": us-east-1
            key_alias: root-key-test
            regions:
              - us-east-1
            key_stack_arns:
              "arn:aws:kms:us-east-1:965384286431:key/184567d9-cffd-4885-acff-0fd2754c504a": "arn:aws:cloudformation:us-east-1:965384286431:stack/TUF-KMS-root-key-test/47a07ed0-0517-11ec-bc80-0af9e889672b"
        metadata_base_url: "http://tuf-repo-s3-buck-tufrepobucket-1xrapc0rkw7yx.s3-website-us-west-2.amazonaws.com/my-bottlerocket-remix/metadata/"
        targets_url: "http://tuf-repo-s3-buck-tufrepobucket-1xrapc0rkw7yx.s3-website-us-west-2.amazonaws.com/my-bottlerocket-remix/targets/"
        file_hosting_config_name: TUF-Repo-S3-Buck
        root_key_threshold: 1
        pub_key_threshold: 1
    aws:
      regions: []
      role: ~
      profile: ~
      region: {}
      ssm_prefix: ~
      s3:
        TUF-Repo-S3-Buck:
          region: us-west-2
          s3_prefix: /my-bottlerocket-remix
          vpc_endpoint_id: vpc-12345
          stack_arn: "arn:aws:cloudformation:us-west-2:965384286431:stack/TUF-Repo-S3-Buck/b450b410-0516-11ec-a7c0-061175597a0d"
          bucket_name: tuf-repo-s3-buck-tufrepobucket-1xrapc0rkw7yx
    vmware: ~ 

    root.json

    {
    "signed": {
      "_type": "root",
      "spec_version": "1.0.0",
      "consistent_snapshot": true,
      "version": 1,
      "expires": "2022-08-23T20:13:33Z",
      "keys": {
        "fe6321b3c61d4f0cc7864e283e4dd007056d8e9ae1dd367166720225efa16681": {
          "keytype": "rsa",
          "keyval": {
            "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAw+Kz32mPNDLtM4Qn+G3X\n+hf/l9h3KCFOGtBpeNxQN9NLtExA3e1QLGS6yQQNVa7ELPIvWep9hcoraxP2kv5X\nLhMdzy0cmFVFWwtlt3/Srd9paihW+cP53ODnr2/KeXUlLAco13s26dMzvxOZmwAg\nxhDzj+846n592WRWGcuGO80kWSFgfipufwld32jbUZoC9OqwvIBVn1Qk3BHm4GHI\ntG/VG65CRdHXtZ2FSC4KgeVjJons3mqGjcgXwN2dVuWmmCJgrTKvUru706KctxF3\nnvFXTMmg0MosM4woArXCHUb82lugFZeLic+egSLupivNceblubVMU2xxF3SE98hv\nQxmaWBI2sQ+4uBFvh1KkKVwAHeSfbrrU+oNYsilVXcHc+hXiyhneMxkOkt21l0iP\nP8XnOyzGLz6dEtc7aMqGlIDGPdVkQgLl/MSnRPpb641uFGENKUo8MWGCXz33G7wB\n1wtwZXJFVMdTv2juhG0Umft8lp/USuxfO7eQOe+cCmpXAgMBAAE=\n-----END PUBLIC KEY-----\n"
          },
          "scheme": "rsassa-pss-sha256"
        },
        "f5b8391e577e7abd0354963177ecfc2b2aff6c6aa62b59df72c37a57020acb83": {
          "keytype": "rsa",
          "keyval": {
            "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnkREp096HUOSmwgeM/K4\n4Zzky0h3rmSBXdJ+wn4q/fFjKYKkVKwxu3H2aa/GueJAb1q/SqVBHism5JNZl46E\ngTczchClMvPDQsshKH9IQ0t4bl8quaH04+zpnzWoYMQHptIv56yTTirVsgg7NBkx\noi8pXXC+fZdH1ed+qKUmr5hwz1wlZrWVwY3SCYLv7QYi/4sL8mRw8tzx7IDd4+5d\nZBXC2Qb9loQ6zjFIoxSubz5Fl1zuGk+cAG/TdOzangr0poGFwu9Xm0mh2RByuAbX\nJ61SZWRaAb9AwkTy7C2Du6hZ0ZwSfYoKcGWfnylVYLrreAxOPi3X6odNKgWt12Yq\na95+lv0n/830dg36zZ9I0eVMQZgIupM4zAUlv3QvBA83QaQnrQV5JSCYawcftah6\nbQ1BaZlAnJi9rGwZ7sGj7deJtz+BqXwSOPA3cdm9q2rinxXlLyllLlQsWcvbvVlx\nBKgi2K5gI9ZxM0T9qBvo7jy0m1GDhXve9+BrdbXmJ351AgMBAAE=\n-----END PUBLIC KEY-----\n"
          },
          "scheme": "rsassa-pss-sha256"
        }
      },
      "roles": {
        "root": {
          "keyids": [
            "fe6321b3c61d4f0cc7864e283e4dd007056d8e9ae1dd367166720225efa16681"
          ],
          "threshold": 1
        },
        "snapshot": {
          "keyids": [
            "f5b8391e577e7abd0354963177ecfc2b2aff6c6aa62b59df72c37a57020acb83"
          ],
          "threshold": 1
        },
        "targets": {
          "keyids": [
            "f5b8391e577e7abd0354963177ecfc2b2aff6c6aa62b59df72c37a57020acb83"
          ],
          "threshold": 1
        },
        "timestamp": {
          "keyids": [
            "f5b8391e577e7abd0354963177ecfc2b2aff6c6aa62b59df72c37a57020acb83"
          ],
          "threshold": 1
        }
      }
    },
    "signatures": [
      {
        "keyid": "fe6321b3c61d4f0cc7864e283e4dd007056d8e9ae1dd367166720225efa16681",
        "sig": "813107cdf34a3181bcd6eb0f83cdce350670e26bf381f535a3e09e33d638d50c18a203f91f1c1ad2966efd689579d5c433db6093811f13d6a3a1d964ebd6839e8edf67c9b968591cd2b5a845cdb360e634cfb72328773010e0646fc1a75738cbddf008f5981e713f7364aec2b987f3125b5764317e4786843307973d6635daa4c7ea2b2a83db77663ccf6288d82f8695bd648376b2105a891d64da91af5a3f63544d4369c1a8da23f36d1039eabb2ea13abc7f6549d281c00a96db6daf32eed51180c966b0ab4251d47a1dffd57cc504b69de0c482d92d909040078e20d7ad59a27f9627aab780e3bcf3e2f5e150d5d72107d8b1fffbc7081b602588dc2608313af97d4b8d9207222c86fe9b626dd76d65ebd2c4278afe0272bb5fd84d9047356463f1463c62a813fe72641f857b970c131db9ddddc14c6d0b67aac4735a5da763ccf5450b6455f55fd4c2b6823ec19c223987a37feeac9047b2b5cd8f38c467fb6c69ed10ad78b7a02d5cca4ab4774ac58ca61bf8c2b4c5706858ef8d45f0e3"
      }
    ]
    }
    

    Resources show up correctly in my account (root.json populated in s3 bucket, all public access off for s3 bucket, correct bucket policy, kms keys present)

  • Completed E2E testing (cargo make, cargo make repo, cargo make ami, pulling updates)

Draft Review:
Here are some initial reviews that etungsten left and were addressed: aashnasheth#9

Next PR:

  • Implementation of check-infra-lock (which will check the existence of the lock file and advise users to clean up resources if needed)
  • S3 sync in MakeFile
  • A way to clean up any created stacks if infrasys fails unexpectedly in the middle of execution

Terms of contribution:
By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.

@aashnasheth aashnasheth force-pushed the infrasys branch 3 times, most recently from 6ea4f47 to 788eebc Compare August 24, 2021 19:10
@aashnasheth aashnasheth changed the title infrasys: add a new binary that will automatically spin up TUF infrastructure in AWS infrasys: create TUF infra in AWS using cargo make create-infra Aug 24, 2021
@aashnasheth aashnasheth changed the title infrasys: create TUF infra in AWS using cargo make create-infra infrasys: create TUF infra in AWS using cargo make create-infra Aug 24, 2021
@etungsten etungsten requested review from tjkirch, zmrow and etungsten and removed request for tjkirch August 24, 2021 19:55
@aashnasheth aashnasheth marked this pull request as ready for review August 24, 2021 20:36
.context(error::MissingConfig {
missing: "config field for a kms key",
})?
.create_kms_keys()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to make create_kms_keys a trait of the config so users aren't surprised when config fields are changed. We also considered passing a mutable reference to config to create_kms_keys(), but thought this way was more clear.

);
}

// Set key_id using a publication key (if one is not already provided)
Copy link
Contributor Author

@aashnasheth aashnasheth Aug 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to set key_id in this method as it's the only one that differentiates roles (we only want key_id to be set for publication keys, not root keys)

@aashnasheth aashnasheth force-pushed the infrasys branch 2 times, most recently from 79b3377 to 1504ebd Compare August 25, 2021 15:57
@bottlerocket-os bottlerocket-os deleted a comment from aashnasheth Aug 25, 2021
@aashnasheth
Copy link
Contributor Author

aashnasheth commented Aug 25, 2021

Pushes above add some extra tests and make minor changes to existing ones:

  • Adds bucket policy insertion test and prefix test
  • Adds support for empty prefixes

@aashnasheth
Copy link
Contributor Author

Push above addresses warnings raised by cargo clippy

@etungsten etungsten requested a review from webern August 25, 2021 22:43
Copy link
Contributor

@etungsten etungsten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we talked about this at some point and now is a good time to do it:
In addition to just setting up the Infrastructure, we should also test out whether we're able to use said infrastructure to actually update a live Bottlerocket host with a custom TUF repository. We should include that in the testing description since that's the ultimate goal.

@aashnasheth
Copy link
Contributor Author

Push above addresses etungsten's comments (and includes a small fix for default prefixes)

@aashnasheth
Copy link
Contributor Author

Push above makes a small change to bucket policy tests

@aashnasheth
Copy link
Contributor Author

Push above adds a new commit that checks if a lock file exists or not (signaling users to clean up resources and re-run infrasys accordingly)

Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only made it through main.rs today. It looks good. Most of what I have are little things not impacting correctness/design.

@aashnasheth
Copy link
Contributor Author

Push above adds further pubsys integration for InfraConfig::from_path_or_default usage (not just InfraConfig::from_path)

Copy link
Contributor

@zmrow zmrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publishing what I have for now.

},

#[snafu(display("Failed to get parent of path: {}", path.display()))]
Parent { path: PathBuf },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least in practice - this seems to crossover with the Path error variant above.

Copy link
Contributor Author

@aashnasheth aashnasheth Sep 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that if there's an issue with the parent() method in specific, might be nice to know where the error is coming from instead of a general "invalid path"?
For some reason if rust's parent() method breaks, we would be able to catch it here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having a separate error variant for this makes sense. The path itself isn't necessarily invalid, it's just that it doesn't have a parent (e.g. it might be /).

}

async fn check_infra_lock(toml_path: &Path) -> Result<()> {
let lock_path = toml_path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels odd to me to have a function that's checking for the existence of a file, but the argument is a different file. Perhaps we can determine lock_path beforehand, and pass it to this function.

Copy link
Contributor Author

@aashnasheth aashnasheth Sep 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wanted to make sure that users weren't able to change where the lock file lived (should always live next to Infra.toml). If we pass it in via commandline, we would do this path splicing in the Makefile. We were thinking it would be easier to do via code inside main.rs?

Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have a lot left to get through!
But the main theme of this pass is that there is some duplicate code involving the loading of either Infra.toml or Infra.lock.

It's great when files in a PR have more red than green and there is an opportunity to make that happen here in all the files that need to load either toml or lock.

@aashnasheth
Copy link
Contributor Author

Push above addresses initial webern and zmrow comments

@aashnasheth
Copy link
Contributor Author

Push above addresses webern code duplication concerns with pubsys-integration code

@aashnasheth
Copy link
Contributor Author

Push above removes check-infra-lock subcommand and calls it as a method at the beginning of the run() method instead

@aashnasheth
Copy link
Contributor Author

Push above adds KMSClient code to pubsys-integration commit

@aashnasheth
Copy link
Contributor Author

Push above adds webern's code to clean up tools/pubsys-setup/src/main.rs diff

Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's getting close, Nice work!

Adds binary (and supporting files) to automatically spin up TUF infra (S3 Bucket+Policy, KMS Keys, and populated root.json) across multiple regions in a single account.
Edits pubsys-config (and supporting files) to accomodate new infrasys fields in Infra.toml.
@aashnasheth
Copy link
Contributor Author

Push addresses webern comments

Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@etungsten etungsten merged commit eda7728 into bottlerocket-os:develop Sep 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants