diff --git a/.github/workflows/run-demo.yml b/.github/workflows/run-demo.yml index bddcaae..76bde1f 100644 --- a/.github/workflows/run-demo.yml +++ b/.github/workflows/run-demo.yml @@ -19,6 +19,13 @@ jobs: uses: gittuf/gittuf-installer@f31e69c7c18c7473cbce18ed69a559b945d3a738 with: gittuf-version: 0.8.0 - - name: Run demo with --no-prompt + - name: Install click with pip + run: | + pip install click + - name: Run demo with --automatic run: | - python3 run-demo.py --no-prompt + python3 run-demo.py --automatic + - name: Run run deployments example with --automatic + run: | + python3 example_deployments.py --automatic + diff --git a/README.md b/README.md index 47e97d3..3d19a45 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # gittuf/demo -A demo of gittuf. +A set of demos for gittuf. This repository contains two distinct demos: + +`run-demo.py` +This demo showcases the basic features of gittuf. This includes creating +policies, recording changes in the rsl, and running gittuf verification. +You will see both the happy path, where verification is successful, and +the failing path, where verification is not successful. + +`example_deployments.py` +This demo showcases some of the more involved features of gittuf. +This includes creating delagated policies as well as using multiple +keys with a set threshold for number of required signatures. From +the result of this script, you will see an example state of the +policy file in a deployment. ## Install gittuf @@ -43,8 +56,7 @@ You will set up a directory structure as follows: Where `keys` will be copied from this repository. You will create two Git-compatible signing keys, one that is authorized for the demo policy and one -that is not. You will see how policies are created, commit changes and run -`gittuf` to verify, both on the happy and failing path: +that is not. ```bash # Temporary playground diff --git a/example_deployments.py b/example_deployments.py new file mode 100644 index 0000000..0cb6fcf --- /dev/null +++ b/example_deployments.py @@ -0,0 +1,428 @@ +import os +import tempfile +import click +import subprocess + +from utils import prompt_key, display_command, run_command, check_binaries + +REQUIRED_BINARIES = ["git", "gittuf", "ssh-keygen"] + +def generate_key_hash_store(directory): + """ Generate a hash which stores the relative path to all keys within a directory """ + return { + key.split('.')[0]: { + "private": os.path.join(directory, key), + "public": os.path.join(directory, f"{key}.pub") + } + for key in os.listdir(directory) if not key.endswith('.pub') + } + +@click.command() +@click.option( + "--automatic", default=False, type=bool, is_flag=True, + help="Whether to wait for input before each command is run." +) +@click.option( + "--repository-directory", default="", + help="The path where the script should store the working copy of the repository." +) + +def deployment(automatic, repository_directory): + """ Runs a series of commands which emulates a possible gittuf deployment """ + # OS SETUP + # Set up directory structure and keys + current_dir = os.getcwd() + root_keys_dir = "keys/roots" + policy_keys_dir = "keys/policy" + people_keys_dir = "keys/people" + + # Ensure the keys have the correct access permissions + key_dir_list = [root_keys_dir, policy_keys_dir, people_keys_dir] + for dir in key_dir_list: + for key in os.listdir(dir): + os.chmod(os.path.join(dir, key), 0o600) + + # Select folder for the working repository copy + working_dir = repository_directory + if working_dir == "": + tmp_dir = tempfile.TemporaryDirectory() + working_dir = tmp_dir.name + else: + working_dir = os.path.abspath(repository_directory) + + # Sort key paths into hash for convenient access + root_keys_store = generate_key_hash_store(root_keys_dir) # For R1, R2, R3 + policy_keys_store = generate_key_hash_store(policy_keys_dir) # For P1, P2, P3 + people_keys_store = generate_key_hash_store(people_keys_dir) # For Alice, Bob, Carol, etc. + + # REPOSITORY SETUP + # Initialize the Git repository in the chosen directory + prompt_key(automatic, "Initialize Git repository") + cmd = "git init -b main" + display_command(cmd) + run_command(cmd, 0) + + # Set repo config + prompt_key(automatic, "Set repo config to use demo identity and root key") + cmd = f"git config --local user.signingkey {root_keys_store['R1']['private']}" + display_command(cmd) + run_command(cmd, 0) + cmd = "git config --local gpg.format ssh" + display_command(cmd) + run_command(cmd, 0) + cmd = "git config --local commit.gpgsign true" + display_command(cmd) + run_command(cmd, 0) + + cmd = "git config --local user.name gittuf-demo" + display_command(cmd) + run_command(cmd, 0) + cmd = "git config --local user.email gittuf.demo@example.com" + display_command(cmd) + run_command(cmd, 0) + + prompt_key(automatic, "Set PAGER") + os.environ["PAGER"] = "cat" + display_command("export PAGER=cat") + + # GITTUF COMMANDS + # Initialize roots of trust + prompt_key(automatic, "Initialize gittuf root of trust") + cmd = f"gittuf trust init -k {root_keys_store['R1']['private']}" + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust add-root-key" + f" -k {root_keys_store['R1']['private']}" + f" --root-key {root_keys_store['R2']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust add-root-key" + f" -k {root_keys_store['R1']['private']}" + f" --root-key {root_keys_store['R3']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust update-root-threshold" + f" -k {root_keys_store['R1']['private']}" + " --threshold 2" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = f"gittuf trust sign -k {root_keys_store['R2']['private']}" + display_command(cmd) + run_command(cmd, 0) + + cmd = f"gittuf trust sign -k {root_keys_store['R3']['private']}" + display_command(cmd) + run_command(cmd, 0) + + cmd = "gittuf trust apply" + display_command(cmd) + run_command(cmd, 0) + + # Initialize top-level policy keys + prompt_key(automatic, "Initialize top-level policy keys") + cmd = ( + "gittuf trust add-policy-key" + f" -k {root_keys_store['R1']['private']}" + f" --policy-key {policy_keys_store['P1']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust add-policy-key" + f" -k {root_keys_store['R1']['private']}" + f" --policy-key {policy_keys_store['P2']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust add-policy-key" + f" -k {root_keys_store['R1']['private']}" + f" --policy-key {policy_keys_store['P3']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf trust update-policy-threshold" + f" -k {root_keys_store['R1']['private']}" + " --threshold 2" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = f"gittuf trust sign -k {root_keys_store['R2']['private']}" + display_command(cmd) + run_command(cmd, 0) + + cmd = f"gittuf trust sign -k {root_keys_store['R3']['private']}" + display_command(cmd) + run_command(cmd, 0) + + cmd = "gittuf trust apply" + display_command(cmd) + run_command(cmd, 0) + + # Initialize policy file and add policy keys and sign-off + prompt_key(automatic, "Add policy keys and sign-off") + cmd = ( + "gittuf policy init" + f" -k {policy_keys_store['P1']['private']}" + " --policy-name targets" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {policy_keys_store['P1']['private']}" + f" --public-key {people_keys_store['Alice']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {policy_keys_store['P1']['private']}" + f" --public-key {people_keys_store['Bob']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {policy_keys_store['P1']['private']}" + f" --public-key {people_keys_store['Carol']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {policy_keys_store['P1']['private']}" + f" --public-key {people_keys_store['Helen']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {policy_keys_store['P1']['private']}" + f" --public-key {people_keys_store['Ilda']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + # Add rules and sign-off + prompt_key(automatic, "Add rules and sign-off") + cmd = ( + "gittuf policy add-rule" + f" -k {policy_keys_store['P1']['private']}" + " --rule-name protect-main-prod" + " --rule-pattern git:refs/heads/main" + " --rule-pattern git:refs/heads/prod" + f" --authorize-key {people_keys_store['Alice']['public']}" + f" --authorize-key {people_keys_store['Bob']['public']}" + f" --authorize-key {people_keys_store['Carol']['public']}" + " --threshold 2" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-rule" + f" -k {policy_keys_store['P1']['private']}" + " --rule-name protect-ios-app" + " --rule-pattern file:ios/*" + f" --authorize-key {people_keys_store['Alice']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-rule" + f" -k {policy_keys_store['P1']['private']}" + " --rule-name protect-android-app" + " --rule-pattern file:android/*" + f" --authorize-key {people_keys_store['Bob']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-rule" + f" -k {policy_keys_store['P1']['private']}" + " --rule-name protect-core-libraries" + " --rule-pattern file:src/*" + f" --authorize-key {people_keys_store['Carol']['public']}" + f" --authorize-key {people_keys_store['Helen']['public']}" + f" --authorize-key {people_keys_store['Ilda']['public']}" + " --threshold 2" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy sign" + f" -k {policy_keys_store['P1']['private']}" + " --policy-name targets" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy sign" + f" -k {policy_keys_store['P2']['private']}" + " --policy-name targets" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = "gittuf policy apply" + display_command(cmd) + run_command(cmd, 0) + + # Create folders and add initial git commit + prompt_key(automatic, "Create folders and add initial git commit") + cmd = "echo \"Hello, world!\" > README.md" + display_command(cmd) + + subprocess.call(cmd, shell=True) + + cmd = "mkdir src ios android" + display_command(cmd) + run_command(cmd, 0) + + cmd = "git add README.md" + display_command(cmd) + run_command(cmd, 0) + + cmd = "git commit -q -S -m 'Initial commit'" + display_command(cmd) + run_command(cmd, 0) + + cmd = "git branch -q prod" + display_command(cmd) + run_command(cmd, 0) + + # Add delegated policy protect-ios-app + prompt_key(automatic, "Add delegated policy protect-ios-app") + cmd = ( + "gittuf policy init" + f" -k {policy_keys_store['P3']['private']}" + " --policy-name protect-ios-app" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {people_keys_store['Alice']['private']}" + " --policy-name protect-ios-app" + f" --public-key {people_keys_store['Dana']['public']}" + f" --public-key {people_keys_store['George']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + # Add rule for protect-ios-app + prompt_key(automatic, "Add rule for protect-ios-app") + cmd = ( + "gittuf policy add-rule" + f" -k {people_keys_store['Alice']['private']}" + " --policy-name protect-ios-app" + " --rule-name authorize-ios-team" + " --rule-pattern file:ios/*" + f" --authorize-key {people_keys_store['Dana']['public']}" + f" --authorize-key {people_keys_store['George']['public']}" + " --threshold 1" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy sign" + f" -k {people_keys_store['Alice']['private']}" + " --policy-name protect-ios-app" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = "gittuf policy apply" + display_command(cmd) + run_command(cmd, 0) + + # Add delegated policy protect-android-app + prompt_key(automatic, "Add delegated policy protect-android-app") + cmd = ( + "gittuf policy init" + f" -k {policy_keys_store['P3']['private']}" + " --policy-name protect-android-app" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy add-key" + f" -k {people_keys_store['Bob']['private']}" + " --policy-name protect-android-app" + f" --public-key {people_keys_store['Eric']['public']}" + f" --public-key {people_keys_store['Frank']['public']}" + ) + display_command(cmd) + run_command(cmd, 0) + + # Add rule for protect-android-app + prompt_key(automatic, "Add rule for protect-android-app") + cmd = ( + "gittuf policy add-rule" + f" -k {people_keys_store['Bob']['private']}" + " --policy-name protect-android-app" + " --rule-name authorize-android-team" + " --rule-pattern file:android/*" + f" --authorize-key {people_keys_store['Eric']['public']}" + f" --authorize-key {people_keys_store['Frank']['public']}" + " --threshold 1" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = ( + "gittuf policy sign" + f" -k {people_keys_store['Bob']['private']}" + " --policy-name protect-android-app" + ) + display_command(cmd) + run_command(cmd, 0) + + cmd = "gittuf policy apply" + display_command(cmd) + run_command(cmd, 0) + + # Verify policy + prompt_key(automatic, "Verify policy") + cmd = "gittuf --verbose verify-ref refs/gittuf/policy" + display_command(cmd) + run_command(cmd, 0) + + # Show final policy state + prompt_key(automatic, "Final policy state") + cmd = "gittuf policy list-rules" + display_command(cmd) + run_command(cmd, 0) + +if __name__ == "__main__": + check_binaries(REQUIRED_BINARIES) + deployment() # pylint: disable=no-value-for-parameter + diff --git a/keys/people/Alice b/keys/people/Alice new file mode 100644 index 0000000..8136b17 --- /dev/null +++ b/keys/people/Alice @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQS3/X4lDoOxtnykhP19wNp7yWzSskEP +4XIWzrt5PePP4k88vOe3fHOE3zQMXaDqJyPthbaPts50mpTfNSQTQzDDAAAAsCEhxR0hIc +UdAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLf9fiUOg7G2fKSE +/X3A2nvJbNKyQQ/hchbOu3k948/iTzy857d8c4TfNAxdoOonI+2Fto+2znSalN81JBNDMM +MAAAAhAI95tABAM6Odk4xwpmaOV4YoNHpHISbBkfmyw8YCjooTAAAAF2hhb0BoYW8tMTNa +OTgwLUEtQUFTNlUx +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Alice.pub b/keys/people/Alice.pub new file mode 100644 index 0000000..08f94a5 --- /dev/null +++ b/keys/people/Alice.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLf9fiUOg7G2fKSE/X3A2nvJbNKyQQ/hchbOu3k948/iTzy857d8c4TfNAxdoOonI+2Fto+2znSalN81JBNDMMM= diff --git a/keys/people/Bob b/keys/people/Bob new file mode 100644 index 0000000..0a99561 --- /dev/null +++ b/keys/people/Bob @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSFr2ZxtNA4QgDkE9A2IUHG2Sg7SdAi +KT2NsXv45MjJ82nM7H+GtHkGhHNr5+Z3MnfS7NTss1m51Am9kYghP3mSAAAAsNQ66XTUOu +l0AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIWvZnG00DhCAOQT +0DYhQcbZKDtJ0CIpPY2xe/jkyMnzaczsf4a0eQaEc2vn5ncyd9Ls1OyzWbnUCb2RiCE/eZ +IAAAAgcZxdeu+dCeGVkDOh9koeARkLW7QiO0X41jcq6yesPaIAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Bob.pub b/keys/people/Bob.pub new file mode 100644 index 0000000..1438afe --- /dev/null +++ b/keys/people/Bob.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIWvZnG00DhCAOQT0DYhQcbZKDtJ0CIpPY2xe/jkyMnzaczsf4a0eQaEc2vn5ncyd9Ls1OyzWbnUCb2RiCE/eZI= diff --git a/keys/people/Carol b/keys/people/Carol new file mode 100644 index 0000000..56228ab --- /dev/null +++ b/keys/people/Carol @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQw8kuJufUzj5LVaZ6aA6ZK4o3BI8Oq +e54hnZjJu4FjCnurabUsvK6ruvBflxfz8BFzThYulzBY8WLtovqLlxafAAAAsIzoof6M6K +H+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDDyS4m59TOPktVp +npoDpkrijcEjw6p7niGdmMm7gWMKe6tptSy8rqu68F+XF/PwEXNOFi6XMFjxYu2i+ouXFp +8AAAAgR+z3+UyHRek3ITPEhiQcdLuzrwyZ5dKjdeQ3AnEIdNQAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Carol.pub b/keys/people/Carol.pub new file mode 100644 index 0000000..bcac362 --- /dev/null +++ b/keys/people/Carol.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDDyS4m59TOPktVpnpoDpkrijcEjw6p7niGdmMm7gWMKe6tptSy8rqu68F+XF/PwEXNOFi6XMFjxYu2i+ouXFp8= diff --git a/keys/people/Dana b/keys/people/Dana new file mode 100644 index 0000000..8faed4f --- /dev/null +++ b/keys/people/Dana @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTBMUrRsuV0zy0cVFdZoCbQqbKn4jAM +wSk/L1fa6HbUMKgj9MxQ2ys152jQXxeSC/5cYZtRm7eQZIv67Ac8H1biAAAAsJA7UICQO1 +CAAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMExStGy5XTPLRxU +V1mgJtCpsqfiMAzBKT8vV9rodtQwqCP0zFDbKzXnaNBfF5IL/lxhm1Gbt5Bki/rsBzwfVu +IAAAAhAPLYetNZywOdppWljMAiiOznXNGoGRTd/pfaNp0x6s+6AAAAF2hhb0BoYW8tMTNa +OTgwLUEtQUFTNlUx +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Dana.pub b/keys/people/Dana.pub new file mode 100644 index 0000000..25287e0 --- /dev/null +++ b/keys/people/Dana.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMExStGy5XTPLRxUV1mgJtCpsqfiMAzBKT8vV9rodtQwqCP0zFDbKzXnaNBfF5IL/lxhm1Gbt5Bki/rsBzwfVuI= diff --git a/keys/people/Eric b/keys/people/Eric new file mode 100644 index 0000000..21c1b5b --- /dev/null +++ b/keys/people/Eric @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQT/CwUgDYONaXWR3pDzEufwy9MCUNFx +gUcrXbjnmMCASigcZQPiMsk0vq1l6nFp9fn26qFBGws1TwPg4oW7k+4GAAAAsFQ2V7pUNl +e6AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP8LBSANg41pdZHe +kPMS5/DL0wJQ0XGBRytduOeYwIBKKBxlA+IyyTS+rWXqcWn1+fbqoUEbCzVPA+DihbuT7g +YAAAAgG6xMEu2LIhhZjHhUd0dy5bXzIu4beJKhdpJVMvROc0MAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Eric.pub b/keys/people/Eric.pub new file mode 100644 index 0000000..982094e --- /dev/null +++ b/keys/people/Eric.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP8LBSANg41pdZHekPMS5/DL0wJQ0XGBRytduOeYwIBKKBxlA+IyyTS+rWXqcWn1+fbqoUEbCzVPA+DihbuT7gY= diff --git a/keys/people/Frank b/keys/people/Frank new file mode 100644 index 0000000..00ffab7 --- /dev/null +++ b/keys/people/Frank @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRjxxMdlE+TLaGJIZyhRLYlgPIlmL81 +4ovjUFM+HUCLoSZzb/2atqaGtGSCzeunMHxhewGcUHAZgdCrp3xIsX9yAAAAsIUkYh+FJG +IfAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGPHEx2UT5MtoYkh +nKFEtiWA8iWYvzXii+NQUz4dQIuhJnNv/Zq2poa0ZILN66cwfGF7AZxQcBmB0KunfEixf3 +IAAAAgO6TdSdrLJzfUPXiwQ4dpS+eoZVwuvCrG2NV4JiwgYF0AAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Frank.pub b/keys/people/Frank.pub new file mode 100644 index 0000000..dba0616 --- /dev/null +++ b/keys/people/Frank.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGPHEx2UT5MtoYkhnKFEtiWA8iWYvzXii+NQUz4dQIuhJnNv/Zq2poa0ZILN66cwfGF7AZxQcBmB0KunfEixf3I= diff --git a/keys/people/George b/keys/people/George new file mode 100644 index 0000000..b2fcde9 --- /dev/null +++ b/keys/people/George @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQXol2/ILQmI7/zfdLtFWuibeF6Bhlf +llbSFWS+kW8myI/L9zCQRYcIGRjawI5c2bdSr4aLnBds2hrzJ4c8t7BFAAAAsLSIhFe0iI +RXAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBeiXb8gtCYjv/N9 +0u0Va6Jt4XoGGV+WVtIVZL6RbybIj8v3MJBFhwgZGNrAjlzZt1KvhoucF2zaGvMnhzy3sE +UAAAAgFLjWhztgHkMa7VD1Ep/bD6Q6t7Pg9J93GVD7+KC3jT4AAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/George.pub b/keys/people/George.pub new file mode 100644 index 0000000..3f29b15 --- /dev/null +++ b/keys/people/George.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBeiXb8gtCYjv/N90u0Va6Jt4XoGGV+WVtIVZL6RbybIj8v3MJBFhwgZGNrAjlzZt1KvhoucF2zaGvMnhzy3sEU= diff --git a/keys/people/Helen b/keys/people/Helen new file mode 100644 index 0000000..c3fce83 --- /dev/null +++ b/keys/people/Helen @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTuqDUQllHCl0vnybICUXYLpyZHnVqV +WhkYw26uvg1tCyxMfSpCE4o7N4NRYExK2zlG5C2Le0KkWISmbJxh6hYQAAAAsO9MkHzvTJ +B8AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO6oNRCWUcKXS+fJ +sgJRdgunJkedWpVaGRjDbq6+DW0LLEx9KkITijs3g1FgTErbOUbkLYt7QqRYhKZsnGHqFh +AAAAAhAPvsgpjOTmbwFf1MfEZJT3L8ArleVZ7bTpuemK/NgDVOAAAAF2hhb0BoYW8tMTNa +OTgwLUEtQUFTNlUx +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Helen.pub b/keys/people/Helen.pub new file mode 100644 index 0000000..6cc53fb --- /dev/null +++ b/keys/people/Helen.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO6oNRCWUcKXS+fJsgJRdgunJkedWpVaGRjDbq6+DW0LLEx9KkITijs3g1FgTErbOUbkLYt7QqRYhKZsnGHqFhA= diff --git a/keys/people/Ilda b/keys/people/Ilda new file mode 100644 index 0000000..cb05632 --- /dev/null +++ b/keys/people/Ilda @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSvD0XuL2/3qi4D2B906uMIGBWn8v/R +D6RZP0WoVcyQy224sumCI6d+fcUv5dMfNhZ5GDNOtcR5xQNg5c9es6vuAAAAsMw37UzMN+ +1MAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK8PRe4vb/eqLgPY +H3Tq4wgYFafy/9EPpFk/RahVzJDLbbiy6YIjp359xS/l0x82FnkYM061xHnFA2Dlz16zq+ +4AAAAgSqbBSMlGc+oAuRRROs4i9y+6V9Nz1+V9qIxs55PxNysAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/people/Ilda.pub b/keys/people/Ilda.pub new file mode 100644 index 0000000..33c71b6 --- /dev/null +++ b/keys/people/Ilda.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK8PRe4vb/eqLgPYH3Tq4wgYFafy/9EPpFk/RahVzJDLbbiy6YIjp359xS/l0x82FnkYM061xHnFA2Dlz16zq+4= diff --git a/keys/policy/P1 b/keys/policy/P1 new file mode 100644 index 0000000..fb3b0c3 --- /dev/null +++ b/keys/policy/P1 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRASUrmKq99jg9dVfkGCsRZKDHJFz0Y +1W3YSMc1kgJNYehQVt+jYffaO58pDc4KsSkl6jUc+RlG95jK/bKT/OJiAAAAsM/G7SjPxu +0oAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEBJSuYqr32OD11V ++QYKxFkoMckXPRjVbdhIxzWSAk1h6FBW36Nh99o7nykNzgqxKSXqNRz5GUb3mMr9spP84m +IAAAAgFIhIszFSHe9Nz4+lA0Se8djuOblAk6H3qk7yoejffCEAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/policy/P1.pub b/keys/policy/P1.pub new file mode 100644 index 0000000..656931a --- /dev/null +++ b/keys/policy/P1.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEBJSuYqr32OD11V+QYKxFkoMckXPRjVbdhIxzWSAk1h6FBW36Nh99o7nykNzgqxKSXqNRz5GUb3mMr9spP84mI= diff --git a/keys/policy/P2 b/keys/policy/P2 new file mode 100644 index 0000000..b3846c9 --- /dev/null +++ b/keys/policy/P2 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTb2zic71malUJR7SmNbsBJLBkWdt5u +0sej4S7EIXZt7PEJjEhGw4wLry9UYCSKt4/PoWxTW6lHnHQr5rlNy3AIAAAAsCFxpnYhca +Z2AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNvbOJzvWZqVQlHt +KY1uwEksGRZ23m7Sx6PhLsQhdm3s8QmMSEbDjAuvL1RgJIq3j8+hbFNbqUecdCvmuU3LcA +gAAAAgOz0FXTPpio7ns6Tvh4o0jm3UtOGjiJExxjoYN80swGcAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/policy/P2.pub b/keys/policy/P2.pub new file mode 100644 index 0000000..490e4e3 --- /dev/null +++ b/keys/policy/P2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNvbOJzvWZqVQlHtKY1uwEksGRZ23m7Sx6PhLsQhdm3s8QmMSEbDjAuvL1RgJIq3j8+hbFNbqUecdCvmuU3LcAg= diff --git a/keys/policy/P3 b/keys/policy/P3 new file mode 100644 index 0000000..99cfb24 --- /dev/null +++ b/keys/policy/P3 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTx/yD4qylE8xVlhrFFOkJmGanIAzlE +AycKBjY8uuqa88ChQH2yF1G83a8O1o980gaM+gytZav3JHsa6h0EK24cAAAAsAOKUssDil +LLAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPH/IPirKUTzFWWG +sUU6QmYZqcgDOUQDJwoGNjy66przwKFAfbIXUbzdrw7Wj3zSBoz6DK1lq/ckexrqHQQrbh +wAAAAhANBxTvd6FisV8XZBaSxpubk9RrPTSj4ZvWPBextA8vR3AAAAF2hhb0BoYW8tMTNa +OTgwLUEtQUFTNlUx +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/policy/P3.pub b/keys/policy/P3.pub new file mode 100644 index 0000000..a29f63c --- /dev/null +++ b/keys/policy/P3.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPH/IPirKUTzFWWGsUU6QmYZqcgDOUQDJwoGNjy66przwKFAfbIXUbzdrw7Wj3zSBoz6DK1lq/ckexrqHQQrbhw= diff --git a/keys/roots/R1 b/keys/roots/R1 new file mode 100644 index 0000000..e11209a --- /dev/null +++ b/keys/roots/R1 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSWJnF18adX0tYBk80mHOTPLgwzt1Yr +09f2n2Na+p9iWVP7hhOEZI9oPJK4VEsrd+9JP3mzTfINFtKXf8AxN7H9AAAAsI15K4WNeS +uFAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJYmcXXxp1fS1gGT +zSYc5M8uDDO3VivT1/afY1r6n2JZU/uGE4Rkj2g8krhUSyt370k/ebNN8g0W0pd/wDE3sf +0AAAAgJixEVX3ZM9wXYH4WsaYI0xKZZ/FVIV6x1jivqCQRrysAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/roots/R1.pub b/keys/roots/R1.pub new file mode 100644 index 0000000..e2ad398 --- /dev/null +++ b/keys/roots/R1.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJYmcXXxp1fS1gGTzSYc5M8uDDO3VivT1/afY1r6n2JZU/uGE4Rkj2g8krhUSyt370k/ebNN8g0W0pd/wDE3sf0= diff --git a/keys/roots/R2 b/keys/roots/R2 new file mode 100644 index 0000000..81c2eae --- /dev/null +++ b/keys/roots/R2 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSmr0gEDLr2baQVltP/EFRijagOR1RF +v9zBNpxlcH2XzahKnt0dPj1QhvDLQHXT2iQZVHIApbaXzx5hG0Sp6KKJAAAAsOoPZ7jqD2 +e4AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKavSAQMuvZtpBWW +0/8QVGKNqA5HVEW/3ME2nGVwfZfNqEqe3R0+PVCG8MtAddPaJBlUcgCltpfPHmEbRKnooo +kAAAAgdSrZG1gKJyX/KeExuIabg9fAZ03msCIjP/txSO5EN+UAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/roots/R2.pub b/keys/roots/R2.pub new file mode 100644 index 0000000..f90e736 --- /dev/null +++ b/keys/roots/R2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKavSAQMuvZtpBWW0/8QVGKNqA5HVEW/3ME2nGVwfZfNqEqe3R0+PVCG8MtAddPaJBlUcgCltpfPHmEbRKnoook= diff --git a/keys/roots/R3 b/keys/roots/R3 new file mode 100644 index 0000000..51ed698 --- /dev/null +++ b/keys/roots/R3 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRBNDSIYsZYo/sDqspAJvC8CxQ5D58K +Dk31drTZdL4Bi0s107s2qwcx2hanBhco7ea7/hdKJqO+qfoyKem/i/knAAAAsItgir2LYI +q9AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEE0NIhixlij+wOq +ykAm8LwLFDkPnwoOTfV2tNl0vgGLSzXTuzarBzHaFqcGFyjt5rv+F0omo76p+jIp6b+L+S +cAAAAgNaDFHzbICAyFsvaX2o2I29Acck5pi2VK8u3neE0oTxIAAAAXaGFvQGhhby0xM1o5 +ODAtQS1BQVM2VTEB +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/roots/R3.pub b/keys/roots/R3.pub new file mode 100644 index 0000000..517fb6c --- /dev/null +++ b/keys/roots/R3.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEE0NIhixlij+wOqykAm8LwLFDkPnwoOTfV2tNl0vgGLSzXTuzarBzHaFqcGFyjt5rv+F0omo76p+jIp6b+L+Sc= diff --git a/run-demo.py b/run-demo.py index 87e9d9a..7339afc 100644 --- a/run-demo.py +++ b/run-demo.py @@ -1,114 +1,114 @@ #!/usr/bin/env python import os -import shlex import shutil -import subprocess -import sys import tempfile +import click +from utils import prompt_key, display_command, run_command, check_binaries -NO_PROMPT = False REQUIRED_BINARIES = ["git", "gittuf", "ssh-keygen"] - -def check_binaries(): - for p in REQUIRED_BINARIES: - if not shutil.which(p): - raise Exception(f"required command {p} not found") - - -def prompt_key(prompt): - if NO_PROMPT: - print("\n" + prompt) - return - inp = False - while inp != "": - try: - inp = input(f"\n{prompt} -- press any key to continue") - except Exception: - pass - - -def display_command(cmd): - print(f"[{os.getcwd()}] $ {cmd}") - - -def run_demo(): +@click.command() +@click.option( + "--automatic", default=False, type=bool, is_flag=True, + help="Whether to wait for input before each command is run." +) +@click.option( + "--repository-directory", default="", + help="The path where the script should store the working copy of the repository." +) + +def run_demo(automatic, repository_directory): + # Repository Setup + # Set up directory structure and keys current_dir = os.getcwd() keys_dir = "keys" - tmp_dir = tempfile.TemporaryDirectory() - tmp_keys_dir = os.path.join(tmp_dir.name, keys_dir) - tmp_repo_dir = os.path.join(tmp_dir.name, "repo") + # Select folder for the working repository copy + working_dir = repository_directory + if working_dir == "": + tmp_dir = tempfile.TemporaryDirectory() + working_dir = tmp_dir.name + else: + working_dir = os.path.abspath(repository_directory) + + # Select folder for the working repository copy + tmp_keys_dir = os.path.join(working_dir, keys_dir) + tmp_repo_dir = os.path.join(working_dir, "repo") + tmp_keys_dir = shutil.copytree(os.path.join(current_dir, keys_dir), tmp_keys_dir) - shutil.copytree(os.path.join(current_dir, keys_dir), tmp_keys_dir) os.mkdir(tmp_repo_dir) os.chdir(tmp_repo_dir) + # Ensure correct permissions for keys for key in os.listdir(tmp_keys_dir): os.chmod(os.path.join(tmp_keys_dir, key), 0o600) + # Compute folder paths authorized_key_path_git = os.path.join(tmp_keys_dir, "authorized.pub") unauthorized_key_path_git = os.path.join(tmp_keys_dir, "unauthorized.pub") - authorized_key_path_policy = os.path.join(tmp_keys_dir, "authorized.pub") - prompt_key("Initialize Git repository") + # Initialize the Git repository in the chosen directory + prompt_key(automatic, "Initialize Git repository") cmd = "git init -b main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Set repo config to use demo identity and test key") + # Set the configuration options needed to sign commits. For this demo, the + # "authorized" key is used, but note that this is not the key used for + # managing the policy. + prompt_key(automatic, "Set repo config to use demo identity and test key") cmd = f"git config --local gpg.format ssh" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) cmd = f"git config --local commit.gpgsign true" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) cmd = f"git config --local user.signingkey {authorized_key_path_git}" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) cmd = f"git config --local user.name gittuf-demo" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) cmd = f"git config --local user.email gittuf.demo@example.com" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Set PAGER") + prompt_key(automatic, "Set PAGER") os.environ["PAGER"] = "cat" display_command(f"export PAGER=cat") - prompt_key("Initialize gittuf root of trust") + prompt_key(automatic, "Initialize gittuf root of trust") cmd = "gittuf trust init -k ../keys/root" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Add policy key to gittuf root of trust") + prompt_key(automatic, "Add policy key to gittuf root of trust") cmd = ( "gittuf trust add-policy-key" " -k ../keys/root" " --policy-key ../keys/targets.pub" ) display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Initialize policy") + prompt_key(automatic, "Initialize policy") cmd = "gittuf policy init -k ../keys/targets" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Add key definition to policy") + prompt_key(automatic, "Add key definition to policy") cmd = ( "gittuf policy add-key" " -k ../keys/targets" " --public-key ../keys/authorized.pub" ) display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Add rule to protect the main branch") + prompt_key(automatic, "Add rule to protect the main branch") cmd = ( "gittuf policy add-rule" " -k ../keys/targets" @@ -117,80 +117,89 @@ def run_demo(): f" --authorize-key {authorized_key_path_policy}" ) display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "gittuf policy apply" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Make change to repo's main branch") + prompt_key(automatic, "Make change to repo's main branch") display_command("echo 'Hello, world!' > README.md") with open("README.md", "w") as fp: fp.write("Hello, world!\n") cmd = "git add README.md" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git commit -m 'Initial commit'" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Record change to main in RSL") + prompt_key(automatic, "Record change to main in RSL") cmd = "gittuf rsl record main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git show refs/gittuf/reference-state-log" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Verify branch protection for this change") + prompt_key(automatic, "Verify branch protection for this change") cmd = "gittuf verify-ref main" display_command(cmd) - subprocess.run(shlex.split(cmd), check=True) + run_command(cmd, 0) - prompt_key("gittuf's verification succeeded!") + prompt_key(automatic, "gittuf's verification succeeded!") - prompt_key("Update repo config to use unauthorized key") + prompt_key(automatic, "Update repo config to use unauthorized key") cmd = f"git config --local user.signingkey {unauthorized_key_path_git}" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Make unauthorized change to repo's main branch") - display_command("echo 'This is not allowed!' >> README.md") + prompt_key(automatic, "Make unauthorized change to repo's main branch") + cmd = "echo 'This is not allowed!' >> README.md" + display_command(cmd) with open("README.md", "a") as fp: fp.write("This is not allowed!\n") + cmd = "git add README.md" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git commit -m 'Update README.md'" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Record change to main in RSL") + prompt_key(automatic, "Record change to main in RSL") cmd = "gittuf rsl record main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git show refs/gittuf/reference-state-log" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Verify branch protection for this change") + prompt_key(automatic, "Verify branch protection for this change") cmd = "gittuf verify-ref main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 1) - prompt_key("gittuf detected a violation of the branch protection rule!") + prompt_key(automatic, "gittuf detected a violation of the branch protection rule!") - prompt_key("Rewind to last good state to test file protection rules") + prompt_key(automatic, "Rewind to last good state to test file protection rules") cmd = "git reset --hard HEAD~1" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git update-ref refs/gittuf/reference-state-log refs/gittuf/reference-state-log~1" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = f"git config --local user.signingkey {authorized_key_path_git}" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Add rule to protect README.md") + prompt_key(automatic, "Add rule to protect README.md") cmd = ( "gittuf policy add-rule" " -k ../keys/targets" @@ -199,50 +208,54 @@ def run_demo(): f" --authorize-key {authorized_key_path_policy}" ) display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) cmd = "gittuf policy apply" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Make change to README.md using unauthorized key") + prompt_key(automatic, "Make change to README.md using unauthorized key") cmd = f"git config --local user.signingkey {unauthorized_key_path_git}" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + + cmd = "echo 'This is not allowed!' >> README.md" display_command("echo 'This is not allowed!' >> README.md") with open("README.md", "a") as fp: fp.write("This is not allowed!\n") + cmd = "git add README.md" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git commit -m 'Update README.md'" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("But create RSL entry using authorized key") + prompt_key(automatic, "But create RSL entry using authorized key") cmd = f"git config --local user.signingkey {authorized_key_path_git}" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "gittuf rsl record main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) + cmd = "git show refs/gittuf/reference-state-log" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 0) - prompt_key("Verify all rules for this change") + prompt_key(automatic, "Verify all rules for this change") cmd = "gittuf verify-ref main" display_command(cmd) - subprocess.call(shlex.split(cmd)) + run_command(cmd, 1) - prompt_key( + prompt_key(automatic, "gittuf detected a **file** protection rule violation even though the" " branch protection rule was met!" ) if __name__ == "__main__": - if len(sys.argv) > 1: - if sys.argv[1] == "--no-prompt": - NO_PROMPT = True - check_binaries() - run_demo() + check_binaries(REQUIRED_BINARIES) + run_demo() # pylint: disable=no-value-for-parameter + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..24684cd --- /dev/null +++ b/utils.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import os +import shlex +import shutil +import subprocess + +def check_binaries(required_binaries): + """Checks that the supplied binaries are present on the system""" + for p in required_binaries: + if not shutil.which(p): + raise Exception(f"required command {p} not found") + +def prompt_key(auto, prompt): + """Controls the flow of the demo for each step""" + if auto: + print(f"\n{prompt}") + return + inp = False + while inp != "": + try: + inp = input(f"\n{prompt} -- press Enter to continue") + except Exception: + pass + +def display_command(cmd): + """Displays the supplied command with the current directory prepended""" + print(f"[{os.getcwd()}] $ {cmd}") + +def run_command(cmd, expected_retcode): + """Runs the supplied command and checks for the expected return code""" + retcode = subprocess.call(shlex.split(cmd)) + if retcode != expected_retcode: + raise Exception(f"Expected {expected_retcode} from process but it exited with {retcode}.") +