Skip to content

Configure OPC UA certificates infrastructure for the connector for OPC UA

Elsie4ever edited this page May 8, 2025 · 3 revisions

Use the az iot ops connector opcua command to configure trusted list certificates, issuer list certificates and enterprise grade application instance certificate. Addition commands will help you create Azure keyvault secret and then add the secret reference to secretproviderclass opc-ua-connector and corresponding secretsync.

NOTE secret sync must be enabled before proceed the command, and make sure you have Key Vault Secrets Officer access for your Azure key vault.

Add a trusted certificate to the OPC UA Broker's trusted certificate list. The certificate file extension must be .der or .crt. Azure resource secretproviderclass 'opc-ua-connector' and secretsync 'aio-opc-ua-broker-trust-list' will be created if not found.

az iot ops connector opcua trust add --instance <instance> --resource-group <instanceresourcegroup>
--certificate-file "<certificate>.der"

Add an issuer certificate to the OPC UA Broker's issuer certificate list. The certificate file extension must be .der, .crt or .crl. When adding a .crl file, a .der or .crt file with same file name must be added first. Azure resource secretproviderclass 'opc-ua-connector'and secretsync 'aio-opc-ua-broker-issuer-list' will be created if not found.

az iot ops connector opcua issuer add --instance <instance> --resource-group <instanceresourcegroup>
--certificate-file "<certificate>.der"

Add an enterprise grade client application instance certificate. The public key file extension must be .der and private key file extension must be .pem. Please make sure to use same filename for public key and private key file. Azure resource secretproviderclass 'opc-ua-connector' and secretsync 'aio-opc-ua-broker-client-certificate' will be created if not found.

az iot ops connector opcua client add --instance <instance> --resource-group <instanceresourcegroup>
--public-key-file "<newopc>.der" --private-key-file "<newopc>.pem" --subject-name <aio-opc-opcuabroker>
--application-uri <application-uri>

Remove trusted certificate(s) from the OPC UA Broker's trusted certificate list. Remove trusted certificates called 'testcert1.der' and 'testcert2.crt' from trusted certificate list. Removing all trusted certificates from the OPC UA Broker's trusted certificate list will trigger deletion of the secretsync resource 'aio-opc-ua-broker-trust-list'. Note: Add space-separated certificate names to remove using --certificate-names input, the names can be found under the corresponding secretsync resource property targetKey.

az iot ops connector opcua trust remove --instance instance --resource-group instanceresourcegroup --certificate-names testcert1.der testcert2.crt

Remove issuer certificate(s) from the OPC UA Broker's issuer certificate list. Remove issuer certificates and its revocation list with .crl extension from issuer certificate list. Removing all issuer certificates from the OPC UA Broker's issuer certificate list will trigger deletion of the secretsync resource 'aio-opc-ua-broker-issuer-list'. Please make sure to remove corresponding .crl if exist when removing .der/.crt certificate to avoid orphaned secret.

az iot ops connector opcua issuer remove --instance instance --resource-group instanceresourcegroup --certificate-names testcert.der testcert.crl

Remove client application instance certificate from the OPC UA Broker. Remove client certificates from the OPC UA Broker's client certificate store. Removing all certificates from the OPC UA Broker's client certificate store will trigger deletion of the secretsync resource 'aio-opc-ua-broker-client-certificate'. And this operation will trigger the fallback to default (cert-manager based) certificate. This fallback requires an aio extension update. Please make sure to remove both public(.der) and private(.pem) key certificate pair to avoid orphaned secret.

az iot ops connector opcua client remove --instance instance --resource-group instanceresourcegroup --certificate-names testcert.der testcert.pem

Show details of secretsync resource 'aio-opc-ua-broker-trust-list'.

az iot ops connector opcua trust show --instance instance --resource-group instanceresourcegroup

Show details of secretsync resource 'aio-opc-ua-broker-issuer-list'.

az iot ops connector opcua issuer show --instance instance --resource-group instanceresourcegroup

Show details of secretsync resource 'aio-opc-ua-broker-client-certificate'.

az iot ops connector opcua client show --instance instance --resource-group instanceresourcegroup

Configure certificate secret with custom name

Add a trusted certificate to the OPC UA Broker's trusted certificate list with custom secret name.

az iot ops connector opcua trust add --instance <instance> --resource-group <instanceresourcegroup>
--certificate-file "<certificate>.crt" --secret-name <custom-secret-name>

Add an issuer certificate to the OPC UA Broker's issuer certificate list with custom secret name.

az iot ops connector opcua issuer add --instance <instance> --resource-group <instanceresourcegroup>
--certificate-file "<certificate>.crt" --secret-name <custom-secret-name>

Add an enterprise grade client application instance certificate with custom public and private key secret name.

az iot ops connector opcua client add --instance <instance> --resource-group <instanceresourcegroup>
--public-key-file "<newopc>.der" --private-key-file "<newopc>.pem" --subject-name <aio-opc-opcuabroker>
--application-uri <application-uri> --public-key-secret-name <public-secret-name> --private-key-secret-name <private-secret-name>

Configure certificate secret and skip the overwrite confirmation prompt when the secret already exists. Add a trusted certificate to the trusted certificate list and skip the overwrite confirmation prompt when the secret already exists.

az iot ops connector opcua trust add --instance instance --resource-group instanceresourcegroup --certificate-file "certificate.der" --overwrite-secret

Remove certificates including remove related keyvault secret. Remove trusted certificates from trusted certificate list, including remove related keyvault secret.

az iot ops connector opcua trust remove --instance instance --resource-group instanceresourcegroup --certificate-names testcert1.der testcert2.crt --include-secrets

Test scenario

Test trusted certificate list

  1. Deploy the OPC PLC simulator

  2. Extract the OPC PLC application instance certificate and add it to the OPC UA connector trust list

kubectl -n azure-iot-operations get secret opc-plc-default-application-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > opcplc.crt
  1. Add extracted certificate to trusted certificate list
az iot ops connector opcua trust add --instance $AIO_INSTANCE_NAME --resource-group $RESOURCE_GROUP --certificate-file "./opcplc.crt"
  1. Verify secret got created in Azure Key Vault.

  2. Verify kubernetes secret got created

    You can also verify if data flows by following quickstart steps

  3. Add AIO Connector certificate to PLC Simulator Trust list

# Extract the OPC UA connector application instance certificate and add it to the OPC PLC trust list
kubectl -n azure-iot-operations get secret aio-opc-opcuabroker-default-application-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > opcuabroker.crt
data=$(kubectl create secret generic temp --from-file=opcuabroker.crt=./opcuabroker.crt --dry-run=client -o jsonpath='{.data}')
kubectl patch secret opc-plc-trust-list -n azure-iot-operations -p "{\"data\": $data}"
rm ./opcuabroker.crt
  1. Follow rest of quickstart to set up end to end and make sure Connector and connect to PLC.

Test enterprise grade application instance certificate

Steps:

  1. Save following helm charts as opc-plc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: opc-plc-000000
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: opcplc-000000
  template:
    metadata:
      labels:
        app.kubernetes.io/component: opcplc-000000
    spec:
      containers:
      - name: opc-plc
        image: mcr.microsoft.com/iotedge/opc-plc:latest
        args:
          - "--ph=opcplc-000000"
          - "--cdn=opcplc-000000"
          - "--ut"
          - "--sph"
          - "--sn=5"
          - "--sr=10"
          - "--fn=10"
          - "--ftl=212"
          - "--ftu=273"
          - "--ftr=True"
          - "--veryfastrate=1000"
          - "--gn=1"
          - "--pn=50000"
          - "--at=FlatDirectory"
          - "--drurs"
        ports:
        - containerPort: 50000
        volumeMounts:
          - name: opc-plc-default-application-cert
            mountPath: /app/pki/own
          - name: opc-plc-trust-list
            mountPath: /app/pki/trusted
      volumes:
        - name: opc-plc-default-application-cert
          secret:
            secretName: opc-plc-default-application-cert
        - name: opc-plc-trust-list
          secret:
            secretName: opc-plc-trust-list
      serviceAccountName: opcplc-000000-service-account
---
apiVersion: v1
kind: Service
metadata:
  name: opcplc-000000
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/component: opcplc-000000
  ports:
    - port: 50000
      protocol: TCP
      targetPort: 50000
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: opc-plc-self-signed-issuer
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: opc-plc-default-application-cert
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  secretName: opc-plc-default-application-cert
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  issuerRef:
    name: opc-plc-self-signed-issuer
    kind: Issuer
  commonName: OpcPlc
  dnsNames:
    - opcplc-000000
    - opcplc-000000.azure-iot-operations.svc.cluster.local
    - opcplc-000000.azure-iot-operations
  uris:
    - urn:OpcPlc:opcplc-000000
  usages:
    - digital signature
    - key encipherment
    - data encipherment
    - server auth
    - client auth
  privateKey:
    algorithm: RSA
    size: 2048
  encodeUsagesInRequest: true
  isCA: false
---
apiVersion: v1
kind: Secret
metadata:
  name: opc-plc-trust-list
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
data: {}
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: opcplc-000000-service-account
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: opc-plc-000000-secret-access-role
  namespace: azure-iot-operations
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: opc-plc-000000-secret-access-rolebinding
  namespace: azure-iot-operations
subjects:
- kind: ServiceAccount
  name: opcplc-000000-service-account
  namespace: azure-iot-operations
roleRef:
  kind: Role
  name: opc-plc-000000-secret-access-role
  apiGroup: rbac.authorization.k8s.io
  1. connect to the cluster that has AIO deployment and run kubectl apply -f <path-to-file>\opc-plc.yaml You should see following output:
deployment.apps/opc-plc-000000 created
service/opcplc-000000 created
issuer.cert-manager.io/opc-plc-self-signed-issuer created
certificate.cert-manager.io/opc-plc-default-application-cert created
secret/opc-plc-trust-list created
serviceaccount/opcplc-000000-service-account created
role.rbac.authorization.k8s.io/opc-plc-000000-secret-access-role created
rolebinding.rbac.authorization.k8s.io/opc-plc-000000-secret-access-rolebinding created
  1. Create asset endpoint profile use az iot ops asset endpoint create opcua --name myprofile -g myresourcegroup --instance myinstance --target-address opc.tcp://opcplc-000000:50000
  2. Create asset using asset endpoint profile just created az iot ops asset create
az iot ops asset create --name myasset -g testjiacjucc0407-120422770 --endpoint-profile myprofile --instance myinstance0407 --tags NodeID="ns=2;s=Channel1.Device1.Tag1" TagName="Tag1"
  1. Validate the created asset CR kubectl get assets myasset -n azure-iot-operations -o yaml and you should see an untrusted certificate issue, this means the server is connected, but failed due to the certificate not trusted
apiVersion: deviceregistry.microsoft.com/v1
kind: Asset
metadata:
  annotations:
    management.azure.com/apiVersion: "2024-11-01"
    management.azure.com/bridgeLocation: EastUS2
    management.azure.com/cloudEnvironment: AzureCloud
    management.azure.com/correlationId: 2fe48616-7ab0-4bea-97d0-1d61c3e95538
    …
status:
  errors:
  - code: 400
    message: '{"title":"Disconnected","detail":"Session creation failure","lastTransitionTime":"2025-04-07T23:12:57.7108905Z","reasons":{"StatusCode":"BadCertificateUntrusted","SimbolicId":"BadCertificateUntrusted","Message":"Certificate
      is not trusted.","RejectedCertificate":"…'
  1. Get self-signed client certificates using public key:

kubectl -n azure-iot-operations get secret opc-plc-default-application-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > opcuabroker.crt

openssl x509 -outform der -in opcuabroker.crt -out opcuabroker.der

private key:

kubectl -n azure-iot-operations get secret opc-plc-default-application-cert -o jsonpath='{.data.tls\.key}' | base64 -d > opcuabroker.pem

  1. Configure client certificate using
az iot ops connector opcua client add --instance myinstance --resource-group resourcegroup --public-key-file "<path-to-file>\opcuabroker.der" --private-key-file "<path-to-file>\opcuabroker.pem"

now the error code in asset should change to:

status:
  errors:
  - code: 200
message: '{"title":"Connected","lastTransitionTime":"2025-04-07T23:55:45.7913102Z"}'

this means you are now connected!

Note For CA signed enterprise grade certificate, please Save the CA certificate and the CRL in the aio-opc-ua-broker-issuer-list secret before proceeding client add

# Append CA certificate to the issuer list secret as a new entry
az iot ops connector opcua issuer add --instance <your instance name> --resource-group <your resource group> --certificate-file "./my-server-ca.der"

# Append the CRL to the issuer list secret as a new entry
az iot ops connector opcua issuer add --instance <your instance name> --resource-group <your resource group> --certificate-file "./my-server-ca.crl"

For a PEM encoded certificate in a file such as ./my-server-ca.crt, run the following commands:

# Append CA certificate to the issuer list secret as a new entry
az iot ops connector opcua issuer add --instance <your instance name> --resource-group <your resource group> --certificate-file "./my-server-ca.crt"

# Append the CRL to the issuer list secret as a new entry
az iot ops connector opcua issuer add --instance <your instance name> --resource-group <your resource group> --certificate-file "./my-server-ca.crl"
Clone this wiki locally