-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ClientsCertificate feature spec #8823
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for creating the spec. I really appreciate the effort put into this non-trivial and important feature.
I had a bit of a look, and please keep in mind that it's early in the morning here so maybe I'm not quite fully awake yet.
I think there are two things missing from the spec.
-
Are HTTPS client certs per-source, or sent to all sources automatically, or only sent after getting a 401, like the current auth workflow? What happens if I have two sources that each need a different https client cert?
-
Pretty much everything in the
nuget.config
file can be edited withnuget.exe
commands, for examplenuget config -set ..
,nuget sources add ..
. This spec should propose commands to add, list, edit and delete client certificate config.
designs/NuGet-ClientCertificates.md
Outdated
Can be configured with 2 Add item's and item text body | ||
|
||
- Key="**Path**" Value="Absolute or relative path to certificate file". Relative path resolve have 2 stages: first relative to configuration file origin, second relative to current application directory. | ||
- Optional Key="**Password**" Value="Encrypted password". Password for certificate file. Encrypted in same manner as PackageSourceCredential password. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the certificate needs a password or hardware key to unlock, and the password is not specified in the config? Will entering the password be supported by an interactive mode restore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current implementation will fail with error on certificate load. It is bit complicated to me introduce such logic with out help.
designs/NuGet-ClientCertificates.md
Outdated
|
||
## Configuration components implementation | ||
|
||
Added several new SettingItem's to parse configuration above. Certificates search logic encapsulated inside. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementations are supposed to implement a spec, not the other way around. Either this is too specific information for a spec, or if the point is to propose the new APIs, then it should read like a proposal and if it changes, the PR will have to change to meet the spec.
designs/NuGet-ClientCertificates.md
Outdated
|
||
## NuGet Cli implementation as example | ||
|
||
1) Internally certificates extracted from configuration items on base [Command.Execute](https://github.com/NuGet/NuGet.Client/blob/d4f53c3e523493fcbe35c537cb004e9a3e228abd/src/NuGet.Clients/NuGet.CommandLine/Commands/Command.cs) with `ClientCertificateProvider.Provide` method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, the spec is supposed to be the "source of truth", so if there's a bug, or if someone decides to rewrite the client from scratch, the specs should contain the information required. The spec shouldn't refer back to implementations, or PRs, to specify how the feature works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plaese, look at new version. Is it better?
Added clarification for fromCert body certificate format
Misspell fix
Changed configuration accordingly to implementation changes
From my experience and tests HttpClientHandler internally pick up most suitable client certificate. Ignores them if client certificate not required. |
Inline PEM certificates description change
Extended and reworked a bit
@zivkan added implementation. Described new command in new version of spec |
designs/NuGet-ClientCertificates.md
Outdated
|
||
## NuGet configuration changes | ||
|
||
Provide new configuration section `clientCertificates` which may have children of 3 types: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was busy last week and didn't have a chance to look at this again, but I quickly checked the existing client credentials configuration, and think client certs could fit in there:
<configuration>
<packageSourceCredentials>
<source1>
<add key='UserName' value='test_user' />
<add key='ClearTextPassword' value='test_password' />
</source1>
<source2>
<add key='CertStore' value='cert:\CurrentUser\My' />
<add key='FindByThumbprint' value='4894671ae5aa84840cc1079e89e82d426bc24ec6' />
</source2>
<source3>
<add key='CertFile' value='certificate.pfx' />
<add key='ClearTextPassword' value='...' />
</source3>
<source4>
<add key='CertPEM' value='-----BEGIN CERTIFICATE-----
MIIEjjCCA3agAwIBAgIJIBkBGf8AAAAPMA0GCSqGSIb3DQEBCwUAMIGzMQswCQYD
...
tJl1UvF7GWJd0yNyPVqCCnBY
-----END CERTIFICATE-----' />
</source4>
</packageSourceCredentials>
</configuration>
I don't think we have a schema for the nuget.config file, but if we did, this would avoid needing to change it. Certs that need passwords could use the existing password and clear text password to specify the cert password. There would be no ambiguity about when different sources need different certs (and it will be easier to avoid "leaking" cert info to sources that don't need client certs).
I think this would be a better user experience. @nkolev92 @anangaur @rrelyea what do you think?
@BlackGad FYI, the NuGet client team had a first spec review about this. As is common in my experience with the first review of any spec, we came away with more questions than answers. If you'd like to be part of future spec reviews, let me know and we'll try to include you. Keep in mind the client team is on the west coast of the US, so any meetings will be during business hours there. Also many people take vacation in December (myself included), so there may be additional delays during the next month. The key points from the meeting:
I need to contact internal security experts to find out their opinions/recommendations about HTTPS client certificates in general. Some specific points to check: Is it acceptable to store private key passwords in configuration files (encrypted only, or plain text ok?). Are embedded PEM certificates ok? (someone believes that Azure used to use PEM certifcates for auth, but stopped. need more details if it's security related)
There was a preference for re-using the
Need to support interactive mode for command line operations, for everything that already supports interactive mode (restore/push. maybe dotnet add package?). Need to consider whether NuGet's credential plugin model should also support https client certs, and if so how it all works. For example, if a Windows Hello PIN certificate is used, does the credential provider pass the PIN to NuGet? If face unlock, or hardware 2FA is needed, how does it all work?
if we go ahead with using |
First, thanks for your response, appreciate it :)
My opinion is that
Can you specify the approximate time of reviews?
Client certificates required for Also I insist on generic client certificate configuration (not linked to concrete source). In case of linking - NuGet configuration structure and internal implementation complexity will be increased. HttpClientHandler already has logic (has stood the test of time) for mapping client certificates to appropriate endpoints. Example:
So in case of client certificate linked to specific source you need to specify it 2 times. Or to simplify configuration create same thing that NuGet already has with package source configs (sections I think configuration must be simple and understandable. Additional linked config sections contravene to this. |
It can be, but not always. Before joining the NuGet team I worked on an application using a microservice architecture and HTTPS client certificates were used for both securing the channel and authentication (cert thumbprint gets looked up in an auth database/service to ensure it's whitelisted, but also to find client/user claims, which is used for authorization to certain APIs). Additionally, Azure Active Directory's docs has a section about certificate based authentication. I'm not 100% sure, but I think TFS/Azure DevOps allows this as a mode of authentication, not just transport security. Finally, one customer in the NuGet/Home issue about adding this feature used the word authentication numerous times. While it wasn't clear that they really meant user auth, it also wasn't clear that they only meant TLS. My experience with client certs has been related to client auth, not extra security on the transport layer, so I'm not comfortable making this a solely transport layer concern. One of the former members of the Artifacts Credential Provider asked to be informed when the feature is complete, so clearly they intended to use it as part of the authentication flow (but possibly just for transport security, there wasn't enough information). In an internal email someone from the same team asked if credentials would even be needed if nuget supported mutual ssl, hinting that they might use client cert for auth.
We'll try to be flexible, but we guess you're in Europe, which means that our morning corresponds to your evening. I think a reasonable number of people might be willing to do a 9am meeting, but I'm not confident that we'll get acceptance for earlier meetings. However, I'll be in Australia all December, and there's no way we can get a suitable time for all 3 locations.
Customers generally don't care about difficulty in implementing when the experience is poor. If we have multiple options with similar customer experiences, then we can decide based on which is easier to implement, but we shouldn't reject good user experiences just because it might be difficult to implement.
This is already true for credentials, and I don't remember seeing complaints. nuget.config supports environment variable substitution, so if your concern is having to change values in multiple places when a cert gets renewed, there's already an option to reduce duplication. |
Ok, finally I understood your complaint. I was care about transport security and didn't care about mutual SSL auth. You want to support BOTH features :)
Current solution implements FIRST feature. To support both I purpose:
<configuration>
<packageSourceCredentials>
<source1>
<add key='Certificate' value='<id of client certificate from clientCertificates section>' />
</source1>
</packageSourceCredentials>
</configuration> Logic for cases
|
I was trying to create a sample app to validate, but I couldn't get kestrel and httpclient to talk to each other with a client cert. Anyway, my concern about keeping two configurations, one for transport security, another for mutual auth, is that it implies that the two can be used together (one cert for TLS, another for auth). However, the HTTPS protocol only allows a single client certificate to be used (the cert might be in a chain, but two independent certs cannot be used in a single conection). Although HttpClientHandler has a certificate collection, if you look at the APIs to validate the client certificate on Kestrel, you'll see the API only provides a single certificate (and its chain). So, while keeping the TLS and certificate auth sections separate has the advantage of avoiding needing to repeat the cert information when using multiple sources that use the same cert, it has the disadvantage to being confusing to people who don't know the HTTPS protocol well. Since I was unable to get a demo app working, I wasn't able to test what actually happens, but it looks to me that HttpClient might always use the first certificate, in which case your proposal wouldn't work when using two or more sources that need different certificates. A better case would be that HttpClient tries them one-by-one until one succeeds, but this has multiple potential problems:
If client certs is only in a per-source configuration, the only disadvantage I can think of is there's less duplication when using multiple sources that use the same cert. I'm getting on a plane soon, so I don't have time to dig into this further now, but so far I'm unconvinced that a global TLS client certificate configuration is a good idea. |
I'm the security person mentioned. I have a concern around the inline certificate as a parameter - this seems to encourage checking the certificate into source control, which is something we'd want to avoid. Given you already have a parameter to use a file in one of the standard formats I am unsure why an inline parameter is necessary, given the risk. As for separating out certificates, one for HTTPS and one for Auth, that's not really how it works. Certificates are sent during the HTTPS handshake, you don't get to swap them over after that's taken place. You only have one chance to set a certificate. I wouldn't ever rely on HttpClient selecting the right certificate, if certificate auth is in place it should be an explicit selection, rather than hoping the correct one is used. As for leakage of certificates, that is a concern considering a certificate identifies a user, so it's PII, and now we have GDPR considerations. Certificate parameters should be limited to a single server and not sent to multiple servers by default. A parameter to say "I don't care, splash my certificate everywhere" might be acceptable if it's not on by default. |
Ok, so conclusion is: inline certificates definitions must be removed and certificate automatic spreading denied. Am I right? If so, I think,
<configuration>
...
<clientCertificates>
<fromStorage name="unique name 1">
<!-- Require at least one reference to source -->
<add key="TargetSource" value="<source name from packageSources section>" />
...
<!-- Optional. CurrentUser by default -->
<add key="StoreLocation" value="CurrentUser" />
<!-- Optional. My by default -->
<add key="StoreName" value="My" />
<!-- Optional. FindByThumbprint by default -->
<add key="FindType" value="FindByThumbprint" />
<!-- Required. -->
<add key="FindValue" value="4894671ae5aa84840cc1079e89e82d426bc24ec6" />
</fromStorage>
<fromFile name="unique name 2">
<!-- Require at least one reference to source -->
<add key="TargetSource" value="<source name from packageSources section>" />
...
<!-- Absolute or relative path to certificate file. -->
<add key="Path" value=".\certificate.pfx" />
<!-- Encrypted password -->
<add key="Password" value="..." />
</fromFile>
</clientCertificates>
...
</configuration>
|
That would certainly make me happier :) |
I've updated spec accordingly to latest discussion. Real implementation will be fixed after final spec approving. |
Sorry about the delay for the last month. I don't believe it's an issue any longer since the design changed, but I was concerned that the original proposal to add multiple certs to the I'll talk to the PMs over the next week to see if we can get the changes to the config file and the CLI arguments approved, so that the implementation can be unblocked. |
No it is not critical :) one certificate search can be specified to multiple sources (I definitely will use such case). It is common that one search may return several valid certificates which differs only by expiration date. Certificates usually renews by domain policies when them still valid (pc cert storage). That's why it is bad idea to artificially limit search count per one source. So in real life I see it like NuGet configuration under source control inside project which points to search certificate from pc or user cert storage (pc in active directory) and backup local certificate file search for adhoc work (domain controller is unavailable do the different circumstances for a while) |
@BlackGad in case the email address you used in your commit messages is not correct, please contact me (find my email address in any of my commit messages). I scheduled a spec review meeting and sent an invite to what I hope is your email address. |
Hope will join the meeting |
We had another meeting for this spec. Special thanks to @BlackGad for joining. Here are the follow up items:
@BlackGad explained his preference about being able to use a single config which "just works" whether at work (where a cert is available in the store), or at home (where a personal certificate file is available on disk, at a location ignored by source control, so that it isn't checked it). @zivkan proposed that instead it should be a one-time configuration in the user profile. A developer's work & home machine can then have different user profile configurations, removing the need to use a single configuration that specified multiple certificates, and therefore each source can have just a single certificate defined.
edit: forgot one
|
designs/NuGet-ClientCertificates.md
Outdated
|
||
- `-Path` `string` - Path to certificate file added to a file client certificate source. | ||
|
||
- `-TargetSource` `string` - Required semicolon separated option to specify NuGet sources on which client certificate will be applied. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't think of it during the meeting, but one problem with using semicolons on a CLI is that some shells use the semicolon to separate multiple commands written on a single line. Therefore, if we use semicolon as the separator, it increases the risk that customers will accidentally run unintended commands. While the commands would likely fail and therefore not have a large risk of causing damage, it might be better to change the design to use another separator that reduces the risk of bad commands being run accidentally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extend client-certificates command with additional required -TargetSource parameter (same multivalue behavior as -CertificateFingerprint has).
It is not mine invention )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can a source name contain a semicolon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depends on how NuGet internal NuGet.exe command line parser works. Just reused existing API.
Here's a little pros and cons table I came up with to compare allowing a single certificate to be assigned to multiple sources vs requiring each source to have a single certificate definition, even if it's to a certificate used by another source.
Regarding the second scenario when a cert changes, a simple find and replace makes it easy to update a configuration file that lists a single certificate multiple times. Even if the customer is using a CLI to modify the command, they can use their shell's command buffer to go back to the last command and modify the source name, without having to re-type the whole command. I personally don't think this is much of a downside, especially considering how infrequently certs usually expire. After writing this comment, honestly I feel more strongly that a single certificate definition per source design is better, as I think we could simplify some things, mainly the CLI commands to modify the config file. |
Updated -Check parameter description
I think it's better to have a single certificate for a source.
|
@heng-liu you mean attribute in package source(1) or client certificate configuration attribute (reference to source)(2) In case of 1: <configuration>
...
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Contoso" value="https://contoso.com/packages/" clientCertificate="<unique name 1>" />
</packageSources>
<clientCertificates>
<fromStorage name="unique name 1">
...
</fromStorage>
</clientCertificates>
...
</configuration> In case of 2: <configuration>
...
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Contoso" value="https://contoso.com/packages/" />
</packageSources>
<clientCertificates>
<fromStorage name="Contoso">
...
</fromStorage>
</clientCertificates>
...
</configuration> From this 2 I prefer second one (because of separated configuration that will not affect existing In general as far as I see NuGet team leaning towards one certificate per one source solution. For me it is not crucial to have one to many relations :) So will change specification a bit to satisfy requirements. |
I have updated specification accordingly to our latest discussion. Please take a look when will have time. |
The spec should call out error scenarios and the associated error messages. |
Just had another meeting with @karann-msft and @rrelyea. What we'd like to see before signing off on the spec:
<clientCerts>
<storeCert packageSource="Contoso"
storeLocation="CurrentUser"
storeName="My"
findBy="thumbprint"
findValue="4894671ae5aa84840cc1079e89e82d426bc24ec6" />
<fileCert packageSource="Foo"
path=".\certificate.pfx"
password="..." />
</clientCerts> Key difference that I remember I doing:
|
I already reacted on the comment, but wanted to write a message of approval too :)
|
Spec was updated. Some remarks to @zivkan comment:
One configuration is fine but it can return several valid certificates accordingly to search criteria. And it is valid situation in case of overlapped expiration date strategy. So it is reasonable to use any (first) certificate from search.
Imagine that domain controller fail and automatic client certificate enrollment did not happened. You have PC configuration with additional package source (secured with certificate ofc). Now imagine you want to create new project which will have no dependencies from custom packages (only official one). Studio will show error end deny any future development? It is better to warn user not deny.
Disagree. On initial PC setup we use batch commands with sequential choco nuget installation and nuget PC configuration. Admin does not care where NuGet physically stores its configuration file, he wants setup environment via console with batch commands. Also new PC can be connected to domain after initial configuration where automatic certificates enrollment feature active. Personally I expecting warning message that certificate does not exist rather than error message and link to huge tutorial where you describe physical nuget configuration file location on selected environment (windows/linux/per user) and big configuration file syntax specification. |
Also like new configuration syntax you purposed 👍 |
There's two things here. First, your question is does NuGet fail a restore when one source cannot be contacted, even if no packages are needed from that source? The answer is yes, that's how it works right now. There is an issue about it, but it's out of scope of this spec to change that behaviour. So, given we know the restore is going to fail anyway, failing fast minimises the chance that the customer walks away from the computer after restore starts (huge solutions can have hundreds of packages and take minutes to complete), only to come back to find it failed, so they have to fix the issue and run another restore. Failing fast is a common pattern in systems development.
The CLI tools are primarily aimed at interactive usage. Working in scripts is a good idea, so I'd be more open to a @karann-msft @rrelyea what do you think? |
Purposing name this flag |
Updated accordingly to discussion |
@NuGet/nuget-client last chance to review otherwise I'll merge it after giving it another pass myself. |
I'll take a look today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful. 👏
Thanks @BlackGad
Ok, I will start new implementation accordingly this specification. It will be easier to start from scratch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BlackGad thank you for your patience while we got the spec sorted out.
Also, just a little reminder that @rrelyea recently checked in some changes to make dotnet nuget [add|remove|...] source
, which I believe also affected nuget.exe. So, the way to add commands to both the dotnet cli and nuget cli will be different now compared to your previous efforts. Hopefully it's now easier to get new commands to work in both places.
Added draft specification accordingly to request