Skip to content

Configure a `DataSource` to connect to a managed Postgres server on Digital Ocean with SSL/TLS encryption

I am trying the managed database service on DigitalOcean.com, having created a Postgres instance.

Digital Ocean defaults to requiring remote connections be made with SSL (actually TLS) encryption.

How does one configure a JDBC DataSource implementation for providing connections to such a database server?

This Question is similar to Produce a DataSource object for Postgres JDBC, programmatically but adds the specific elements of SSL/TLS encryption and using a managed database remotely.

Answer

Obtaining a DataSource implementation

Usually your JDBC driver provides an implementation of javax.sql.DataSource.

For more details, see my Answer to a similar Question.

PGSimpleDataSource

If using the JDBC driver from jdbc.postgresql.org, you can use the org.postgresql.ds.PGSimpleDataSource class as your DataSource implementation.

Instantiate an object of type PGSimpleDataSource, then call setter methods to provide all the info needed to connect to your database server. The web page on the DigitalOcean site lists all these pieces of info for your particular database instance.

Basically this:

PGSimpleDataSource dataSource = new PGSimpleDataSource();

dataSource.setServerName( "your-server-address-goes-here" );
dataSource.setPortNumber( your-port-number-goes-here );
dataSource.setDatabaseName( "defaultdb" );
dataSource.setUser( "doadmin" );
dataSource.setPassword( "your-password-goes-here" );

Plural server names and port numbers

Unfortunately, the singular version of the methods setServerName and setPortNumber are deprecated. While annoying, we should probably use the plural version of those methods (setServerNames & setPortNumbers) that each take an array. We populate a pair of single-element arrays, String[] and int[], with our server’s address and port number using array literals:

  • { “your-server-address-goes-here” }
  • { your-port-number-goes-here }
PGSimpleDataSource dataSource = new PGSimpleDataSource();

String[] serverAddresses = { "your-server-address-goes-here" };
dataSource.setServerNames( serverAddresses );
int[] serverPortNumbers = { your-port-number-goes-here };
dataSource.setPortNumbers( serverPortNumbers );
dataSource.setDatabaseName( "defaultdb" );
dataSource.setUser( "doadmin" );
dataSource.setPassword( "your-password-goes-here" );

SSL/TLS encryption

Lastly we need to add info for the SSL/TLS encryption addressed in the Question.

Ironically, do not call setSsl( true )

You would think we would call the setSsl method and pass true. But, no. While counter-intuitive, setting that to true will cause your connection attempts to fail. I do not know why that is. But trial-and-error led me to avoid that call.

CA certificate

In order for your client-side Java app to initiate the SSL/TLS connection, the JDBC driver must have access to the CA certificate used by Digital Ocean. On your Digital Ocean admin page, click the Download CA certificate link to download a tiny text file. That file will be named ca-certificate.crt.

We need to feed the text of that file to our JDBC driver as a string. Do the following to load the file into a text string.

// Get CA certificate used in TLS connections.
Path path = Paths.get( "/Users/basilbourque/Downloads/ca-certificate.crt" );
String cert = null;
try
{
    cert = Files.readString( path , StandardCharsets.UTF_8 );
    System.out.println( "cert = " + cert );
}
catch ( IOException ex )
{
    throw new IllegalStateException( "Unable to load the TLS certificate needed to make database connections." );
}
Objects.requireNonNull( cert );
if ( cert.isEmpty() ) {throw new IllegalStateException( "Failed to load TLS cert." ); }

Pass that CA certificate text to your JDBC driver with a call to DataSource::setSslCert. Note that we are not calling setSslRootCert. Do not mix up the two similarly-named methods.

dataSource.setSslCert( cert );

Testing the connection

Lastly, we can use the configured DataSource object to make a connection to the database. That code will look like this:

// Test connection
try (
        Connection conn = dataSource.getConnection() ;
        // …
)
{
    System.out.println( "DEBUG - Postgres connection made. " + Instant.now() );
    // …
}
catch ( SQLException e )
{
    e.printStackTrace();
}

Complete example code

Putting all that together, see something like this.

// Get CA certificate used in TLS connections.
Path path = Paths.get( "/Users/basilbourque/Downloads/ca-certificate.crt" );
String cert = null;
try
{
    cert = Files.readString( path , StandardCharsets.UTF_8 );
    System.out.println( "cert = " + cert );
}
catch ( IOException ex )
{
    throw new IllegalStateException( "Unable to load the TLS certificate needed to make database connections." );
}
Objects.requireNonNull( cert );
if ( cert.isEmpty() ) {throw new IllegalStateException( "Failed to load TLS cert." ); }

// PGSimpleDataSource configuration
PGSimpleDataSource dataSource = new PGSimpleDataSource();

String[] serverAddresses = { "your-server-address-goes-here" };
dataSource.setServerNames( serverAddresses );
int[] serverPortNumbers = { your-port-number-goes-here };
dataSource.setPortNumbers( serverPortNumbers );
dataSource.setSslCert( cert );
dataSource.setDatabaseName( "defaultdb" );
dataSource.setUser( "doadmin" );
dataSource.setPassword( "your-password-goes-here" );

// Test connection
try (
Connection conn = dataSource.getConnection() ;
// …
)
{
System.out.println( "DEBUG - Postgres connection made. " + Instant.now() );
// …
}
catch ( SQLException e )
{
e.printStackTrace();
}

Trusted source

When creating your Postgres instance on DigitalOcean.com, you will be offered the ability to restrict incoming connection requests. You can specify a single IP address to be expected by the Postgres server, a “trusted source” in Digital Ocean’s lingo. Any hackers attempting to connect to your Postgres server will be ignored as they originate from other IP addresses.

Tip: Click inside the data-entry field for this IP address number to have the web page automatically detect and offer the IP address being currently used by your web browser. If you are running your Java code from the same computer, use that same IP number as your single trusted source.

Otherwise, type in the IP address of the computer running your Java JDBC code.

Other issues

I have not addressed elaborations, such as proper security protocols to use to manage your CA certificate file, or such as externalizing your connection info by getting a DataSource object from a JNDI server rather than hard-coding. But hopefully the examples above help you get started.