diff --git a/.travis.yml b/.travis.yml index 9eb6f44428aab..e7f428e80ef3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -139,6 +139,10 @@ script: presto-product-tests/bin/run_on_docker.sh \ singlenode-cassandra -g cassandra fi + if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT ]]; then + presto-product-tests/bin/run_on_docker.sh \ + multinode-tls-kerberos -g cli,group-by,join,tls + fi - | if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT_2 ]]; then presto-product-tests/bin/run_on_docker.sh \ diff --git a/presto-docs/src/main/sphinx/security/internal-communication.rst b/presto-docs/src/main/sphinx/security/internal-communication.rst index c7fdf4bf24dbb..30ef4c33f40e4 100644 --- a/presto-docs/src/main/sphinx/security/internal-communication.rst +++ b/presto-docs/src/main/sphinx/security/internal-communication.rst @@ -113,6 +113,26 @@ To enable SSL/TLS for Presto internal communication, do the following: internal-communication.https.keystore.key= +Internal SSL/TLS communication with Kerberos +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If :doc:`Kerberos` authentication is enabled, specify valid Kerberos +credentials for the internal communication, in addition to the SSL/TLS properties. + + .. code-block:: none + + internal-communication.kerberos.enabled=true + +.. note:: + + The service name and keytab file used for internal Kerberos authentication is + taken from server Kerberos authentication properties, documented in :doc:`Kerberos`, + ``http.server.authentication.krb5.service-name`` and ``http.server.authentication.krb5.keytab`` + respectively. Make sure you have the Kerberos setup done on the worker nodes as well. + The Kerberos principal for internal communication is built from + ``http.server.authentication.krb5.service-name`` after appending it with the hostname of + the node where Presto is running on and default realm from Kerberos configuration. + Performance with SSL/TLS enabled -------------------------------- diff --git a/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationConfig.java b/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationConfig.java index bba835864b579..3577f0839e2c6 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationConfig.java @@ -18,9 +18,13 @@ public class InternalCommunicationConfig { + public static final String INTERNAL_COMMUNICATION_KERBEROS_ENABLED = "internal-communication.kerberos.enabled"; + private boolean httpsRequired; private String keyStorePath; private String keyStorePassword; + private boolean kerberosEnabled; + private boolean kerberosUseCanonicalHostname = true; public boolean isHttpsRequired() { @@ -58,4 +62,28 @@ public InternalCommunicationConfig setKeyStorePassword(String keyStorePassword) this.keyStorePassword = keyStorePassword; return this; } + + public boolean isKerberosEnabled() + { + return kerberosEnabled; + } + + @Config(INTERNAL_COMMUNICATION_KERBEROS_ENABLED) + public InternalCommunicationConfig setKerberosEnabled(boolean kerberosEnabled) + { + this.kerberosEnabled = kerberosEnabled; + return this; + } + + public boolean isKerberosUseCanonicalHostname() + { + return kerberosUseCanonicalHostname; + } + + @Config("internal-communication.kerberos.use-canonical-hostname") + public InternalCommunicationConfig setKerberosUseCanonicalHostname(boolean kerberosUseCanonicalHostname) + { + this.kerberosUseCanonicalHostname = kerberosUseCanonicalHostname; + return this; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationModule.java b/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationModule.java new file mode 100644 index 0000000000000..554a9451daa50 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/server/InternalCommunicationModule.java @@ -0,0 +1,79 @@ +/* + * 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.facebook.presto.server; + +import com.google.inject.Binder; +import com.google.inject.Module; +import io.airlift.configuration.AbstractConfigurationAwareModule; +import io.airlift.http.client.HttpClientConfig; +import io.airlift.http.client.spnego.KerberosConfig; + +import java.io.UncheckedIOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Locale; + +import static com.facebook.presto.server.InternalCommunicationConfig.INTERNAL_COMMUNICATION_KERBEROS_ENABLED; +import static com.facebook.presto.server.security.KerberosConfig.HTTP_SERVER_AUTHENTICATION_KRB5_KEYTAB; +import static com.google.common.base.Verify.verify; +import static io.airlift.configuration.ConditionalModule.installModuleIf; +import static io.airlift.configuration.ConfigBinder.configBinder; + +public class InternalCommunicationModule + extends AbstractConfigurationAwareModule +{ + @Override + protected void setup(Binder binder) + { + InternalCommunicationConfig internalCommunicationConfig = buildConfigObject(InternalCommunicationConfig.class); + configBinder(binder).bindConfigGlobalDefaults(HttpClientConfig.class, config -> { + config.setKeyStorePath(internalCommunicationConfig.getKeyStorePath()); + config.setKeyStorePassword(internalCommunicationConfig.getKeyStorePassword()); + }); + + install(installModuleIf(InternalCommunicationConfig.class, InternalCommunicationConfig::isKerberosEnabled, kerberosInternalCommunicationModule())); + } + + private Module kerberosInternalCommunicationModule() + { + return binder -> { + InternalCommunicationConfig clientKerberosConfig = buildConfigObject(InternalCommunicationConfig.class); + com.facebook.presto.server.security.KerberosConfig serverKerberosConfig = buildConfigObject(com.facebook.presto.server.security.KerberosConfig.class); + verify(serverKerberosConfig.getKeytab() != null, "%s must be set when %s is true", HTTP_SERVER_AUTHENTICATION_KRB5_KEYTAB, INTERNAL_COMMUNICATION_KERBEROS_ENABLED); + + configBinder(binder).bindConfigGlobalDefaults(KerberosConfig.class, kerberosConfig -> { + kerberosConfig.setConfig(serverKerberosConfig.getKerberosConfig()); + kerberosConfig.setKeytab(serverKerberosConfig.getKeytab()); + kerberosConfig.setUseCanonicalHostname(clientKerberosConfig.isKerberosUseCanonicalHostname()); + }); + + String kerberosPrincipal = serverKerberosConfig.getServiceName() + "/" + getLocalCanonicalHostName(); + configBinder(binder).bindConfigGlobalDefaults(HttpClientConfig.class, httpClientConfig -> { + httpClientConfig.setAuthenticationEnabled(true); + httpClientConfig.setKerberosPrincipal(kerberosPrincipal); + httpClientConfig.setKerberosRemoteServiceName(serverKerberosConfig.getServiceName()); + }); + }; + } + + private static String getLocalCanonicalHostName() + { + try { + return InetAddress.getLocalHost().getCanonicalHostName().toLowerCase(Locale.US); + } + catch (UnknownHostException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java index aed9d66134630..186d443bca9c4 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java @@ -166,7 +166,6 @@ import io.airlift.configuration.AbstractConfigurationAwareModule; import io.airlift.discovery.client.ServiceDescriptor; import io.airlift.discovery.server.EmbeddedDiscoveryModule; -import io.airlift.http.client.HttpClientConfig; import io.airlift.slice.Slice; import io.airlift.stats.GcMonitor; import io.airlift.stats.JmxGcMonitor; @@ -244,11 +243,7 @@ protected void setup(Binder binder) // TODO: move to CoordinatorModule install(installModuleIf(EmbeddedDiscoveryConfig.class, EmbeddedDiscoveryConfig::isEnabled, new EmbeddedDiscoveryModule())); - InternalCommunicationConfig internalCommunicationConfig = buildConfigObject(InternalCommunicationConfig.class); - configBinder(binder).bindConfigGlobalDefaults(HttpClientConfig.class, config -> { - config.setKeyStorePath(internalCommunicationConfig.getKeyStorePath()); - config.setKeyStorePassword(internalCommunicationConfig.getKeyStorePassword()); - }); + install(new InternalCommunicationModule()); configBinder(binder).bindConfig(FeaturesConfig.class); diff --git a/presto-main/src/main/java/com/facebook/presto/server/security/KerberosConfig.java b/presto-main/src/main/java/com/facebook/presto/server/security/KerberosConfig.java index b12308b83ac45..48cd126b9e0f6 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/security/KerberosConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/server/security/KerberosConfig.java @@ -21,6 +21,8 @@ public class KerberosConfig { + public static final String HTTP_SERVER_AUTHENTICATION_KRB5_KEYTAB = "http.server.authentication.krb5.keytab"; + private File kerberosConfig; private String serviceName; private File keytab; @@ -56,7 +58,7 @@ public File getKeytab() return keytab; } - @Config("http.server.authentication.krb5.keytab") + @Config(HTTP_SERVER_AUTHENTICATION_KRB5_KEYTAB) public KerberosConfig setKeytab(File keytab) { this.keytab = keytab; diff --git a/presto-product-tests/README.md b/presto-product-tests/README.md index f432434bfb47b..61d2ae9eb75b4 100644 --- a/presto-product-tests/README.md +++ b/presto-product-tests/README.md @@ -150,6 +150,12 @@ where profile is one of either: multiple Docker containers. Presto is configured to only accept connections on the HTTPS port (7878), and both coordinator and worker traffic is encrypted. For multinode-tls, the default configuration is 1 coordinator and 2 workers. +- **multinode-tls-kerberos** - psuedo-distributed Hadoop installation running on a + single Docker container and a distributed installation of kerberized Presto + running on multiple Docker containers. Presto is configured to only accept + connections on the HTTPS port (7778), and both coordinator and worker traffic + is encrypted and kerberized. For multinode-tls-kerberos, the default configuration + is 1 coordinator and 2 workers. - **singlenode** - pseudo-distributed Hadoop installation running on a single Docker container and a single node installation of Presto also running on a single Docker container. diff --git a/presto-product-tests/bin/run_on_docker.sh b/presto-product-tests/bin/run_on_docker.sh index 05c711d614f77..3f86cda9ba73f 100755 --- a/presto-product-tests/bin/run_on_docker.sh +++ b/presto-product-tests/bin/run_on_docker.sh @@ -123,6 +123,8 @@ if [[ "$ENVIRONMENT" == "multinode" ]]; then PRESTO_SERVICES="${PRESTO_SERVICES} presto-worker" elif [[ "$ENVIRONMENT" == "multinode-tls" ]]; then PRESTO_SERVICES="${PRESTO_SERVICES} presto-worker-1 presto-worker-2" +elif [[ "$ENVIRONMENT" == "multinode-tls-kerberos" ]]; then + PRESTO_SERVICES="${PRESTO_SERVICES} presto-worker-1 presto-worker-2" fi # check docker and docker compose installation diff --git a/presto-product-tests/conf/docker/multinode-tls-kerberos/compose.sh b/presto-product-tests/conf/docker/multinode-tls-kerberos/compose.sh new file mode 100755 index 0000000000000..048647a041f95 --- /dev/null +++ b/presto-product-tests/conf/docker/multinode-tls-kerberos/compose.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +SCRIPT_DIRECTORY=${BASH_SOURCE%/*} + +source ${SCRIPT_DIRECTORY}/../common/compose-commons.sh + +docker-compose \ +-f ${SCRIPT_DIRECTORY}/../common/standard.yml \ +-f ${SCRIPT_DIRECTORY}/../common/kerberos.yml \ +-f ${SCRIPT_DIRECTORY}/docker-compose.yml \ +"$@" diff --git a/presto-product-tests/conf/docker/multinode-tls-kerberos/docker-compose.yml b/presto-product-tests/conf/docker/multinode-tls-kerberos/docker-compose.yml new file mode 100644 index 0000000000000..90afa44e703cc --- /dev/null +++ b/presto-product-tests/conf/docker/multinode-tls-kerberos/docker-compose.yml @@ -0,0 +1,55 @@ +version: '2' +services: + + presto-master: + domainname: docker.cluster + hostname: presto-master + image: '${HADOOP_BASE_IMAGE}-kerberized:${DOCKER_IMAGES_VERSION}' + command: /docker/volumes/conf/docker/files/presto-launcher-wrapper.sh multinode-tls-kerberos-master run + ports: + - '7778:7778' + networks: + default: + aliases: + - presto-master.docker.cluster + volumes: + - ../../../conf/presto/etc/environment-specific-catalogs/singlenode-kerberos-hdfs-no-impersonation/hive.properties:/docker/volumes/conf/presto/etc/catalog/hive.properties + + presto-worker-1: + domainname: docker.cluster + hostname: presto-worker-1 + image: '${HADOOP_BASE_IMAGE}-kerberized:${DOCKER_IMAGES_VERSION}' + extends: + file: ../common/standard.yml + service: java-8-base + command: /docker/volumes/conf/docker/files/presto-launcher-wrapper.sh multinode-tls-kerberos-worker run + networks: + default: + aliases: + - presto-worker-1.docker.cluster + depends_on: + - presto-master + volumes_from: + - presto-master + + presto-worker-2: + domainname: docker.cluster + hostname: presto-worker-2 + image: '${HADOOP_BASE_IMAGE}-kerberized:${DOCKER_IMAGES_VERSION}' + extends: + file: ../common/standard.yml + service: java-8-base + command: /docker/volumes/conf/docker/files/presto-launcher-wrapper.sh multinode-tls-kerberos-worker run + networks: + default: + aliases: + - presto-worker-2.docker.cluster + depends_on: + - presto-master + volumes_from: + - presto-master + + application-runner: + environment: + - TEMPTO_PROFILE_CONFIG_FILE=/docker/volumes/conf/tempto/tempto-configuration-for-docker-kerberos.yaml + - CLI_ARGUMENTS=--server https://presto-master.docker.cluster:7778 --keystore-path /docker/volumes/conf/presto/etc/docker.cluster.jks --keystore-password 123456 --krb5-config-path /etc/krb5.conf --krb5-principal presto-client/presto-master.docker.cluster@LABS.TERADATA.COM --krb5-keytab-path /etc/presto/conf/presto-client.keytab --krb5-remote-service-name presto-server --krb5-disable-remote-service-hostname-canonicalization diff --git a/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-master.properties b/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-master.properties new file mode 100644 index 0000000000000..8c68596a6893c --- /dev/null +++ b/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-master.properties @@ -0,0 +1,50 @@ +# +# 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. +# + +# +# WARNING +# ^^^^^^^ +# This configuration file is for development only and should NOT be used be +# used in production. For example configuration, see the Presto documentation. +# + +node.id=will-be-overwritten +node.environment=test +node.internal-address-source=FQDN + +coordinator=true +node-scheduler.include-coordinator=true +discovery-server.enabled=true +discovery.uri=https://presto-master.docker.cluster:7778 + +query.max-memory=1GB +query.max-memory-per-node=512MB + +http-server.http.enabled=false +http-server.https.enabled=true +http-server.https.port=7778 +http-server.https.keystore.path=/docker/volumes/conf/presto/etc/docker.cluster.jks +http-server.https.keystore.key=123456 + +http.authentication.krb5.config=/etc/krb5.conf +http-server.authentication.type=KERBEROS +http.server.authentication.krb5.service-name=presto-server +http.server.authentication.krb5.keytab=/etc/presto/conf/presto-server.keytab + +internal-communication.https.required=true +internal-communication.https.keystore.path=/docker/volumes/conf/presto/etc/docker.cluster.jks +internal-communication.https.keystore.key=123456 + +internal-communication.kerberos.enabled=true +internal-communication.kerberos.use-canonical-hostname=false diff --git a/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-worker.properties b/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-worker.properties new file mode 100644 index 0000000000000..cfd90bc75cfa6 --- /dev/null +++ b/presto-product-tests/conf/presto/etc/multinode-tls-kerberos-worker.properties @@ -0,0 +1,49 @@ +# +# 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. +# + +# +# WARNING +# ^^^^^^^ +# This configuration file is for development only and should NOT be used be +# used in production. For example configuration, see the Presto documentation. +# + +node.id=will-be-overwritten +node.environment=test +node.internal-address-source=FQDN + +coordinator=false +discovery-server.enabled=false +discovery.uri=https://presto-master.docker.cluster:7778 + +query.max-memory=1GB +query.max-memory-per-node=512MB + +http-server.http.enabled=false +http-server.https.enabled=true +http-server.https.port=7778 +http-server.https.keystore.path=/docker/volumes/conf/presto/etc/docker.cluster.jks +http-server.https.keystore.key=123456 + +http.authentication.krb5.config=/etc/krb5.conf +http-server.authentication.type=KERBEROS +http.server.authentication.krb5.service-name=presto-server +http.server.authentication.krb5.keytab=/etc/presto/conf/presto-server.keytab + +internal-communication.https.required=true +internal-communication.https.keystore.path=/docker/volumes/conf/presto/etc/docker.cluster.jks +internal-communication.https.keystore.key=123456 + +internal-communication.kerberos.enabled=true +internal-communication.kerberos.use-canonical-hostname=false diff --git a/presto-product-tests/conf/tempto/tempto-configuration-for-docker-kerberos.yaml b/presto-product-tests/conf/tempto/tempto-configuration-for-docker-kerberos.yaml index 54e2d063630d6..6e9c06e2a0403 100644 --- a/presto-product-tests/conf/tempto/tempto-configuration-for-docker-kerberos.yaml +++ b/presto-product-tests/conf/tempto/tempto-configuration-for-docker-kerberos.yaml @@ -24,6 +24,8 @@ databases: presto: host: presto-master.docker.cluster port: 7778 + http_port: 8080 + https_port: ${databases.presto.port} server_address: https://${databases.presto.host}:${databases.presto.port} # jdbc_user in here should satisfy two requirements in order to pass SQL standard access control checks in Presto: