org.elasticsearch.plugin.readonlyrest.SSLEngineProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.elasticsearch.plugin.readonlyrest.SSLEngineProvider.java

Source

/*
 *    This file is part of ReadonlyREST.
 *
 *    ReadonlyREST is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    ReadonlyREST is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with ReadonlyREST.  If not, see http://www.gnu.org/licenses/
 */

package org.elasticsearch.plugin.readonlyrest;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Singleton;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;

import javax.net.ssl.SSLException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.Key;
import java.security.PrivilegedAction;
import java.security.cert.Certificate;
import java.util.Base64;

@Singleton
public class SSLEngineProvider {
    public final ConfigurationHelper conf;
    private final Logger logger = Loggers.getLogger(this.getClass());
    private SslContext context = null;

    @Inject
    public SSLEngineProvider(Settings settings) throws Exception {
        this.conf = ConfigurationHelper.getInstance(settings, null);
        if (conf.sslEnabled) {
            if (!Strings.isNullOrEmpty(conf.sslCertChainPem) && !Strings.isNullOrEmpty(conf.sslPrivKeyPem)) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    @Override
                    public Void run() {
                        try {
                            logger.info("Loading SSL context with certChain=" + conf.sslCertChainPem + ", privKey="
                                    + conf.sslPrivKeyPem);
                            // #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
                            context = SslContextBuilder
                                    .forServer(new File(conf.sslCertChainPem), new File(conf.sslPrivKeyPem), null)
                                    .build();
                        } catch (SSLException e) {
                            logger.error("Failed to load SSL CertChain & private key!");
                            e.printStackTrace();
                        }
                        return null;
                    }
                });

                // Everything is configured
                logger.info("SSL configured through cert_chain and privkey");
                return;
            }

            logger.info("SSL cert_chain and privkey not configured, attempting with JKS keystore..");

            try {
                char[] keyStorePassBa = null;
                if (!Strings.isNullOrEmpty(conf.sslKeyStorePassword)) {
                    keyStorePassBa = conf.sslKeyStorePassword.toCharArray();
                }

                // Load the JKS keystore
                java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
                ks.load(new java.io.FileInputStream(conf.sslKeyStoreFile), keyStorePassBa);

                char[] keyPassBa = null;
                if (!Strings.isNullOrEmpty(conf.sslKeyPassword)) {
                    keyPassBa = conf.sslKeyPassword.toCharArray();
                }

                // Get PrivKey from keystore
                if (Strings.isNullOrEmpty(conf.sslKeyAlias)) {
                    if (ks.aliases().hasMoreElements()) {
                        String inferredAlias = ks.aliases().nextElement();
                        logger.info(
                                "SSL ssl.key_alias not configured, took first alias in keystore: " + inferredAlias);
                        conf.sslKeyAlias = inferredAlias;
                    } else {
                        throw new ElasticsearchException("No alias found, therefore key found in keystore!");
                    }
                }
                Key key = ks.getKey(conf.sslKeyAlias, keyPassBa);
                if (key == null) {
                    throw new ElasticsearchException(
                            "Private key not found in keystore for alias: " + conf.sslKeyAlias);
                }

                // Create a PEM of the private key
                StringBuilder sb = new StringBuilder();
                sb.append("---BEGIN PRIVATE KEY---\n");
                sb.append(Base64.getEncoder().encodeToString(key.getEncoded()));
                sb.append("\n");
                sb.append("---END PRIVATE KEY---");
                String privateKey = sb.toString();
                logger.info("Discovered key from JKS");

                // Get CertChain from keystore
                Certificate[] cchain = ks.getCertificateChain(conf.sslKeyAlias);

                // Create a PEM of the certificate chain
                sb = new StringBuilder();
                for (Certificate c : cchain) {
                    sb.append("-----BEGIN CERTIFICATE-----\n");
                    sb.append(Base64.getEncoder().encodeToString(c.getEncoded()));
                    sb.append("\n");
                    sb.append("-----END CERTIFICATE-----\n");
                }
                String certChain = sb.toString();
                logger.info("Discovered cert chain from JKS");

                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    @Override
                    public Void run() {
                        try {
                            // #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
                            context = SslContextBuilder
                                    .forServer(new ByteArrayInputStream(certChain.getBytes(StandardCharsets.UTF_8)),
                                            new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)),
                                            null)
                                    .build();
                        } catch (Exception e) {
                            logger.error("Failed to load SSL CertChain & private key from Keystore!");
                            e.printStackTrace();
                        }
                        return null;
                    }
                });

            } catch (Throwable t) {
                logger.error("Failed to load SSL certs and keys from JKS Keystore!");
                t.printStackTrace();
            }

        }
    }

    public SslContext getContext() throws Exception {
        if (conf.sslEnabled) {
            return context;
        } else
            throw new IllegalAccessException("SSL not enabled, cannot make a SSL engine");
    }
}