konpyutaika / nifikop

The NiFiKop NiFi Kubernetes operator makes it easy to run Apache NiFi on Kubernetes. Apache NiFI is a free, open-source solution that support powerful and scalable directed graphs of data routing, transformation, and system mediation logic.
https://konpyutaika.github.io/nifikop/
Apache License 2.0
126 stars 44 forks source link

New feature to use commercial certs that are created and signed externally. #447

Open aoinc13 opened 1 month ago

aoinc13 commented 1 month ago

Is your feature request related to a problem?

The object listenersConfig.sslSecrets.tlsSecretName is used to create and sign certs as needed. But there is no provision to bypass automated cert creation and configure pre-created certs.

Describe the solution you'd like to see

We would like the operator to use and configure pre-created, signed certs. For example, if you have a wildcard cert from Verisign and you want Nifi to use it. We aren't interested in using self-signed certs or Cert Manager Issuers to create new certs. We would like to plug in an existing cert.

Describe alternatives you've considered

We could create a secret that contains the pre-created cert, mount it in Nifi, and override Nifi properties to point to the mounted pre-created cert. This solution should be documented if adopted. If would be nice if the operator did this for us.

Additional context

No response

juldrixx commented 1 month ago

Hello, you can inject your cerficate in NiFi TrustStore using the initContainers and overring Bootstrap configuration to ask the JVM to use it instead of the default one. You can doing it with something like that:

  initContainers:
{{- if and .Values.nifiCluster.rootca.configmap.name .Values.nifiCluster.rootca.configmap.key }}
    - command:
      - sh
      - -c
      - |
        echo "$ROOT_CA_CERT" > /opt/nifi/nifi-current/conf/rootca.crt && \
        cp -f ${JAVA_HOME}/lib/security/cacerts /opt/nifi/nifi-current/conf/cacerts_updated.jks && \
        keytool -import -alias rootca -file /opt/nifi/nifi-current/conf/rootca.crt -storetype jks -keystore /opt/nifi/nifi-current/conf/cacerts_updated.jks -noprompt -storepass changeit
      image: {{ .Values.nifiCluster.image }}
      name: import-ca
      volumeMounts:
        - mountPath: /opt/nifi/nifi-current/conf
          name: conf
      env:
        - name: ROOT_CA_CERT
          valueFrom:
            configMapKeyRef:
              name: {{ .Values.nifiCluster.rootca.configmap.name }}
              key: {{ .Values.nifiCluster.rootca.configmap.key }}
...
    bootstrapProperties:
      overrideConfigs: |
        {{- if and .Values.nifiCluster.rootca.configmap.name .Values.nifiCluster.rootca.configmap.key }}
        java.arg.truststore=-Djavax.net.ssl.trustStore=/opt/nifi/nifi-current/conf/cacerts_updated.jks
        {{- end }}

Or you can just mount your certificates as volume to the main container and use a StandardSSLContextService.

You can mount a ConfigMap or a Secret, both ways work.

lfreinag commented 1 week ago

Thanks for this posting this solution here. It did not work for me and I might be missing some experience and/or background.

Here are the challenges I encountered:

  1. I am missing how you declare the volume conf as I could not mount an emptyDir.
  2. The bootstrapProperties were correctly ingested but I could not read the new file. Possible solution: I think you need to restart all the nodes at the same time, otherwise the cluster will fail to connect. (I tried this later on with my solution).
  3. How do you mount the conf volume later on?

I share here what was my solution in case it can help others.

I added a job and a PVC to ingest the certificate authority. Then mounted that on top of the actual cacerts file. (I am aware that this might not be the best practice).

The job definition:

+apiVersion: batch/v1
+kind: Job
+metadata:
+  name: ca-installer
+  namespace: nifi-cluster
+spec:
+  completions: 1
+  template:
+    spec:
+      containers:
+        - name: ca-installer
+          image: docker.io/library/bash:5
+          args:
+            - sh
+            - -c
+            - exit 0;
+          volumeMounts:
+            - name: shared-ca-file
+              mountPath: /artifact
+      initContainers:
+        - name: install-custom-ca
+          image: apache/nifi:1.25.0
+          command:
+            - sh
+            - -c
+            - |
+              echo "$ROOT_CA_CERT" > /opt/nifi/nifi-current/conf/rootca.crt && \
+              cp -fv ${JAVA_HOME}/lib/security/cacerts /opt/nifi/nifi-current/conf/ && \
+              keytool -import -alias rootca -file /opt/nifi/nifi-current/conf/rootca.crt -storetype jks -keystore /opt/nifi/nifi-current/conf/cacerts -noprompt -storepass changeit
+          volumeMounts:
+            - mountPath: /opt/nifi/nifi-current/conf
+              name: shared-ca-file
+          env:
+            - name: ROOT_CA_CERT
+              valueFrom:
+                configMapKeyRef:
+                  name: custom-ca-config
+                  key: custom-ca.pem
+      restartPolicy: Never
+      volumes:
+        - name: shared-ca-file
+          persistentVolumeClaim:
+            claimName: custom-ca-files
+        - name: custom-ca-config
+          configMap:
+            name: custom-ca-config
+  backoffLimit: 0

The PVC definition (example):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: custom-ca-files
  namespace: nifi-cluster
spec:
  storageClassName: "your-class-name"
  resources:
    requests:
      storage: 1Mi
    limits:
      storage: 2Mi
  accessModes:
    - ReadWriteMany

On the values.yaml files you will need to mount the volume like this:

externalVolumeConfigs:
+          - name: shared-custom-ca
+            mountPath: "/opt/java/openjdk/lib/security/cacerts"
+            subPath: cacerts
+            persistentVolumeClaim:
+              claimName: custom-ca-files

And of course you should have a configMap with your ca data (example):

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-ca-config
  namespace: nifi-cluster
data:
  custom-ca.pem: |
    -----BEGIN CERTIFICATE-----
    <Your cert info here>
    -----END CERTIFICATE-----
juldrixx commented 1 week ago

You can create the conf volume like that in NifiCluster resource.

...
nodeConfigGroups:
  default_group:
   ...
    storageConfigs:
      - mountPath: /opt/nifi/nifi-current/conf
        name: conf
        reclaimPolicy: Delete
        metadata:
          labels:
            my-label: my-value
          annotations:
            my-annotation: my-value
        pvcSpec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 5Gi
          storageClassName: ssd-wait