{"id":19336737,"url":"https://github.com/samrocketman/repository-secrets","last_synced_at":"2025-04-23T01:30:52.200Z","repository":{"id":25788537,"uuid":"29227046","full_name":"samrocketman/repository-secrets","owner":"samrocketman","description":"A proof of concept for securing repository secrets for a build ecosystem where the repository is easily scrutinized by unauthorized parties.","archived":false,"fork":false,"pushed_at":"2025-02-16T21:11:56.000Z","size":140,"stargazers_count":2,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T06:12:15.085Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samrocketman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-01-14T04:22:42.000Z","updated_at":"2025-02-16T21:11:59.000Z","dependencies_parsed_at":"2024-12-21T08:28:04.320Z","dependency_job_id":"d114c162-d6d7-4eb4-b59d-12570d9256a7","html_url":"https://github.com/samrocketman/repository-secrets","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samrocketman%2Frepository-secrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samrocketman%2Frepository-secrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samrocketman%2Frepository-secrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samrocketman%2Frepository-secrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samrocketman","download_url":"https://codeload.github.com/samrocketman/repository-secrets/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250352184,"owners_count":21416454,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T03:12:13.854Z","updated_at":"2025-04-23T01:30:52.189Z","avatar_url":"https://github.com/samrocketman.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Securing repository secrets\n\nThis is a simple proof of concept for securing repository secrets.\n\nThe idea is to encrypt the secrets using a public key, which can be widely distributed. Then, within a CI system or delivery pipeline, the secrets are decrypted using a private key.  Examples securely storing private keys are included.\n\n* [Old Proof of concept](docs/proof_of_concept.md)\n* [2024 proof of concept](docs/2024_proof_of_concept.md)\n\n# repository-secrets.sh CLI utility\n\n### System Requirements\n\n- GNU Bash\n- GNU coreutils or similar providing `cat`, `cp`, `dd`, `mktemp`, `shasum` or\n  `sha256sum`, and `tr`.\n- OpenSSL 3 (OpenSSL 3.2 or later recommended).\n- [yq](https://github.com/mikefarah/yq/) for YAML.\n- GNU or BSD `awk`\n- `xxd`\n\n### Example\n\nGenerate a private and public key pair.\n\n    openssl genrsa -out /tmp/id_rsa 4096\n    openssl rsa -in /tmp/id_rsa -pubout -outform pem -out /tmp/id_rsa.pub\n\nEncrypt some text.\n\n    echo supersecret | ./repository-secrets.sh encrypt -o cipher.yaml\n\nResults in cipher YAML like the following.\n\n```yaml\nopenssl_aes_args: -aes-256-cbc -pbkdf2 -iter 600000\nopenssl_rsa_args: -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:SHA256\npbkdf2_password_length: 389\npbkdf2_salt_length: 8\ndata: |-\n  sIk/SjszW6tJz3M7gaqkHQ==\nhash: |-\n  CUmTHeMGuJxwlxtgfpI9gP5Hu4q9ewluifadchPcDRExb/L4J1CoF9g7ssJ/T4zk\n  96htHxZwRkLWqEekBktgN5SFmn1n905xYB8xvEXRrfysz/FDb92E4lmFxxAKOf+V\n  A4YVCu36VskxhN15W+TNgiOfhJl0cTpyy9z3L9zwuIKfQG1fq4x8bb9E5t1N/RTy\n  ZRmJByE4BcGWxPB0OIkaikkZnze71CV+Cg8F3Ovmn3fORN61aGeUhMAkbsMHB6Jp\n  zU3JJ9yZS/UPwQgsv1VMHjNdMAkoxZWecdT0OCNU5d3G+Shn/TDBLYgtqT02aQiU\n  74cBRVb2/xxjYnQqWBLbcE5xVjtuG0TODnHH7gWD/bjiEbRi+rxB8FF1dbopa26D\n  8NfVeQh2T4SdVKJkbyAPlY0vPhcxAihf0HoryS8SPraLIYAFBnZWMruW9WSdGe2c\n  oPvXaqAjZiR57UbZOps9ssxyjQzoZAtO46ynsTuYNTjwEt7y1rx5p9E+09KSJBsg\n  r/cNNke3cGuRcAAh3lEHpJycyYjNWorzyIk/1F91dxJxQIrssA2ehXEN19lZCPT/\n  Bs8ynGN8A7j3gRHwnEtpW8Vx1YeWsfa+zudO1PzEoOISj4PCu4I757D01zUm28jZ\n  +MD08rkuEbHiWHITcran9VCbLNX9Hhpz4/tzJ32iOr4=\n```\n\nWhich you can then decrypt.\n\n```bash\n./repository-secrets.sh decrypt -i cipher.yaml\n# prints to stdout supersecret\n```\n\nEncryption and decryption can be handled in streams.\n\n```bash\necho another secret | ./repository-secrets.sh encrypt | ./repository-secrets.sh decrypt\n# 'another secret' is printed on stdout\n```\n\n### Docker example\n\nUsing a minimal alpine image to encrypt and decrypt secrets.\n\n    docker build -t rs .\n    echo plaintext | docker run -i rs encrypt | docker run -i rs decrypt\n\n### Help documentation\n\n`repository-secrets.sh` was written based on the 2024 proof of concept document.\n\nHelp documentation: `./repository-secrets.sh help`\n\n```\nSYNOPSIS\n  ./repository-secrets.sh encrypt [options]\n  ./repository-secrets.sh decrypt [options]\n  ./repository-secrets.sh rotate-key [options]\n\n\nDESCRIPTION\n  A utility for performing one-way encryption or decryption on files using RSA\n  key pairs.  The intent is to have a client encrypt data with an RSA public\n  key and a backend system use this same script to decrypt the data with an RSA\n  private key.\n\n\nSUBCOMMANDS\n  encrypt\n      Performs encryption operations with an RSA public key and outputs an\n      encrypted cipher YAML file.  Binary data is allowed.\n\n  decrypt\n      Performs decryption operations with an RSA private key and outputs\n      original plain text.  May output binary data if binary data was\n      originally encrypted.\n\n  rotate-key\n      Performs private key rotation on enciphered YAML without changing\n      symmetrically encrypted data.  This will not modify data,\n      openssl_aes_args, or openssl_rsa_args keys in the enciphered YAML.\n\n\nENCRYPT SUBCOMMAND OPTIONS\n  -p FILE\n  --public-key FILE\n    An RSA public key which will be used for encrypting data.\n    Default: PUBLIC_KEY environment variable\n\n  -i FILE\n  --in-file FILE\n    Plain input meant to be encrypted.  Can be plain text or binary data.\n    Default: stdin\n\n  -o FILE\n  --output FILE\n    Encrypted ciphertext in a plain-text friendly YAML format.  If the output\n    file already exists as cipher YAML, then only the data and hash will be\n    updated.\n    Default: stdout\n\n\nDECRYPT SUBCOMMAND OPTIONS\n  -k FILE\n  --private-key FILE\n    An RSA private key which will be used for decrypting data.\n    Default: PRIVATE_KEY environment variable\n\n  -i FILE\n  --in-file FILE\n    Encrypted ciphertext in a plain-text friendly YAML format.\n    Default: stdin\n\n  -o FILE\n  --output FILE\n    Plain input meant to be which has been decrypted.\n    Default: stdout\n\n  -s FIELD\n  --skip-field FIELD\n    Sometimes upon decryption you may want to override the AES or RSA\n    decryption options.  This option allows you to set an environment variable\n    of the same name while ignoring the value in the cipher YAML file.  FIELD\n    may be one of the following values: openssl_aes_args or openssl_rsa_args.\n    This option can be specified multiple times to skip multiple fields.\n    Default: ''\n\n\nROTATE-KEY SUBCOMMAND OPTIONS\n  -k FILE\n  --private-key FILE\n    An RSA private key which will be used to decrypt keys salt, passin, and\n    hash within a cipher YAML file.\n    Default: PRIVATE_KEY environment variable\n\n  -p FILE\n  --public-key FILE\n    An RSA public key which will be used to re-encrypt keys salt, passin, and\n    hash within a cipher YAML file.\n    Default: PUBLIC_KEY environment variable\n\n  -f FILE\n  --input-output-file FILE\n    A cipher YAML file in which the salt, passin, and hash are updated with the\n    new private key.  The data will not be modified.\n\n\nENVIRONMENT VARIABLES\n  pbkdf2_salt_length\n    The length of salt used by PBKDF2 during encryption or decryption.  Must be\n    an integer between 1 and 16.\n    Default: '8'\n\n  openssl_aes_args\n    Arguments used on openssl for AES encryption or decryption.\n    Default: '-aes-256-cbc -pbkdf2 -iter 600000'\n\n  openssl_rsa_args\n    Arguments used on openssl for RSA encryption or decryption.\n    Default: '-pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:SHA256'\n\n  PRIVATE_KEY\n    Path to RSA private key file used for decryption.  Used as -keyin argument\n    for openssl pkeyutl.\n    Default: '/tmp/id_rsa'\n\n  PUBLIC_KEY\n    Path to RSA public key file used for encryption.  Used as -keyin argument\n    for openssl pkeyutl.\n    Default: '/tmp/id_rsa.pub'\n\n  pbkdf2_password_length\n    Number of characters of the passphrased used to AES encrypt data.  This\n    should be set to the largest number possible given the current design.  The\n    current default assumes RSA 4096-bit keys.  If you require 2048-bit keys,\n    then set the password length to 108.\n    Default: 364\n\n\nEXAMPLES\n\n  Generate RSA key pair for examples.\n\n    openssl genrsa -out /tmp/id_rsa 4096\n    openssl rsa -in /tmp/id_rsa -pubout -outform pem -out /tmp/id_rsa.pub\n\n  Encrypt data\n\n    echo plaintext | ./repository-secrets.sh encrypt -o /tmp/cipher.yaml\n    ./repository-secrets.sh decrypt -i output.yaml\n\n  Working with binary data is the same.\n\n    echo plaintext | gzip -9 | ./repository-secrets.sh encrypt -o /tmp/cipher.yaml\n    ./repository-secrets.sh decrypt -i /tmp/cipher.yaml | gunzip\n\n  Rotate private/public key pair.\n\n    ./repository-secrets.sh rotate-key -k old-private-key.pem -p new-public-key.pub -f /tmp/cipher.yaml\n\n  Alternate example.\n\n    export PRIVATE_KEY=old-private-key.pem\n    export PUBLIC_KEY=new-public-key.pub\n    ./repository-secrets.sh rotate-key -f /tmp/cipher.yaml\n\nAWS KMS SECRETS ENGINE\n\n  Install kms.so (on Linux) or kms.dylib (on Mac).\n\n    curl -sSfLO https://raw.githubusercontent.com/samrocketman/yml-install-files/refs/tags/v3.1/download-utilities.sh\n    chmod 755 download-utilities.sh\n    curl -sSfL https://github.com/samrocketman/openssl-engine-kms/releases/download/0.1.1/openssl-engine-kms.yaml | ./download-utilities.sh -\n\n  The above commands will download either ./libopenssl_engine_kms.so or\n  libopenssl_engine_kms.dylib depending on your architecture and OS.  If\n  nothing was downloaded, then your platform does not have a pre-compiled\n  binary.  The binary must be copied to OpenSSL engines-3 directory.\n\n  On Linux, install kms.so.\n\n    find /usr/lib -type d -name engines-3 | xargs -I'{}' cp ./libopenssl_engine_kms.so '{}/kms.so'\n\n  On arm64 MacOS, install kms.dylib\n\n    cp libopenssl_engine_kms.dylib /opt/homebrew/Cellar/openssl@3/3.4.0/lib/engines-3/kms.dylib\n\n  In AWS KMS, create an asymmetric key to be used for encryption and\n  decryption.  The key spec should be RSA_4096.  Export the public key for\n  local encryption which won't require an AWS login to encrypt.\n\n    openssl pkey -engine kms -inform engine -in arn:aws:kms:... -pubout \u003e kms.pub\n\n  Encrypting a file, binary data, or plaintext data using the public key.\n\n    export PUBLIC_KEY=kms.pub\n    echo hello | ./repository-secrets.sh encrypt -o cipher.yaml\n\n  On a backend system, use AWS KMS to decrypt the data with the private key.\n\n    export openssl_rsa_args='-keyform engine -engine kms -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:SHA256'\n    export PRIVATE_KEY=arn:aws:kms:...\n    ./repository-secrets.sh decrypt -i cipher.yaml\n\nOLD OPENSSL NOTICE\n\n  Old OpenSSL versions before OpenSSL 3.2 do not have -saltlen option available.\n  The environment variable defaults are intended to reflect this.\n\n  For even older OpenSSL, you might not want to use\n  RSA/ECB/OAEPWithSHA-256AndMGF1Padding and instead use RSA/ECB/PKCS1Padding.\n  You can accomplish this by overriding openssl_rsa_args with an empty space.\n  Note the space is required so that the veriable is non-zero length.\n\n    export openssl_rsa_args=' '\n    echo hello | ./repository-secrets.sh encrypt\n\n\nALGORITHMS\n\n  SHA-256 for data integrity verification.\n  RSA/ECB/OAEPWithSHA-256AndMGF1Padding for asymmetric encryption storage.\n  AES/CBC/PKCS5Padding for symmetric encryption storage.\n  PBKDF2WithHmacSHA256 for key derivation; 600k iterations with 16-byte salt.\n\nSOURCE\n  Created by Sam Gleske\n  https://github.com/samrocketman/yml-install-files\n  https://github.com/samrocketman/openssl-engine-kms\n  https://github.com/samrocketman/repository-secrets\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamrocketman%2Frepository-secrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamrocketman%2Frepository-secrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamrocketman%2Frepository-secrets/lists"}