5 Passage (short for "Pass Agent") is a Go process that serves as caching proxy for macOS keychain items.
7 It is intended to fulfil the use case where you have a command-line process that needs to periodically access a passphrase in the keychain, but you don't want to have to click "Allow" every single time (too cumbersome), nor "Always Allow" (too coarse). For more information, see **Security**, below.
13 If you have a working Go environment and `$GOPATH` is set, you can do:
16 go get github.com/wincent/passage
19 Additionally, you will need to install the [keybase/go-keychain](https://github.com/keybase/go-keychain) library as a dependency:
22 go get github.com/keybase/go-keychain
28 cd $GOPATH/src/wincent/passage
32 ## Running automatically as a launch agent
35 sudo cp passage /usr/local/bin
36 cp contrib/com.wincent.passage.plist ~/Library/LaunchAgents
38 # NOTE: This won't work if run inside a tmux session:
39 launchctl load -w -S Aqua ~/Library/LaunchAgents/com.wincent.passage.plist
44 Send a JSON object with keys "service" and "account" to the Passage socket at `~/.passage.sock`. On the first request, you may see a keychain permission dialog, but on subsequent requests, you'll get a cached result back immediately.
47 echo '{"service":"test item","account":"test name"}' | nc -U ~/.passage.sock
50 The resulting password is written back over the socket. You could call this a "write of Passage" (ha!). In the event that the item is missing or there is an error, the connection closes without any output, although errors do get printed to the standard output of the Passage process itself. This is for convenient consumption by clients (ie. so that they don't have to implement JSON parsing themselves).
52 ### Invalidating the cache
54 There are two ways to invalidate the cache. One is to just restart the Passage process. The other is to send it a `SIGUSR1` signal.
56 You'll need the process PID in order to do that, and you can get it in several ways:
59 # The old-fashioned way:
62 # Or to be more specific, assuming you installed and ran from `/usr/local/bin/`:
63 ps xww | grep /usr/local/bin/passage | grep -v grep
65 # The first column so you can extract it like this:
66 ps xww | grep /usr/local/bin/passage | grep -v grep | awk '{print $1}'
68 # If you launched using launchctl (but note: only works outside tmux):
69 launchctl list | grep passage
71 # The PID is the first column in the output of `launchctl list`, so you can
72 # extract it like this:
73 launchctl list | grep passage | awk '{print $1}'
75 # Once you've got the PID, say in a variable $PID, send the signal like this:
78 # A more clever way using launchctl that works anywhere (including inside tmux)
79 # and doesn't just get the PID but actually sends the signal as well:
80 launchctl kill SIGUSR1 "gui/$(id -u)/com.wincent.passage"
85 There are no options. This was quickly hacked together to address a specific need.
89 The `~/.passage.sock` socket is created with user-only (`0700`) permissions, but can still be accessed by privileged users and processes running on the system. As such, it falls somewhere in the middle of the spectrum of password storage (ordered approximately from most to least secure):
91 * **Storage in the system keychain, requiring explicit "Allow" intervention for each access:** This is the most secure, but also the most onerous. Key material only leaves the key chain with active approval (interaction with an intrusive GUI dialog).
92 * **Temporary storage in a process like `ssh-agent`:** This is quite secure not only because of the limited storage duration, but because the key material *never* leaves the agent; rather, other processes delegate work to be done by the agent. For example, when `ssh` tries to connect to a server, it will ask `ssh-agent` to perform the operations for private-key authentication, but there is no straightforward way to extract the actual private key material for the agent. The most a local attacker can do is perform operations as you, and only while they have access to the agent. Note, however, this approach is not viable for situations like those in which you *do* need access to the private key.
93 * **Temporary storage in the memory of the `passage` process:** Passphrases can only be inserted into `passage` via explicit approval, and only remain there for the duration of the session. You can shorten the duration in various ways, such as:
94 1. Terminating the app or logging out.
95 2. Setting up a cron job to periodically send a `SIGUSR1` to `passage` to cause it to drop its cache; you can do this with `crontab -e` and a method like the `launchctl kill` one documented above.
96 3. Arranging to send `SIGUSR1` whenever the screensaver kicks in or the screen locks (something you should be able to do with a tool like [Hammerspoon](http://www.hammerspoon.org/)).
97 * **Storage in the system keychain, having previously granted "Always Allow" access:** This is problematic if you're using the `security` command-line tool provided by Apple, because once you've granted "Always Allow", you will never be asked for permission again. Even if you intended to grant access only to `my-special-script.sh`, the permission will actually be associated with the `security` tool, which any process can call. So here the security is contingent on an attacker not being able to run code as you (or trick/coerce you into running it).
98 * **Storage in plain-text on the filesystem:** The security here is only so good as the security of the filesystem: privileged users will be able to retrieve the password regardless of filesystem permission. An unprivileged, determined attacker with local access will be able to elevate privileges by one means or another and gain access, even in the face of restrictive permissions.
104 Passage uses the [keybase/go-keychain](https://github.com/keybase/go-keychain) library to access the keychain, which currently only knows how to read "generic" (A.K.A. "application") passwords, not "Internet" passwords.
108 Passage is written and maintained by Greg Hurrell (greg@hurrell.net).
112 The official Passage source code repo is at:
114 - http://git.wincent.com/passage.git
118 - https://github.com/wincent/passage
119 - https://gitlab.com/wincent/passage
120 - https://bitbucket.org/ghurrell/passage
122 Patches are welcome via the usual mechanisms (pull requests, email, posting to the project issue tracker etc).
126 The official website for Passage is:
128 - https://github.com/wincent/passage
130 Bug reports should be submitted to the issue tracker at:
132 - https://github.com/wincent/passage/issues
136 Please see [`CHANGELOG.md`](CHANGELOG.md) in this repository.
140 Copyright 2016-present Greg Hurrell. All rights reserved.
142 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
144 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
145 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
147 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.