All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.floragunn.searchguard.tools.SearchGuardAdmin Maven / Gradle / Ivy

There is a newer version: 5.0.0-rc1-6d
Show newest version
/*
 * Copyright 2015 floragunn UG (haftungsbeschränkt)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.floragunn.searchguard.tools;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.elasticsearch.action.WriteConsistencyLevel;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;

import com.floragunn.searchguard.ssl.SearchGuardSSLPlugin;
import com.floragunn.searchguard.ssl.util.SSLConfigConstants;

public class SearchGuardAdmin {

    public static void main(final String[] args) throws Exception {

        final HelpFormatter formatter = new HelpFormatter();
        Options options = new Options();
        options.addOption( "nhnv", "disable-host-name-verification", false, "Disable hostname verification" );
        options.addOption( "nrhn", "disable-resolve-hostname", false, "Disable hostname beeing resolved" );
        options.addOption(Option.builder("ts").longOpt("truststore").hasArg().argName("file").required().desc("Path to truststore (in JKS format").build());
        options.addOption(Option.builder("ks").longOpt("keystore").hasArg().argName("file").required().desc("Path to keystore (in JKS format").build());
        options.addOption(Option.builder("tspass").longOpt("truststore-password").hasArg().argName("password").desc("Truststore password").build());
        options.addOption(Option.builder("kspass").longOpt("keystore-password").hasArg().argName("password").desc("Keystore password").build());
        options.addOption(Option.builder("cd").longOpt("configdir").hasArg().argName("directory").desc("Directory for config files").build());
        options.addOption(Option.builder("h").longOpt("hostname").hasArg().argName("host").desc("Elasticsearch host").build());
        options.addOption(Option.builder("p").longOpt("port").hasArg().argName("port").desc("Elasticsearch port").build());
        options.addOption(Option.builder("cn").longOpt("clustername").hasArg().argName("clustername").desc("Clustername").build());
        options.addOption( "sniff", "enable-sniffing", false, "Enable client.transport.sniff" );
        options.addOption( "icl", "ignore-clustername", false, "Ignore clustername" );
        options.addOption(Option.builder("r").longOpt("retrieve").desc("retrieve current config").build());
        options.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").desc("file").build());
        options.addOption(Option.builder("t").longOpt("type").hasArg().argName("file-type").desc("file-type").build());
        options.addOption(Option.builder("tsalias").longOpt("truststore-alias").hasArg().argName("alias").desc("Truststore alias").build());
        options.addOption(Option.builder("ksalias").longOpt("keystore-alias").hasArg().argName("alias").desc("Keystore alias").build());
        options.addOption(Option.builder("ec").longOpt("enabled-ciphers").hasArg().argName("cipers").desc("Comma separated list of TLS ciphers").build());
        options.addOption(Option.builder("ep").longOpt("enabled-protocols").hasArg().argName("protocols").desc("Comma separated list of TLS protocols").build());
        
        String hostname = "localhost";
        int port = 9300;
        String kspass = "changeit";
        String tspass = kspass;
        String cd = ".";
        String ks;
        String ts;
        boolean nhnv = false;
        boolean nrhn = false;
        boolean sniff = false;
        boolean icl = false;
        String clustername = "elasticsearch";
        String file = null;
        String type = null;
        boolean retrieve = false;
        String ksAlias = null;
        String tsAlias = null;
        String[] enabledProtocols = new String[0];
        String[] enabledCiphers = new String[0];
        
        CommandLineParser parser = new DefaultParser();
        try {
            CommandLine line = parser.parse( options, args );
            hostname = line.getOptionValue("h", hostname);
            port = Integer.parseInt(line.getOptionValue("p", String.valueOf(port)));
            kspass = line.getOptionValue("kspass", kspass); //TODO null? //when no passwd is set
            tspass = line.getOptionValue("tspass", tspass); //TODO null? //when no passwd is set
            cd = line.getOptionValue("cd", cd);
            ks = line.getOptionValue("ks");
            ts = line.getOptionValue("ts");
            nhnv = line.hasOption("nhnv");
            nrhn = line.hasOption("nrhn");
            clustername = line.getOptionValue("cn", clustername);
            sniff = line.hasOption("sniff");
            icl = line.hasOption("icl");
            file = line.getOptionValue("f", file);
            type = line.getOptionValue("t", type);
            retrieve = line.hasOption("r");
            ksAlias = line.getOptionValue("ksalias", ksAlias);
            tsAlias = line.getOptionValue("tsalias", tsAlias);
            
            String enabledCiphersString = line.getOptionValue("ec", null);
            String enabledProtocolsString = line.getOptionValue("ep", null);
            
            if(enabledCiphersString != null) {
                enabledCiphers = enabledCiphersString.split(",");
            }
            
            if(enabledProtocolsString != null) {
                enabledProtocols = enabledProtocolsString.split(",");
            }
            
        }
        catch( ParseException exp ) {
            System.err.println("Parsing failed.  Reason: " + exp.getMessage());
            formatter.printHelp("sgadmin.sh", options, true);
            return;
        }
        
        
        System.out.println("Connect to "+hostname+":"+port);
        Socket socket = new Socket();
        
        try {
            
            socket.connect(new InetSocketAddress(hostname, port));
            
          } catch (java.net.ConnectException ex) {
            System.out.println("Seems there is no elasticsearch running on "+hostname+":"+port+" - Will exit");
            System.exit(-1);
          } finally {
              try {
                socket.close();
            } catch (Exception e) {
                //ignore
            }
          }

        final Settings.Builder settingsBuilder = Settings
                .builder()
                .put("path.home", ".")
                .put("path.conf", ".")
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_FILEPATH, ks)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_FILEPATH, ts)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_PASSWORD, kspass)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_PASSWORD, tspass)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION, !nhnv)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME, !nrhn)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENABLED, true)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_TYPE, ks.endsWith(".jks")?"JKS":"PKCS12")
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_TYPE, ts.endsWith(".jks")?"JKS":"PKCS12")
                
                .putArray(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENABLED_CIPHERS, enabledCiphers)
                .putArray(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENABLED_PROTOCOLS, enabledProtocols)
                
                .put("cluster.name", clustername)
                .put("client.transport.ignore_cluster_name", icl)
                .put("client.transport.sniff", sniff);
                
                if(ksAlias != null) {
                    settingsBuilder.put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_ALIAS, ksAlias);
                }
                
                if(tsAlias != null) {
                    settingsBuilder.put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_ALIAS, tsAlias);
                }
        
                Settings settings = settingsBuilder.build();  
              

        try (TransportClient tc = TransportClient.builder().settings(settings).addPlugin(SearchGuardSSLPlugin.class)
                .build()
                .addTransportAddress(new InetSocketTransportAddress(new InetSocketAddress(hostname, port)))) {

            final ClusterHealthResponse chr = tc.admin().cluster().health(new ClusterHealthRequest().waitForYellowStatus()).actionGet();

            final boolean timedOut = chr.isTimedOut();

            
            if (timedOut) {
                System.out.println("Cluster state timeout");
                System.exit(-1);
            }

            /*System.out.println(chr.getStatus());
            System.out.println(chr.getActivePrimaryShards());
            System.out.println(chr.getActiveShards());
            System.out.println(chr.getInitializingShards());
            System.out.println(chr.getNumberOfDataNodes());
            System.out.println(chr.getNumberOfPendingTasks());
            System.out.println(chr.getRelocatingShards());
            System.out.println(chr.getUnassignedShards());
            System.out.println(chr.getIndices());*/
            
            final boolean indexExists = tc.admin().indices().exists(new IndicesExistsRequest("searchguard")).actionGet().isExists();

            if (!indexExists) {
                System.out.print("searchguard index does not exists, attempt to create it ... ");

                final boolean indexCreated = tc.admin().indices().create(new CreateIndexRequest("searchguard")
                // .mapping("config", source)
                // .settings(settings)
                .settings("index.number_of_shards", 1, "index.number_of_replicas", chr.getNumberOfDataNodes()-1)
                        ).actionGet().isAcknowledged();

                if (indexCreated) {
                    System.out.println("done");
                } else {
                    System.out.println("failed");
                    return;// System.exit(-1);
                }

            } else {
                System.out.println("Index does already exists");
            }
            
            if(retrieve) {
                String date = new SimpleDateFormat("yyyy-MMM-dd_HH-mm-ss", Locale.ENGLISH).format(new Date());
                
                boolean success = retrieveFile(tc, cd+"/sg_config_"+date+".yml", "config");
                success = success & retrieveFile(tc, cd+"/sg_roles_"+date+".yml", "roles");
                success = success & retrieveFile(tc, cd+"/sg_roles_mapping_"+date+".yml", "rolesmapping");
                success = success & retrieveFile(tc, cd+"/sg_internal_users_"+date+".yml", "internalusers");
                success = success & retrieveFile(tc, cd+"/sg_action_groups_"+date+".yml", "actiongroups");
                System.exit(success?0:-1);
            }
            
            
            
            System.out.println("populate config ...");
            
            if(file != null) {
                if(type == null) {
                    System.out.println("type missing");
                    System.exit(-1);
                }
                
                if(!Arrays.asList(new String[]{"config", "roles", "rolesmapping", "internalusers","actiongroups" }).contains(type)) {
                    System.out.println("Invalid type '"+type+"'");
                    System.exit(-1);
                }
                
                boolean success = uploadFile(tc, file, type);
                System.exit(success?0:-1);
            }

            boolean success = uploadFile(tc, cd+"/sg_config.yml", "config");
            success = success & uploadFile(tc, cd+"/sg_roles.yml", "roles");
            success = success & uploadFile(tc, cd+"/sg_roles_mapping.yml", "rolesmapping");
            success = success & uploadFile(tc, cd+"/sg_internal_users.yml", "internalusers");
            success = success & uploadFile(tc, cd+"/sg_action_groups.yml", "actiongroups");
            
            System.out.println("Wait a short time ...");
            Thread.sleep(5000);
            System.out.println("Done with "+(success?"success":"failures"));
            System.exit(success?0:-1);
        }
        // TODO audit changes to searchguard index
    }
    
    private static boolean uploadFile(Client tc, String filepath, String type) {
        System.out.println("Will update '"+type+"' with "+filepath);
        try (Reader reader = new FileReader(filepath)) {

            final String id = tc
                    .index(new IndexRequest("searchguard").type(type).id("0").refresh(true)
                            .consistencyLevel(WriteConsistencyLevel.DEFAULT).source(readXContent(reader, XContentType.YAML)))
                            .actionGet().getId();

            if ("0".equals(id)) {
                System.out.println("   SUCC Configuration for '"+type+"' created or updated");
                return true;
            } else {
                System.out.println("   FAIL Configuration for '"+type+"' failed for unknown reasons. Pls. consult logfile of elasticsearch");
            }
        } catch (IOException e) {
            System.out.println("   FAIL Configuration for '"+type+"' failed because of "+e.toString());
        }
        
        return false;
    }
    
    private static boolean retrieveFile(Client tc, String filepath, String type) {
        System.out.println("Will retrieve '"+type+"' into "+filepath);
        try (Writer writer = new FileWriter(filepath)) {

            final GetResponse response = tc.get(new GetRequest("searchguard").type(type).id("0").refresh(true).realtime(false)).actionGet();

            if (response.isExists()) {
                if(response.isSourceEmpty()) {
                    System.out.println("   FAIL Configuration for '"+type+"' failed because of empty source");
                    return false;
                }
                
                String json = XContentHelper.convertToJson(response.getSourceAsBytesRef(), true, true);
                writer.write(json);
                System.out.println("   SUCC Configuration for '"+type+"' stored in "+filepath);
                return true;
            } else {
                System.out.println("   FAIL Get configuration for '"+type+"' because it does not exist");
            }
        } catch (IOException e) {
            System.out.println("   FAIL Get configuration for '"+type+"' failed because of "+e.toString());
        }
        
        return false;
    }

    private static BytesReference readXContent(final Reader reader, final XContentType xContentType) throws IOException {
        XContentParser parser = null;
        try {
            parser = XContentFactory.xContent(xContentType).createParser(reader);
            parser.nextToken();
            final XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.copyCurrentStructure(parser);
            return builder.bytes();
        } finally {
            if (parser != null) {
                parser.close();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy