GAN Remote Certificates - Auto Approval

  1. How to automatically approve certificates for Incoming GAN connections?
  2. When the certificate is approved, is it saved in the internal db?
  3. What Java classes are used to read the status of the certificates of the Incoming connections that are not yet approved?

Thanks in advance

Hi Derrick,

Could you give us more details on what you’re trying to do with this information first? And what version of Ignition are you are planning to attempt this on?

The best way to do this would be to create metro-keystore’s using your own CA. Process would be something like the following (assuming a Java JDK and openssl are installed):

  1. Create a new keystore:
keytool -genkey -alias metro-key -keyalg RSA -keysize 2048 -keystore metro-keystore
  1. Create a new Certificate Signing Request using the above keystore:
keytool -certreq -alias metro-key -file metro-keystore.csr -keystore metro-keystore
  1. Sign the request using your CA. When doing this, I have the following in my openssl.cnf file for use with the extensions flag and adjust the subjectAltName part as necessary:
####################################################################
		# Metro Keystore
		[ metro ]
		keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign
		extendedKeyUsage = clientAuth, serverAuth
		subjectAltName = IP:127.0.0.1, DNS:gw*, URI:uri://*/metro
		subjectKeyIdentifier = hash
		authorityKeyIdentifier = keyid, issuer

Example signing command:

openssl ca -config ../openssl.cnf -extensions metro -out metro-keystore.pem -infiles metro-keystore.csr
  1. Import your root public key for your CA:
keytool -import -trustcacerts -alias root -file ../root/root-ca.crt -keystore metro-keystore
  1. Import signed certificate
keytool -import -trustcacerts -alias metro-key -file metro-keystore.pem -keystore metro-keystore

The above should get you a keystore that can be placed in the {IGNITION_HOME}/webserver directory. It would replace the file that is already there.

There are a couple of other things that are needed to make this work:

  1. The default password for the metro-keystore is metro. The above process will not allow you to use this password because it is too short. Instead you will have to set it to something else and tell Ignition about it. This is done by:
    1. Open {IGNITION_HOME}/data/ignition.conf
    2. Find the # Java Additional Parameters header in the file
    3. Add wrapper.java.additional.<#>=-Dmetro.keystore.password=<Your Password> replacing the <#> with a number that isn’t being used and <Your Password> with the password you set for the keystore.
  2. Ignition must trust your root certificate. You must put the public key in the following directory (as of 8.1.14) and anything signed by that cert will be automatically trusted.:
    {IGNITION_HOME}/data/gateway-network/client/security/pki/trusted/certs.

The certificate isn’t saved in the internal DB, but the approval of the connection is. There are different options of configuring a Gateway for allowing connections but your options boil down to:

  1. Trust anything with a trusted cert
  2. Manually approve everything (can be done via a WebDev API endpoint if you can build it)
  3. Whitelist specific Gateways

In terms of what Java classes you can use to get Incoming connections, you would need to use a combination of the following:

com.inductiveautomation.ignition.gateway.IgnitionGateway
com.inductiveautomation.ignition.gateway.gan.IncomingConnection
simpleorm.dataset.SQuery

Garth

1 Like

Hey @ggross ,

Thank you for your response, I have a couple of questions

  1. Are the “Extensions” mandatory for the certificate?
  2. since the instance of ignition will be hosted on a kubernetes pod, I don’t know for sure what IP address will be used, how do we tackle this?
  3. How do we test/know if the keystore that we have generated is correct, because I have created a keystore but my local instance of ignition went down after that.(Please refer my keystore and conf file)

keystore password = metroPassword

I used openssl x.509 command to sign the certificate as opposed to the openssl ca command, will this create any issues?

Do you have any experience with the Keystore Explorer tool? It looks like I can access the default Keystore with the ‘metro’ password and even import certificates but I am not able to create a certificate chain once it is imported.

Also can you share your copy of the openssl.cnf file? I Keep getting this error when using the openssl ca command to sign
“Using configuration from openssl.cnf
variable lookup failed for ca::default_ca”

Thank you in advance
metro-keystore.zip (9.1 KB)

Hi @derrickdvdsn,

  1. The extensions aspect, specifically subjectAltName, of the certificate extensions is required but the IP aspect of it is not. If you will be connecting everything via hostname, then the DNS and URI parts of the subjectAltName would be all that you would need. Keep in mind my example is using wildcards and you would need to make your trusted items a lot more narrow than what I provided.
  2. See 1. If you have a fully qualified domain name for these pods, you would want to use that when creating the certificate for the Subject Alternate Name, if everything can be reached via hostname (which I doubt will occur in a production environment) using a hostname would be an option.
  3. It looks like your ignition.conf file isn’t setup correctly as it is missing the wrapper key at the beginning. It is currently:
java.additional.11=-Dmetro.keystore.password=metroPassword

if should be:

wrapper.java.additional.11=-Dmetro.keystore.password=metroPassword

The certificate itself looks like it was created properly and is loading into my local Ignition instance. Once you get Ignition started by fixing the configure file, you can confirm this by logging into the Gateway and navigating to the Config > Networking > Gateway Network > Diagnostics screen. Under Local Certificate Information you will see the information you provided when creating the certificate.

Using openssl x.509 you should be fine as long as you are able to add the x509v3_config options. While I have not configured a certificate this way myself, they all adhere to a specific format to be trusted. The certificate just needs to contain everything the client and server require to negotiate a connection.

While I have used Keystore Explorer to validate the contents of a keystore, I haven’t tried to replace anything. I know I have helped people do it in the past, but the details on how to do it escape me.

Here is what my openssl.cnf looks like. Keep in mind that while I have this working, I am by no means an expert in how certificates and their options work. You will need to confirm the options used to sign the certificates match you organization’s security policies:
openssl.cnf.zip (1.9 KB)

Garth

Thank you very much @ggross for your prompt response, I too am still learning about how the SSL/TLS mechanism works, will keep you posted on how things turn out

@ggross , I am able to successfully make the GAN connection automatically in the dev environment but how do I do this in the production environment? I already have an EAM gateway that is connected to about 100 other gateways. What effect will changing the keystore have on the other GAN connections? What’s the best way to go about it?

Fist thing you will need to do is make sure the public key of your signing certificate is trusted by both Gateways that are talking to each other. This is done by placing the key in the following directory (or mapping an external folder with the cert into the container location) for all Gateways that interact with each other using your self signed certificates.:

/data/gateway-network/client/security/pki/trusted/certs

While this will automatically trust the certs, even if the Require Two Way Auth option is set, it doesn’t take care of the Connection Policy option. You have the following options for this:

Approved Only: Default option and would require you to manually approve each Gateway, or you could create a resource via WebDev that would allow you to make an API call to approve untrusted Gateways.

Unrestricted: Still requires the certificate to be trusted to connect, but will let anything with a trusted cert connect.

SpecifiedList: You would need to provide a list of Gateway names that are allowed to connect.

You would need to figure out which scenario would be best for your setup but all options can automated should it be necessary.

I am attaching example WebDev resources for getting pending connections and approving them. There are called like the following and are set to use authentication by default:

  • Get Connections (HTTP Get): http://<Gateway URL>/system/webdev/Test/getPendingConnections
  • Approve Connection (HTTP Post): http://<Gateway URL>/system/webdev/Test/approveConnection
    • Payload Example (Both items provided by Get Connections): {"incGwName": "gw2", "incGwUuid": "55c7edfe-98aa-41a6-88d3-5e18da1d3ed3"}

WebDev_GANExamples.zip (6.9 KB)

Garth

@ggross what effect will changing the webserver/metro-keystore have on the existing GAN connections in the gateway?

Depends on where it is changed.

It will affect any Gateway that has a direct incoming/outgoing connection to the gateway with the changed certificate.

If you have a single Gateway that 100 other Gateways connect to and you change the cert on that 1 Gateway, then all 100 remote Gateways would need to trust the certificate prior to remote Gateway each Gateway being able to go back online.

If it is a single remote Gateway that you replace the certificate for, then you would just need to trust it on any of the Gateways it is making a connection to.

Not sure the best way to approach making the change over, but making sure that the public key of the signing cert is pushed to the <Ignition Home>/data/gateway-network/client/security/pki/trusted/certs directory on all Gateways would help make things go a lot smoother.

Garth

@ggross and @jrosenkrans , I am finally able to implement this. All I had to do was add the metro-keystore only on the containerized instance of Ignition and create an Outgoing Connection to the EAM Gateway. And I had to approve the certificates only the 1st time of connection and all other subsequent connections from different container IDs were automatically approved.

Thank you so much.

Solution:

Create SelfSigned rootCA certificate:

  1. First we would need a private key to generate the rootCA certificate:
    openssl genrsa -out rootCAkey.pem 4096
  2. Next we will create one custom openssl configuration file required to generate the Certificate Signing request and add X.509 extensions to our RootCA certificate:
    #cat openssl.cnf
    `[ req ]
    distinguished_name = req_distinguished_name
    policy = policy_match
    x509_extensions = v3_ca

For the CA policy

[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name) ## Print this message
stateOrProvinceName_default = California ## This is the default value
localityName = Locality Name (eg, city) ## Print this message
localityName_default = Folsom ## This is the default value
0.organizationName = Organization Name (eg, company) ## Print this message
0.organizationName_default = Inductive Automation ## This is the default value
organizationalUnitName = Organizational Unit Name (eg, section) ## Print this message
organizationalUnitName_default = QA ## This is the default value
commonName = Common Name (eg, your name or your server hostname) ## Print this message
commonName_max = 64

v3_ca

[ v3_ca ]
authorityKeyIdentifier = keyid, issuer
basicConstraints = critical,CA:true
extendedKeyUsage = serverAuth, clientAuth
keyUsage = keyCertSign,cRLSign
subjectAltName = @alt_section
subjectKeyIdentifier = hash

[alt_section]
email = your-email.com`

3.Let us go ahead and create our RootCA certificate

openssl req -new -x509 -days 3650 -config openssl.cnf -key rootCAkey.pem -out rootCA.pem


1. Create a new keystore

   `keytool -genkey -alias metro-key -keyalg RSA -keysize 2048 -keystore metro-keystore`

2. Create a new Certificate Signing Request using the above keystore:
`keytool -certreq -alias metro-key -file metro-keystore.csr -keystore metro-keystore`

3. Sign the certificate:
`openssl x509 -req -in metro-keystore.csr -CA rootCA.pem -CAkey rootCAkey.pem -CAcreateserial -out WCNP.crt -days 500 -sha256 -extfile openssl.cnf`

   Note: This openssl.cnf file is different from the root certificate.This openssl.cnf file should have the following contents

   `keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = IP:127.0.0.1, DNS:ignition-wcnp-lab, URI:uri://*/metro
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid, issuer`

4. Import your root public key for your CA:
`keytool -import -trustcacerts -alias root -file rootCA.pem -keystore metro-keystore`

5. Import signed certificate:
`keytool -import -trustcacerts -alias metro-key -file WCNP.crt -keystore metro-keystore`

Inspect the newly created keystore using the Keystore Explorer tool to verify.

Since I am using a derived image, these were my configuration for the Dockerfile
`
COPY ./backup.gwbk /usr/local/bin/ignition/backup.gwbk
COPY ./register-module.sh /usr/local/bin/ignition/register-module.sh
COPY ./Walmart-SSO-signed.modl /usr/local/bin/ignition/user-lib/modules/Walmart-SSO-signed.modl
COPY ./Ignition-Kafka-signed.modl /usr/local/bin/ignition/user-lib/modules/Ignition-Kafka-signed.modl
# COPY ./ssl.pfx /usr/local/bin/ignition/data/local/ssl.pfx
COPY ./rootCA.pem /usr/local/bin/ignition/data/gateway-network/client/security/pki/trusted/certs/rootCA.pem
COPY ./metro-keystore /usr/local/bin/ignition/webserver/metro-keystore

RUN chmod +x register-module.sh && ./register-module.sh


ENV IGNITION_UID=999  
ENV IGNITION_GID=999


RUN chown -R ${IGNITION_UID}:${IGNITION_GID} ${IGNITION_INSTALL_LOCATION:?expected in environment}

USER 999

ENTRYPOINT ["docker-entrypoint.sh","-r","/usr/local/bin/ignition/backup.gwbk","-n","ignition-wcnp-lab", "-m","16384","--","-Dmetro.keystore.password=metroPassword","-Dmetro.keystore.alias=metro-key"]

`
Hope this helps. 

Thank you IA Team for all your support
1 Like