-
Notifications
You must be signed in to change notification settings - Fork 2
/
windows-client-configuration.html
273 lines (208 loc) · 16.8 KB
/
windows-client-configuration.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
<!DOCTYPE html>
<html>
<head>
<title>Secure WinRM - Windows Client Configuration</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="css/manual.css">
<script language="JavaScript" src="scripts/TextSelection.js"></script>
</head>
<body>
<p>Status: <strong>DRAFT</strong></p>
<h1>Windows Client Configuration</h1>
<p>The steps that must be run on the Windows client must be run by the user(s) that need to connect to the server.</p>
<div class="windows-client">
<h2>Making Sure the Server Hostname and Server Certificate Subject Match</h2>
<p>It is important that the server hostname that you use matches the subject of the certificate. If they don not match
the client will refuse to make a connection.</p>
<p>If the server hostname cannot be mapped to an IP address by the DNS or WINS server on your network, you need to add
a mapping to an IP address to <code class="path">C:\Windows\System32\drivers\etc\hosts</code> manually. This file
can only be changed by an administrator.</p>
<h2>Ensuring the Client Uses Secure Authentication Methods</h2>
<div class="important-for-security">
<div>
<p>This step changes machine-global settings that affects all users of the machine. Make sure you do not break
somebody else's setup.</p>
</div>
</div>
<p>
<a href="https://msdn.microsoft.com/en-us/library/aa384372(v=vs.85).aspx#WinRM_Client_Default_Configuration_Settings">By
default almost all authentication methods are enabled for the WinRM client</a>. Basic authentication sends the
password to the server, which is always undesirable as a malicious or hacked server can use the password for other
purposes. Credential security support provider (CredSSP) authentication behaves similarly to basic authentication
and <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb931352(v=vs.85).aspx">will send your
credentials to the server</a>, so it should be left disabled if possible. Digest authentication <a
href="https://en.wikipedia.org/wiki/Digest_access_authentication#Disadvantages">does not provide the strong
security of certificates</a> and is not supported by (modern) WinRM servers.</p>
<p>The minimal set of authentication methods to enable is certificate and negotiate authentication. Certificate
authentication is needed to allow clients to authenticate using certificates. Negotiate authentication is needed to
be able to (amongst others) configure WinRM using the <code class="command">winrm</code> command.</p>
<p>If you have an environment with Active Directory Domain Services, you very likely want to leave Kerberos
authentication enabled.</p>
<p>The settings can only be changed if the WinRM service is running. Check if it is already running with the following
command:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wccc-gws')" class="copy-to-clipboard">PS > <span
id="wccc-gws">Get-Service WinRM</span></span></pre>
<p>If the WinRM service is not running, start it using:</p>
<div class="lock-container">
<div class="lock-powershell"></div>
<pre class="powershell"><span onclick="selectTextOfElement('wccc-sws')" class="copy-to-clipboard">PS > <span
id="wccc-sws">(Get-Service WinRM).Start()</span></span></pre>
</div>
<p>Now set the authentication that the client is allowed to use:</p>
<div class="lock-container">
<div class="lock-powershell"></div>
<pre class="powershell"><span onclick="selectTextOfElement('wccc-scam')" class="copy-to-clipboard">PS > <span
id="wccc-scam">winrm set WinRM/Config/Client/Auth '@{Basic="false";Digest="false";Kerberos="false";Negotiate="true";Certificate="true";CredSSP="false"}'</span></span></pre>
</div>
<h2>Install Certificate Authority Certificate of the Server</h2>
<div class="important-for-security">
<div>
<p>In this step the server certificate is retrieved, but it cannot be verified using the existing known
certificate authorities. Verify the thumbprint manually to ensure that you are installing the correct server
certificate as a certificate authority. After installing the certificate as a certificate authority, all
certificates signed by that certificate will be accepted without user notification or confirmation.</p>
<p>Note that the installed certificate is not the only certificate that can identify the server hostname. If
somebody can get a certificate authority (that is installed in the client certificate store) to create a
certificate for the same server hostname, the client will successfully verify a malicious server that uses the
server hostname. If you don not trust certificate authorities (<a
href="https://blog.hqcodeshop.fi/archives/330-Whats-wrong-with-HTTPS-Part-2-Untrustworthy-Certificate-Authorities.html">there
are reasons to not trust them</a>), you need to pin the server certificate. This is possible but quite
cumbersome, and will not be covered by this manual.</p>
</div>
</div>
<p>The certificate of the server can simply be downloaded by sending an HTTP request to the WinRM HTTPS endpoint on
the server. The request will fail (the error is caught and ignored) because the server certificate cannot be
verified, but the request will contain the unverifiable certificate afterwards. The thumbprint of the retrieved
certificate is verified manually below:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-rsc1')" class="copy-to-clipboard">PS > <span
id="wcc-rsc1">$webRequest = [Net.WebRequest]::Create("https://<span class="configuration-dependent-value"><server hostname></span>:5986/wsman")</span></span>
<span onclick="selectTextOfElement('wcc-rsc2')" class="copy-to-clipboard">PS > <span id="wcc-rsc2">try { $webRequest.GetResponse() } catch {} # Catch and ignore the certificate error</span></span>
<span onclick="selectTextOfElement('wcc-rsc3')" class="copy-to-clipboard">PS > <span id="wcc-rsc3">$serverCert = $webRequest.ServicePoint.Certificate</span></span></pre>
<p>Output the thumbprint of the certificate and verify that it matches the thumbprint of the certificate generated in
the server configuration chapter:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-vsc')" class="copy-to-clipboard">PS > <span
id="wcc-vsc">$serverCert.GetCertHashString()</span></span></pre>
<p>If the server certificate is correct, you can add it as a certificate authority to your certificate store. The
PowerShell path of the certificate store location where the certificate will be stored is <code class="path">Cert:\CurrentUser\Root\</code>.
A confirmation dialog will appear for the third command:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-sccar1')" class="copy-to-clipboard">PS > <span
id="wcc-sccar1">$store = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "Root", "CurrentUser"</span></span>
<span onclick="selectTextOfElement('wcc-sccar2')" class="copy-to-clipboard">PS > <span id="wcc-sccar2">$store.Open('ReadWrite')</span></span>
<span onclick="selectTextOfElement('wcc-sccar3')" class="copy-to-clipboard">PS > <span id="wcc-sccar3">$store.Add($serverCert)</span></span>
<span onclick="selectTextOfElement('wcc-sccar4')" class="copy-to-clipboard">PS > <span
id="wcc-sccar4">$store.Close()</span></span></pre>
<h2>Create Certificate for the Client</h2>
<p>Just like the server the client needs a certificate, so the server can verify the identity of the client. Unlike on
the server, the standard PowerShell cmdlet can be used to generate the client certificate.</p>
<p>The following text extension will be set in the created certificate:</p>
<ul>
<li><p><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379367(v=vs.85).aspx#EnhancedKeyUsage">Enhanced
key usage</a> (2.5.29.37): <a
href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa378132(v=vs.85).aspx">The certificate can be
used for authenticating a client</a> (1.3.6.1.5.5.7.3.2).</p></li>
<li><p><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379367(v=vs.85).aspx#AlternativeNames">Alternative
names</a> (2.5.29.17): <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa378081(v=vs.85).aspx">User
principal name</a> (upn)</p></li>
</ul>
<p>Every certificate identifies a subject. The subject name can be anything. To make identifying certificates easier,
you can, for example, include the client hostname or the certificate purpose. A good value for the subject name is
your Windows username or Microsoft Account email address:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-ccc')" class="copy-to-clipboard">PS > <span
id="wcc-ccc">$winClientCert = New-SelfSignedCertificate -Type Custom -Subject <span
class="configuration-dependent-value"><subject></span> -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2","2.5.29.17={text}upn=<span
class="configuration-dependent-value"><subject></span>") -KeyUsage DigitalSignature,KeyEncipherment -CertStoreLocation Cert:\CurrentUser\My\</span></span></pre>
<p>The thumbprint is going to be needed to verify whether the correct public key has been downloaded by the server, so
display it and copy it somewhere:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-dcc')" class="copy-to-clipboard">PS > <span
id="wcc-dcc">$winClientCert</span></span></pre>
<p>This certificate can be used to authenticate against multiple servers. There is no need to create a client
certificate for each server to be accessed using WinRM.</p>
<p>Export the (public part) of the certificate to a file, so it can be transferred to the server. For <code
class="command"><fingerprint></code> you can use <code class="command">$($winClientCert.Thumbprint)</code>:
</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-ecc')" class="copy-to-clipboard">PS > <span
id="wcc-ecc">Export-Certificate -Cert Cert:\CurrentUser\My\<span class="configuration-dependent-value"><fingerprint></span> -FilePath <span
class="configuration-dependent-value"><export file path></span>.crt</span></span></pre>
</div>
<div class="windows-server">
<h2>Install Certificate of the Client</h2>
<div class="important-for-security">
<div>
<p>In this step the client certificate is copied to the server, but it cannot be verified that the certificate was
not modified during transit. Verify the thumbprint manually to ensure that you are installing the correct client
certificate as a certificate authority and trusted person. After installing the certificate as a certificate
authority, all certificates signed by that certificate will be accepted without user notification or
confirmation.</p>
</div>
</div>
<p>Copy the exported client certificate to the server. You can use insecure methods to do this, because the
certificate only contains a public key. As the name implies, having other people know this key does not pose a
security risk.</p>
<p>Once the client certificate is on the server, load it using:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-gcc')" class="copy-to-clipboard">PS > <span
id="wcc-gcc">$winClientCert = Get-PfxCertificate <span class="configuration-dependent-value"><certificate file path></span></span></span></pre>
<p>Output the thumbprint of the certificate and verify that it matches the thumbprint of the certificate generated
above:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-vcc')" class="copy-to-clipboard">PS > <span
id="wcc-vcc">$winClientCert.GetCertHashString()</span></span></pre>
<p>If the client certificate is correct, you can add it as a certificate authority and a trusted person to the
certificate store of the server. The reason the certificate also has to be installed as a certificate authority, is
that this enables the server to verify the copy of the certificate installed as a trusted person:</p>
<div class="lock-container">
<div class="lock-powershell"></div>
<pre class="powershell"><span onclick="selectTextOfElement('wccc-icc1')" class="copy-to-clipboard">PS > <span
id="wccc-icc1">Import-Certificate -FilePath <span class="configuration-dependent-value"><certificate file path></span> -CertStoreLocation Cert:\LocalMachine\Root</span></span>
<span onclick="selectTextOfElement('wccc-icc2')" class="copy-to-clipboard">PS > <span id="wccc-icc2">Import-Certificate -FilePath <span
class="configuration-dependent-value"><certificate file path></span> -CertStoreLocation Cert:\LocalMachine\TrustedPeople</span></span></pre>
</div>
<h2>Attach Client Certificate to User</h2>
<p>Now the server only needs to know as which user the WinRM session must run when it is authenticated using the
client certificate. You can use an existing user or create a user specifically for WinRM sessions.</p>
<p>Add the client certificate to the WinRM client certificate store. The subject that the client will use during WinRM
authentication, and the credentials of the local user to use on successful authentication, will be associated with
the certificate.</p>
<p>In the dialog asking for credentials, enter the credentials of the local user as which the WinRM sessions must be
run:</p>
<div class="lock-container">
<div class="lock-powershell"></div>
<pre class="powershell"><span onclick="selectTextOfElement('wccc-mcctu')" class="copy-to-clipboard">PS > <span
id="wccc-mcctu">New-Item -Path WSMan:\localhost\ClientCertificate -Subject '<span
class="configuration-dependent-value"><subject></span>' -URI * -Issuer ((Get-PfxCertificate <span
class="configuration-dependent-value"><certificate file path></span>).Thumbprint) -Credential (Get-Credential)</span></span></pre>
</div>
</div>
<div class="windows-client">
<h2>Test Connection on the Client</h2>
<p>Everything should be set up correctly now. There is a cmdlet specifically for testing if everything is working as
expected. By using the parameter <code class="command">-Authentication ClientCertificate</code> we force the use of
HTTPS:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-tc')" class="copy-to-clipboard">PS > <span
id="wcc-tc">Test-WSMan -ComputerName <span class="configuration-dependent-value"><server hostname></span> -Authentication ClientCertificate -CertificateThumbprint <span
class="configuration-dependent-value"><client certificate thumbprint></span></span></span></pre>
<p>You should see output similar to:</p>
<pre class="output">wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor : Microsoft Corporation
ProductVersion : OS: 10.0.14393 SP: 0.0 Stack: 3.0</pre>
<p>If the previous command was successful you can open a PowerShell session on the server:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-eps1')" class="copy-to-clipboard">PS > <span
id="wcc-eps1">$session = New-PSSession -ComputerName <span class="configuration-dependent-value"><server hostname></span> -CertificateThumbprint <span
class="configuration-dependent-value"><client certificate thumbprint></span></span></span>
<span onclick="selectTextOfElement('wcc-eps2')" class="copy-to-clipboard">PS > <span id="wcc-eps2">Enter-PSSession -Session $session</span></span></pre>
<div class="windows-server">
<p>The prompt should change and should now have a prefix containing the server hostname. Execute any command you
want. When you are done, exit the session using <code class="command">exit</code>:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-xps')" class="copy-to-clipboard">[<span
class="configuration-dependent-value"><server hostname></span>]: PS > <span id="wcc-xps">exit</span></span></pre>
</div>
<p>Finally clean up the PowerShell session on the client machine:</p>
<pre class="powershell"><span onclick="selectTextOfElement('wcc-rps')" class="copy-to-clipboard">PS > <span
id="wcc-rps">Remove-PSSession -Session $session</span></span></pre>
<p>Congratulations, you have successfully set up a fully certificate-based WinRM system.</p>
</div>
<ul class="navigation">
<li class="previous"><a href="server-configuration.html">⇐ Server configuration</a></li>
<li class="next"><a href="linux-client-configuration.html">Linux client configuration ⇒</a></li>
</ul>
</body>
</html>