Browse Source

Support combined PEM cert+key for Edge gRPC SSL

Reuse PemSslCredentials (already handles combined PEM, separate files,
and encrypted keys) instead of duplicating PEM parsing logic.
Wire it into gRPC via GrpcSslContexts + KeyManagerFactory.

- Make private_key config optional (default empty) for combined PEM
- Add key_password config for encrypted private keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pull/15205/head
Sergey Matvienko 3 months ago
parent
commit
9de53e7781
  1. 27
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  2. 9
      application/src/main/resources/thingsboard.yml

27
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
import io.grpc.stub.StreamObserver;
import jakarta.annotation.Nullable;
import jakarta.annotation.PreDestroy;
@ -37,7 +40,8 @@ import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.transport.config.ssl.PemSslCredentials;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.EdgeId;
@ -67,7 +71,6 @@ import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -110,8 +113,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private boolean sslEnabled;
@Value("${edges.rpc.ssl.cert}")
private String certFileResource;
@Value("${edges.rpc.ssl.private_key}")
@Value("${edges.rpc.ssl.private_key:}")
private String privateKeyResource;
@Value("${edges.rpc.ssl.key_password:}")
private String keyPassword;
@Value("${edges.state.persistToTelemetry:false}")
private boolean persistToTelemetry;
@Value("${edges.rpc.client_max_keep_alive_time_sec:1}")
@ -176,9 +181,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
.addService(this);
if (sslEnabled) {
try {
InputStream certFileIs = ResourceUtils.getInputStream(this, certFileResource);
InputStream privateKeyFileIs = ResourceUtils.getInputStream(this, privateKeyResource);
builder.useTransportSecurity(certFileIs, privateKeyFileIs);
setupSsl(builder);
} catch (Exception e) {
log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e);
throw new RuntimeException("Unable to set up SSL context!", e);
@ -199,6 +202,18 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
log.info("Edge RPC service initialized!");
}
private void setupSsl(NettyServerBuilder builder) throws Exception {
PemSslCredentials credentials = new PemSslCredentials();
credentials.setCertFile(certFileResource);
credentials.setKeyFile(StringUtils.isEmpty(privateKeyResource) ? null : privateKeyResource);
credentials.setKeyPassword(keyPassword);
credentials.init(false);
SslContext sslContext = GrpcSslContexts.configure(
SslContextBuilder.forServer(credentials.createKeyManagerFactory())).build();
builder.sslContext(sslContext);
}
@PreDestroy
public void destroy() {
if (server != null) {

9
application/src/main/resources/thingsboard.yml

@ -1487,10 +1487,13 @@ edges:
ssl:
# Enable/disable SSL support
enabled: "${EDGES_RPC_SSL_ENABLED:false}"
# Cert file to be used during TLS connectivity to the cloud
# Path to the server certificate PEM file. May contain the full chain and optionally the private key (combined PEM).
# For a combined PEM file containing both the certificate chain and the private key, leave private_key empty.
cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}"
# Private key file associated with the Cert certificate. This key is used in the encryption process during a secure connection
private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}"
# Path to the private key PEM file. Optional when the key is already included in the cert file (combined PEM).
private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:}"
# Password for encrypted private keys. Optional, only needed for password-protected keys.
key_password: "${EDGES_RPC_SSL_KEY_PASSWORD:}"
# Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes
max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}"
# Maximum length of telemetry (time-series and attributes) message the cloud sends to the edge. By default, there is no limitation.

Loading…
Cancel
Save