2

Objective

Because of Nifi integration with other tools through HTTP, I have to make ListenHTTP processor public facing. API Gateway on all 3 environments is too expensive for me. So I closed all VM ingress ports (except the one needed for ListenHTTP) for outer networks.

Issue

My configuration of ListenHTTP with StandardRestrictedSSLContextService doesn't work. Without SSL it worked, but was unsecure.

user$ curl -X POST -H "Content-Type: application/json" --data "test" https://localhost:7070/test
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

.....

user$ curl -X POST -H "Content-Type: application/json" --data "test" --cacert cacerts.jks  https://localhost:7070/test
curl: (77) error setting certificate verify locations:
  CAfile: cacerts.jks
  CApath: none

Question

How to make ListenHTTP work with SSL certificates? What am I doing wrong?

More detailed questions:

  1. Should I copy cacerts.jsk to the machine from which I issue the query? As far as I understand, StandardRestrictedSSLContextService will verify if the client has certificate in TrustStore.
  2. If I need to protect only a single port with ListenHTTP processor - then I don't need nifi.security.needClientAuth property or all environment variables defined at "Standalone Instance, Two-Way SSL" section, right? I'm little bit confused because both Docker Image and StandardRestrictedSSLContextService contains the same configs, i.e. KEYSTORE_TYPE.

Already done

  1. I have a general idea about KeyStore & TrustStore from this question and the documentation.
  2. I have launched Nifi Docker container v1.10.0 with up & running ListenHTTP processor on 7070 port.
  3. I have created keystore.jks and cacerts.jks files due to the instruction inside Nifi container.
  4. I have configured ListenHTTP to use StandardRestrictedSSLContextService controller with the following configs: enter image description here.
VB_
  • 45,112
  • 42
  • 145
  • 293
  • convert cacert.jks to `pem` format, use `pem` in `-cacert` parameter for curl. truststore not needed for ListenHTTP processor – daggett Jul 02 '20 at 12:59

1 Answers1

1

The SSLContextService you're using probably doesn't contain a certificate which is signed by a publicly-accessible certificate authority (CA) like (for explanation purposes only; not endorsement) Comodo, Verisign, Let's Encrypt, etc.

Certificates signed by those CAs are generally trusted automatically by arbitrary clients because whoever builds the client (Java, Google/Microsoft/Mozilla/Apple for a browser, Microsoft/Apple/Linux Distro for the OS) has preemptively included those top-level public certs in the truststore of the client. The truststore you created cacerts.jks is in Java Keystore format, which curl doesn't happen to understand. You can export the public certificate from that keystore to a standalone file in PEM format using the commands here, but that will only solve the immediate problem of allowing curl with an arbitrary truststore to connect.

If you want generic external clients to be able to connect over TLS, you'll need to use a certificate in NiFi's keystore that is signed by a well-known CA. You can use any commercial CA for this purpose, but Let's Encrypt does offer this service for free and is very widely used. Once you are using a certificate signed by a CA, any* client will be able to connect.

If this is for internal/enterprise use only, and all allowed clients are controllable by you, then you can use a self-signed certificate (like you are doing now if you followed Simon's instructions), and export the public certificate to whatever format your other clients need in order to establish trust with this particular server. Theoretically, you could also enforce that each client attempting to connect also needs to present a certificate that the server (NiFi) can verify -- this is called mutual-authentication TLS and adds another layer of security because only authenticated clients will be able to make requests to this server. If you choose to do so, that's when the SSLContextService in ListenHTTP would need a truststore component as well.

Without knowing your explicit situation, I would heavily recommend option 1 (the signed cert).

Andy
  • 13,916
  • 1
  • 36
  • 78
  • thanks for such a detailed answer! I have only one client - ADF WebHook activity https://learn.microsoft.com/en-us/azure/data-factory/control-flow-webhook-activity#client-certificate. The issue is that I have only server CA certificate, and need to create somehow a linked client certificate. Or generate a new pair with Let's Encrypt... Thanks! – VB_ Jul 03 '20 at 07:28
  • I finally realize that two-way SSL add significant complexity to deplyment. I may fall back to bigger costs but simpler option: API Gateway for SSL termination + Basic Auth. AFAIK, Nifi doesn't support Basic Auth out-of-the-box, so I'm going to do that with RouteOnAttribute processor. – VB_ Jul 03 '20 at 07:30
  • 1
    The server and client certificates do not need to be related in any way -- to authenticate a client, just make sure that the client's public certificate or one of it's signers is present in the `truststore.jks` you reference in the `SSLContextService` for the `ListenHTTP` processor. You can generate that client certificate using `openssl` or Let's Encrypt independently of the server certificate. – Andy Jul 03 '20 at 17:31