bbyars/mountebank

View on GitHub
src/views/docs/protocols/https.ejs

Summary

Maintainability
Test Coverage
<%
title = 'https'
description = 'The HTTPS protocol support provided by mountebank'
%>

<%- include('../../_header') -%>

<h1>https</h1>

<p>The https is very similar to the http protocol, except for the fact that it uses
a certificate key pair.  If you use the built-in self-signed certificate, you may have to
configure your code to disable certificate validation (e.g. use the <code>-k</code>
switch in <code>curl</code>).  Alternatively, you may pass in the key pair yourself.</p>

<h2>Imposter Creation Parameters</h2>

<table>
  <tr>
    <th>Parameter</th>
    <th>Options</th>
    <th>Required?</th>
    <th>Default</th>
    <th>Description</th>
  </tr>
  <tr>
    <td><code>protocol</code></td>
    <td><code>https</code></td>
    <td>Yes</td>
    <td>N/A</td>
    <td>&nbsp;</td>
  </tr>
  <tr>
    <td><code>port</code></td>
    <td>Any valid port number</td>
    <td>No</td>
    <td>A randomly assigned port.  mountebank will return the actual value
    in the <code>POST</code> response.</td>
    <td>The port to run the imposter on.</td>
  </tr>
  <tr>
    <td><code>key</code></td>
    <td>A PEM-formatted string</td>
    <td>No</td>
    <td>A built in private key</td>
    <td>The SSL server private key</td>
  </tr>
  <tr>
    <td><code>cert</code></td>
    <td>A PEM-formatted string</td>
    <td>No</td>
    <td>A built in self-signed certificate</td>
    <td>The SSL server certificate</td>
  </tr>
  <tr>
    <td><code>mutualAuth</code></td>
    <td><code>true</code> or <code>false</code></td>
    <td>No</td>
    <td><code>false</code></td>
    <td>If <code>true</code>, the server will request a client certificate.
    Since the goal is simply to virtualize a server requiring mutual auth,
    invalid certificates will not be rejected.</td>
  </tr>
  <tr>
    <td><code>ciphers</code></td>
    <td><a href='https://nodejs.org/dist/latest-v14.x/docs/api/tls.html#tls_modifying_the_default_tls_cipher_suite'>See here</a></td>
    <td>No</td>
    <td><a href='https://nodejs.org/dist/latest-v14.x/docs/api/tls.html#tls_tls_getciphers'>Here</a></td>
    <td>For older (and insecure) https servers, this field allows you to override the cipher used to communicate</td>
  </tr>
  <tr>
    <td><code>name</code></td>
    <td>Any string</td>
    <td>No</td>
    <td>empty string</td>
    <td>Included in the logs, useful when multiple imposters are set up.</td>
  </tr>
  <tr>
    <td><code>recordRequests</code></td>
    <td><code>true</code> or <code>false</code></td>
    <td>No</td>
    <td>false</td>
    <td>Adds <a href='/docs/api/mocks'>mock verification</a> support by remembering the requests
        made to this imposter.  Note that this represents a memory leak for any long running
        <code>mb</code> process, as requests are never forgotten.</td>
  </tr>
  <tr>
    <td><code>stubs</code></td>
    <td>Valid stubs</td>
    <td>No</td>
    <td>An empty array</td>
    <td>The list of stubs responsible for matching a request and returning a response</td>
  </tr>
  <tr>
    <td><code>defaultResponse</code></td>
    <td>A valid response, see the <a href='/docs/protocols/http'>http</a> page for response fields</td>
    <td>No</td>
    <td><pre><code>
{
  "statusCode": 200,
  "headers": {
    "connection": "close"
   },
  "body": ""
}
    </code></pre></td>
    <td>The default response to send if no predicate matches. Also represents the default values
        that get merged into a response that doesn't specify every field</td>
  </tr>
  <tr>
    <td><code>allowCORS</code></td>
    <td>boolean</td>
    <td>No</td>
    <td>false</td>
    <td>If true, mountebank will allow all CORS preflight requests on the imposter.</td>
  </tr>
  <tr>
    <td><code>rejectUnauthorized</code></td>
    <td>boolean</td>
    <td>No</td>
    <td>false</td>
    <td>If true, mountebank will validate the certificate against the list of supplied CAs.</td>
  </tr>
  <tr>
    <td><code>ca</code></td>
    <td>Array of PEM-formatted certificates</td>
    <td>No</td>
    <td>null</td>
    <td>Use when setting <code>rejectUnauthorized</code> to true to provide a list of certificates to validate against.
        When <code>rejectUnauthorized</code> is true and <code>mutualAuth</code> is true, mountebank will request a client certificate.</td>
  </tr>
</table>

<p>See the <a href='/docs/protocols/http'>http</a> page for examples.  Just be sure
  to change the <code>protocol</code> parameter to <code>https</code> during imposter
  creation!</p>

<h2>Handling trust</h2>

<p>By default, mountebank will use an internal self-signed certificate.  While this allows you
to explore the API in a simple zero-config manner, it also means that you need to configure
your system under test to ignore trust errors. This is A Very Bad Idea. It puts you at risk
of accidentally leaving that configuration in production, exposing your network to unnecessary risk.</p>

<p>There are at least three solutions that are safe for testing and remove the possibility
of your test strategy causing trust violations in production:</p>

<ul class='bullet-list'>
    <li>Test against an http imposter, and trust that the SSL/TLS libraries your system under
    test is using just work.</li>
    <li>Create your own self-signed certificate for testing <i>and add it to your local trust store only.</i>
    Have a look at <a href='https://github.com/FiloSottile/mkcert'>mkcert</a> for a simple way
    of doing this.</li>
    <li>Use a real trusted certificate for testing. If you can't have your organization create
    one for you, <a href='https://letsencrypt.org/'>Let's Encrypt</a> offers a free service
    to create trusted certificates.</li>
</ul>

<p>The mutual authentication example below shows including them in your imposter setup.</p>

<h2>Mutual Authentication</h2>

<p>mountebank supports mutual authentication involving client certificates both for the
server itself and through proxies.  The following example demonstrates this capability,
as well as setting up your own certificate for the server.  First we'll set up a server
that expects client certificates by using the <code>mutualAuth</code> field:</p>

<testScenario name='https-mutual-auth'>
    <step type='http'>
<pre><code>POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json

{
  "port": 4545,
  "protocol": "https",
  "name": "origin server",
  <strong class='highlight2'>"mutualAuth": true</strong>,
  "key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCrvse04YkxtVGagyvGJCsvv7LTfLK5uR/ZIJKDYCnuF+BqBzM4\nlko8O39vx+Lz9FfF11Xl+CN1aY37YurLYOle3dC/qslSbQDe2TJN7lcVHVssePvc\nO5IExpvNFV5LYtmyCMKJHxpnIprv/trUso5obqzzXhFVPV9SQbFH/snInwIDAQAB\nAoGARywlqLD6YO4qJiULw+4DM6N2oSwBCPRN3XYhIW59kdy1NFtNf7rQgsuJUTJ9\nu+lbYnKNd2LwltyqaS4h7Sx5KRhpFNmMpyVsBf5J2q3fbfmrsXt+emY7XhVTc1NV\nizUWYyxCoTTeMWvN/6NYpPV0lSxq7jMTFVZrWQUMqJclxpECQQDTlGwALtAX1Y8u\nGKsEHPkoq9bhHA5N9WAboQ4LQCZVC8eBf/XH//2iosYTXRNgII2JLmHmmxJHo5iN\nJPFMbnoHAkEAz81osJf+yHm7PBBJP4zEWZCV25c+iJiPDpj5UoUXEbq47qVfy1mV\nDqy2zoDynAWitU7PeHyZ8ozfyribPoR2qQJAVmvMhXKZmvKnLivzRpXTC9LMzVwZ\nV6x/Wim5w8yrG5fZIMM0kEG2xwR3pZch/+SsCzl/0aLLn6lp+VT6nr6NZwJBAMxs\nHrvymoLvNeDtiJFK0nHliXafP7YyljDfDg4+vSYE0R57c1RhSQBJqgBV29TeumSw\nJes6cFuqeBE+MAJ9XxkCQDdUdhnA8HHQRNetqK7lygUep7EcHHCB6u/0FypoLw7o\nEUVo5KSEFq93UeMr3B7DDPIz3LOrFXlm7clCh1HFZhQ=\n-----END RSA PRIVATE KEY-----",
  "cert": "-----BEGIN CERTIFICATE-----\nMIIB6TCCAVICCQCZgxbBD0CG4zANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJV\nUzETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMVGhvdWdodFdvcmtzMB4X\nDTEzMTIyOTE2NDAzN1oXDTE0MDEyODE2NDAzN1owOTELMAkGA1UEBhMCVVMxEzAR\nBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDFRob3VnaHRXb3JrczCBnzANBgkq\nhkiG9w0BAQEFAAOBjQAwgYkCgYEAq77HtOGJMbVRmoMrxiQrL7+y03yyubkf2SCS\ng2Ap7hfgagczOJZKPDt/b8fi8/RXxddV5fgjdWmN+2Lqy2DpXt3Qv6rJUm0A3tky\nTe5XFR1bLHj73DuSBMabzRVeS2LZsgjCiR8aZyKa7/7a1LKOaG6s814RVT1fUkGx\nR/7JyJ8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCPhixeKxIy+ftrfPikwjYo1uxp\ngQ18FdVN1pbI//IIx1o8kJuX8yZzO95PsCOU0GbIRCkFMhBlqHiD9H0/W/GvWzjf\n7WFW15lL61y/kH1J0wqEgoaMrUDjHZvKVr0HrN+vSxHlNQcSNFJ2KdvZ5a9dhpGf\nXOdprCdUUXzSoJWCCg==\n-----END CERTIFICATE-----",
  "stubs": [{
    "responses": [<strong class='highlight1'>{ "is": { "body": "It worked!" } }</strong>]
  }]
}
</code></pre>
    </step>

<p>If we try and connect to this server without providing a client certificate, it will immediately terminate the connection.
Let's create an imposter that will proxy to it, providing the necessary credentials:</p>

    <step type='http'>
<pre><code>POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json

{
  "port": 5555,
  "protocol": "http",
  "name": "proxy",
  "stubs": [
    {
      "responses": [
        {
          "proxy": {
            "to": "https://localhost:4545",
            <strong class='highlight2'>"key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCrvse04YkxtVGagyvGJCsvv7LTfLK5uR/ZIJKDYCnuF+BqBzM4\nlko8O39vx+Lz9FfF11Xl+CN1aY37YurLYOle3dC/qslSbQDe2TJN7lcVHVssePvc\nO5IExpvNFV5LYtmyCMKJHxpnIprv/trUso5obqzzXhFVPV9SQbFH/snInwIDAQAB\nAoGARywlqLD6YO4qJiULw+4DM6N2oSwBCPRN3XYhIW59kdy1NFtNf7rQgsuJUTJ9\nu+lbYnKNd2LwltyqaS4h7Sx5KRhpFNmMpyVsBf5J2q3fbfmrsXt+emY7XhVTc1NV\nizUWYyxCoTTeMWvN/6NYpPV0lSxq7jMTFVZrWQUMqJclxpECQQDTlGwALtAX1Y8u\nGKsEHPkoq9bhHA5N9WAboQ4LQCZVC8eBf/XH//2iosYTXRNgII2JLmHmmxJHo5iN\nJPFMbnoHAkEAz81osJf+yHm7PBBJP4zEWZCV25c+iJiPDpj5UoUXEbq47qVfy1mV\nDqy2zoDynAWitU7PeHyZ8ozfyribPoR2qQJAVmvMhXKZmvKnLivzRpXTC9LMzVwZ\nV6x/Wim5w8yrG5fZIMM0kEG2xwR3pZch/+SsCzl/0aLLn6lp+VT6nr6NZwJBAMxs\nHrvymoLvNeDtiJFK0nHliXafP7YyljDfDg4+vSYE0R57c1RhSQBJqgBV29TeumSw\nJes6cFuqeBE+MAJ9XxkCQDdUdhnA8HHQRNetqK7lygUep7EcHHCB6u/0FypoLw7o\nEUVo5KSEFq93UeMr3B7DDPIz3LOrFXlm7clCh1HFZhQ=\n-----END RSA PRIVATE KEY-----",
            "cert": "-----BEGIN CERTIFICATE-----\nMIIB6TCCAVICCQCZgxbBD0CG4zANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJV\nUzETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMVGhvdWdodFdvcmtzMB4X\nDTEzMTIyOTE2NDAzN1oXDTE0MDEyODE2NDAzN1owOTELMAkGA1UEBhMCVVMxEzAR\nBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDFRob3VnaHRXb3JrczCBnzANBgkq\nhkiG9w0BAQEFAAOBjQAwgYkCgYEAq77HtOGJMbVRmoMrxiQrL7+y03yyubkf2SCS\ng2Ap7hfgagczOJZKPDt/b8fi8/RXxddV5fgjdWmN+2Lqy2DpXt3Qv6rJUm0A3tky\nTe5XFR1bLHj73DuSBMabzRVeS2LZsgjCiR8aZyKa7/7a1LKOaG6s814RVT1fUkGx\nR/7JyJ8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCPhixeKxIy+ftrfPikwjYo1uxp\ngQ18FdVN1pbI//IIx1o8kJuX8yZzO95PsCOU0GbIRCkFMhBlqHiD9H0/W/GvWzjf\n7WFW15lL61y/kH1J0wqEgoaMrUDjHZvKVr0HrN+vSxHlNQcSNFJ2KdvZ5a9dhpGf\nXOdprCdUUXzSoJWCCg==\n-----END CERTIFICATE-----"</strong>
          }
        }
      ]
    }
  ]
}</code></pre>
    </step>

<p>With the client certificate specified, we should now be able to successfully proxy:</p>

    <step type='http'>
<pre><code>GET / HTTP/1.1
Host: localhost:5555</code></pre>

        <assertResponse>
<pre><code>HTTP/1.1 200 OK
Connection: close
Date: <volatile>Thu, 09 Jan 2014 02:30:31 GMT</volatile>
Transfer-Encoding: chunked

<strong class='highlight1'>It worked!</strong></code></pre>
        </assertResponse>
    </step>

    <step type='http'>
<code class='hidden'>DELETE /imposters/5555 HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json</code>
    </step>

    <step type='http'>
<code class='hidden'>DELETE /imposters/4545 HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json</code>
    </step>
</testScenario>

<%- include('../../_footer') -%>