-
Notifications
You must be signed in to change notification settings - Fork 65
TLS SSL support
R serve has built-in support for TLS/SSL. This page aims to document the concepts and details on how to use it.
Transport Layer Security (TLS) is a way to use encrypted channel for communication between two parties. All communication over this channel is encrypted and thus safe from eavesdropping. It is the successor of SSL (Secure Sockets Layer) and users typically encounter it as part of HTTPS which is a way to secure the HTTP protocol, but it can be used for any communication.
Rserve supports TLS encrypted channels in following ways:
-
QAP + TLS: encrypts the Rserve-native QAP protocol which is used to communicate between Rserve clients and the Rserve server. Configuration directive:
qap.tls.port
(andtls.port
for compatibility but discouraged for its ambiguity) -
QAP TLS upgrade: Rserve uses (unencrypted) QAP on the regular
qap.port
, but allowsCMD_switch
to switch from unencrypted to encrypted TLS communication. The switch is then optional and initiated by the client (discouraged, makes sense only if you need to support both clients with and without TLS support for historical reasons). Configuration directive:switch.qap.tls enable
, uses regularqap.port
-
HTTPS (HTTP + TLS): Rserve acts as a secure web-server using the HTTP protocol over TLS channel. Configuration directive:
http.tls.port
(andhttps.port
for compatibility) -
WebSockets + TLS: encrypted WebSockets connection that uses QAP wrapped in WebSockets protocol (see rserve.js). Configuration directive:
websockets.tls.port
All above servers can be enabled in addition to any other existing modes, so let's say if you want to only enable HTTPS on its default port would would use http.tls.port 443
and qap disable
to disable the non-encrypted QAP.
Search for tls.port
in the NEWS file for more details which serves as the official documentation.
In addition to enabling the corresponding server by setting one of the *.tls.port
options, you have to also set the following configuration options:
-
tls.key
: mandatory, must point to the TLS private key in PEM format -
tls.cert
: mandatory, must point to the certificate that matches the above key, also in PEM format -
tls.ca
: optional, if you certificate requires additional certificate authority (CA) certificates then you must provide them here
If you are using a self-signed certificate then you only only need to set tls.key
and tls.cert
. Note, however, that the certificate validation will always fail, so, e.g., if you use RSclient::RS.connect
you have to also set verify=FALSE
(or set ca=
to the same value as tls.cert
), otherwise the connection will fail.
If you use a real certificate, you almost always have to also set tls.ca
, because certificate authorities (CAs) no longer issue certificates that are issued by the root authorities. Therefore they will supply not only the certificate for your server, but also a chain of intermediate certificates. Those are simply concatenated certificates on the CA in one PEM file. If the CA gave you multiple, just create one file by concatenating all of them into one file.
Hint: very often nowadays the server certificate and the CA certificates are concatenated into a single file often called "full.pem" (e.g., if you use Let's Encrypt). In that case you can set both tls.cert
and tls.ca
to that same file as long as the server certificate is the first one.
Since Rserve 1.8-8 client certificates are supported. Previous version would accept them but they were not checked. Starting with version 1.8-8 the tls.client
configuration defines whether client certificates are validated, required or what conditions they have to fulfill. The most permissive setting is tls.client=none
which does not request nor check the client certificate. This is appropriate if your server requires no client certificate authentication and where you want all clients to be accepted. The next levels is request
which asks the client for a certificate, but does not require that it is present. require
requests a client certificate and requires that it is valid using the server's chair of trust (see tls.ca
above). Additional entries match:
, prefix:
and suffix:
can be use to restrict which (valid) certificates are accepted based on their common name - see the NEWS entry for Rserve 1.8-8 for details.
First, your system must have the OpenSSL library and Rserve must be compiled against it in order to be able to use TLS support. When you install Rserve from sources you should see the following output during the installation:
checking openssl/rsa.h usability... yes
checking openssl/rsa.h presence... yes
checking for openssl/rsa.h... yes
checking for library containing RSA_generate_key... -lcrypto
checking openssl/ssl.h usability... yes
checking openssl/ssl.h presence... yes
checking for openssl/ssl.h... yes
checking for library containing SSL_CTX_load_verify_locations... -lssl
If any of the above fails, Rserve cannot use TLS. Rserve will fall back to unencrypted connections if the initialisation of TLS fails. This includes failures due invalid keys or certificates as well. Versions before Rserve 1.8-8 did so silently, so, unfortunately, there was no visual indication that TLS failed or TLS support was not present. From version 1.8-8 on there will be a warning, but Rserve will still fall back to unencrypted connections.
It is possible to test the TLS functionality using openssl s_client
command line tool. For example, with qap.tls.port 6312
you can test with
openssl s_client my.server.com:6312
if you want to use local server but with valid certificate you can also use
openssl s_client -servername my.server.com 127.0.0.1:6312
For a QAP+TLS connection the output should end with:
---
read R BLOCK
Rsrv0103QAP1
--------------
If the client connection gets aborted immediately on connect, check your tls.client
setting and set it to none
if you want all TLS clients to be accepted.
For security reasons it is sometimes desirable to use separate modules to split risk across layered processes. For example, if Rserve
is used in an enterprise environment where it has to run as root
to switch to users according to authentication, it is ofter desirable to shield this process from network access. This is possible by using local unix sockets for communication to the main Rserve
process and use a separate process for encryption and communication. Rserve provides a HTTP + WebSocket proxy for this purposes. The proxy implements the HTTP/WebSockets protocol and communicates with Rserve using local sockets and QAP protocol. The proxy also supports optional TLS layer in the same way as discussed above. The executable is called forward
and it installed as part of Rserve. You can locate it using
system.file("libs", "forward", package="Rserve")
in R. For more details see
$ forward -h
Usage: forward [-h] [-p <http-port>] [-w <ws-port>] [-s <QAP-socket>] [-R <Rscript-socket>] [-r <doc-root>]
[-k <TLS-key-path> [-c <TLS-cert-path>] [-C <TLS-CA-path>]] [-u <ulog-socket>] [-S <host>[:<port>]]]
Finally, another option often used with HTTP and WebSockets is nginx
reverse-proxy. It can be used to handle the encryption as well as load-balancing if multiple Rserve
s are required. Note that this only works for WebSocket connections, not for "naked" QAP protocol.
Sample nginx
configuration (from load-balanced RCloud deployment):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream rcloud_servers {
# least_conn is load balancing based on selecting a server from the pool which has the
# least active sessions from NginX. Default is round robin.
least_conn;
# List all of the rcloud server pool in the format <FQDN>:<PORT>
include /etc/rcloud-servers;
}
server {
listen 443;
server_name <<your-server-name-here>>;
location / {
try_files $uri $uri/ @proxy;
}
location @proxy {
proxy_pass http://rcloud_servers;
proxy_set_header X-Real-IP $remote_addr;
# allow connection upgrade to WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# we may serve long-running queries,
# so we set this really high for now ...
proxy_read_timeout 30m;
}
ssl on;
ssl_certificate /etc/.../fullchain.pem;
ssl_certificate_key /etc/.../privkey.pem;
}
The list of servers is expected in /etc/rcloud-servers
in the form <host>:<port>
and they are typically the forward
proxy setup to connect to local Rserve with unix sockets locally.
The REngine Java API does not have specific facilities for TLS support, but the RConnection(Socket)
API can use used to use regular Java TLS facilities with Rserve. For example:
SSLContext ctx = SSLContext.getInstance("TLS");
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket)factory.createSocket("myserver", 6312);
socket.startHandshake();
RConnection c = new RConnection(socket);
will connect to a TLS-enabled instance of Rserve, e.g.:
R CMD Rserve --RS-set qap=false --RS-set tls.key=server.key \
--RS-set tls.cert=server.crt --RS-set tls.ca=ca.cert.pem \
--RS-set qap.tls.port=6312
Refer to Java documentation on key stores and trust stores on how to manage them.
If you don't want to use public key infrastructure and manage your own keys and certificates instead, here is a simple example using openssl
and keytool
:
## The first two steps establish your own Certificate Authority (needed only once)
## generate CA key
openssl genrsa -out ca.key 4096
## create a CA certificate
openssl req -new -x509 -days 1825 -key ca.key -out ca.cert.pem
## create a Java trust store which trusts your CA
keytool -importcert -file ca.cert.pem -alias ca -storepass changeit -keystore truststore.jks
## NOTE: the password is not a joke, that is the default trust store password in Java
## You can use this trust store with:
## java -Djavax.net.ssl.trustStore=truststore.jks ...
## now you can generate new server (or client) certificates
## generate server key
openssl genrsa -out server.key 2048
## generate certificate signing request (CSR)
## common name (CN) should match the host name you will use
openssl req -new -key server.key -out server.csr
## sign that certificate with your CA key and certificate
openssl x509 -req -days 365 -in server.csr -CA ca.cert.pem -CAkey ca.key -CAcreateserial -out server.crt