Closed brainwane closed 4 years ago
If we want to roll out to TestPyPI first, we'll probably want to feature-flag this feature, which would be a bit of work.
It might be preferable to do a true "beta" on PyPI instead, with a limited group of users. This could just be a boolean field on the User
model that enables the feature, which we set in the admin UI. And we could just skip this check and enable it for all TestPyPI users, and eventually remove it once we've rolled everything out.
This could allow us to do a timeline like:
If things go awry during initial testing, is there any chance we will need to wipe tokens from users' accounts?
Possibly? I think we should be explicit in our "beta" announcement that this might happen, so users aren't surprised.
Are there particular categories of user we need to make sure we get in the beta test?
I think all the groups you listed would be ideal.
people who habitually block a lot of cookies/JS
This is a good point -- it looks pretty likely that our dependency on qrious
means that this won't work for users blocking JavaScript. We probably need to test this and add a fallback, or maybe revisit this dependency. (cc @woodruffw)
Is test.pypi.org the right place for this?
I think it is. We don't really claim that TestPyPI should be depended on for much.
If we want to roll out to TestPyPI first, we'll probably want to feature-flag this feature, which would be a bit of work.
It might be preferable to do a true "beta" on PyPI instead, with a limited group of users. This could just be a boolean field on the
User
model that enables the feature, which we set in the admin UI. And we could just skip this check and enable it for all TestPyPI users, and eventually remove it once we've rolled everything out.This could allow us to do a timeline like:
- 1 week period on TestPyPI for everyone
- 2 week beta on PyPI for select users
- Open to all users
+1 to this approach.
This is a good point -- it looks pretty likely that our dependency on
qrious
means that this won't work for users blocking JavaScript. We probably need to test this and add a fallback, or maybe revisit this dependency.
It should be relatively easy to add a fallback here: we can display the otpauth://
URL directly, and the user can copy-and-paste that into their TOTP application or use their QR generator of choice. Right now that URL is only available via the aria-label
of the canvas that we render the QR in, but that wouldn't be hard to change at all. (Sidenote: I'm not a frontend developer at all, so I have no idea how to check whether a user has disabled Javascript. Is it just <noscript>
?)
This could allow us to do a timeline like:
- 1 week period on TestPyPI for everyone
- 2 week beta on PyPI for select users
- Open to all users
This looks good to me!
@nlhkabu - does this feel ok to you? If so then I will declare consensus and start planning this, writing sets of "hey come test this" announcement text, etc.
Hi @brainwane - sounds very reasonable to me.
I'd like to run some direct user tests alongside open testing - sessions where I am able to observe users and understand anywhere they get stuck.
I've set up a Google form for registering users for this. @brainwane would you be able to:
To be clear, I don't think that manual user testing should block the launch of 2FA, unless I find any major technical issues that are not raised by open beta testing.
@nlhkabu One small comment on the form: might want to ask for their PyPI username as well, so we can enable 2FA for them during the private beta.
OK, draft announcements:
For distutils-sig/discuss.python.org and other longform places:
Subject line: PyPI two-factor auth (2FA) trial May 3-20
Dear PyPI users:
To increase the security of PyPI downloads, we're beginning to introduce two-factor authentication (2FA) as a login security option, and want project maintainers and owners to start testing it.
Starting this Friday, May 3rd, you'll be able to use 2FA on Test PyPI. And if you'd like to try 2FA on official PyPI, please fill out this Google form so we can invite you to the private beta, which we plan to hold 3-20 May.
PyPI currently supports a single 2FA method: generating a code through a Time-based One-time Password (TOTP) application. After you set up 2FA on your PyPI account, then you must provide a TOTP (along with your username and password) to log in. Therefore, to use 2FA on PyPI, you'll need to provision an application (usually a mobile phone app) in order to generate authentication codes; our our testing wiki page gives you suggestions and pointers.
This change only applies to the login step, not package uploads.
More details at our testing wiki page.
During this testing period, if things go awry, there's a chance we will need to wipe tokens from users' accounts, so if you choose to try it, please be forewarned. We suggest you make sure you have a PyPI-verified email address on your user account before trying the feature, to make potential account recovery smoother.
And please let us know if you run into glitches.
We expect to end this testing period on May 20th, then enable the optional 2FA feature for all PyPI users, and move on to working on WebAuthn support.
Thanks to the Open Technology Fund for funding this work. More progress reports at the Packaging Working Group's wiki page.
-the PyPI team
for pypi-announce:
Subject line: PyPI two-factor auth (2FA) trial May 3-20
Dear PyPI users:
To increase the security of PyPI downloads, we're beginning to introduce two-factor authentication (2FA) as a login security option, and want project maintainers and owners to start testing it.
Starting this Friday, May 3rd, you'll be able to use 2FA on Test PyPI. And if you'd like to try 2FA on official PyPI, please fill out this Google form so we can invite you to the private beta, which we plan to hold 3-20 May.
More details at our testing wiki page.
We expect to end this testing period on May 20th, then enable the 2FA feature for all PyPI users, and move on to working on WebAuthn support.
Thanks to the Open Technology Fund for funding this work. More progress reports at our wiki page.
-the PyPI team
(I need to add a note calling out that this doesn’t affect the upload endpoint currently.)
Looks good to me @brainwane - thank you. You can probably change this sentence to be shorter:
If you set up 2FA on your PyPI account, you must provide a second method of identity verification (other than your username and password) to log in.
First:
And if you'd like to try 2FA on official PyPI, please fill out this Google form so we can invite you to the private beta, which we plan to hold 3-20 May.
Should this say that it's really more of a user test? It's not just joining a beta, the plan is to sit with these folks for an hour, right? I eagerly clicked the link, then was disappointed since I didn't have the hour.
Second: Is there a plan to require 2FA eventually, or, if there isn't a plan, is it worth threatening that that might happen eventually? I think it makes sense to have 2FA for software repos like PyPi, but I know requiring it would be difficult. Maybe there's a middle ground to start pushing for.
@mlissner You don't need to be available for that hour to be in the beta, just respond "no". :)
I'm like 10% sure that changed? Anyway, good!
When we flipped the switch for this feature on Test PyPI & canon PyPI, we did not initially update the user model so that existing Test PyPI accounts had the feature on. Ernest (I believe) ran some SQL to update all the Test PyPI accounts yesterday and turn on the flag.
Right now, new Test PyPI accounts (as in, created since that time yesterday) do not have the flag set so they don't see 2FA in their account settings.
I'd like @ewdurbin to run the "turn this on for everybody on Test PyPI" SQL again just to get that taken care of, then figure out what to do next.
I think @di told me that turning it on by default for all Test PyPI accounts would also turn it on for all canon PyPI accounts (does this mean we ought to have a better feature flag system in general, or just for this feature?) (and I presume that constraint would also stop us from doing something to fix this like: verifying the email address triggers turning on 2FA support).
Reran the enabling of 2FA on test.pypi.org
Today's the 20th and it looks like we aren't done with the beta yet, because #5866 is a blocker.
Once we finish that, there are some UI issues that we should fix, but it's ok to roll out 2FA to everyone on pypi.org before fixing them.
And then a note for next time: let's address #5869 (or having "flip bit in user model" in the runbook for the WebAuthN rollout).
@ewdurbin @dstufft @di do we have any way of knowing how many users on PyPI have turned on 2fa? Is that even a useful statistic?
I naively assume that it would be, and that we would want the number to go up over time, and that we would want a rough estimation in particular of how many users have it turned on who own or maintain at least one package.
(I am ok with the answer being "it is a pain to check that" in which case I will probably ask that we check it, like, nowish and then again in a couple months, as we check how well our staging/rollout/publicity are going. I am of course also ok with the answer being "we cannot know that" or "it is a vulnerability to say this publicly" or what have you.)
warehouse=> SELECT ur.role_name,
warehouse-> Count(*)
warehouse-> FROM users AS u
warehouse-> JOIN (SELECT DISTINCT user_id,
warehouse(> role_name
warehouse(> FROM roles) AS ur
warehouse-> ON u.id = ur.user_id
warehouse-> WHERE u.totp_secret IS NOT NULL
warehouse-> GROUP BY ur.role_name;
role_name | count
------------+-------
Maintainer | 62
Owner | 214
(2 rows)
Update as of this time:
warehouse=> SELECT ur.role_name,
warehouse-> Count(*)
warehouse-> FROM users AS u
warehouse-> JOIN (SELECT DISTINCT user_id,
warehouse(> role_name
warehouse(> FROM roles) AS ur
warehouse-> ON u.id = ur.user_id
warehouse-> WHERE u.totp_secret IS NOT NULL
warehouse-> GROUP BY ur.role_name;
role_name | count
------------+-------
Maintainer | 88
Owner | 326
(2 rows)
@nlhkabu For the WebAuthn rollout, @ewdurbin suggested that we initially add a badge in the Account Settings marking the WebAuthn 2FA method as "beta". I've thus filed #5976 just now.
Update as of today:
role_name | count
------------+-------
Maintainer | 105
Owner | 446
(2 rows)
Per our conversation in the Friday meeting we plan to roll out the WebAuthn feature on test.pypi.org and on pypi.org with the "beta" badge (#5976) but without doing a bunch of messy bit-flipping. I've also updated https://wiki.python.org/psf/WarehousePackageMaintainerTesting so it'll be ready to publicize.
Working on copy for blog post(s), mailing list posts, social media, and so on.
OK, today we're rolling WebAuthn out as beta and asking a few people to test it, and in the afternoon we'll post blog posts and I'll notify distutils-sig et alia. Later this week I'll publicize it to some more communities, and then in maybe 10 days we'll remove the "beta" badge and I'll email pypi-announce and python-announce. Once that is done I figure we can close the issue.
@brainwane I've tried that on Test PyPI.
So I have a TOTP set up. I clicked on Add 2FA with security key
.
It prompted me to enter a Key name
which I did (Yubikey Neo
).
After that, clicking Provision key
does nothing visually. So I've opened DevTools.
I can see a successful GET request to https://test.pypi.org/manage/account/webauthn-provision/options with some JSON payload in the response. It looks legit, contains my user data and a challenge.
After clicking more times on that button, each of them produces an exception being logged to the JS console.
The same happenes on prod PyPI, in incognito mode, with browser extensions disabled.
Google Chrome Version 69.0.3497.81 (Official Build) (64-bit) running Gentoo Linux
[8] bind-modal-keys.js:43 Uncaught (in promise) DOMException: A request is already pending.
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ webauthn.js:179
(anonymous) @ webauthn.js:23
r @ runtime.js:55
(anonymous) @ runtime.js:293
t.(anonymous function) @ runtime.js:107
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
d @ raven.js:445
[2] bind-modal-keys.js:43 Uncaught (in promise) DOMException: The operation either timed out or was not allowed. See: https://w3c.github.io/webauthn/#sec-assertion-privacy.
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
Promise.then (async)
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ webauthn.js:179
(anonymous) @ webauthn.js:23
r @ runtime.js:55
(anonymous) @ runtime.js:293
t.(anonymous function) @ runtime.js:107
r @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
(anonymous) @ bind-modal-keys.js:43
d @ raven.js:445
Thanks for the bug report @webknjaz - I've turned that into #6050 so we can address it there.
We've had a few issues (such as #6106) due to a misconfiguration of test.pypi.org, and we're getting more user testing now thanks to outreach by @nlhkabu (such as this email to the Education SIG which I forwarded to Software Carpentry's discussion list). I'm going to wait till we have a few more data points from Nicole's testing, so we can fix issues that crop up. Then I'll call the beta finished and we should make further public announcements.
Thanks @brainwane. FYI, I have user testing scheduled most days in the coming week. The final session is scheduled for Tuesday 9th.
@nlhkabu ran user tests, summed up her findings in #6173, and we've now addressed those issues, yay! But we still have a few WebAuthn issues left in the milestone before I'll call the beta finished.
Once we approve #6084 we'll have one more auth feature -- API tokens -- to test via a beta period on test.pypi.org and pypi.org, so I'll keep this issue open to track that, then close it.
@ewdurbin May I ask:
@brainwane I'll query for those numbers before lunch, unfortunately we don't have this in Datadog at the moment, we'd need to implement a scheduled task to populate them... which perhaps is worthy of doing.
- how many users have turned on 2FA at all?
warehouse=> select count(distinct user_id) from (SELECT user_id::uuid from user_security_keys UNION SELECT id::uuid from users where totp_secret is not null) as a;
count
-------
1688
(1 row)
- how many users have a TOTP method provisioned, and how many have a WebAuthn key provisioned?
TOTP
warehouse=> select count(*) from users where totp_secret is not null;
count
-------
1632
(1 row)
WebAuthn
warehouse=> select count(distinct user_id) from user_security_keys ;
count
-------
132
(1 row)
- what proportion & number of logins over the past week were protected by 2FA?
This we may be able to get from Datadog...
- how many API tokens currently exist?
warehouse=> select count(*) from macaroons ;
count
-------
21
(1 row)
Past Week:
Percentage of Logins using Two-Factor Authentication: 7.71%
Edited Sept. 8th to start using the beta blockers milestone to track the stuff below.
Issues we need to resolve before ending WebAuthn beta:
Issue(s) we need to resolve before ending API token beta:
@token
username and pypi
prefixed #6345After the beta's done:
Percentage of Logins (to pypi.org via browser, I think) using Two-Factor Authentication:
May: 2.25% June: 3.08% July 1-21: 4.62% July 21-25: 9.72% (July 25th was our post to Discourse about API tokens) July 25-28: 15.45%
And, for logins in the past 2 days, it's 23.1%. That is mostly a weekend, so it'll be interesting to see whether the trend changes on weekdays...
Is there any plan for an API to create upload tokens? E.g. I'd like to have a command-line tool prompt me once for my password & 2FA code, then obtain and store a project-scoped token to use for uploads.
We have updated the token username and prefix in #6342.
username: @token
=> __token__
password/token: pypi:<base64 token body>
=> pypi-<base64 token body>
These changes should alleviate the need for escaping heroics.
The previous format will continue to work for now, but users will be notified to update their configurations to match the new syntax before the beta period is over.
Contractors on the OTF-funded work need to stop/deprioritize work on the security features in order to ensure we complete the accessibility and internationalization work by the end of the month. Therefore, even though some security features are still in beta, I'm closing the milestone.
I'm writing a discuss.python.org/distutils-sig post now to update our community on the end of the OTF-funded work. I'd like for us to have a few more beta blockers closed before we send out a pypi-announce email, to reduce how much time we and other volunteers spend on support issues; it's ok with me if that means the email doesn't go out for a few more weeks.
Update for today:
- how many users have turned on 2FA at all?
warehouse=> select count(distinct user_id) from (SELECT user_id::uuid from user_security_keys UNION SELECT id::uuid from users where totp_secret is not null) as a;
count
-------
3544
(1 row)
- how many users have a TOTP method provisioned, and how many have a WebAuthn key provisioned?
TOTP
warehouse=> select count(*) from users where totp_secret is not null;
count
-------
3398
(1 row)
WebAuthn
warehouse=> select count(distinct user_id) from user_security_keys ;
count
-------
336
(1 row)
- how many API tokens currently exist?
warehouse=> select count(*) from macaroons ;
count
-------
1127
(1 row)
Almost ready for API tokens to leave beta! Just waiting to land https://github.com/pypa/packaging.python.org/pull/687 .
@ewdurbin may I ask for an updated count, on how many users have turned on 2FA at all, and how many users have a TOTP method provisioned, and how many have a WebAuthn key provisioned?
My current draft of an announcement email:
Subject: Start using 2FA and API tokens on PyPI
Dear PyPI users:
To increase the security of PyPI downloads, we have added two-factor authentication (2FA) as a login security option, and API tokens for uploading packages.
If you maintain or own a project on the Python Package Index pypi.org , you should start using these features. Click "help" on PyPI for instructions.
Details and plans for the future:
2FA: PyPI's implementation of the WebAuthn standard means you can use any 2FA device that meets the FIDO standard. 2FA only affects logging in via a web browser, and not (yet) package uploads.
API tokens: use these (instead of username and password) to authenticate when uploading packages to PyPI. You can make tokens that work for all your uploads. You can also make tokens whose scope is limited to one specific package. That way, if a token is compromised, you can just revoke and recreate that token, instead of having to change your password in lots of automated processes.
For more details and instructions, click "help" on PyPI: https://pypi.org/help/ . (These features are also available on Test PyPI.)
In the future, PyPI will set and enforce a policy requiring users with two-factor authentication enabled to use API tokens to upload (rather than just their password, without a second factor). We do not yet know when we will make this policy change.
Thanks to the Open Technology Fund for funding this work. More work is in progress on pip and PyPI -- see https://wiki.python.org/psf/PackagingWG .
-Sumana Harihareswara on behalf of the PyPI team
Update as of this moment:
- how many users have turned on 2FA at all?
warehouse=> select count(distinct user_id) from (SELECT user_id::uuid from user_security_keys UNION SELECT id::uuid from users where totp_secret is not null) as a;
count
-------
5362
(1 row)
- how many users have a TOTP method provisioned, and how many have a WebAuthn key provisioned?
TOTP
warehouse=> select count(*) from users where totp_secret is not null;
count
-------
5090
(1 row)
WebAuthn
warehouse=> select count(distinct user_id) from user_security_keys ;
count
-------
571
(1 row)
- how many API tokens currently exist?
warehouse=> select count(*) from macaroons ;
count
-------
3232
(1 row)
I've made the launch announcement on pypi-announce and thus I now declare this issue closed. Thank you, everybody.
What's the problem this feature will solve? To finish #996 (see #5567), we need to test MFA with real users on real packages; asking them to spin up dev environments is too hard and won't help multi-maintainer projects reason well about what MFA policies they want to set up.
Describe the solution you'd like My tentative suggestion is:
Additional context
Cc @ewdurbin .