Skip to content

Commit ed6cce4

Browse files
authored
Merge pull request #2730 from pietroalbini/cargo-token-process
Fetching cargo registry tokens from external processes
2 parents baa3cac + 38eac0c commit ed6cce4

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

text/2730-cargo-token-from-process.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
- Feature Name: `cargo_token_from_process`
2+
- Start Date: 2019-07-22
3+
- RFC PR: [rust-lang/rfcs#2730](https://github.com/rust-lang/rfcs/pull/2730)
4+
- Cargo Issue: [rust-lang/cargo#8933](https://github.com/rust-lang/cargo/issues/8933)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add a cargo setting to fetch registry authentication tokens by calling an
10+
external process.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
Some interactions with a registry require an authentication token, and Cargo
16+
currently stores such token in plaintext in the [`.cargo/credentials`][creds]
17+
file. While Cargo properly sets permissions on that file to only allow the
18+
current user to read it, that's not enough to prevent other processes ran by
19+
the same user from reading the token.
20+
21+
This RFC aims to provide a way to configure Cargo to instead fetch the token
22+
from any secrets storage system, for example a password manager or the system
23+
keyring.
24+
25+
[creds]: https://doc.rust-lang.org/stable/cargo/reference/config.html#credentials
26+
27+
# Guide-level explanation
28+
[guide-level-explanation]: #guide-level-explanation
29+
30+
Suppose a user has their authentication token stored in a password manager, and
31+
the password manager provides a command, `/usr/bin/cargo-creds`, to decrypt and
32+
print that token in a secure way. Instead of storing the token in plaintext,
33+
the user can add this snippet to their own Cargo config to authenticate with
34+
crates.io:
35+
36+
```toml
37+
[registry]
38+
credential-process = "/usr/bin/cargo-creds"
39+
```
40+
41+
When authentication is required, Cargo will execute the command to acquire the
42+
token, which will never be stored by Cargo on disk.
43+
44+
It will be possible to use `credential-process` on both crates.io and alternative
45+
registries.
46+
47+
# Reference-level explanation
48+
[reference-level-explanation]: #reference-level-explanation
49+
50+
A new key, `credential-process`, will be added to the `[registry]` and
51+
`[registries.NAME]` sections of the configuration file. When a `token` key is
52+
also present, the latter will take precedence over `credential-process` to
53+
maintain backward compatibility, and a warning will be issued to let the user
54+
know about that.
55+
56+
The `registry.credential-process` value will be used for all registries. If a
57+
specific registry specifies the value in the `registries` table, then that
58+
will take precedence.
59+
60+
The `credential-process` key accepts either a string containing the executable
61+
and arguments or an array containing the executable name and the arguments.
62+
This follows Cargo's convention for executables defined in config.
63+
64+
There are special strings in the `credential-process` that Cargo will replace
65+
with a given value:
66+
67+
* `{name}` — Name of the registry.
68+
* `{api_url}` — The API URL.
69+
* `{action}` — The authentication action (described below).
70+
71+
```toml
72+
[registry]
73+
credential-process = 'cargo osxkeychain {action}'
74+
75+
[registries.my-registry]
76+
credential-process = ['/path/to/myscript', '{name}']
77+
```
78+
79+
There are two different kinds of token processes that Cargo supports. The
80+
simple "basic" kind will only be called by Cargo when it needs a token. This
81+
is intended for simple and easy integration with password managers, that can
82+
often use pre-existing tooling. The more advanced "Cargo" kind supports
83+
different actions passed as a command-line argument. This is intended for more
84+
pleasant integration experience, at the expense of requiring a Cargo-specific
85+
process to glue to the password manager. Cargo will determine which kind is
86+
supported by the `credential-process` definition. If it contains the
87+
`{action}` argument, then it uses the advanced style, otherwise it assumes it
88+
only supports the "basic" kind.
89+
90+
## Basic authenticator
91+
92+
A basic authenticator is a process that returns a token on stdout. Newlines
93+
will be trimmed. The process inherits the user's stdin and stderr. It should
94+
exit 0 on success, and nonzero on error.
95+
96+
With this form, `cargo login` and `cargo logout` are not supported and return
97+
an error if used.
98+
99+
## Cargo authenticator
100+
101+
The protocol between the Cargo and the process is very basic, intended to
102+
ensure the credential process is kept as simple as possible. Cargo will
103+
execute the process with the `{action}` argument indicating which action to
104+
perform:
105+
106+
* `store` — Store the given token in secure storage.
107+
* `get` — Get a token from storage.
108+
* `erase` — Remove a token from storage.
109+
110+
The `cargo login` command will use `store` to save a token. Commands that
111+
require authentication, like `cargo publish`, will use `get` to retrieve a
112+
token. A new command, `cargo logout` will be added which will use the `erase`
113+
command to remove a token.
114+
115+
The process inherits the user's stderr, so the process can display messages.
116+
Some values are passed in via environment variables (see below). The expected
117+
interactions are:
118+
119+
* `store` — The token is sent to the process's stdin, terminated by a newline.
120+
The process should store the token keyed off the registry name. If the
121+
process fails, it should exit with a nonzero exit status.
122+
123+
* `get` — The process should send the token to its stdout (trailing newline
124+
will be trimmed). The process inherits the user's stdin, should it need to
125+
receive input.
126+
127+
If the process is unable to fulfill the request, it should exit with a
128+
nonzero exit code.
129+
130+
* `erase` — The process should remove the token associated with the registry
131+
name. If the token is not found, the process should exit with a 0 exit
132+
status.
133+
134+
## Environment
135+
136+
The following environment variables will be provided to the executed command:
137+
138+
* `CARGO` — Path to the `cargo` binary executing the command.
139+
* `CARGO_REGISTRY_NAME` — Name of the registry the authentication token is for.
140+
* `CARGO_REGISTRY_API_URL` — The URL of the registry API.
141+
142+
# Drawbacks
143+
[drawbacks]: #drawbacks
144+
145+
*No known drawbacks yet.*
146+
147+
# Rationale and alternatives
148+
[rationale-and-alternatives]: #rationale-and-alternatives
149+
150+
The solution proposed by this RFC isn't tied to any secret storage services and
151+
can be adapted to work with virtually any secret storage the user might rely
152+
on, while being relatively easy to understand and use.
153+
154+
# Prior art
155+
[prior-art]: #prior-art
156+
157+
Multiple command line tools implement this system or a similar one to retrieve
158+
authentication tokens or other secrets:
159+
160+
* [awscli][awscli] includes the `credentials_process` setting which calls
161+
a process with arguments provided by the user. The process is expected to
162+
emit JSON that contains the access key.
163+
* [Docker CLI][docker] offers "credential stores", programs the Docker CLI
164+
calls with specific arguments expecting JSON output. Implementations are
165+
provided for common storage systems, and the protocol is documented for users
166+
who want to integrate with their custom system.
167+
* [Ansible Vault][ansible] allows to specify an executable file as the
168+
decryption password, executing it when needed.
169+
* [Git] has a credential mechanism using store/get/erase arguments, and
170+
`key=value` parameters send and received with the process.
171+
172+
[awscli]: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sourcing-external.html
173+
[docker]: https://docs.docker.com/engine/reference/commandline/login/#credentials-store
174+
[ansible]: https://docs.ansible.com/ansible/latest/user_guide/vault.html#providing-vault-passwords
175+
[git]: https://git-scm.com/docs/gitcredentials#_custom_helpers
176+
177+
# Unresolved questions
178+
[unresolved-questions]: #unresolved-questions
179+
180+
*No known unresolved questions yet.*
181+
182+
# Future possibilities
183+
[future-possibilities]: #future-possibilities
184+
185+
To allow for a better user experience for users of popular secret storages,
186+
Cargo can provide built-in support for common systems. It is proposed that a
187+
`credential-process` with a `cargo:` prefix will use some internal support. For
188+
example, `credential-process = 'cargo:system-keychain'`.
189+
190+
Additionally, the community could create Cargo plugins that implement
191+
different storage systems. For example, a hypothetical Cargo plugin could be
192+
specified as `credential-process = 'cargo credential-1password {action}'`.
193+
194+
Encrypting the stored tokens or alternate authentication methods are out of the
195+
scope of this RFC, but could be proposed in the future to provide additional
196+
security for our users.
197+
198+
Future RFCs introducing new kinds of secrets used by Cargo (i.e. 2FA codes)
199+
could also add support for fetching those secrets from a process, in a similar
200+
way to this RFC. Defining how that should work is outside the scope of this RFC
201+
though.

0 commit comments

Comments
 (0)