Setup HTTPs Authentication for Presto / Trino
Published: 2023-10-08 06:55:04

Version: Presto 0.261 / Trino 360

In this article, we are going to enable HTTPS-based front-end password authentication for the Presto CLI or JDBC and ODBC drivers. The Presto CLI sends the username and password to the Presto coordinator, who then validates those credentials using an external LDAP service or user password store file.

## Step 1: Generate TLS Java Keystore File
When using Kerberos and LDAP authentication, the Presto coordinator must be accessed via HTTPS. The Presto coordinator uses Java Keystore files for TLS configuration. These keys are generated using keytool and stored in the Java Keystore file of the Presto coordinator.
`keytool -genkeypair -alias presto_keystore -keyalg RSA -keystore /etc/presto_keystore.jks`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7fdl1qu512yw7asfhgn.png)

**Note: Please Make Sure CN is your IP address!**

## Step 2: Generate TLS Java Truststore File
The truststore file contains the certificate of a trusted TLS/SSL server or a certificate of a trusted certification authority that identifies the server.

To ensure secure access to the Presto coordinator over HTTPS, clients can configure a truststore.

In order for the Presto CLI to trust the Presto coordinator, the coordinator’s certificate must be imported into the CLI’s truststore.

```
keytool -exportcert -keystore /etc/presto_keystore.jks -alias presto_keystore -file /etc/alternatives/jre/lib/security/presto_certificate.cer -storepass {your password}
keytool -import -alias presto_trust -keystore /etc/alternatives/jre/lib/security/cacerts -file /etc/alternatives/jre/lib/security/presto_certificate.cer --storepass changeit -trustcacerts
# changeit is the default password for Java truststore
```

## Step 3: Install a trusted certificate to the client in code
vim ~/InstallCert.java

```
/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
 
public class InstallCert {
            
 
 public static void main(String[] args) throws Exception {
            
  String host;
  int port;
  char[] passphrase;
  if ((args.length == 1) || (args.length == 2)) {
            
   String[] c = args[0].split(":");
   host = c[0];
   port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
   String p = (args.length == 1) ? "changeit" : args[1];
   passphrase = p.toCharArray();
  } else {
            
   System.out
     .println("Usage: java InstallCert <host>[:port] [passphrase]");
   return;
  }
 
  File file = new File("jssecacerts");
  if (file.isFile() == false) {
            
   char SEP = File.separatorChar;
   File dir = new File(System.getProperty("java.home") + SEP + "lib"
     + SEP + "security");
   file = new File(dir, "jssecacerts");
   if (file.isFile() == false) {
            
    file = new File(dir, "cacerts");
   }
  }
  System.out.println("Loading KeyStore " + file + "...");
  InputStream in = new FileInputStream(file);
  KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
  ks.load(in, passphrase);
  in.close();
 
  SSLContext context = SSLContext.getInstance("TLS");
  TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
  tmf.init(ks);
  X509TrustManager defaultTrustManager = (X509TrustManager) tmf
    .getTrustManagers()[0];
  SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
  context.init(null, new TrustManager[] {
             tm }, null);
  SSLSocketFactory factory = context.getSocketFactory();
 
  System.out
    .println("Opening connection to " + host + ":" + port + "...");
  SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
  socket.setSoTimeout(10000);
  try {
            
   System.out.println("Starting SSL handshake...");
   socket.startHandshake();
   socket.close();
   System.out.println();
   System.out.println("No errors, certificate is already trusted");
  } catch (SSLException e) {
            
   System.out.println();
   e.printStackTrace(System.out);
  }
 
  X509Certificate[] chain = tm.chain;
  if (chain == null) {
            
   System.out.println("Could not obtain server certificate chain");
   return;
  }
 
  BufferedReader reader = new BufferedReader(new InputStreamReader(
    System.in));
 
  System.out.println();
  System.out.println("Server sent " + chain.length + " certificate(s):");
  System.out.println();
  MessageDigest sha1 = MessageDigest.getInstance("SHA1");
  MessageDigest md5 = MessageDigest.getInstance("MD5");
  for (int i = 0; i < chain.length; i++) {
            
   X509Certificate cert = chain[i];
   System.out.println(" " + (i + 1) + " Subject "
     + cert.getSubjectDN());
   System.out.println("   Issuer  " + cert.getIssuerDN());
   sha1.update(cert.getEncoded());
   System.out.println("   sha1    " + toHexString(sha1.digest()));
   md5.update(cert.getEncoded());
   System.out.println("   md5     " + toHexString(md5.digest()));
   System.out.println();
  }
 
  System.out
    .println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
  String line = reader.readLine().trim();
  int k;
  try {
            
   k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
  } catch (NumberFormatException e) {
            
   System.out.println("KeyStore not changed");
   return;
  }
 
  X509Certificate cert = chain[k];
  String alias = host + "-" + (k + 1);
  ks.setCertificateEntry(alias, cert);
 
  OutputStream out = new FileOutputStream("jssecacerts");
  ks.store(out, passphrase);
  out.close();
 
  System.out.println();
  System.out.println(cert);
  System.out.println();
  System.out
    .println("Added certificate to keystore 'jssecacerts' using alias '"
      + alias + "'");
 }
 
 private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
 
 private static String toHexString(byte[] bytes) {
            
  StringBuilder sb = new StringBuilder(bytes.length * 3);
  for (int b : bytes) {
            
   b &= 0xff;
   sb.append(HEXDIGITS[b >> 4]);
   sb.append(HEXDIGITS[b & 15]);
   sb.append(' ');
  }
  return sb.toString();
 }
 
 private static class SavingTrustManager implements X509TrustManager {
            
 
  private final X509TrustManager tm;
  private X509Certificate[] chain;
 
  SavingTrustManager(X509TrustManager tm) {
            
   this.tm = tm;
  }
 
  public X509Certificate[] getAcceptedIssuers() {
            
   throw new UnsupportedOperationException();
  }
 
  public void checkClientTrusted(X509Certificate[] chain, String authType)
    throws CertificateException {
            
   throw new UnsupportedOperationException();
  }
 
  public void checkServerTrusted(X509Certificate[] chain, String authType)
    throws CertificateException {
            
   this.chain = chain;
   tm.checkServerTrusted(chain, authType);
  }
 }
 
}
```

Compile InstallCert.java and run

`javac InstallCert.java
java InstallCert {coordinator hostname} or
java InstallCert {coordinator hostname}:port or
java InstallCert {coordinator hostname}:port password`

Enter 1, enter, and generate a certificate named jssecacerts in the current directory. Copy the certificate to the $JAVA_HOME/jre/lib/security directory

`mv jssecacerts /etc/alternatives/jre/lib/security/`

## Step 4. Repeat Step 1~3 on each of your worker node. Remember to replace IP in Step 1 with current node IP.

## Step 5. Configure Presto/Trino config.properties.
Remain the same on each of your coordinator and worker node.

`discovery.uri=http://ip-10-186-17-52.ap-northeast-1.compute.internal:9002
http-server.authentication.type = PASSWORD,CERTIFICATE
http-server.https.enabled = true
node.internal-address-source = FQDN
http-server.http.enabled = true
http-server.https.port = 8889
http-server.http.port = 9002
http-server.https.keystore.path = /etc/presto_keystore.jks
http-server.https.keystore.key = {replace with you keystore password}`

Create an password-authenticator.properties file on the coordinator

Here you can configure the use of LDAP or password file authentication. We will use password file authentication.

For more configuration details, please refer to the official website: https://prestodb.io/docs/current/security/ldap.html

`password-authenticator.name=file 
file.password-file=/etc/presto/conf/password.db`

## Step 6. Create Password File
Create an empty password file to get started:
`touch password.db`
Add or update password for user ‘test’
`htpasswd -B -C 10 password.db test`

## Step 7. Restart Presto/Trino
`systemctl restart presto-server
systemctl restart trino-server`

Congratulation!