packaging.python.org icon indicating copy to clipboard operation
packaging.python.org copied to clipboard

Update distributing guide to prefer twine's keyring support, and discourage use of pypirc

Open theacodes opened this issue 8 years ago • 37 comments

This can't be done until this upstream issue is resolved: https://github.com/pypa/twine/issues/216 Original context: https://github.com/pypa/python-packaging-user-guide/pull/271#issuecomment-293357309

theacodes avatar Apr 19 '17 18:04 theacodes

I think this might actually be doable now. twine can't put a password in the keyring, but it can read one.

glyph avatar Oct 23 '17 06:10 glyph

@glyph what would it take for twine to be able to put a password in the keyring?

theacodes avatar Oct 23 '17 17:10 theacodes

@takluyver may know, as I believe flit already does this (at least on Linux).

ncoghlan avatar Oct 24 '17 07:10 ncoghlan

Yup, flit uses keyring on all platforms, so long as it's available (it's a soft dependency).

My strategy is to first try various means to get a stored or provided password. If none is found, we prompt the user, and then if keyring is importable, we store the password. You can see the code to do it here:

https://github.com/takluyver/flit/blob/8e1077c4d425239fea860b143714a2622c4d16db/flit/upload.py#L138-L168

The main UI weakness I'm aware of is that flit provides no mechanism to change the stored password; if you type it wrong, or change it later, you have to go through whatever mechanism the platform provides to change or remove it.

I hope that twine is storing the passwords in a similar way so that both tools can use one stored password.

takluyver avatar Oct 24 '17 08:10 takluyver

@ncoghlan after discussing with Sumana on IRC about the security implications of .pypirc, I strongly think that the tutorials should discourage the use of .pypirc altogether and we should work towards our tools having keyring support. Let me know if you have any thoughts/reservations.

theacodes avatar Dec 13 '17 19:12 theacodes

I think something like twine configure would be awesome.

dstufft avatar Dec 13 '17 20:12 dstufft

+1 from me for moving towards platform credential storage support. We have that freedom now that we're not dependent on the standard library for upload management.

ncoghlan avatar Dec 14 '17 06:12 ncoghlan

Here's a blog post I did on how to prevent .pypirc from being leaked for reference: http://mbacchi.github.io/2017/12/22/3-ways-prevent-secret-leaks-github.html

FYI @brainwane

mbacchi avatar Jan 03 '18 20:01 mbacchi

Thank you, @mbacchi! very helpful.

theacodes avatar Jan 03 '18 21:01 theacodes

I'm on Debian 9.3 (stretch, e.g., stable) and today I successfully made a username/password/website credential available for python-keyring to retrieve (so twine can use it when uploading a package).

I followed, basically, the instructions for Ubuntu 16.04 plus @jaraco's explanation in https://github.com/pypa/twine/pull/208#issuecomment-251584360 , and everything worked out. Starting in a Python 3 virtualenv, and eliding a lot of output and a test python session where I tested that I could set and get a credential, here's what I did:

$ sudo apt install python3-venv libdbus-glib-1-dev
$ pip install secretstorage dbus-python
$ pip install keyring
$ keyring set https://test.pypi.org/legacy/ brainwane
Password for 'brainwane' in 'https://test.pypi.org/legacy/': # [TYPE THIS IN. NOT ECHOED.]
$ python setup.py sdist
$ python setup.py bdist_wheel
$ twine upload --repository-url https://test.pypi.org/legacy/ dist/*
Uploading distributions to https://test.pypi.org/legacy/
Enter your username: brainwane
Uploading Forms990_analysis-1.0.0-py3-none-any.whl
Uploading Forms990_analysis-1.0.1-py3-none-any.whl
Uploading Forms990-analysis-1.0.0.tar.gz
Uploading Forms990-analysis-1.0.1.tar.gz

My opinion: we should put a variant of this into the uploading tutorial and the guide to using Test PyPI, with adjustments for the user's OS.

brainwane avatar Feb 01 '18 22:02 brainwane

I want to reiterate a comment I made here:

But arguably this isn't worth the effort, since the default access control granted by keyring is that any other Python application may access it (via keyring.get_password) and there doesn't seem to be a way to scope this just to twine (but perhaps I'm missing something).

To be more explicit, the following lines (run anywhere, not just from within twine) would give @brainwane back the password in plaintext:

>>> import keyring
>>> keyring.get_password('https://test.pypi.org/legacy/', 'brainwane')

I don't really see the value in putting this in the guide unless it's at least marginally more secure that keeping a password in ~/.pypirc, which it seems like it's not (but again, totally possible that I'm missing something here).

EDIT: Perhaps this is all understood, and it could be argued that using the keyring over ~/.pypirc helps prevents the leaking of passwords, so maybe it is worth doing after all. Just wanted to raise that point. 🙂

di avatar Feb 01 '18 22:02 di

It doesn't protect against untrusted code running in your user account, but storing the password in the keyring is an improvement in other ways:

  • If your computer is stolen, the thief can't access the keyring data unless they know your login password - the keyring is encrypted even if you haven't set up an encrypted filesystem (AIUI).
  • Your .pypirc file might be readable by other users who can log on to the same computer, if the permissions are not set correctly. The guide currently describes creating this file with no mention of setting its permissions, and at least on Linux the default file permissions allow anyone to read it.

There's arguably also a psychological reason: putting PyPI passwords in a plaintext config file signals that they're not that important. Using a specific mechanism for storing secrets would suggest that PyPA thinks how you store your password matters.

We definitely shouldn't see using keyring as the end of the road on this. Glyph has done some work on getting the credentials from a password manager, for instance. And hopefully Warehouse will add features like tokens to upload a specific project. But if we can recommend keyring rather than storing passwords in .pypirc, I think that's a step forwards.

takluyver avatar Feb 01 '18 23:02 takluyver

All great points @takluyver. Consider me convinced, and thanks for laying out an explanation.

di avatar Feb 01 '18 23:02 di

We'll be publicizing pypi.org widely probably within the next two weeks, and I would very much welcome help on this, so the packaging guide (which we link to consistently within https://pypi.org/help/ and elsewhere on the site) mentions keying as a preferred credential store.

I've just put out a new release of twine and would be happy to put out a point release that includes command-line help with keyring plus guidance in the README (per pypa/twine#277). Then we could update the packaging and distribution guide, remove or move the .pypirc guidance, and point to the twine/keyring documentation.

brainwane avatar Mar 08 '18 22:03 brainwane

I'm working towards getting Twine 1.10.1 out in the next few days and would strongly welcome doc improvements related to keyring to get into that release!

brainwane avatar Mar 15 '18 01:03 brainwane

Twine 1.11.0 is now out and includes guidance in the README for using keyring.

@jonparrott and I have, in our experience, run into hiccups in getting keyring support to work well on Linux. We'll file bugs appropriately. Once the user experience is smoother, then the packaging/distro guide should replace .pypirc recommendations with keyring recommendations.

brainwane avatar Mar 20 '18 20:03 brainwane

Hey all, I'm a bit new to python packaging and have a general question about this particular bit of the docs, no idea about the best way to ask but this issue seems to be the most relevant place

The docs mention that one should install twine and run twine upload --repository-url https://test.pypi.org/legacy/ dist/* - but setup has this functionality built in, doesn't it? What's the difference between the twine command and python setup.py bdist_wheel (or any other preferred versions) upload -r https://test.pypi.org/legacy/? Is it mostly that twine has better authentication tooling?

I was thinking I'd pop over here and make a PR to add the setup-upload info to the docs and found this thread, so I hope it doesn't hurt to ask

asyncjake avatar Jun 29 '18 22:06 asyncjake

@jaktonik Depending on exactly which Python environment you're using, the native distutils upload may happen over HTTP, exposing your PyPI password as cleartext. There's also some newer metadata fields which distutils on its own won't extract and publish correctly, but twine makes sure are handled.

We should add that info as a footnote on https://packaging.python.org/guides/tool-recommendations/, perhaps by linking out to https://twine.readthedocs.io/en/latest/#why-should-i-use-this

ncoghlan avatar Jun 30 '18 05:06 ncoghlan

Hello wonderful people! 👋

I feel like the section in the python packaging guide about storing your password in plaintext in pypirc should be removed immediately. It's a separate issue of if twine keyring support is ready for everyone, or if alternative instructions are ready.

https://packaging.python.org/guides/using-testpypi/?highlight=username#setting-up-testpypi-in-pypirc

The note about setting permissions 'properly' is also not good advice. Instead it should just say Do not store passwords in the pypirc file. Storing passwords in plain text is never a good idea., and not have instructions on how to store plain text passwords in files.

The important part is removing the recommendation on storing passwords like this as soon as possible.

illume avatar Aug 17 '18 08:08 illume

Here's where the python docs talk about storing the password in pypirc: https://docs.python.org/3/distutils/packageindex.html#the-pypirc-file

illume avatar Aug 17 '18 08:08 illume

Hi @illume, feel free to see a pull request!

theacodes avatar Aug 17 '18 16:08 theacodes

Thanks @theacodes :)

illume avatar Aug 17 '18 16:08 illume

The Python docs now no longer mention .pypirc, yay! Thanks @kojoidrissa!

I'm having trouble figuring out when, but someone removed the .pypirc text from source/tutorials/packaging-projects.rst -- but it doesn't explicitly mention the keyring, so I think this issue is not yet resolved.

brainwane avatar Oct 08 '19 11:10 brainwane

@brainwane

I think the relevant file is source/guides/distributing-packages-using-setuptools.rst. It still mentions .pypirc and suggests creating a file with the user's password in plain text.

https://github.com/pypa/packaging.python.org/blob/c8339b381d0446b1c73aa94689869266beb0d334/source/guides/distributing-packages-using-setuptools.rst#create-an-account

pradyunsg avatar Oct 08 '19 15:10 pradyunsg

Checking in on the desired outcome of this issue. I've been passively tracking issues related to twine and credentials (most recently https://github.com/pypa/packaging-problems/issues/313). My sense is that the documentation around .pypirc, API tokens, and keyring is fragmented and inconsistent. I searched this repo, warehouse, twine, and packaging-problems for mentions of those items; my findings are below.

Here is a list of possible next steps, which I hope prompts folks with opinions to chime in. :)

  1. Document the current commands necessary to use twine, keyring, and API tokens
    • Not sure if that belongs in this repo (as suggested by this issue), or twine (as suggested by https://github.com/pypa/twine/issues/11)
  2. Document the format of .pypirc, including an example that relies on keyring and API tokens
    • Probably in this repo
  3. Update the docs in this repo, warehouse, and twine to be consistent with and reference the new documentation
  4. Streamline the process of configuring credentials for twine

The entry point for help using API tokens seems to be https://pypi.org/help/#apitoken (source), or the token management page (source). Both suggest storing the token in .pypirc; the former links to https://packaging.python.org/guides/distributing-packages-using-setuptools/#create-an-account, which provides this warning:

Be aware that this stores your token in plaintext.

Compare that to https://packaging.python.org/guides/using-testpypi/#setting-up-testpypi-in-pypirc:

Do not store passwords in the pypirc file. Storing passwords in plain text is never a good idea.

Reading over this issue, it seems the preferred solution is to use keyring. However, keyring doesn't seem to be documented in this repo or in warehouse. It's introduced at https://twine.readthedocs.io/en/latest/#keyring-support. I haven't seen any mention of how to use keyring with API tokens (though I suspect it'd be to use __token__ for the username).

Furthermore, there doesn't seem to be centralized documentation of .pypirc. In https://github.com/pypa/twine/pull/340/, there was an attempt to add it to the twine docs, but that was closed in favor of adding it to this repo.

bhrutledge avatar Jan 20 '20 19:01 bhrutledge

Related: https://github.com/pypa/packaging.python.org/issues/628 and https://github.com/pypa/twine/issues/496 re: project-scoped API tokens. The suggestion seems to be to configure "proxy" repositories for each project, with distinct tokens.

I haven't investigated if/how those are supported by twine when using keyring. Maybe @jaraco has a quick answer?

bhrutledge avatar Jan 20 '20 20:01 bhrutledge

I haven't investigated if/how those are supported by twine when using keyring.

I did some investigation, and it's not obvious how to use multiple project API tokens with twine and keyring: https://github.com/pypa/twine/issues/565.

bhrutledge avatar Jan 26 '20 12:01 bhrutledge

The twine/keyring integration assumes one password per user and index. What keyring needs to support multiple credentials is some way to distinguish the tokens by project name. Perhaps instead of relying on __token__ for the username, it should use PyPI token for {project} and fallback to PyPI token (but still transmit __token__ during the upload). Other syntax is possible, like __token__/{project}.

Per-project credentials wasn't considered in the original design, so a new design is called for.

jaraco avatar Jan 26 '20 18:01 jaraco

For flit, I was planning to stuff the project name associated with a token into the 'username' for keyring, e.g. token:{project}. I was assuming this would not be visible to users in normal operation. It would be good if flit and twine can settle on a common convention for this.

I know the secret storage protocol offered by Gnome allows for arbitrary key-value metadata associated with a secret, which can be used to look it up. In principle, this would be a neat way of storing a token scope, but it would only work for keyring if there are similar mechanisms on all supported platforms.

takluyver avatar Jan 26 '20 18:01 takluyver

+1 to having a common scheme shared by all tools. Just please don't require the 'project' to be specified. For a person managing a single project or two, that's fine, but where many projects share a token, you want keyring to store that credential exactly once. Perhaps token[:designator] where the user can provide an optional designator for the token (or none at all for the sole or primary token).

jaraco avatar Jan 26 '20 19:01 jaraco