mountebank

mountebank - over the wire test doubles


the apothecary

https

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 -k switch in curl). Alternatively, you may pass in the key pair yourself.

Imposter Creation Parameters

Parameter Options Required? Default Description
protocol https Yes N/A  
port Any valid port number No A randomly assigned port. mountebank will return the actual value in the POST response. The port to run the imposter on.
key A PEM-formatted string No A built in private key The SSL server private key
cert A PEM-formatted string No A built in self-signed certificate The SSL server certificate
mutualAuth true or false No false If true, 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.
ciphers See here No Here For older (and insecure) https servers, this field allows you to override the cipher used to communicate
name Any string No empty string Included in the logs, useful when multiple imposters are set up.
recordRequests true or false No false Adds mock verification support by remembering the requests made to this imposter. Note that this represents a memory leak for any long running mb process, as requests are never forgotten.
stubs Valid stubs No An empty array The list of stubs responsible for matching a request and returning a response
defaultResponse A valid response, see the http page for response fields No

{
  "statusCode": 200,
  "headers": {
    "connection": "close"
   },
  "body": ""
}
    
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
allowCORS boolean No false If true, mountebank will allow all CORS preflight requests on the imposter.
rejectUnauthorized boolean No false If true, mountebank will validate the certificate against the list of supplied CAs.
ca Array of PEM-formatted certificates No null Use when setting rejectUnauthorized to true to provide a list of certificates to validate against. When rejectUnauthorized is true and mutualAuth is true, mountebank will request a client certificate.

See the http page for examples. Just be sure to change the protocol parameter to https during imposter creation!

Handling trust

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.

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

The mutual authentication example below shows including them in your imposter setup.

Mutual Authentication

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 mutualAuth field:

POST /imposters HTTP/1.1
Host: localhost:48451
Accept: application/json
Content-Type: application/json

{
  "port": 4545,
  "protocol": "https",
  "name": "origin server",
  "mutualAuth": true,
  "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": [{ "is": { "body": "It worked!" } }]
  }]
}

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:

POST /imposters HTTP/1.1
Host: localhost:48451
Accept: application/json
Content-Type: application/json

{
  "port": 5555,
  "protocol": "http",
  "name": "proxy",
  "stubs": [
    {
      "responses": [
        {
          "proxy": {
            "to": "https://localhost:4545",
            "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-----"
          }
        }
      ]
    }
  ]
}

With the client certificate specified, we should now be able to successfully proxy:

GET / HTTP/1.1
Host: localhost:5555
HTTP/1.1 200 OK
Connection: close
Date: Thu, 09 Jan 2014 02:30:31 GMT
Transfer-Encoding: chunked

It worked!