AWS IoT Core
Lamassu's connector for AWS IoT Core syncs device identities with the AWS platform, handling CA registration, Thing provisioning and certificate lifecycle automatically and event-driven.
Lamassu's integration with AWS IoT Core bridges the PKI managed by Lamassu and AWS's connectivity platform. When a device obtains its certificate through the EST enrollment process, the connector ensures that certificate and its associated identity are recognized in AWS IoT Core without manual intervention. The result is that the device can authenticate directly to AWS with the same X.509 certificate Lamassu issued.
From a protocol standpoint, the device authenticates to AWS IoT Core via mutual TLS (mTLS): it presents its Lamassu-issued X.509 certificate as the client certificate, and AWS IoT Core verifies the certificate chain up to the CA registered in the account. Lamassu acts as the certification authority (CA); the connector ensures that CA is registered in AWS and that the device's certificate is known to the platform before the device attempts to connect (in auto mode) or on its first connection (in JITP mode).
This document is aimed both at the PKI administrator, who needs to configure the connector and register CAs, and at the device firmware developer, who needs to understand how Things are named, how the Device Shadow works and what happens when a certificate is revoked. For the firmware developer, the most important fact is that the AWS Thing name (ThingName) always corresponds to the Common Name (CN) of the certificate issued by Lamassu, regardless of the provisioning mode.
Connector architecture
The AWS IoT Core connector is an independent Go process, a binary or container separate from Lamassu's core. In a monolithic deployment it is compiled by default (build tag !noaws) and activated with aws_iot_manager.enabled: true in the configuration file. In distributed deployments, it runs as its own container with access to Lamassu's CA, DMS Manager and Device Manager services over HTTP.
The connector does not poll Lamassu's services periodically. Instead, it subscribes to Lamassu's internal event bus (AMQP or AWS SQS/SNS, configurable) and reacts to the events it cares about: CA creation or update, DMS creation or update, device identity binding, certificate status change, certificate metadata update and device metadata update. This event-driven model ensures synchronization with AWS is nearly immediate and produces no polling load.
To avoid infinite loops, the connector ignores any event whose CloudEvent source field matches its own origin URI. This prevents the metadata writes the connector itself performs from re-triggering the same flow.
When several connectors run in parallel, for example for different AWS accounts, each instance has its own connector_id. All metadata the connector writes to Lamassu is stored under the key lamassu.io/iot/{connector_id}, allowing coexistence without collisions.
Prerequisites
Before enabling the connector you need an AWS account with the proper IAM permissions and the connector correctly configured. The deployment process (Docker Compose, Kubernetes or binary) is described in the deployment documentation.
Required IAM permissions
The IAM identity operating the connector needs at minimum the following permissions on the target account's resources:
| IAM action | Purpose |
|---|---|
iot:GetRegistrationCode | Obtain the verification code for CA registration in the Primary Account |
iot:RegisterCACertificate | Register the CA in AWS IoT |
iot:DescribeCACertificate | Check the CA registration status |
iot:CreateThing / iot:UpdateThing | Create or update Things in IoT Core |
iot:RegisterThing | Provision Things through a template |
iot:RegisterCertificate | Register device certificates |
iot:UpdateCertificate | Change a certificate's status (ACTIVE / INACTIVE / REVOKED) |
iot:CreatePolicy / iot:AttachPolicy | Create and attach IoT policies |
iot:CreateThingGroup / iot:AddThingToThingGroup | Manage Thing groups |
iot:CreateProvisioningTemplate | Create JITP templates |
iotdata:GetThingShadow / iotdata:UpdateThingShadow | Read and write the Device Shadow |
The forced-disconnect mechanism after revocation also requires access to the AWS IoT data plane API (iot:Connect with SigV4 over WebSocket), negotiated implicitly with the connector's credentials.
Configuration parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
connector_id | string | Yes | Unique identifier of this connector instance. Used as the metadata namespace: lamassu.io/iot/{connector_id}. |
aws_config.region | string | Yes | AWS region where the connector operates. |
aws_config.auth_method | string | Yes | Authentication method: static, role or empty (SDK default credential chain). |
aws_config.access_key_id | string | Only with auth_method=static | AWS access key. |
aws_config.secret_access_key | string | Only with auth_method=static | AWS secret key. |
aws_config.session_token | string | No | Session token (with auth_method=static). |
aws_config.role_arn | string | Only with auth_method=role | ARN of the role to assume. |
aws_config.endpoint_url | string | No | Alternative URL for all AWS services (useful with LocalStack in tests). |
subscriber_event_bus | object | Yes | Configuration of the event bus the connector subscribes to (AMQP or AWS SQS/SNS). |
dms_manager_client | string | Yes | HTTP URL of Lamassu's DMS Manager service. |
device_manager_client | string | Yes | HTTP URL of Lamassu's Device Manager. |
ca_client | string | Yes | HTTP URL of Lamassu's CA service. |
CA registration in AWS IoT Core
For devices to authenticate to AWS IoT Core with certificates issued by a Lamassu CA, that CA must first be registered in the AWS account. The connector handles this registration automatically in response to the EventCreateCAKey, EventImportCAKey and EventUpdateCAMetadataKey events, but the operation only fires when the administrator explicitly requests it through the CA metadata field.
How to request registration
Registration is triggered by setting registration.status = "REQUESTED" on the lamassu.io/iot/{connector_id} key of the CA's metadata. This can be done from the Lamassu console in the CA management section or through the API.
Once the event is received, the connector attempts to register the CA in AWS and updates the registration.status field in the CA's metadata with the result: "SUCCEEDED" if registration was successful, or "FAILED" with an error message otherwise. The process is asynchronous, so you must periodically check the metadata status until the operation completes.
Primary Account
When the primary_account parameter is true, the connector runs the full registration flow with proof-of-possession of the private key:
- It calls
iot:GetRegistrationCodeto obtain AWS's verification code. - The connector internally generates an ephemeral RSA-2048 key pair and builds a CSR whose Common Name is exactly that registration code. It then calls Lamassu's signing service so the registered CA issues a real X.509 verification certificate signing that CSR. The ephemeral private key is used only for this purpose and is neither stored nor sent to AWS.
- It sends the CA (in PEM) and the verification certificate to AWS via
iot:RegisterCACertificate, enabling registration withAllowAutoRegistration: true.
Only one account can own a CA in a given AWS region. If the CA is already registered with the same serial number, the connector silently skips the step.
The tags AWS IoT Core applies to the registered CA certificate are LMS.CA.ID, LMS.CA.SN and LMS.CA.CN.
Secondary Account (SNI-only)
When primary_account is false, the connector registers the CA using CertificateMode: SNI_ONLY. This mode requires no verification code or access to the CA's private key, so it is the right choice when the CA is managed from another AWS account or when the administrator does not hold the private key. It is the usual method to register the same CA in several AWS accounts simultaneously.
DMS configuration
Each DMS participating in the AWS IoT Core integration must have the metadata key lamassu.io/iot/{connector_id} configured. The most important field of that configuration is registration_mode, which determines how devices are provisioned in AWS.
There are three registration modes, and the choice among them depends on the fleet's provisioning strategy.
none mode
In this mode the connector creates no Things, groups or policies in AWS IoT Core. It only syncs certificate status (active, revoked, suspended) as it changes in Lamassu. It is the right mode when Thing provisioning in AWS is managed by an external system and Lamassu only needs to keep certificate status up to date.
auto mode
auto mode delegates full device provisioning in AWS to Lamassu at enrollment time. When the device successfully completes its EST enrollment and Lamassu emits the EventBindDeviceIdentityKey event, the connector automatically executes the following sequence:
- Creates or updates the IoT policies configured in the DMS.
- Creates the root group
LAMASSUin the AWS account (if it does not exist) and the child groups configured in the DMS as subgroups ofLAMASSU. If the groups already exist, theResourceAlreadyExistsExceptionerror is ignored, making the operation idempotent. - If the Thing already existed in AWS IoT Core, the connector iterates all certificates (principals) currently attached to that Thing and marks them as
REVOKEDin AWS, ensuring previous certificates cannot keep being used to authenticate. - Calls
iot:RegisterThingwith a CloudFormation-style provisioning template that creates the Thing, registers the device's certificate (PEM of the device certificate and of the issuing CA certificate) and attaches the configured policies.
Once provisioning completes, the connector stores the resulting certificate's ARN in the Lamassu certificate's metadata under the key lamassu.io/iot/{connector_id} and marks the device as Registered: true in Device Manager.
The Thing name in AWS is the Common Name (CN) of the device's certificate. This convention is fundamental for the firmware developer: the /CN= value the device includes in its CSR when enrolling becomes the ThingName it must identify itself with in all MQTT operations against AWS IoT Core.
jitp mode
Just-In-Time Provisioning (JITP) mode delegates provisioning to AWS instead of the connector doing it directly. When the connector detects the DMS is in jitp mode, it creates a JITP provisioning template in AWS IoT Core and attaches it to the registered CA certificate. This template is created or updated at the moment the DMS is created or updated in Lamassu — not at device enrollment time. From then on, when a device with a certificate issued by that CA connects to AWS IoT Core for the first time, AWS executes the template automatically: it creates the Thing, registers the certificate and activates it.
The Thing name in the JITP template is derived from AWS::IoT::Certificate::CommonName, which corresponds to the device certificate's CN — the same convention as auto mode.
This mode requires the jitp_config.provisioning_role_arn field to be configured with the ARN of an IAM role AWS IoT Core can assume to execute the template. If not configured, the connector uses the default arn:aws:iam::{account}:role/JITPRole and logs a warning. The administrator must ensure that role exists in the account and has the necessary permissions before enabling JITP mode.
Device provisioning (automatic mode)
For the firmware developer, auto mode is transparent: the device performs its EST enrollment as usual and, as a side effect, it is provisioned in AWS IoT Core with no additional step.
The full flow from the device's perspective is:
# 1. The device generates its private key and CSR with the CN that will be its ThingName
openssl ecparam -name prime256v1 -genkey -noout -out device.key
openssl req -new -key device.key -sha256 -out device.csr \
-subj "/CN=my-device-001/O=Acme/OU=IoT"
# 2. The device enrolls in Lamassu via EST
openssl req -in device.csr -outform DER | base64 -w 0 > device.csr.b64
curl -s --cacert lamassu-trust.pem \
--cert bootstrap.crt --key bootstrap.key \
-H "Content-Type: application/pkcs10" \
--data-binary "@device.csr.b64" \
"https://<EST_HOST>/.well-known/est/<DMS_ID>/simpleenroll" \
-o enroll.b64
# 3. The device extracts its certificate
base64 -d -i enroll.b64 | openssl pkcs7 -inform DER -print_certs -out device.crt
# From this point on, the connector automatically creates the Thing "my-device-001"
# in AWS IoT Core. The device can connect to AWS with the same certificate.Once the connector completes provisioning, the device can connect to the AWS IoT Core MQTT endpoint using its private key and the certificate issued by Lamassu. The ThingName it must identify itself with in AWS is exactly the certificate's CN.
Device provisioning (JITP mode)
In JITP mode the device also performs its EST enrollment conventionally and obtains its certificate from Lamassu. The difference is that AWS IoT Core does not know the device until it attempts its first connection.
When the device establishes its first MQTT connection to AWS IoT Core presenting the Lamassu-issued certificate, AWS detects that the certificate belongs to a CA registered with a JITP template, executes that template and creates the Thing automatically. The initial connection may fail or be delayed while the template runs; firmware must be prepared to retry the connection.
For the JITP flow to work correctly:
- The CA must be registered in AWS in Primary Account mode (SNI-only mode does not support JITP).
- The IAM role configured in
jitp_config.provisioning_role_arnmust exist and have permissions to create Things, register certificates and attach policies in the AWS account. - The device must present both its certificate and the issuing CA's certificate in the TLS handshake with AWS IoT Core.
Certificates provisioned via JITP do not have the connector's metadata key (lamassu.io/iot/{connector_id}) in Lamassu. As a consequence, status changes of those certificates in Lamassu are not automatically synced to AWS through the state synchronization mechanism described in the next section.
Certificate status synchronization
The connector listens for the EventUpdateCertificateStatusKey event and, when a certificate holding the connector's metadata key changes status in Lamassu, replicates that change in AWS IoT Core. The following table shows the status mapping:
| Status in Lamassu | Revocation reason | Status in AWS IoT Core |
|---|---|---|
Active | None | ACTIVE |
Revoked | certificateHold | INACTIVE |
Revoked | Any other reason | REVOKED |
Only the Active and Revoked states produce a call to iot:UpdateCertificate. The other lifecycle states of Lamassu (RenewalWindow, AboutToExpire, Expired) are not synced to AWS IoT Core; the certificate status in AWS remains unchanged until the status in Lamassu becomes Active or Revoked.
Forced disconnect on revocation
When the connector marks a certificate as REVOKED or INACTIVE in AWS, it additionally performs a forced disconnect of the device. To do so it opens an MQTT connection over WebSocket (with SigV4 authentication using the connector's credentials) to the AWS IoT Core data endpoint, using the certificate's CN as the MQTT client identifier, and closes it immediately. This terminates any active MQTT session the device may have at that moment.
This forced disconnect happens at the moment Lamassu records the status change. If the device tries to reconnect after the disconnect, AWS IoT Core will reject the connection because the certificate is already listed as REVOKED or INACTIVE.
Certificates provisioned in JITP mode do not have this automatic sync, since they lack the connector's metadata key.
Device Shadow and automation
The connector can write to the AWS IoT Core Device Shadow to send action signals to the device firmware. This feature is enabled per DMS with shadow_config.enable: true.
If shadow_config.shadow_name holds a value, the connector uses a Named Shadow with that name; otherwise it uses the Classic Shadow (the Thing's default shadow).
What the connector writes to the shadow
The connector writes to the desired.identity_actions field of the shadow document. Each entry contains a Unix timestamp in milliseconds. The device firmware must read this field and act accordingly.
There are two action types:
UPDATE_CERTIFICATE: written when a certificate's preventive renewal window activates — that is, when the triggered field of the preventive renewal delta flips from false to true. This action tells the device it must start the re-enrollment process to renew its certificate before it expires.
UPDATE_TRUST_ANCHOR_LIST: written to all devices of a DMS when the DMS's ManagedCAs list changes. This action tells the device it must update its trusted CA store.
The shadow write process consists of reading the current shadow (iotdata:GetThingShadow), merging the new actions with the existing ones and writing the result (iotdata:UpdateThingShadow). If the shadow does not exist, the connector creates it. After a successful write, the list of pending actions in the device's metadata in Lamassu is cleared.
{
"state": {
"desired": {
"identity_actions": {
"UPDATE_CERTIFICATE": 1716300000000,
"UPDATE_TRUST_ANCHOR_LIST": 1716299000000
}
}
}
}AWS IoT policies
The connector creates and attaches the policies configured in the DMS. A typical policy for a device using MQTT and the Device Shadow has this structure:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:client/${iot:Connection.Thing.ThingName}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Publish"
],
"Resource": [
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topic/$aws/things/${iot:Connection.Thing.ThingName}",
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/[name/${ShadowName}/]*"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}",
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/[name/${ShadowName}/]*"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Receive"
],
"Resource": [
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topic/$aws/things/${iot:Connection.Thing.ThingName}",
"arn:aws:iot:<AWS_REGION>:<AWS_ACCOUNT_ID>:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/[name/${ShadowName}/]*"
]
}
]
}In the policy above, the <AWS_REGION> and <AWS_ACCOUNT_ID> placeholders must be replaced with the AWS region and the twelve-digit account identifier of the target account before applying the policy. The ${iot:Connection.Thing.ThingName} variable automatically limits each permission to the Thing the device is connected as, following the principle of least privilege. The ${ShadowName} variable in the shadow ARNs is a placeholder that must be replaced with the shadow_config.shadow_name value configured in the DMS; if the Classic Shadow is used, it can be simplified by removing the name/${ShadowName}/ segment. AWS IoT policies do not support regex alternation; the [name/${ShadowName}/]* notation represents the optionality of that segment within ARN syntax constraints.
The first statement lets the device connect using its ThingName as the MQTT identifier. The second authorizes publishing to the Thing's generic MQTT topic and to the shadow topics, needed so the device can update its reported state. The last two statements grant subscribe and receive permissions over the same topics, letting the device receive shadow updates (for example, the UPDATE_CERTIFICATE and UPDATE_TRUST_ANCHOR_LIST actions written by the connector).
Troubleshooting
Symptom: The CA registration stays in REQUESTED indefinitely.
Cause: The connector is not running or is not subscribed to the event bus. It can also indicate incorrect IAM credentials, insufficient permissions or that the metadata key was written with a wrong connector_id.
Resolution: Verify the connector process is active and the logs show no AWS communication errors. Check the metadata field was written under the exact key lamassu.io/iot/{connector_id}. If the error persists, the connector will update the status to FAILED with a descriptive message in the CA metadata.
Symptom: A certificate's status is not synced to AWS after a revocation.
Cause: Most commonly the certificate was provisioned in JITP mode. JITP certificates lack the connector's metadata key and are outside the state synchronization mechanism. If the DMS is in auto mode, it may indicate the initial provisioning did not complete correctly and the AWS ARN was not stored in the certificate metadata.
Resolution: For JITP certificates, migrate the DMS to auto mode and re-provision the affected devices. For auto mode certificates, verify the certificate has the AWS ARN stored in its metadata; if not, repeat the provisioning.
Symptom: The forced disconnect does not terminate the device's MQTT session after revocation.
Cause: The device uses an MQTT client ID different from the certificate's CN. The forced disconnect opens a connection using the CN as client ID; if the firmware uses another identifier, the disconnect does not affect that session.
Resolution: Make sure the firmware uses exactly the certificate's CN as the MQTT client identifier. That CN matches the ThingName in AWS IoT Core.
Symptom: The device receives CLIENT_ID_REJECTED when connecting to AWS IoT Core.
Cause: The MQTT client ID does not match the ThingName, which is always the CN of the certificate issued by Lamassu.
Resolution: Verify the firmware uses exactly the value of the CN field included in the enrollment CSR as the MQTT client identifier.
Symptom: The device's first MQTT connection in JITP mode fails or times out.
Cause: This is expected behavior while AWS executes the JITP template on the device's first connection.
Resolution: Firmware must implement retries with exponential backoff for the first connection. If the failure persists beyond a few minutes, verify the IAM role configured in jitp_config.provisioning_role_arn exists in the account and has permissions to create Things, register certificates and attach policies.
Symptom: The Device Shadow is not updated when it should be.
Cause: The shadow feature is disabled in the DMS configuration, the connector's credentials lack IoT data-plane permissions, or the firmware reads a different shadow than the configured one.
Resolution: Check that shadow_config.enable is true in the connector metadata for the corresponding DMS. If a Named Shadow is used, verify shadow_config.shadow_name holds the correct name and that the firmware reads that Named Shadow and not the Classic Shadow. Confirm the connector's credentials have iotdata:GetThingShadow and iotdata:UpdateThingShadow permissions.