- if you don’t have access to a password manager
- impact on completion
- if you have access to a password manager
- using the secret in your command
- remove the secret
- using your own secret provider, or the built-in netrc one
There are plenty of use cases that need to use some secret value.
Let’s dig into how those are done in clk.
Because clk uses click, the most obvious way of using a secret value is to use the click way, with hide_input=True, confirmation_prompt=True, prompt=True
: This will ask twice for the password, hiding the input.
For the sake of the example, let’s imagine you create a tool that calls some http endpoint that requires a bearer token.
Something that would be called like this.
clk http --base-url http://somesite --bearer sometoken get /something
This translates into something that looks like the following python code.
class HTTPConfig:
pass
@group()
@option("--bearer", help="The bearer token to request the API", prompt=True, confirmation_prompt=True, hide_input=True, expose_class=HTTPConfig)
@option("--base-url", help="The url of the site to play with", expose_class=HTTPConfig)
def http():
"Commands to play with some OAuth 2 protected site"
@http.command()
@argument("path", help="The endpoint to call")
def get(path):
"Perform a get request from the site"
print(f"Calling {config.http.base_url}/{path} with bearer token {config.http.bearer}")
If you simply use that command, it will prompt you for the password. This may be not very practical if you are used to call this command pretty often.
To get more information about the pattern behind expose_class=HTTPConfig
, see the tutorial about dynamic parameters.
You may be tempted to add the secret in a simple parameter, like this.
clk parameter set http --bearer mytoken
New global parameters for http: --bearer mytoken
clk http --base-url someurl get someendpoint
Calling someurl/someendpoint with bearer token mytoken
This works, but it keeps the bearer token in plain text into the clk parameters. That is not ideal security-wise.
What we can do instead is use a special value for this parameter that tells clk to lookup the value in your password manager instead.
clk parameter set http --bearer noeval:secret:http_bearer
Removing global parameters of http: --bearer mytoken
New global parameters for http: --bearer secret:http_bearer
Here is some explanation:
- the
noeval:
prefix tells clk not to replace the following content, so that the secret is not actually looked up when you create the parameter. That way, the parameter will be the literalsecret:http_bearer
instead of its content, secret:http_bearer
tells clk to get the secret from your secret manager at the keyhttp_bearer
If the secret is not there, clk will fail and tell you that it could not find it.
clk http --base-url someurl get someendpoint
error: Could not find the secret for http_bearer
Now, there are two situations: either you have access to a password manager, or you don’t.
if you don’t have access to a password manager
If clk
needs to get access to a secret but you don’t have a password manager, it will fail1. But you can tell clk
to ask you for the password by providing the --ask-secret
flag.
clk --ask-secret http --base-url someurl get someendpoint
warning: Could not find the secret for http_bearer
Please provide the secret http_bearer:
Repeat for confirmation:
Calling someurl/someendpoint with bearer token test
As you can see, the error message became a warning message and it falls back on asking you to provide manually the secret.
If you don’t provide the same value, the program will continuously ask for it until you successfully provide the secret or stop it..
warning: Could not find the secret for http_bearer
Please provide the secret http_bearer:
Repeat for confirmation:
Error: The two entered values do not match.
Please provide the secret http_bearer:
impact on completion
The fact that a secret is missing should not impact the completion, hence if you try to complete the following, the code should provide the appropriate completion without failing.
clk http --base-ur<TAB>
--base-url
Even if you use --ask-secret
.
clk --ask-secret http --base-ur<TAB>
--base-url
if you have access to a password manager
If you have access to a password manager and want to use it, here is how you would proceed.
Every password manager has its particularities, therefore clk
does not try to guess where the secret is located. It uses keyring to deal with secrets and provides a few commands to lookup and store secrets using keyring.
To store a secret, use this command:
clk secret set http_bearer
As you can see, you don’t directly write the secret in the command line, for the same reasons explained above.
Let’s assume for the sake of this use case that you typed “mytoken” when prompted for a value.
Then, you can look up a secret with:
clk secret show http_bearer
http_bearer *****
The secret is not actually shown, to avoid secrets to easily show up when you don’t want them to. What this command tells is that a secret actually exists associated to that key. The only information that you have is that there indeed exists a secret with that name.
To have clk
show the secret, you need to add the --secret
flag.
clk secret show http_bearer --secret
http_bearer mytoken
It might look cumbersome to simply see this piece of information, but this is actually the point: avoiding leaking your password.
In case you want to provide this secret to another command and just want the secret, nothing more.
clk secret show http_bearer --secret --field secret
mytoken
Now, putting things together, the previous command will work as expected.
clk http --base-url someurl get someendpoint
Calling someurl/someendpoint with bearer token mytoken
using the secret in your command
This secret can also be used directly in the source code, using the get_secret
function from clk.
from clk import get_secret
@command()
def dosomething():
'Example of using secrets'
click.echo(get_secret('http_bearer'))
clk dosomething
mytoken
remove the secret
You can remove this secret now using the unset command. But beware that the command won’t work anymore as its parameter explicitly lookup for this secret.
clk secret unset http_bearer
Then, you can verify that the secret is gone.
clk secret show http_bearer
warning: No secret set
using your own secret provider, or the built-in netrc one
The secret feature of clk
is only a small wrapper on top of keyring. If you configured keyring to use a backend, it will be used in clk.
If you want to overwrite this in clk
, you can pass the import value in the --keyring
option.
For example, clk comes with a keyring to read secrets from netrc, implemented by the class NetrcKeyring
of the module clk.keyrings
.
Let’s try to use netrc to provide the secret. The built-in netrc keyring assume that the secret name is put in the field machine
of the netrc file and that the secret is in the field password.
Write this content in ~/.netrc
machine http_bearer
password thevalue
Then, you can tell clk
to use this keyring by providing the appropriate value to the --keyring
option.
clk --keyring clk.keyrings.NetrcKeyring secret show http_bearer --secret
http_bearer thevalue
Footnotes
1 this is so that if you use clk
in scripts, you will be aware that something went wrong