Skip to content

Server Security Support

astarche edited this page Oct 7, 2021 · 2 revisions

Caution

grpc-device allows clients direct access to NI driver APIs. This allows the client to control connected hardware and, in some cases, allows unsafe operations and access to the underlying system. Always ensure that your grpc-device server is secure.

Introduction

The gRPC server supports both server-side TLS and mutual TLS. Security configuration is accomplished by setting the server_cert, server_key and root_cert values in the server's configuration file. The server expects the certificate files specified in the configuration file to exist in a certs folder that is located in the same directory as the configuration file being used by the server.

In the default case the server expects the certs folder to be located in the same folder as the server executable itself:

server_installation_folder/
├── certs/
│   ├── client_self_signed_crt.pem
│   ├── server_privatekey.pem
│   └── server_self_signed_crt.pem
├── ni_grpc_device_server
└── server_config.json

If a path to the configuration file is specified when starting the server then the certs folder must be located in the same location as the specified configuration file:

server_installation_folder/
└── ni_grpc_device_server

config_file_folder/
├── certs/
│   ├── client_self_signed_crt.pem
│   ├── server_privatekey.pem
│   └── server_self_signed_crt.pem
└── mutual_tls.json
  1. When none of the security-related configuration values are set then the server defaults to an insecure (no SSL/TLS) configuration. Additionally, if one of the server_cert or server_key values is set but not the other then the server will also default to an insecure configuration. Specifying one of the two is considered an incomplete configuration.
  2. To configure the server for server-side TLS then set both the server_cert and server_key values. In this configuration only the identity of the server is verified:
{
   "port": 31763,
   "security" : {
      "server_cert": "server_self_signed_crt.pem",
      "server_key": "server_privatekey.pem",
      "root_cert": ""
   }
}
  1. To configure the server for mutual TLS then set the server_cert, server_key and root_cert values. This configuration verifies the identity of the client in addition to the identity of the server. When the root_cert is specified the server always requests a client certificate:
{
   "port": 31763,
   "security" : {
      "server_cert": "server_self_signed_crt.pem",
      "server_key": "server_privatekey.pem",
      "root_cert": "client_self_signed_crt.pem"
   }
}

Note: The server's configuration (insecure, server-side TLS or mutual TLS) will always be printed to the terminal when the server starts.

Creating Certificates

A detailed explanation of security considerations is outside of the scope of this document. To read more about SSL/TLS in gRPC refer to this document.

There are many tools available to produce certificate files for SSL/TLS. One such tool is openssl and below is a simple example that creates self-signed server and client certificates:

openssl genrsa -passout pass:1111 -des3 -out ca.key 2048
openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=US/ST=Texas/L=Austin/O=NI/OU=R&D/CN=MachineName"
openssl genrsa -passout pass:1111 -des3 -out server_privatekey.pem 2048
openssl req -passin pass:1111 -new -key server_privatekey.pem -out server_csr.pem -subj "/C=US/ST=Texas/L=Austin/O=NI/OU=R&D/CN=MachineName"
openssl x509 -req -passin pass:1111 -days 3650 -in server_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out server_self_signed_crt.pem
openssl rsa -passin pass:1111 -in server_privatekey.pem -out server_privatekey.pem
openssl genrsa -passout pass:1111 -des3 -out client_privatekey.pem 2048
openssl req -passin pass:1111 -new -key client_privatekey.pem -out client_csr.pem -subj "/C=US/ST=Texas/L=Austin/O=NI/OU=R&D/CN=MachineName"
openssl x509 -passin pass:1111 -req -days 3650 -in client_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out client_self_signed_crt.pem
openssl rsa -passin pass:1111 -in client_privatekey.pem -out client_privatekey.pem

Configuring the Client (Python Example)

The examples in this section make use of the certificates generated in the Creating Certificates section. Your specific configuration (i.e. the certificate files you use) will be dependent on how you generate the certificate files.

  1. To establish a connection to an insecure server then call the insecure_channel(..) method:
channel = grpc.insecure_channel(serverAddress)
  1. To establish a connection to a server configured for server-side TLS then call the secure_channel(..) method and set root_certificates to point to the server's self-signed certificate:
root_cert = open('server_self_signed_crt.pem', 'rb').read()
creds = grpc.ssl_channel_credentials(root_certificates=root_cert)
channel = grpc.secure_channel(serverAddress, creds)
  1. To establish a connection to a server configured for mutual TLS then call the secure_channel(..) method and set:

    • root_certificates to point to the server's self-signed certificate
    • certificate_chain to point to the client's self-signed certificate
    • private_key to point to the client's private key
root_cert = open('server_self_signed_crt.pem', 'rb').read()
client_cert = open('client_self_signed_crt.pem', 'rb').read()
client_key = open('client_privatekey.pem', 'rb').read()
creds = grpc.ssl_channel_credentials(root_certificates=root_cert , private_key=client_key, certificate_chain=client_cert)
channel = grpc.secure_channel(serverAddress, creds)

Note: When using the grpclib.client API with supporting files generated using betterproto the security configuration on the client side is different than the examples above. The grpclib.client API uses SSLContext from the Python ssl module. Below is an example configured for mutual TLS:

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_cert_chain("client_self_signed_crt.pem", "client_privatekey.pem")
ctx.load_verify_locations("server_self_signed_crt.pem")
ctx.set_alpn_protocols(['h2'])
channel = Channel(host="localhost", port=31763, ssl=ctx)

Troubleshooting Security Configuration

  1. If the server can't find the certificate file at the expected location then the server will fail to start and an error message will be printed to the terminal. In this case verify that the certificate file exists at the correct location and that the user has read permissions for the file.
  2. If a client fails to connect to the server the cause can be one of many reasons. To determine if the failure is related to the security configuration review the server's terminal output. SSL/TLS error messages will be printed to the terminal where the server is running. Below is an example error message on the server side when the server expects a client certificate but does not receive it:
E0301 12:10:55.011000000  1136 ssl_transport_security.cc:1455] Handshake failed with fatal error SSL_ERROR_SSL: error:100000c0:SSL routines:OPENSSL_internal:PEER_DID_NOT_RETURN_A_CERTIFICATE.
  1. One common error state occurs when a client creates a channel for a configuration different from the server. For example, the server might be configured for mutual TLS and the client configures an insecure channel (no security) or server-side TLS (no client certificate specified). In this case the client will typically see an UNAVAILABLE error status on the first RPC call. The gRPC connection utilizes lazy intialization and therefore the connection isn't established until the first RPC called is made. Below is an example call stack from a Python client:
<class 'grpc._channel._InactiveRpcError'>
Traceback (most recent call last):
  File "C:\Users\username\Desktop\Demo\client.py", line 22, in <module>
    response = server.IsReservedByClient(serverTypes.IsReservedByClientRequest(reservation_id=reservation_id, client_id=client_id))
  File "C:\Users\username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\grpc\_channel.py", line 923, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "C:\Users\username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\grpc\_channel.py", line 826, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
        status = StatusCode.UNAVAILABLE
        details = "failed to connect to all addresses"
        debug_error_string = "{"created":"@1614621623.440000000","description":"Failed to pick subchannel","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":5391,"referenced_errors":[{"created":"@1614621623.440000000","description":"failed to connect to all addresses","file":"src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc","file_line":398,"grpc_status":14}]}"

Table of Contents

Internal Development

Creating and Setting Up a gRPC Server

Server Security Support

Creating a gRPC Client

gRPC Client Examples

Session Utilities API Reference

Driver Documentation

gRPC API Differences From C API

Sharing Driver Sessions Between Clients

C API Docs
NI-DAQmx
NI-DCPOWER
NI-DIGITAL PATTERN DRIVER
NI-DMM
NI-FGEN
NI-FPGA
NI-RFmx Bluetooth
NI-RFmx NR
NI-RFmx WCDMA
NI-RFmx GSM
NI-RFmx CDMA2k
NI-RFmx Instr
NI-RFmx LTE
NI-RFmx SpecAn
NI-RFmx TD-SCDMA
NI-RFmx WLAN
NI-RFSA
NI-RFSG
NI-SCOPE
NI-SWITCH
NI-TCLK
NI-XNET
Clone this wiki locally