{"id":13781700,"url":"https://github.com/stevelr/age-op","last_synced_at":"2025-04-11T20:12:30.781Z","repository":{"id":170793698,"uuid":"647016792","full_name":"stevelr/age-op","owner":"stevelr","description":"simple CLI encryption without the footguns: age + 1password","archived":false,"fork":false,"pushed_at":"2023-10-16T21:19:22.000Z","size":26,"stargazers_count":46,"open_issues_count":5,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-11T20:12:25.127Z","etag":null,"topics":["1password-cli","backups","cli","encryption"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevelr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-apache","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-05-29T22:03:00.000Z","updated_at":"2025-03-31T05:15:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"b95ef3e2-282f-462a-a9db-aadc955d3bc0","html_url":"https://github.com/stevelr/age-op","commit_stats":null,"previous_names":["stevelr/age-op"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevelr%2Fage-op","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevelr%2Fage-op/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevelr%2Fage-op/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevelr%2Fage-op/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevelr","download_url":"https://codeload.github.com/stevelr/age-op/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248473126,"owners_count":21109628,"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":["1password-cli","backups","cli","encryption"],"created_at":"2024-08-03T18:01:28.387Z","updated_at":"2025-04-11T20:12:30.760Z","avatar_url":"https://github.com/stevelr.png","language":"Shell","funding_links":[],"categories":["cli","Tools"],"sub_categories":[],"readme":"# Simple CLI encryption without the footguns\n\nThis [`age-op`](./age-op) bash script combines the awesome [age](https://github.com/FiloSottile/age) cli for encryption and decryption, with 1password for secure key storage.\nIt can also create age-compatible ed25519 keys and store them in a 1password vault. \nThrough the magic of 1password-host integration, it takes advantage of the various conveniences for unlocking the vault: biometrics, touch-id, apple watch (macos), yubi key, etc.\n\nExamples below show encryption and decryption of files and streams (stdin/stdout).\nScripts and documentation are provided showing how to use `age-op` on remote servers, CI pipelines, and edge devices.\n\n## Dependencies:\n\n- [age](https://github.com/FiloSottile/age) or [rage](https://github.com/str4d/rage) (To use rage, set the environment variable `AGE=rage`)\n- [1password cli](https://developer.1password.com/docs/cli/) (`op`). See [installation](https://developer.1password.com/docs/cli/get-started#install) instructions for mac, linux, and windows.\n\n\n## Usage Examples\n\nFor help and examples,\n\n```\nage-op [ -h | --help ]\n```\n \n\n### Encrypt \n\nEncrypt a file\n\n```shell\nage-op -e -k KEY_PATH [ -o OUTPUT ] [ -t TMPDIR ] [ FILE ]\n```\n  \nEncrypt multiple files and folders.\n\n```shell\ntar czf - FILE_OR_DIR [ FILE_OR_DIR ... ] | age-op -e -k KEY_PATH -o foo.tar.gz.age\n```\n\nEncrypt database backup\n\n```shell\npg_dump | age-op -e -k KEY_PATH -o db-snapshot-$(date '+%Y%m%d-%H%M%S').age \n```\n\n### Decrypt\n\nDecrypt a file\n\n```shell\nage-op -d -k KEY_PATH [ -o OUTPUT ] [-t TMPDIR ] [ FILE ]\n```\n  \nDecrypt files and folders.\n\n```shell\nage-op -d -k KEY_PATH foo.tar.gz.age | tar xzf -\n```\n\n### Generate a key\n\nGenerate an age identity key and store it in the 1password vault. The type of the new item will be \"Password\", and the key is stored in the field named `password`.\n\n```shell\nage-op -n -k KEY_PATH\n```\n\n## Options\n\n|             | description                                                                                                                                                                                                                                                              |\n|:------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| -k KEY_PATH | (required) path to key in a 1Password vault, in one of the following formats:\u003cbr/\u003e`op://vault/title`\u003cbr/\u003e`op://vault/title/field`\u003cbr/\u003e`op://vault/title/section/field`\u003cbr/\u003eThe first variant can be used when the field name is `password`.                              |\n| -o OUTPUT   | path to output file. If `-` or if not specified, stdout is used                                                                                                                                                                                                          |\n| -t TMPDIR   | TMPDIR is a private folder where keys are briefly stored so they can be read by `age`, then quickly removed.\u003cbr/\u003eOn linux, the default is `/run/user/USERID`. On macos, the default is `$TMPDIR`. Both of these folders are usually owned by current user with mode 700. |\n| FILE        | path to input file. If `-` or if not specified, stdin is used.                                                                                                                                                                                                           |\n\n\n## 1Password access: local and remote\n\nThe 1Password cli `op`, used inside `age-op`, can access a 1Password vault in one of three ways - through a locally installed desktop app, as a service account, or via a 1Password Connect Server.\nThe latter two methods don't require a local app, and are especially useful for headless servers, CI pipelines, and edge devices.\n\n\n### Local connection to desktop app\n\nThe `op` cli should automatically connect to a locally installed app. In some cases, you may need to run \n`eval $(op signin)` to authenticate the current shell.\n\n\n### Service Account\n\nFor service account authentication, `op` uses a token from \nthe environment variable `OP_SERVICE_ACCOUNT_TOKEN` . The service account\nis configured from the 1Password web UI, where you generate and download tokens.\n\nAlthough a service account token provides limited access - access is limited to specific approved vaults, and only\nuntil the token expiration date - it's still a secret that needs some protection.\nFor a CI pipeline, or in AWS or K8s where you can inject environment variables, it's straightforward\nto add the token to the environment of the remote process.\nIf you are connecting to the server via ssh from a trusted host (e.g., your workstation or laptop), you can pass the environment variable\nas part of the ssh session, either with the ssh command\n\n```shell\nssh -o SendEnv=OP_SERVICE_ACCOUNT_TOKEN ...\n```\n\nor by adding `SendEnv` or `SetEnv` to the ssh client configuration (`~/.ssh/config`)\n\n```\nHost some-remote\n    # Pass the token from my env\n    SendEnv OP_SERVICE_ACCOUNT_TOKEN\n    # or, send the value from this config file\n    SetEnv OP_SERVICE_ACCOUNT_TOKEN=\u003cTOKEN\u003c\u003e\n```\n\nOn the remote host, you'll probably need to add a line to `/etc/ssh/sshd_config` to tell the ssh server to accept the environment variable.\n\n```\nAcceptEnv OP_SERVICE_ACCOUNT_TOKEN\n```\n\n### 1Password Connect Server\n\nA 1Password Connect Server is another method `op` can use to access a vault.\nInstead of connecting to the 1Password cloud, it connects to a server that you run on your own infrastructure.  \n\nThis method has the additional overhead of starting a server, but there is some additional flexibility.\nSee [op-connect](./op-connect) in this repository for a docker-compose file, scripts, and additional instructions.\n\nI use this in cases when I ssh from a desktop or laptop that has 1password installed,\nand I want to use different encryption keys and access policies for different remote hosts. The connect server runs for the duration of the ssh session,\nhandling callbacks from the remote apps. The credentials are short-lived\n(only as long as the ssh session), fine-tuned to the target, and no keys or tokens are left behind on the remote servers.\n \n\n## Alternatives\n\nSome of the popular alternatives to `age` I considered.\n\n- __openssl__ cli. Although often recommended in blogs and SO, it [has significant flaws and footguns](https://security.stackexchange.com/questions/182277/is-openssl-aes-256-cbc-encryption-safe-for-offsite-backup)\n\n- __gnupg__ (gpg) has so many options [it's easy to make poor choices](https://github.com/FiloSottile/age/discussions/432), and is far from the ideal \"secure by default\".\n\n- __7zip__ (7z) uses aes-256, but [doesn't retain unix ownership and permissions](https://www.redhat.com/sysadmin/encrypting-decrypting-7zip), so isn't good for archive backups.\n\n- __aescrypt__ - hard to review since the [git repo is out of date](https://github.com/paulej/AESCrypt), even though that's still linked from aescrypt dot com.\n\n- __aws__ encryption cli - requires AWS KMS, and I wanted 1Password\n\n- __veracrypt__, __rclone__, and __restic__: All three of these are well-regarded, and I use them for other use cases, but they are too heavyweight (IMO) for a simple cli for encrypting stdin or a file at a time.\n\n- Writing yet-another tool. Not worth writing another binary.\n\n`age` is broadly used, was written by a smart and thoughtful author, and stands on the shoulders of chacha20-poly1305 (RFC7539), x25519 (RFC 7748), HKDF-SHA-256 (RFC 5869).\nThe short `age-op` script is easy to review, and doesn't introduce any new encryption algorithms or do anything fancy. \nAll files created or read by `age-op` are 100% compatible with `age` and `rage`, so there is no lock-in or risk of incompatibility.\n\n\n## Future\n\nThere may be [reasons](https://github.com/stevelr/age-op/issues/1) for building this as an age plugin, or for taking advantage of future plugins. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevelr%2Fage-op","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevelr%2Fage-op","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevelr%2Fage-op/lists"}