Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to connect to secure grpc port #35

Merged
merged 6 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ingester-example/src/main/java/io/greptime/TestConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public static GreptimeDB connectToDefaultDB() {
.router(null)
// Sets authentication information. If the DB is not required to authenticate, we can ignore this.
.authInfo(AuthInfo.noAuthorization())
// Enable TLS connection when remote port is secured by TLS
// .tlsOptions(new TlsOptions())
// A good start ^_^
.build();

Expand Down
42 changes: 38 additions & 4 deletions ingester-grpc/src/main/java/io/greptime/rpc/GrpcClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@
import io.grpc.ConnectivityState;
import io.grpc.ManagedChannel;
import io.grpc.MethodDescriptor;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.channel.ChannelOption;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.stub.ClientCalls;
import io.grpc.stub.StreamObserver;
Expand All @@ -68,6 +71,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import javax.net.ssl.SSLException;

/**
* Grpc client implementation.
Expand Down Expand Up @@ -542,11 +546,41 @@ private Channel getCheckedChannel(Endpoint endpoint, Consumer<Throwable> onFaile
return null;
}

private SslContext newSslContext(TlsOptions tlsOptions) {

try {
SslContextBuilder builder = GrpcSslContexts.forClient();

if (tlsOptions.getClientCertChain().isPresent() && tlsOptions.getPrivateKey().isPresent()) {
if (tlsOptions.getPrivateKeyPassword().isPresent()) {
builder.keyManager(tlsOptions.getClientCertChain().get(), tlsOptions.getPrivateKey().get(),
tlsOptions.getPrivateKeyPassword().get());
} else {
builder.keyManager(tlsOptions.getClientCertChain().get(), tlsOptions.getPrivateKey().get());
}
}

if (tlsOptions.getRootCerts().isPresent()) {
builder.trustManager(tlsOptions.getRootCerts().get());
}

return builder.build();
} catch (SSLException e) {
throw new RuntimeException("Failed to configure SslContext", e);
}
}

private IdChannel newChannel(Endpoint endpoint) {
ManagedChannel innerChannel = NettyChannelBuilder //
.forAddress(endpoint.getAddr(), endpoint.getPort()) //
.usePlaintext() //
.executor(this.asyncPool) //
NettyChannelBuilder innerChannelBuilder =
NettyChannelBuilder.forAddress(endpoint.getAddr(), endpoint.getPort());

if (this.opts.getTlsOptions().isPresent()) {
innerChannelBuilder.useTransportSecurity().sslContext(newSslContext(this.opts.getTlsOptions().get()));
} else {
innerChannelBuilder.usePlaintext();
}

ManagedChannel innerChannel = innerChannelBuilder.executor(this.asyncPool) //
.intercept(this.interceptors) //
.maxInboundMessageSize(this.opts.getMaxInboundMessageSize()) //
.flowControlWindow(this.opts.getFlowControlWindow()) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.greptime.limit.LimitedPolicy;
import io.greptime.models.AuthInfo;
import io.greptime.rpc.RpcOptions;
import io.greptime.rpc.TlsOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -279,6 +280,18 @@ public Builder router(Router<Void, Endpoint> router) {
return this;
}

/**
* Set `TlsOptions` to use secure connection between client and server. Set to `null` to use
* plaintext connection instead.
*
* @param tlsOptions for configure secure connection, set to null to use plaintext
* @return this builder
*/
public Builder tlsOptions(TlsOptions tlsOptions) {
this.rpcOptions.setTlsOptions(tlsOptions);
return this;
}

/**
* A good start, happy coding.
*
Expand Down
17 changes: 17 additions & 0 deletions ingester-rpc/src/main/java/io/greptime/rpc/RpcOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.greptime.rpc;

import io.greptime.common.Copiable;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
Expand Down Expand Up @@ -100,6 +101,20 @@ public class RpcOptions implements Copiable<RpcOptions> {

private boolean enableMetricInterceptor = false;

/**
* Set `TlsOptions` to use secure connection between client and server. Set to `null` to use
* plaintext connection instead.
*/
private Optional<TlsOptions> tlsOptions = Optional.empty();

public Optional<TlsOptions> getTlsOptions() {
return tlsOptions;
}

public void setTlsOptions(TlsOptions tlsOptions) {
this.tlsOptions = Optional.ofNullable(tlsOptions);
}

public boolean isUseRpcSharedPool() {
return useRpcSharedPool;
}
Expand Down Expand Up @@ -247,6 +262,7 @@ public RpcOptions copy() {
opts.blockOnLimit = this.blockOnLimit;
opts.logOnLimitChange = this.logOnLimitChange;
opts.enableMetricInterceptor = this.enableMetricInterceptor;
opts.tlsOptions = this.tlsOptions;
return opts;
}

Expand All @@ -269,6 +285,7 @@ public String toString() {
", blockOnLimit=" + blockOnLimit + //
", logOnLimitChange=" + logOnLimitChange + //
", enableMetricInterceptor=" + enableMetricInterceptor + //
", tlsOptions=" + tlsOptions + //
'}';
}

Expand Down
98 changes: 98 additions & 0 deletions ingester-rpc/src/main/java/io/greptime/rpc/TlsOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2023 Greptime Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.greptime.rpc;

import io.greptime.common.Copiable;
import java.io.File;
import java.util.Optional;

/**
* GreptimeDB secure connection options
*
* @author Ning Sun<[email protected]>
*/
public class TlsOptions implements Copiable<TlsOptions> {

private Optional<File> clientCertChain = Optional.empty();

private Optional<File> privateKey = Optional.empty();

private Optional<String> privateKeyPassword = Optional.empty();

private Optional<File> rootCerts = Optional.empty();

@Override
public TlsOptions copy() {
TlsOptions that = new TlsOptions();

that.setClientCertChain(this.getClientCertChain());
that.setPrivateKey(this.getPrivateKey());
that.setPrivateKeyPassword(this.getPrivateKeyPassword());
that.setRootCerts(this.getRootCerts());

return that;
}

public Optional<File> getClientCertChain() {
return clientCertChain;
}

public void setClientCertChain(Optional<File> clientCertChain) {
this.clientCertChain = clientCertChain;
}

public Optional<File> getPrivateKey() {
return privateKey;
}

public void setPrivateKey(Optional<File> privateKey) {
this.privateKey = privateKey;
}

public Optional<String> getPrivateKeyPassword() {
return privateKeyPassword;
}

public void setPrivateKeyPassword(Optional<String> privateKeyPassword) {
this.privateKeyPassword = privateKeyPassword;
}

public Optional<File> getRootCerts() {
return rootCerts;
}

public void setRootCerts(Optional<File> rootCerts) {
this.rootCerts = rootCerts;
}

@Override
public String toString() {
return "TlsOptions{"
+ //
"clientCertChain="
+ this.clientCertChain
+ //
", privateKey="
+ this.privateKey
+ //
", privateKeyPassword="
+ this.privateKeyPassword.map((v) -> "****")
+ //
", rootCerts="
+ this.rootCerts
+ '}';
}
}
Loading