Skip to content
New issue

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

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

Already on GitHub? # to your account

Fix Java & Go SDK TLS support #986

Merged
merged 7 commits into from
Sep 7, 2020
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
24 changes: 17 additions & 7 deletions sdk/go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package feast

import (
"context"
"crypto/x509"
"fmt"
"github.com/feast-dev/feast/sdk/go/protos/feast/serving"
"github.com/opentracing/opentracing-go"
Expand All @@ -27,7 +28,7 @@ type GrpcClient struct {
type SecurityConfig struct {
// Whether to enable TLS SSL trasnport security if true.
EnableTLS bool
// Optional: Provides path to TLS certificate use the verify Service identity.
// Optional: Provides path to TLS certificate used the verify Service identity.
TLSCertPath string
// Optional: Credential used for authentication.
// Disables authentication if unspecified.
Expand All @@ -36,33 +37,42 @@ type SecurityConfig struct {

// NewGrpcClient constructs a client that can interact via grpc with the feast serving instance at the given host:port.
func NewGrpcClient(host string, port int) (*GrpcClient, error) {
return NewAuthGrpcClient(host, port, SecurityConfig{
return NewSecureGrpcClient(host, port, SecurityConfig{
EnableTLS: false,
Credential: nil,
})
}

// NewAuthGrpcClient constructs a client that can connect with feast serving instances with authentication enabled.
// NewAuthGrpcClient constructs a secure client that uses security features (ie authentication).
// host - hostname of the serving host/instance to connect to.
// port - post of the host to service host/instancf to connect to.
// securityConfig - security config configures client security.
func NewAuthGrpcClient(host string, port int, security SecurityConfig) (*GrpcClient, error) {
func NewSecureGrpcClient(host string, port int, security SecurityConfig) (*GrpcClient, error) {
feastCli := &GrpcClient{}
adr := fmt.Sprintf("%s:%d", host, port)

// Compile grpc dial options from security config.
options := []grpc.DialOption{grpc.WithStatsHandler(&ocgrpc.ClientHandler{})}
// Configure client TLS.
if !security.EnableTLS {
options = append(options, grpc.WithInsecure())
}
// Read TLS certificate from given path instead of using system certs if specified.
if security.EnableTLS && security.TLSCertPath != "" {
} else if security.EnableTLS && security.TLSCertPath != "" {
// Read TLS certificate from given path.
tlsCreds, err := credentials.NewClientTLSFromFile(security.TLSCertPath, "")
if err != nil {
return nil, err
}
options = append(options, grpc.WithTransportCredentials(tlsCreds))
} else {
// Use system TLS certificate pool.
certPool, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
tlsCreds := credentials.NewClientTLSFromCert(certPool, "")
options = append(options, grpc.WithTransportCredentials(tlsCreds))
}

// Enable authentication by attaching credentials if given
if security.Credential != nil {
options = append(options, grpc.WithPerRPCCredentials(security.Credential))
Expand Down
42 changes: 35 additions & 7 deletions sdk/java/src/main/java/com/gojek/feast/FeastClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@
import io.grpc.CallCredentials;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.net.ssl.SSLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -52,8 +56,8 @@ public class FeastClient implements AutoCloseable {
* @return {@link FeastClient}
*/
public static FeastClient create(String host, int port) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
return new FeastClient(channel, Optional.empty());
// configure client with no security config.
return FeastClient.createSecure(host, port, SecurityConfig.newBuilder().build());
}

/**
Expand All @@ -62,13 +66,37 @@ public static FeastClient create(String host, int port) {
*
* @param host hostname or ip address of Feast serving GRPC server
* @param port port number of Feast serving GRPC server
* @param credentials Call credentials used to provide credentials when calling Feast.
* @param securityConfig security options to configure the Feast client. See {@link
* SecurityConfig} for options.
* @return {@link FeastClient}
*/
public static FeastClient createAuthenticated(
String host, int port, CallCredentials credentials) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
return new FeastClient(channel, Optional.of(credentials));
public static FeastClient createSecure(String host, int port, SecurityConfig securityConfig) {
// Configure client TLS
ManagedChannel channel = null;
if (securityConfig.isTLSEnabled()) {
if (securityConfig.getCertificatePath().isPresent()) {
String certificatePath = securityConfig.getCertificatePath().get();
// Use custom certificate for TLS
File certificateFile = new File(certificatePath);
try {
channel =
NettyChannelBuilder.forAddress(host, port)
.useTransportSecurity()
.sslContext(GrpcSslContexts.forClient().trustManager(certificateFile).build())
.build();
} catch (SSLException e) {
throw new IllegalArgumentException(
String.format("Invalid Certificate provided at path: %s", certificatePath), e);
}
} else {
// Use system certificates for TLS
channel = ManagedChannelBuilder.forAddress(host, port).useTransportSecurity().build();
}
} else {
// Disable TLS
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
}
return new FeastClient(channel, securityConfig.getCredentials());
}

/**
Expand Down
58 changes: 58 additions & 0 deletions sdk/java/src/main/java/com/gojek/feast/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright 2018-2019 The Feast Authors
*
* 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
*
* https://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 com.gojek.feast;

import com.google.auto.value.AutoValue;
import io.grpc.CallCredentials;
import java.util.Optional;

/** SecurityConfig captures the security related configuration for FeastClient */
@AutoValue
public abstract class SecurityConfig {
/**
* Enables authentication If specified, the call credentials used to provide credentials to
* authenticate with Feast.
*/
public abstract Optional<CallCredentials> getCredentials();

/** Whether to use TLS transport security is use when connecting to Feast. */
public abstract boolean isTLSEnabled();

/**
* If specified and TLS is enabled, provides path to TLS certificate use the verify Service
* identity.
*/
public abstract Optional<String> getCertificatePath();

@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setCredentials(Optional<CallCredentials> credentials);

public abstract Builder setTLSEnabled(boolean isTLSEnabled);

public abstract Builder setCertificatePath(Optional<String> certificatePath);

public abstract SecurityConfig build();
}

public static SecurityConfig.Builder newBuilder() {
return new AutoValue_SecurityConfig.Builder()
.setCredentials(Optional.empty())
.setTLSEnabled(false)
.setCertificatePath(Optional.empty());
}
}