Knox Apache

Apache Knox Gateway 0.9.x User’s Guide

Table Of Contents

Introduction

The Apache Knox Gateway is a system that provides a single point of authentication and access for Apache Hadoop services in a cluster. The goal is to simplify Hadoop security for both users (i.e. who access the cluster data and execute jobs) and operators (i.e. who control access and manage the cluster). The gateway runs as a server (or cluster of servers) that provide centralized access to one or more Hadoop clusters. In general the goals of the gateway are as follows:

Quick Start

Here are the steps to have Apache Knox up and running against a Hadoop Cluster:

  1. Verify system requirements
  2. Download a virtual machine (VM) with Hadoop
  3. Download Apache Knox Gateway
  4. Start the virtual machine with Hadoop
  5. Install Knox
  6. Start the LDAP embedded within Knox
  7. Start the Knox Gateway
  8. Do Hadoop with Knox

1 - Requirements

Java

Java 1.6 or later is required for the Knox Gateway runtime. Use the command below to check the version of Java installed on the system where Knox will be running.

java -version

Hadoop

Knox 0.9.0 supports Hadoop 2.x, the quick start instructions assume a Hadoop 2.x virtual machine based environment.

2 - Download Hadoop 2.x VM

The quick start provides a link to download Hadoop 2.0 based Hortonworks virtual machine Sandbox. Please note Knox supports other Hadoop distributions and is configurable against a full-blown Hadoop cluster. Configuring Knox for Hadoop 2.x version, or Hadoop deployed in EC2 or a custom Hadoop cluster is documented in advance deployment guide.

3 - Download Apache Knox Gateway

Download one of the distributions below from the Apache mirrors.

Apache Knox Gateway releases are available under the Apache License, Version 2.0. See the NOTICE file contained in each release artifact for applicable copyright attribution notices.

Verify

While recommended, verify is an optional step. You can verify the integrity of any downloaded files using the PGP signatures. Please read Verifying Apache HTTP Server Releases for more information on why you should verify our releases.

The PGP signatures can be verified using PGP or GPG. First download the KEYS file as well as the .asc signature files for the relevant release packages. Make sure you get these files from the main distribution directory linked above, rather than from a mirror. Then verify the signatures using one of the methods below.

% pgpk -a KEYS
% pgpv knox-0.9.0.zip.asc

or

% pgp -ka KEYS
% pgp knox-0.9.0.zip.asc

or

% gpg --import KEYS
% gpg --verify knox-0.9.0.zip.asc

4 - Start Hadoop virtual machine

Start the Hadoop virtual machine.

5 - Install Knox

The steps required to install the gateway will vary depending upon which distribution format (zip | rpm) was downloaded. In either case you will end up with a directory where the gateway is installed. This directory will be referred to as your {GATEWAY_HOME} throughout this document.

ZIP

If you downloaded the Zip distribution you can simply extract the contents into a directory. The example below provides a command that can be executed to do this. Note the {VERSION} portion of the command must be replaced with an actual Apache Knox Gateway version number. This might be 0.9.0 for example.

unzip knox-{VERSION}.zip

This will create a directory knox-{VERSION} in your current directory. The directory knox-{VERSION} will considered your {GATEWAY_HOME}

6 - Start LDAP embedded in Knox

Knox comes with an LDAP server for demonstration purposes. Note: If the tool used to extract the contents of the Tar or tar.gz file was not capable of making the files in the bin directory executable

cd {GATEWAY_HOME}
bin/ldap.sh start

7 - Create the Master Secret

Run the knoxcli create-master command in order to persist the master secret that is used to protect the key and credential stores for the gateway instance.

cd {GATEWAY_HOME}
bin/knoxcli.sh create-master

The cli will prompt you for the master secret (i.e. password).

7 - Start Knox

The gateway can be started using the provided shell script.

The server will discover the persisted master secret during start up and complete the setup process for demo installs. A demo install will consist of a knox gateway instance with an identity certificate for localhost. This will require clients to be on the same machine or to turn off hostname verification. For more involved deployments, See the Knox CLI section of this document for additional configuration options, including the ability to create a self-signed certificate for a specific hostname.

cd {GATEWAY_HOME}
bin/gateway.sh start

When starting the gateway this way the process will be run in the background. The log files will be written to {GATEWAY_HOME}/logs and the process ID files (PIDS) will b written to {GATEWAY_HOME}/pids.

In order to stop a gateway that was started with the script use this command.

cd {GATEWAY_HOME}
bin/gateway.sh stop

If for some reason the gateway is stopped other than by using the command above you may need to clear the tracking PID.

cd {GATEWAY_HOME}
bin/gateway.sh clean

NOTE: This command will also clear any .out and .err file from the {GATEWAY_HOME}/logs directory so use this with caution.

8 - Do Hadoop with Knox

Invoke the LISTSTATUS operation on WebHDFS via the gateway.

This will return a directory listing of the root (i.e. /) directory of HDFS.

curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/?op=LISTSTATUS'

The results of the above command should result in something to along the lines of the output below. The exact information returned is subject to the content within HDFS in your Hadoop cluster. Successfully executing this command at a minimum proves that the gateway is properly configured to provide access to WebHDFS. It does not necessarily provide that any of the other services are correct configured to be accessible. To validate that see the sections for the individual services in Service Details.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 760
Server: Jetty(6.1.26)

{"FileStatuses":{"FileStatus":[
{"accessTime":0,"blockSize":0,"group":"hdfs","length":0,"modificationTime":1350595859762,"owner":"hdfs","pathSuffix":"apps","permission":"755","replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"mapred","length":0,"modificationTime":1350595874024,"owner":"mapred","pathSuffix":"mapred","permission":"755","replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"hdfs","length":0,"modificationTime":1350596040075,"owner":"hdfs","pathSuffix":"tmp","permission":"777","replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"hdfs","length":0,"modificationTime":1350595857178,"owner":"hdfs","pathSuffix":"user","permission":"755","replication":0,"type":"DIRECTORY"}
]}}

Put a file in HDFS via Knox.

curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp/LICENSE?op=CREATE'

curl -i -k -u guest:guest-password -T LICENSE -X PUT \
    '{Value of Location header from response   above}'

Get a file in HDFS via Knox.

curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp/LICENSE?op=OPEN'

curl -i -k -u guest:guest-password -X GET \
    '{Value of Location header from command response above}'

Apache Knox Details

This section provides everything you need to know to get the Knox gateway up and running against a Hadoop cluster.

Hadoop

An existing Hadoop 2.x cluster is required for Knox to sit in front of and protect. It is possible to use a Hadoop cluster deployed on EC2 but this will require additional configuration not covered here. It is also possible to protect access to a services of a Hadoop cluster that is secured with Kerberos. This too requires additional configuration that is described in other sections of this guide. See Supported Services for details on what is supported for this release.

The Hadoop cluster should be ensured to have at least WebHDFS, WebHCat (i.e. Templeton) and Oozie configured, deployed and running. HBase/Stargate and Hive can also be accessed via the Knox Gateway given the proper versions and configuration.

The instructions that follow assume a few things:

  1. The gateway is not collocated with the Hadoop clusters themselves.
  2. The host names and IP addresses of the cluster services are accessible by the gateway where ever it happens to be running.

All of the instructions and samples provided here are tailored and tested to work “out of the box” against a Hortonworks Sandbox 2.x VM.

Apache Knox Directory Layout

Knox can be installed by expanding the zip/archive file.

The table below provides a brief explanation of the important files and directories within {GATEWWAY_HOME}

Directory Purpose
conf/ Contains configuration files that apply to the gateway globally (i.e. not cluster specific ).
data/ Contains security and topology specific artifacts that require read/write access at runtime
conf/topologies/ Contains topology files that represent Hadoop clusters which the gateway uses to deploy cluster proxies
data/security/ Contains the persisted master secret and keystore dir
data/security/keystores/ Contains the gateway identity keystore and credential stores for the gateway and each deployed cluster topology
data/services Contains service behavior definitions for the services currently supported.
bin/ Contains the executable shell scripts, batch files and JARs for clients and servers.
data/deployments/ Contains deployed cluster topologies used to protect access to specific Hadoop clusters.
lib/ Contains the JARs for all the components that make up the gateway.
dep/ Contains the JARs for all of the components upon which the gateway depends.
ext/ A directory where user supplied extension JARs can be placed to extends the gateways functionality.
pids/ Contains the process ids for running ldap and gateway servers
samples/ Contains a number of samples that can be used to explore the functionality of the gateway.
templates/ Contains default configuration files that can be copied and customized.
README Provides basic information about the Apache Knox Gateway.
ISSUES Describes significant know issues.
CHANGES Enumerates the changes between releases.
LICENSE Documents the license under which this software is provided.
NOTICE Documents required attribution notices for included dependencies.

Supported Services

This table enumerates the versions of various Hadoop services that have been tested to work with the Knox Gateway.

Service Version Non-Secure Secure HA
WebHDFS 2.4.0 y y y
WebHCat/Templeton 0.13.0 y y y
Oozie 4.0.0 y y y
HBase 0.98.0 y y y
Hive (via WebHCat) 0.13.0 y y y
Hive (via JDBC/ODBC) 0.13.0 y y y
Yarn ResourceManager 2.5.0 y y n
Storm 0.9.3 y n n

More Examples

These examples provide more detail about how to access various Apache Hadoop services via the Apache Knox Gateway.

Gateway Samples

The purpose of the samples within the {GATEWAY_HOME}/samples directory is to demonstrate the capabilities of the Apache Knox Gateway to provide access to the numerous APIs that are available from the service components of a Hadoop cluster.

Depending on exactly how your Knox installation was done, there will be some number of steps required in order fully install and configure the samples for use.

This section will help describe the assumptions of the samples and the steps to get them to work in a couple of different deployment scenarios.

Assumptions of the Samples

The samples were initially written with the intent of working out of the box for the various Hadoop demo environments that are deployed as a single node cluster inside of a VM. The following assumptions were made from that context and should be understood in order to get the samples to work in other deployment scenarios:

Steps for Demo Single Node Clusters

There should be little to do if anything in a demo environment that has been provisioned with illustrating the use of Apache Knox.

However, the following items will be worth ensuring before you start:

  1. The sandbox.xml topology is configured properly for the deployed services
  2. That there is a LDAP server running with guest/guest-password user available in the directory

Steps for Ambari Deployed Knox Gateway

Apache Knox instances that are under the management of Ambari are generally assumed not to be demo instances. These instances are in place to facilitate development, testing or production Hadoop clusters.

The Knox samples can however be made to work with Ambari managed Knox instances with a few steps:

  1. You need to have ssh access to the environment in order for the localhost assumption within the samples to be valid.
  2. The Knox Demo LDAP Server is started - you can start it from Ambari
  3. The default.xml topology file can be copied to sandbox.xml in order to satisfy the topology name assumption in the samples.
  4. Be sure to use an actual Java JRE to run the sample with something like:

    /usr/jdk64/jdk1.7.0_67/bin/java -jar bin/shell.jar samples/ExampleWebHdfsLs.groovy

Steps for a Manually Installed Knox Gateway

For manually installed Knox instances, there is really no way for the installer to know how to configure the topology file for you.

Essentially, these steps are identical to the Ambari deployed instance except that #3 should be replaced with the configuration of the out of the box sandbox.xml to point the configuration at the proper hosts and ports.

  1. You need to have ssh access to the environment in order for the localhost assumption within the samples to be valid.
  2. The Knox Demo LDAP Server is started - you can start it from Ambari
  3. Change the hosts and ports within the {GATEWAY_HOME}/conf/topologies/sandbox.xml to reflect your actual cluster service locations.
  4. Be sure to use an actual Java JRE to run the sample with something like:

    /usr/jdk64/jdk1.7.0_67/bin/java -jar bin/shell.jar samples/ExampleWebHdfsLs.groovy

Gateway Details

This section describes the details of the Knox Gateway itself. Including:

URL Mapping

The gateway functions much like a reverse proxy. As such, it maintains a mapping of URLs that are exposed externally by the gateway to URLs that are provided by the Hadoop cluster.

Default Topology URLs

In order to provide compatibility with the Hadoop java client and existing CLI tools, the Knox Gateway has provided a feature called the Default Topology. This refers to a topology deployment that will be able to route URLs without the additional context that the gateway uses for differentiating from one Hadoop cluster to another. This allows the URLs to match those used by existing clients for that may access webhdfs through the Hadoop file system abstraction.

When a topology file is deployed with a file name that matches the configured default topology name, a specialized mapping for URLs is installed for that particular topology. This allows the URLs that are expected by the existing Hadoop CLIs for webhdfs to be used in interacting with the specific Hadoop cluster that is represented by the default topology file.

The configuration for the default topology name is found in gateway-site.xml as a property called: “default.app.topology.name”.

The default value for this property is “sandbox”.

Therefore, when deploying the sandbox.xml topology, both of the following example URLs work for the same underlying Hadoop cluster:

https://{gateway-host}:{gateway-port}/webhdfs
https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/webhdfs

These default topology URLs exist for all of the services in the topology.

Fully Qualified URLs

Examples of mappings for the WebHDFS, WebHCat, Oozie and HBase are shown below. These mapping are generated from the combination of the gateway configuration file (i.e. {GATEWAY_HOME}/conf/gateway-site.xml) and the cluster topology descriptors (e.g. {GATEWAY_HOME}/conf/topologies/{cluster-name}.xml). The port numbers shown for the Cluster URLs represent the default ports for these services. The actual port number may be different for a given cluster.

The values for {gateway-host}, {gateway-port}, {gateway-path} are provided via the gateway configuration file (i.e. {GATEWAY_HOME}/conf/gateway-site.xml).

The value for {cluster-name} is derived from the file name of the cluster topology descriptor (e.g. {GATEWAY_HOME}/deployments/{cluster-name}.xml).

The value for {webhdfs-host}, {webhcat-host}, {oozie-host}, {hbase-host} and {hive-host} are provided via the cluster topology descriptor (e.g. {GATEWAY_HOME}/conf/topologies/{cluster-name}.xml).

Note: The ports 50070, 50111, 11000, 8080 and 10001 are the defaults for WebHDFS, WebHCat, Oozie, HBase and Hive respectively. Their values can also be provided via the cluster topology descriptor if your Hadoop cluster uses different ports.

Note: The HBase REST API uses port 8080 by default. This often clashes with other running services. In the Hortonworks Sandbox Ambari might be running on this port so you might have to change it to a different port (e.g. 60080).

Configuration

Configuration for Apache Knox includes:

  1. Related Cluster Configuration that must be done within the Hadoop cluster to allow Knox to communicate with various services
  2. Gateway Server Configuration - which is the configurable elements of the server itself which applies to behavior that spans all topologies or managed Hadoop clusters
  3. Topology Descriptors which are the descriptors for controlling access to Hadoop clusters in various ways

Related Cluster Configuration

The following configuration changes must be made to your cluster to allow Apache Knox to dispatch requests to the various service components on behalf of end users.

Grant Proxy privileges for Knox user in core-site.xml on Hadoop master nodes

Update core-site.xml and add the following lines towards the end of the file.

Replace FQDN_OF_KNOX_HOST with the fully qualified domain name of the host running the Knox gateway. You can usually find this by running hostname -f on that host.

You can use * for local developer testing if the Knox host does not have a static IP.

<property>
    <name>hadoop.proxyuser.knox.groups</name>
    <value>users</value>
</property>
<property>
    <name>hadoop.proxyuser.knox.hosts</name>
    <value>FQDN_OF_KNOX_HOST</value>
</property>

Grant proxy privilege for Knox in webhcat-site.xml on Hadoop master nodes

Update webhcat-site.xml and add the following lines towards the end of the file.

Replace FQDN_OF_KNOX_HOST with the fully qualified domain name of the host running the Knox gateway. You can use * for local developer testing if the Knox host does not have a static IP.

<property>
    <name>webhcat.proxyuser.knox.groups</name>
    <value>users</value>
</property>
<property>
    <name>webhcat.proxyuser.knox.hosts</name>
    <value>FQDN_OF_KNOX_HOST</value>
</property>

Grant proxy privilege for Knox in oozie-site.xml on Oozie host

Update oozie-site.xml and add the following lines towards the end of the file.

Replace FQDN_OF_KNOX_HOST with the fully qualified domain name of the host running the Knox gateway. You can use * for local developer testing if the Knox host does not have a static IP.

<property>
    <name>oozie.service.ProxyUserService.proxyuser.knox.groups</name>
    <value>users</value>
</property>
<property>
    <name>oozie.service.ProxyUserService.proxyuser.knox.hosts</name>
    <value>FQDN_OF_KNOX_HOST</value>
</property>

Enable http transport mode and use substitution in HiveServer2

Update hive-site.xml and set the following properties on HiveServer2 hosts. Some of the properties may already be in the hive-site.xml. Ensure that the values match the ones below.

<property>
    <name>hive.server2.allow.user.substitution</name>
    <value>true</value>
</property>

<property>
    <name>hive.server2.transport.mode</name>
    <value>http</value>
    <description>Server transport mode. "binary" or "http".</description>
</property>

<property>
    <name>hive.server2.thrift.http.port</name>
    <value>10001</value>
    <description>Port number when in HTTP mode.</description>
</property>

<property>
    <name>hive.server2.thrift.http.path</name>
    <value>cliservice</value>
    <description>Path component of URL endpoint when in HTTP mode.</description>
</property>

Gateway Server Configuration

The following table illustrates the configurable elements of the Apache Knox Gateway at the server level via gateway-site.xml.

property description default
gateway.deployment.dir The directory within GATEWAY_HOME that contains gateway topology deployments. {GATEWAY_HOME}/data/deployments
gateway.security.dir The directory within GATEWAY_HOME that contains the required security artifacts {GATEWAY_HOME}/data/security
gateway.data.dir The directory within GATEWAY_HOME that contains the gateway instance data {GATEWAY_HOME}/data
gateway.services.dir The directory within GATEWAY_HOME that contains the gateway services definitions. {GATEWAY_HOME}/services
gateway.hadoop.conf.dir The directory within GATEWAY_HOME that contains the gateway configuration {GATEWAY_HOME}/conf
gateway.frontend.url The URL that should be used during rewriting so that it can rewrite the URLs with the correct “frontend” URL none
gateway.xforwarded.enabled Indicates whether support for some X-Forwarded-* headers is enabled true
gateway.trust.all.certs Indicates whether all presented client certs should establish trust false
gateway.client.auth.needed Indicates whether clients are required to establish a trust relationship with client certificates false
gateway.truststore.path Location of the truststore for client certificates to be trusted gateway.jks
gateway.truststore.type Indicates the type of truststore JKS
gateway.keystore.type Indicates the type of keystore for the identity store JKS
gateway.jdk.tls.ephemeralDHKeySize jdk.tls.ephemeralDHKeySize, is defined to customize the ephemeral DH key sizes. The minimum acceptable DH key size is 1024 bits, except for exportable cipher suites or legacy mode (jdk.tls.ephemeralDHKeySize=legacy) 2048
gateway.threadpool.max The maximum concurrent requests the server will process. The default is 254. Connections beyond this will be queued. 254
gateway.httpclient.maxConnections The maximum number of connections that a single httpclient will maintain to a single host:port. The default is 32. 32
gateway.httpclient.connectionTimeout The amount of time to wait when attempting a connection. The natural unit is milliseconds but a ‘s’ or ‘m’ suffix may be used for seconds or minutes respectively. The default timeout is system dependent. System Dependent
gateway.httpclient.socketTimeout The amount of time to wait for data on a socket before aborting the connection. The natural unit is milliseconds but a ‘s’ or ‘m’ suffix may be used for seconds or minutes respectively. The default timeout is system dependent but is likely to be indefinite. System Dependent
gateway.httpserver.requestBuffer The size of the HTTP server request buffer. The default is 16K. 16384
gateway.httpserver.requestHeaderBuffer The size of the HTTP server request header buffer. The default is 8K. 8192
gateway.httpserver.responseBuffer The size of the HTTP server response buffer. The default is 32K. 32768
gateway.httpserver.responseHeaderBuffer The size of the HTTP server response header buffer. The default is 8K. 8192
ssl.enabled Indicates whether SSL is enabled for the Gateway true
ssl.include.ciphers A comma separated list of ciphers to accept for SSL. See the JSSE Provider docs for possible ciphers. These can also contain regular expressions as shown in the Jetty documentation. all
ssl.exclude.ciphers A comma separated list of ciphers to reject for SSL. See the JSSE Provider docs for possible ciphers. These can also contain regular expressions as shown in the Jetty documentation. none
ssl.exclude.protocols Excludes a comma separated list of protocols to not accept for SSL or “none” SSLv3

Topology Descriptors

The topology descriptor files provide the gateway with per-cluster configuration information. This includes configuration for both the providers within the gateway and the services within the Hadoop cluster. These files are located in {GATEWAY_HOME}/conf/topologies. The general outline of this document looks like this.

<topology>
    <gateway>
        <provider>
        </provider>
    </gateway>
    <service>
    </service>
</topology>

There are typically multiple <provider> and <service> elements.

/topology
Defines the provider and configuration and service topology for a single Hadoop cluster.
/topology/gateway
Groups all of the provider elements
/topology/gateway/provider
Defines the configuration of a specific provider for the cluster.
/topology/service
Defines the location of a specific Hadoop service within the Hadoop cluster.
Provider Configuration

Provider configuration is used to customize the behavior of a particular gateway feature. The general outline of a provider element looks like this.

<provider>
    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>
    <param>
        <name></name>
        <value></value>
    </param>
</provider>
/topology/gateway/provider
Groups information for a specific provider.
/topology/gateway/provider/role
Defines the role of a particular provider. There are a number of pre-defined roles used by out-of-the-box provider plugins for the gateway. These roles are: authentication, identity-assertion, authentication, rewrite and hostmap
/topology/gateway/provider/name
Defines the name of the provider for which this configuration applies. There can be multiple provider implementations for a given role. Specifying the name is used identify which particular provider is being configured. Typically each topology descriptor should contain only one provider for each role but there are exceptions.
/topology/gateway/provider/enabled
Allows a particular provider to be enabled or disabled via true or false respectively. When a provider is disabled any filters associated with that provider are excluded from the processing chain.
/topology/gateway/provider/param
These elements are used to supply provider configuration. There can be zero or more of these per provider.
/topology/gateway/provider/param/name
The name of a parameter to pass to the provider.
/topology/gateway/provider/param/value
The value of a parameter to pass to the provider.
Service Configuration

Service configuration is used to specify the location of services within the Hadoop cluster. The general outline of a service element looks like this.

<service>
    <role>WEBHDFS</role>
    <url>http://localhost:50070/webhdfs</url>
</service>
/topology/service
Provider information about a particular service within the Hadoop cluster. Not all services are necessarily exposed as gateway endpoints.
/topology/service/role
Identifies the role of this service. Currently supported roles are: WEBHDFS, WEBHCAT, WEBHBASE, OOZIE, HIVE, NAMENODE, JOBTRACKER, RESOURCEMANAGER Additional service roles can be supported via plugins.
topology/service/url
The URL identifying the location of a particular service within the Hadoop cluster.

Hostmap Provider

The purpose of the Hostmap provider is to handle situations where host are known by one name within the cluster and another name externally. This frequently occurs when virtual machines are used and in particular when using cloud hosting services. Currently, the Hostmap provider is configured as part of the topology file. The basic structure is shown below.

<topology>
    <gateway>
        ...
        <provider>
            <role>hostmap</role>
            <name>static</name>
            <enabled>true</enabled>
            <param><name>external-host-name</name><value>internal-host-name</value></param>
        </provider>
        ...
    </gateway>
    ...
</topology>

This mapping is required because the Hadoop services running within the cluster are unaware that they are being accessed from outside the cluster. Therefore URLs returned as part of REST API responses will typically contain internal host names. Since clients outside the cluster will be unable to resolve those host name they must be mapped to external host names.

Hostmap Provider Example - EC2

Consider an EC2 example where two VMs have been allocated. Each VM has an external host name by which it can be accessed via the internet. However the EC2 VM is unaware of this external host name and instead is configured with the internal host name.

External HOSTNAMES:
ec2-23-22-31-165.compute-1.amazonaws.com
ec2-23-23-25-10.compute-1.amazonaws.com

Internal HOSTNAMES:
ip-10-118-99-172.ec2.internal
ip-10-39-107-209.ec2.internal

The Hostmap configuration required to allow access external to the Hadoop cluster via the Apache Knox Gateway would be this.

<topology>
    <gateway>
        ...
        <provider>
            <role>hostmap</role>
            <name>static</name>
            <enabled>true</enabled>
            <param>
                <name>ec2-23-22-31-165.compute-1.amazonaws.com</name>
                <value>ip-10-118-99-172.ec2.internal</value>
            </param>
            <param>
                <name>ec2-23-23-25-10.compute-1.amazonaws.com</name>
                <value>ip-10-39-107-209.ec2.internal</value>
            </param>
        </provider>
        ...
    </gateway>
    ...
</topology>
Hostmap Provider Example - Sandbox

The Hortonworks Sandbox 2.x poses a different challenge for host name mapping. This version of the Sandbox uses port mapping to make the Sandbox VM appear as though it is accessible via localhost. However the Sandbox VM is internally configured to consider sandbox.hortonworks.com as the host name. So from the perspective of a client accessing Sandbox the external host name is localhost. The Hostmap configuration required to allow access to Sandbox from the host operating system is this.

<topology>
    <gateway>
        ...
        <provider>
            <role>hostmap</role>
            <name>static</name>
            <enabled>true</enabled>
            <param>
                <name>localhost</name>
                <value>sandbox,sandbox.hortonworks.com</value>
            </param>
        </provider>
        ...
    </gateway>
    ...
</topology>
Hostmap Provider Configuration

Details about each provider configuration element is enumerated below.

topology/gateway/provider/role
The role for a Hostmap provider must always be hostmap.
topology/gateway/provider/name
The Hostmap provider supplied out-of-the-box is selected via the name static.
topology/gateway/provider/enabled
Host mapping can be enabled or disabled by providing true or false.
topology/gateway/provider/param
Host mapping is configured by providing parameters for each external to internal mapping.
topology/gateway/provider/param/name
The parameter names represent an external host names associated with the internal host names provided by the value element. This can be a comma separated list of host names that all represent the same physical host. When mapping from internal to external host name the first external host name in the list is used.
topology/gateway/provider/param/value
The parameter values represent the internal host names associated with the external host names provider by the name element. This can be a comma separated list of host names that all represent the same physical host. When mapping from external to internal host names the first internal host name in the list is used.

Logging

If necessary you can enable additional logging by editing the log4j.properties file in the conf directory. Changing the rootLogger value from ERROR to DEBUG will generate a large amount of debug logging. A number of useful, more fine loggers are also provided in the file.

Java VM Options

TODO - Java VM options doc.

Persisting the Master Secret

The master secret is required to start the server. This secret is used to access secured artifacts by the gateway instance. Keystore, trust stores and credential stores are all protected with the master secret.

You may persist the master secret by supplying the -persist-master switch at startup. This will result in a warning indicating that persisting the secret is less secure than providing it at startup. We do make some provisions in order to protect the persisted password.

It is encrypted with AES 128 bit encryption and where possible the file permissions are set to only be accessible by the user that the gateway is running as.

After persisting the secret, ensure that the file at config/security/master has the appropriate permissions set for your environment. This is probably the most important layer of defense for master secret. Do not assume that the encryption if sufficient protection.

A specific user should be created to run the gateway this user will be the only user with permissions for the persisted master file.

See the Knox CLI section for descriptions of the command line utilities related to the master secret.

Management of Security Artifacts

There are a number of artifacts that are used by the gateway in ensuring the security of wire level communications, access to protected resources and the encryption of sensitive data. These artifacts can be managed from outside of the gateway instances or generated and populated by the gateway instance itself.

The following is a description of how this is coordinated with both standalone (development, demo, etc) gateway instances and instances as part of a cluster of gateways in mind.

Upon start of the gateway server we:

  1. Look for an identity store at data/security/keystores/gateway.jks. The identity store contains the certificate and private key used to represent the identity of the server for SSL connections and signature creation.
  2. Look for a credential store at data/security/keystores/__gateway-credentials.jceks. This credential store is used to store secrets/passwords that are used by the gateway. For instance, this is where the passphrase for accessing the gateway-identity certificate is kept.

Upon deployment of a Hadoop cluster topology within the gateway we:

  1. Look for a credential store for the topology. For instance, we have a sample topology that gets deployed out of the box. We look for data/security/keystores/sandbox-credentials.jceks. This topology specific credential store is used for storing secrets/passwords that are used for encrypting sensitive data with topology specific keys.

By leveraging the algorithm described above we can provide a window of opportunity for management of these artifacts in a number of ways.

  1. Using a single gateway instance as a master instance the artifacts can be generated or placed into the expected location and then replicated across all of the slave instances before startup.
  2. Using an NFS mount as a central location for the artifacts would provide a single source of truth without the need to replicate them over the network. Of course, NFS mounts have their own challenges.
  3. Using the KnoxCLI to create and manage the security artifacts.

See the Knox CLI section for descriptions of the command line utilities related to the security artifact management.

Keystores

In order to provide your own certificate for use by the gateway, you will need to either import an existing key pair into a Java keystore or generate a self-signed cert using the Java keytool.

Importing a key pair into a Java keystore

One way to accomplish this is to start with a PKCS12 store for your key pair and then convert it to a Java keystore or JKS.

The following example uses openssl to create a PKCS12 encoded store from your provided certificate and private key that are in PEM format.

openssl pkcs12 -export -in cert.pem -inkey key.pem > server.p12

The next example converts the PKCS12 store into a Java keystore (JKS). It should prompt you for the keystore and key passwords for the destination keystore. You must use the master-secret for the keystore password and keep track of the password that you use for the key passphrase.

keytool -importkeystore -srckeystore server.p12 -destkeystore gateway.jks -srcstoretype pkcs12

While using this approach a couple of important things to be aware of:

  1. the alias MUST be “gateway-identity”. You may need to change it using keytool after the import of the PKCS12 store. You can use keytool to do this - for example:

    keytool -changealias -alias "1" -destalias "gateway-identity" -keystore gateway.jks -storepass {knoxpw}
    
  2. the name of the expected identity keystore for the gateway MUST be gateway.jks

  3. the passwords for the keystore and the imported key may both be set to the master secret for the gateway install. You can change the key passphrase after import using keytool as well. You may need to do this in order to provision the password in the credential store as described later in this section. For example:

    keytool -keypasswd -alias gateway-identity -keystore gateway.jks
    

NOTE: The password for the keystore as well as that of the imported key may be the master secret for the gateway instance or you may set the gateway-identity-passphrase alias using the Knox CLI to the actual key passphrase. See the Knox CLI section for details.

The following will allow you to provision the passphrase for the private key that you set during keystore creation above - it will prompt you for the actual passphrase.

bin/knoxcli.sh create-alias gateway-identity-passphrase
Generating a self-signed cert for use in testing or development environments
keytool -genkey -keyalg RSA -alias gateway-identity -keystore gateway.jks \
    -storepass {master-secret} -validity 360 -keysize 2048

Keytool will prompt you for a number of elements used will comprise the distinguished name (DN) within your certificate.

NOTE: When it prompts you for your First and Last name be sure to type in the hostname of the machine that your gateway instance will be running on. This is used by clients during hostname verification to ensure that the presented certificate matches the hostname that was used in the URL for the connection - so they need to match.

NOTE: When it prompts for the key password just press enter to ensure that it is the same as the keystore password. Which, as was described earlier, must match the master secret for the gateway instance. Alternatively, you can set it to another passphrase - take note of it and set the gateway-identity-passphrase alias to that passphrase using the Knox CLI.

See the Knox CLI section for descriptions of the command line utilities related to the management of the keystores.

Using a CA Signed Key Pair

For certain deployments a certificate key pair that is signed by a trusted certificate authority is required. There are a number of different ways in which these certificates are acquired and can be converted and imported into the Apache Knox keystore.

The following steps have been used to do this and are provided here for guidance in your installation. You may have to adjust according to your environment.

General steps:

  1. Stop Knox gateway and back up all files in {GATEWWAY_HOME}/data/security/keystores

    gateway.sh stop
    
  2. Create a new master key for Knox and persist it. The master key will be referred to in following steps as $master-key

    knoxcli.sh create-master -force
    
  3. Create identity keystore gateway.jks. cert in alias gateway-identity

    cd {GATEWWAY_HOME}/data/security/keystore  
    keytool -genkeypair -alias gateway-identity -keyalg RSA -keysize 1024 -dname "CN=$fqdn_knox,OU=hdp,O=sdge" -keypass $keypass -keystore gateway.jks -storepass $master-key -validity 300  
    

    NOTE: $fqdn_knox is the hostname of the Knox host. Some may choose $keypass to be the same as $master-key.

  4. Create credential store to store the $keypass in step 3. This creates __gateway-credentials.jceks file

    knoxcli.sh create-alias gateway-identity-passphrase --value $keypass
    
  5. Generate a certificate signing request from the gateway.jks

    keytool -keystore gateway.jks -storepass $master-key -alias gateway-identity -certreq -file knox.csr
    
  6. Send the knox.csr file to the CA authority and get back the signed certificate (knox.signed). You also need the CA certificate, which normally can be requested through an openssl command or web browser or from the CA.

  7. Import both the CA authority certificate (referred as corporateCA.cer) and the signed Knox certificate back into gateway.jks

    keytool -keystore gateway.jks -storepass $master-key -alias $hwhq -import -file corporateCA.cer  
    keytool -keystore gateway.jks -storepass $master-key -alias gateway-identity -import -file knox.signed  
    

    NOTE: Use any alias appropriate for the corporate CA.

  8. Restart Knox gateway. Check gateway.log to check whether the gateway started properly and clusters are deployed. You can check the timestamp on cluster deployment files

    ls -alrt {GATEWAY_HOME}/data/deployment
    
  9. Verify that clients can use the CA authority cert to access Knox (which is the goal of using public signed cert) using curl or a web browsers which has the CA certificate installed

    curl --cacert supwin12ad.cer -u hdptester:hadoop -X GET 'https://$fqdn_knox:8443/gateway/$topologyname/webhdfs/v1/tmp?op=LISTSTATUS'
    
Credential Store

Whenever you provide your own keystore with either a self-signed cert or an issued certificate signed by a trusted authority, you will need to set an alias for the gateway-identity-passphrase or create an empty credential store. This is necessary for the current release in order for the system to determine the correct password for the keystore and the key.

The credential stores in Knox use the JCEKS keystore type as it allows for the storage of general secrets in addition to certificates.

Keytool may be used to create credential stores but the Knox CLI section details how to create aliases. These aliases are managed within credential stores which are created by the CLI as needed. The simplest approach is to create the gateway-identity-passpharse alias with the Knox CLI. This will create the credential store if it doesn’t already exist and add the key passphrase.

See the Knox CLI section for descriptions of the command line utilities related to the management of the credential stores.

Provisioning of Keystores

Once you have created these keystores you must move them into place for the gateway to discover them and use them to represent its identity for SSL connections. This is done by copying the keystores to the {GATEWAY_HOME}/data/security/keystores directory for your gateway install.

Summary of Secrets to be Managed

  1. Master secret - the same for all gateway instances in a cluster of gateways
  2. All security related artifacts are protected with the master secret
  3. Secrets used by the gateway itself are stored within the gateway credential store and are the same across all gateway instances in the cluster of gateways
  4. Secrets used by providers within cluster topologies are stored in topology specific credential stores and are the same for the same topology across the cluster of gateway instances. However, they are specific to the topology - so secrets for one hadoop cluster are different from those of another. This allows for fail-over from one gateway instance to another even when encryption is being used while not allowing the compromise of one encryption key to expose the data for all clusters.

NOTE: the SSL certificate will need special consideration depending on the type of certificate. Wildcard certs may be able to be shared across all gateway instances in a cluster. When certs are dedicated to specific machines the gateway identity store will not be able to be blindly replicated as host name verification problems will ensue. Obviously, trust-stores will need to be taken into account as well.

Knox CLI

The Knox CLI is a command line utility for the management of various aspects of the Knox deployment. It is primarily concerned with the management of the security artifacts for the gateway instance and each of the deployed topologies or Hadoop clusters that are gated by the Knox Gateway instance.

The various security artifacts are also generated and populated automatically by the Knox Gateway runtime when they are not found at startup. The assumptions made in those cases are appropriate for a test or development gateway instance and assume ‘localhost’ for hostname specific activities. For production deployments the use of the CLI may aid in managing some production deployments.

The knoxcli.sh script is located in the {GATEWAY_HOME}/bin directory.

Help

bin/knoxcli.sh [--help]

prints help for all commands

Knox Version Info

bin/knoxcli.sh version [--help]

Displays Knox version information.

Master secret persistence

bin/knoxcli.sh create-master [--force][--help]

Creates and persists an encrypted master secret in a file within {GATEWAY_HOME}/data/security/master.

NOTE: This command fails when there is an existing master file in the expected location. You may force it to overwrite the master file with the --force switch. NOTE: this will require you to change passwords protecting the keystores for the gateway identity keystores and all credential stores.

Alias creation

bin/knoxcli.sh create-alias name [--cluster c] [--value v] [--generate] [--help]

Creates a password alias and stores it in a credential store within the {GATEWAY_HOME}/data/security/keystores dir.

argument description
name name of the alias to create
--cluster name of Hadoop cluster for the cluster specific credential store otherwise assumes that it is for the gateway itself
--value parameter for specifying the actual password otherwise prompted. Escape complex passwords or surround with single quotes.
--generate boolean flag to indicate whether the tool should just generate the value. This assumes that --value is not set - will result in error otherwise. User will not be prompted for the value when --generate is set.

Alias deletion

bin/knoxcli.sh delete-alias name [--cluster c] [--help]

Deletes a password and alias mapping from a credential store within {GATEWAY_HOME}/data/security/keystores.

argument description
name name of the alias to delete
--cluster name of Hadoop cluster for the cluster specific credential store otherwise assumes ’__gateway’

Alias listing

bin/knoxcli.sh list-alias [--cluster c] [--help]

Lists the alias names for the credential store within {GATEWAY_HOME}/data/security/keystores.

NOTE: This command will list the aliases in lowercase which is a result of the underlying credential store implementation. Lookup of credentials is a case insensitive operation - so this is not an issue.

argument description
--cluster name of Hadoop cluster for the cluster specific credential store otherwise assumes ’__gateway’

Self-signed cert creation

bin/knoxcli.sh create-cert [--hostname n] [--help]

Creates and stores a self-signed certificate to represent the identity of the gateway instance. This is stored within the {GATEWAY_HOME}/data/security/keystores/gateway.jks keystore.

argument description
--hostname name of the host to be used in the self-signed certificate. This allows multi-host deployments to specify the proper hostnames for hostname verification to succeed on the client side of the SSL connection. The default is ‘localhost’.

Topology Redeploy

bin/knoxcli.sh redeploy [--cluster c]

Redeploys one or all of the gateway’s clusters (a.k.a topologies).

Topology Listing

bin/knoxcli.sh list-topologies [--help]

Lists all of the topologies found in Knox’s topologies directory. Useful for specifying a valid –cluster argument.

Topology Validation

bin/knoxcli.sh validate-topology [--cluster c] [--path path] [--help]

This ensures that a cluster’s description (a.k. topology) follows the correct formatting rules. It is possible to specify a name of a cluster already in the topology directory, or a path to any file.

argument description
--cluster name of Hadoop cluster for which you want to validate
--path path to topology file that you wish to validate.

LDAP Authentication and Authorization

bin/knoxcli.sh user-auth-test [--cluster c] [--u username] [--p password] [--g] [--d] [--help]

This command will test a topology’s ability to connect, authenticate, and authorize a user with an LDAP server. The only required argument is the –cluster argument to specify the name of the topology you wish to use. The topology must be valid (passes validate-topology command). If a –u and –p argument are not specified, the command line will prompt for a username and password. If authentication is successful then the command will attempt to use the topology to do an LDAP group lookup. The topology must be configured correctly to do this. If it is not, groups will not return and no errors will be printed unless the --g command is specified. Currently this command only works if a topology supports the use of ShiroProvider for authentication.

argument description
--cluster Required; name of cluster for which you want to test authentication
--u Optional; username you wish you authenticate with.
--p Optional; password you wish to authenticate with
--g Optional; Specify that you are looking to return a user’s groups. If not specified, group lookup errors won’t return.
--d Optional; Print extra debug info on failed authentication

Topology LDAP Bind

bin/knoxcli.sh system-user-auth-test [--cluster c] [--d] [--help]

This command will test a given topology’s ability to connect, bind, and authenticate with the ldap server from the settings specified in the topology file. The bind currently only will with Shiro as the authentication provider. There are also two parameters required inside of the topology for these

argument description
--cluster Required; name of cluster for which you want to test authentication
--d Optional; Print extra debug info on failed authentication

Gateway Service Test

bin/knoxcli.sh service-test [--cluster c] [--hostname hostname] [--port port] [--u username] [--p password] [--d] [--help]

This will test a topology configuration’s ability to connect to multiple hadoop services. Each service found in a topology will be tested with multiple URLs. Results are printed to the console in JSON format..

argument description
--cluster Required; name of cluster for which you want to test authentication
--hostname Required; hostname of the cluster currently running on the machine
--port Optional; port that the cluster is running on. If not supplied CLI will try to read config files to find the port.
--u Required; username to authorize against Hadoop services
--p Required; password to match username
--d Optional; Print extra debug info on failed authentication

Admin API

Access to the administrator functions of Knox are provided by the Admin REST API.

Admin API URL

The URL mapping for the Knox Admin API is simple:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1

Please note that to access that admin API, the user attempting to connect must have admin credentials inside of the LDAP Server

API Documentation
Operations
  1. Server Version
  2. Topology Collection
  3. Topology
Server Version
Description

Calls to Knox and returns the gateway’s current version and the version hash inside of a JSON object.

Example Request URL

https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1/version

Example cURL Request

curl -u admin:admin-password -i -k https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1/version

Response
<ServerVersion>
    <version>{version-number}</version>
    <hash>{version-hash}</hash>
</ServerVersion>
Topology Collection
Description

Calls to Knox and return an array of JSON objects that represent the list of deployed topologies currently inside of the gateway.

Example Request URL

https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/{api-version}/topologies

Example cURL Request

curl -u admin:admin-password -i -k -H Accept:application/json https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1/topologies

Response
[  
  {  
    "href":"https://localhost:8443/gateway/admin/api/v1/topologies/_default",
    "name":"_default",
    "timestamp":"1405633120000",
    "uri":"https://localhost:8443/gateway/_default"
  },
  {  
    "href":"https://localhost:8443/gateway/admin/api/v1/topologies/admin",
    "name":"admin",
    "timestamp":"1406672646000",
    "uri":"https://localhost:8443/gateway/admin"
  }
]  
Topology
Description

Calls to Knox and return a JSON object that represents the requested topology

Example Request URL

https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1/topologies/{topology-name}

Example cURL Request

curl -u admin:admin-password -i -k -H Accept:application/json https://{gateway-host}:{gateway-port}/{gateway-path}/admin/api/v1/topologies/{topology-name}

Response

{ “name”: “admin”, “providers”: [{ “enabled”: true, “name”: “ShiroProvider”, “params”: { “sessionTimeout”: “30”, “main.ldapRealm”: “org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm”, “main.ldapRealm.userDnTemplate”: “uid={0},ou=people,dc=hadoop,dc=apache,dc=org”, “main.ldapRealm.contextFactory.url”: “ldap://localhost:33389", ”main.ldapRealm.contextFactory.authenticationMechanism“: ”simple“, ”urls./**“: ”authcBasic“ }, ”role“: ”authentication“ }, { ”enabled“: true, ”name“: ”AclsAuthz“, ”params“: { ”knox.acl“: ”admin;*;*“ }, ”role“: ”authorization“ }, { ”enabled“: true, ”name“: ”Default“, ”params“: {}, ”role“: ”identity-assertion“ }, { ”enabled“: true, ”name“: ”static“, ”params“: { ”localhost“: ”sandbox,sandbox.hortonworks.com“ }, ”role“: ”hostmap“ }], ”services“: [{ “name”: null, “params”: {}, “role”: “KNOX”, “url”: null }], ”timestamp“: 1406672646000, ”uri“: ”https://localhost:8443/gateway/admin" }

X-Forwarded-* Headers Support

Out-of-the-box Knox provides support for some X-Forwarded-* headers through the use of a Servlet Filter. Specifically the headers handled/populated by Knox are:

If this functionality can be turned off by a configuration setting in the file gateway-site.xml and redeploying the necessary topology/topologies.

The setting is (under the ‘configuration’ tag) :

<property>
    <name>gateway.xforwarded.enabled</name>
    <value>false</value>
</property>

If this setting is absent, the default behavior is that the X-Forwarded-* header support is on or in other words, ‘gateway.xforwarded.enabled’ is set to ‘true’ by default.

Header population

The following are the various rules for population of these headers:

X-Forwarded-For

This header represents a list of client IP addresses. If the header is already present Knox adds a comma separated value to the list. The value added is the client’s IP address as Knox sees it. This value is added to the end of the list.

X-Forwarded-Proto

The protocol used in the client request. If this header is passed into Knox its value is maintained, otherwise Knox will populate the header with the value ‘https’ if the request is a secure one or ‘http’ otherwise.

X-Forwarded-Port

The port used in the client request. If this header is passed into Knox its value is maintained, otherwise Knox will populate the header with the value of the port that the request was made coming into Knox.

X-Forwarded-Host

Represents the original host requested by the client in the Host HTTP request header. The value passed into Knox is maintained by Knox. If no value is present, Knox populates the header with the value of the HTTP Host header.

X-Forwarded-Server

The hostname of the server Knox is running on.

X-Forwarded-Context

This header value contains the context path of the request to Knox.

Authentication

There are two types of providers supported in Knox for establishing a user’s identity:

  1. Authentication Providers
  2. Federation Providers

Authentication providers directly accept a user’s credentials and validates them against some particular user store. Federation providers, on the other hand, validate a token that has been issued for the user by a trusted Identity Provider (IdP).

The current release of Knox ships with an authentication provider based on the Apache Shiro project and is initially configured for BASIC authentication against an LDAP store. This has been specifically tested against Apache Directory Server and Active Directory.

This section will cover the general approach to leveraging Shiro within the bundled provider including:

  1. General mapping of provider config to shiro.ini config
  2. Specific configuration for the bundled BASIC/LDAP configuration
  3. Some tips into what may need to be customized for your environment
  4. How to setup the use of LDAP over SSL or LDAPS

General Configuration for Shiro Provider

As is described in the configuration section of this document, providers have a name-value based configuration - as is the common pattern in the rest of Hadoop.

The following example shows the format of the configuration for a given provider:

<provider>
    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>
    <param>
        <name>{name}</name>
        <value>{value}</value>
    </param>
</provider>

Conversely, the Shiro provider currently expects a shiro.ini file in the web-inf directory of the cluster specific web application.

The following example illustrates a configuration of the bundled BASIC/LDAP authentication config in a shiro.ini file:

[urls]
/**=authcBasic
[main]
ldapRealm=org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.contextFactory.authenticationMechanism=simple
ldapRealm.contextFactory.url=ldap://localhost:33389
ldapRealm.userDnTemplate=uid={0},ou=people,dc=hadoop,dc=apache,dc=org

In order to fit into the context of an INI file format, at deployment time we interrogate the parameters provided in the provider configuration and parse the INI section out of the parameter names. The following provider config illustrates this approach. Notice that the section names in the above shiro.ini match the beginning of the param names that are in the following config:

<gateway>
    <provider>
        <role>authentication</role>
        <name>ShiroProvider</name>
        <enabled>true</enabled>
        <param>
            <name>main.ldapRealm</name>
            <value>org.apache.shiro.realm.ldap.JndiLdapRealm</value>
        </param>
        <param>
            <name>main.ldapRealm.userDnTemplate</name>
            <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
        </param>
        <param>
            <name>main.ldapRealm.contextFactory.url</name>
            <value>ldap://localhost:33389</value>
        </param>
        <param>
            <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
            <value>simple</value>
        </param>
        <param>
            <name>urls./**</name>
            <value>authcBasic</value>
        </param>
    </provider>

This happens to be the way that we are currently configuring Shiro for BASIC/LDAP authentication. This same config approach may be used to achieve other authentication mechanisms or variations on this one. We however have not tested additional uses for it for this release.

LDAP Configuration

This section discusses the LDAP configuration used above for the Shiro Provider. Some of these configuration elements will need to be customized to reflect your deployment environment.

main.ldapRealm - this element indicates the fully qualified class name of the Shiro realm to be used in authenticating the user. The class name provided by default in the sample is the org.apache.shiro.realm.ldap.JndiLdapRealm this implementation provides us with the ability to authenticate but by default has authorization disabled. In order to provide authorization - which is seen by Shiro as dependent on an LDAP schema that is specific to each organization - an extension of JndiLdapRealm is generally used to override and implement the doGetAuhtorizationInfo method. In this particular release we are providing a simple authorization provider that can be used along with the Shiro authentication provider.

main.ldapRealm.userDnTemplate - in order to bind a simple username to an LDAP server that generally requires a full distinguished name (DN), we must provide the template into which the simple username will be inserted. This template allows for the creation of a DN by injecting the simple username into the common name (CN) portion of the DN. This element will need to be customized to reflect your deployment environment. The template provided in the sample is only an example and is valid only within the LDAP schema distributed with Knox and is represented by the users.ldif file in the {GATEWAY_HOME}/conf directory.

main.ldapRealm.contextFactory.url - this element is the URL that represents the host and port of LDAP server. It also includes the scheme of the protocol to use. This may be either ldap or ldaps depending on whether you are communicating with the LDAP over SSL (highly recommended). This element will need to be customized to reflect your deployment environment..

main.ldapRealm.contextFactory.authenticationMechanism - this element indicates the type of authentication that should be performed against the LDAP server. The current default value is simple which indicates a simple bind operation. This element should not need to be modified and no mechanism other than a simple bind has been tested for this particular release.

urls./** - this element represents a single URL_Ant_Path_Expression and the value the Shiro filter chain to apply to it. This particular sample indicates that all paths into the application have the same Shiro filter chain applied. The paths are relative to the application context path. The use of the value authcBasic here indicates that BASIC authentication is expected for every path into the application. Adding an additional Shiro filter to that chain for validating that the request isSecure() and over SSL can be achieved by changing the value to ssl, authcBasic. It is not likely that you need to change this element for your environment.

Active Directory - Special Note

You would use LDAP configuration as documented above to authenticate against Active Directory as well.

Some Active Directory specific things to keep in mind:

Typical AD main.ldapRealm.userDnTemplate value looks slightly different, such as

cn={0},cn=users,DC=lab,DC=sample,dc=com

Please compare this with a typical Apache DS main.ldapRealm.userDnTemplate value and make note of the difference:

`uid={0},ou=people,dc=hadoop,dc=apache,dc=org`

If your AD is configured to authenticate based on just the cn and password and does not require user DN, you do not have to specify value for main.ldapRealm.userDnTemplate.

LDAP over SSL (LDAPS) Configuration

In order to communicate with your LDAP server over SSL (again, highly recommended), you will need to modify the topology file in a couple ways and possibly provision some keying material.

  1. main.ldapRealm.contextFactory.url must be changed to have the ldaps protocol scheme and the port must be the SSL listener port on your LDAP server.
  2. Identity certificate (keypair) provisioned to LDAP server - your LDAP server specific documentation should indicate what is required for providing a cert or keypair to represent the LDAP server identity to connecting clients.
  3. Trusting the LDAP Server’s public key - if the LDAP Server’s identity certificate is issued by a well known and trusted certificate authority and is already represented in the JRE’s cacerts truststore then you don’t need to do anything for trusting the LDAP server’s cert. If, however, the cert is selfsigned or issued by an untrusted authority you will need to either add it to the cacerts keystore or to another truststore that you may direct Knox to utilize through a system property.

Session Configuration

Knox maps each cluster topology to a web application and leverages standard JavaEE session management.

To configure session idle timeout for the topology, please specify value of parameter sessionTimeout for ShiroProvider in your topology file. If you do not specify the value for this parameter, it defaults to 30 minutes.

The definition would look like the following in the topoloogy file:

...
<provider>
    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>
    <param>
        <!--
        Session timeout in minutes. This is really idle timeout.
        Defaults to 30 minutes, if the property value is not defined.
        Current client authentication will expire if client idles
        continuously for more than this value
        -->
        <name>sessionTimeout</name>
        <value>30</value>
    </param>
<provider>
...

At present, ShiroProvider in Knox leverages JavaEE session to maintain authentication state for a user across requests using JSESSIONID cookie. So, a client that authenticated with Knox could pass the JSESSIONID cookie with repeated requests as long as the session has not timed out instead of submitting userid/password with every request. Presenting a valid session cookie in place of userid/password would also perform better as additional credential store lookups are avoided.

Advanced LDAP Authentication

The default configuration computes the bind DN for incoming user based on userDnTemplate. This does not work in enterprises where users could belong to multiple branches of LDAP tree. You could instead enable advanced configuration that would compute bind DN of incoming user with an LDAP search.

Problem with userDnTemplate based Authentication

UserDnTemplate based authentication uses configuration parameter ldapRealm.userDnTemplate. Typical value of userDNTemplate would look like uid={0},ou=people,dc=hadoop,dc=apache,dc=org.

To compute bind DN of the client, we swap the place holder {0} with login id provided by the client. For example, if the login id provided by the client is "guest’,
the computed bind DN would be uid=guest,ou=people,dc=hadoop,dc=apache,dc=org.

This keeps configuration simple.

However, this does not work if users belong to different branches of LDAP DIT. For example, if there are some users under ou=people,dc=hadoop,dc=apache,dc=org and some users under ou=contractors,dc=hadoop,dc=apache,dc=org,
we can not come up with userDnTemplate that would work for all the users.

Using advanced LDAP Authentication

With advanced LDAP authentication, we find the bind DN of the user by searching LDAP directory instead of interpolating bind DN from userDNTemplate.

Example search filter to find the client bind DN

Assuming

LDAP Filter for doing a search to find the bind DN would be

(&(uid=guest)(objectclass=person))

This could find bind DN to be

uid=guest,ou=people,dc=hadoop,dc=apache,dc=org

Please note that the userSearchAttributeName need not be part of bindDN.

For example, you could use

LDAP Filter for doing a search to find the bind DN would be

(&(email=bill.clinton@gmail.com)(objectclass=person))

This could find bind DN to be

uid=billc,ou=contractors,dc=hadoop,dc=apache,dc=org

Advanced LDAP configuration parameters

The table below provides a brief description and sample of the available advanced bind and search configuration parameters.

Parameter Description Default Sample
principalRegex Parses the principal for insertion into templates via regex. (.*) (.*?)\\(.*) (e.g. match US\tom: {0}=US\tom, {1}=US, {2}=tom)
userDnTemplate Direct user bind DN template. {0} cn={2},dc={1},dc=qa,dc=company,dc=com
userSearchBase Search based template. Used with config below. none dc={1},dc=qa,dc=company,dc=com
userSearchAttributeName Attribute name for simplified search filter. none sAMAccountName
userSearchAttributeTemplate Attribute template for simplified search filter. {0} {2}
userSearchFilter Advanced search filter template. Note & is &amp; in XML. none (&amp;(objectclass=person)(sAMAccountName={2}))
userSearchScope Search scope: subtree, onelevel, object. subtree onelevel

Advanced LDAP configuration combinations

There are also only certain valid combinations of advanced LDAP configuration parameters.

Advanced LDAP configuration precedence

The presence of multiple configuration combinations should be avoided. The rules below clarify which combinations take precedence when present.

  1. userSearchBase takes precedence over userDnTemplate
  2. userSearchFilter takes precedence over userSearchAttributeName

Example provider configuration to use advanced LDAP authentication

The example configuration appears verbose due to the presence of liberal comments and illustration of optional parameters and default values. The configuration that you would use could be much shorter if you rely on default values.

<provider>

    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>

    <param>
        <name>main.ldapRealm</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
    </param>

    <param>
        <name>main.ldapContextFactory</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory</value>
    </param>

    <param>
        <name>main.ldapRealm.contextFactory</name>
        <value>$ldapContextFactory</value>
    </param>

    <!-- update the value based on your ldap directory protocol, host and port -->
    <param>
        <name>main.ldapRealm.contextFactory.url</name>
        <value>ldap://hdp.example.com:389</value>
    </param>

    <!-- optional, default value: simple
         Update the value based on mechanisms supported by your ldap directory -->
    <param>
        <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
        <value>simple</value>
    </param>

    <!-- optional, default value: {0}
         update the value based on your ldap DIT(directory information tree).
         ignored if value is defined for main.ldapRealm.userSearchAttributeName -->
    <param>
        <name>main.ldapRealm.userDnTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- optional, default value: null
         If you specify a value for this attribute, useDnTemplate 
         specified above would be ignored and user bind DN would be computed using
         ldap search
         update the value based on your ldap DIT(directory information layout)
         value of search attribute should identity the user uniquely -->
    <param>
        <name>main.ldapRealm.userSearchAttributeName</name>
        <value>uid</value>
    </param>

    <!-- optional, default value: false  
         If the value is true, groups in which user is a member are looked up 
         from LDAP and made available  for service level authorization checks -->
    <param>
        <name>main.ldapRealm.authorizationEnabled</name>
        <value>true</value>
    </param>

    <!-- bind DN used to search for groups and user bind DN.  
         Required if a value is defined for main.ldapRealm.userSearchAttributeName
         or if the value of main.ldapRealm.authorizationEnabled is true -->
    <param>
        <name>main.ldapRealm.contextFactory.systemUsername</name>
        <value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- password for systemUserName.
         Required if a value is defined for main.ldapRealm.userSearchAttributeName
         or if the value of main.ldapRealm.authorizationEnabled is true -->
    <param>
        <name>main.ldapRealm.contextFactory.systemPassword</name>
        <value>${ALIAS=ldcSystemPassword}</value>
    </param>

    <!-- optional, default value: simple
         Update the value based on mechanisms supported by your ldap directory -->
    <param>
        <name>main.ldapRealm.contextFactory.systemAuthenticationMechanism</name>
        <value>simple</value>
    </param>

    <!-- optional, default value: person
         Objectclass to identify user entries in ldap, used to build search 
         filter to search for user bind DN -->
    <param>
        <name>main.ldapRealm.userObjectClass</name>
        <value>person</value>
    </param>

    <!-- search base used to search for user bind DN and groups -->
    <param>
        <name>main.ldapRealm.searchBase</name>
        <value>dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- search base used to search for user bind DN.
         Defaults to the value of main.ldapRealm.searchBase. 
         If main.ldapRealm.userSearchAttributeName is defined, 
         value for main.ldapRealm.searchBase  or main.ldapRealm.userSearchBase 
         should be defined -->
    <param>
        <name>main.ldapRealm.userSearchBase</name>
        <value>dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- search base used to search for groups.
         Defaults to the value of main.ldapRealm.searchBase.
         If value of main.ldapRealm.authorizationEnabled is true,
         value for main.ldapRealm.searchBase  or main.ldapRealm.groupSearchBase should be defined -->
    <param>
        <name>main.ldapRealm.groupSearchBase</name>
        <value>dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- optional, default value: groupOfNames
         Objectclass to identify group entries in ldap, used to build search 
         filter to search for group entries --> 
    <param>
        <name>main.ldapRealm.groupObjectClass</name>
        <value>groupOfNames</value>
    </param>

    <!-- optional, default value: member
         If value is memberUrl, we treat found groups as dynamic groups -->
    <param>
        <name>main.ldapRealm.memberAttribute</name>
        <value>member</value>
    </param>

    <!-- optional, default value: uid={0}
         Ignored if value is defined for main.ldapRealm.userSearchAttributeName -->
    <param>
        <name>main.ldapRealm.memberAttributeValueTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>

    <!-- optional, default value: cn -->
    <param>
        <name>main.ldapRealm.groupIdAttribute</name>
        <value>cn</value>
    </param>

    <param>
        <name>urls./**</name>
        <value>authcBasic</value>
    </param>

    <!-- optional, default value: 30min -->
    <param>
        <name>sessionTimeout</name>
        <value>30</value>
    </param>

</provider>

Special note on parameter main.ldapRealm.contextFactory.systemPassword

The value for this could have one of the following 2 formats

The first format specifies the password in plain text in the provider configuration. Use of this format should be limited for testing and troubleshooting.

We strongly recommend using the second format ${ALIAS=ldcSystemPassword} in production. This format uses an alias for the password stored in credential store. In the example ${ALIAS=ldcSystemPassword}, ldcSystemPassword is the alias for the password stored in credential store.

Assuming the plain text password is “hadoop”, and your topology file name is “hdp.xml”, you would use following command to create the right password alias in credential store.

{GATEWAY_HOME}/bin/knoxcli.sh  create-alias ldcSystemPassword --cluster hdp --value hadoop

LDAP Authentication Caching

Knox can be configured to cache LDAP authentication information. Knox leverages Shiro’s built in caching mechanisms and has been tested with Shiro’s EhCache cache manager implementation.

The following provider snippet demonstrates how to configure turning on the cache using the ShiroProvider. In addition to using org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm in the Shiro configuration, and setting up the cache you must set the flag for enabling caching authentication to true. Please see the property, main.ldapRealm.authenticationCachingEnabled below.

<provider>
    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>
    <param>
        <name>main.ldapRealm</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
    </param>
    <param>
        <name>main.ldapGroupContextFactory</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory</name>
        <value>$ldapGroupContextFactory</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory.url</name>
        <value>ldap://localhost:33389</value>
    </param>
    <param>
        <name>main.ldapRealm.userDnTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <param>
        <name>main.ldapRealm.authorizationEnabled</name>
        <!-- defaults to: false -->
        <value>true</value>
    </param>
    <param>
        <name>main.ldapRealm.searchBase</name>
        <value>ou=groups,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <param>
        <name>main.cacheManager</name>
        <value>org.apache.shiro.cache.ehcache.EhCacheManager</value>
    </param>
    <param>
        <name>main.securityManager.cacheManager</name>
        <value>$cacheManager</value>
    </param>
    <param>
        <name>main.ldapRealm.authenticationCachingEnabled</name>
        <value>true</value>
    </param>
    <param>
        <name>main.ldapRealm.memberAttributeValueTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory.systemUsername</name>
        <value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory.systemPassword</name>
        <value>guest-password</value>
    </param>
    <param>
        <name>urls./**</name>
        <value>authcBasic</value>
    </param>
</provider>

Trying out caching

Knox bundles a template topology files that can be used to try out the caching functionality. The template file located under {GATEWAY_HOME}/templates is sandbox.knoxrealm.ehcache.xml.

To try this out

cd {GATEWAY_HOME}
cp templates/sandbox.knoxrealm.ehcache.xml conf/topologies/sandbox.xml
bin/ldap.sh start
bin/gateway.sh start

The following call to WebHDFS should report: {“Path”:“/user/tom”}

curl  -i -v  -k -u tom:tom-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

In order to see the cache working, LDAP can now be shutdown and the user will still authenticate successfully.

bin/ldap.sh stop

and then the following should still return successfully like it did earlier.

curl  -i -v  -k -u tom:tom-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

Advanced Caching Config

By default the ehcache support in shiro contains a ehcache.xml in its classpath which is the following

<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM. The following properties are translated:

            user.home - User's home directory
            user.dir - User's current working directory
            java.io.tmpdir - Default temp file path
    -->
    <diskStore path="java.io.tmpdir/shiro-ehcache"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
    the CacheManager.

    The following attributes are required:

    maxElementsInMemory            - Sets the maximum number of objects that will be created in memory
    eternal                        - Sets whether elements are eternal. If eternal,  timeouts are ignored and the
                                     element is never expired.
    overflowToDisk                 - Sets whether elements can overflow to disk when the in-memory cache
                                     has reached the maxInMemory limit.

    The following attributes are optional:
    timeToIdleSeconds              - Sets the time to idle for an element before it expires.
                                     i.e. The maximum amount of time between accesses before an element expires
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that an Element can idle for infinity.
                                     The default value is 0.
    timeToLiveSeconds              - Sets the time to live for an element before it expires.
                                     i.e. The maximum time between creation time and when an element expires.
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that and Element can live for infinity.
                                     The default value is 0.
    diskPersistent                 - Whether the disk store persists between restarts of the Virtual Machine.
                                     The default value is false.
    diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value
                                     is 120 seconds.
    memoryStoreEvictionPolicy      - Policy would be enforced upon reaching the maxElementsInMemory limit. Default
                                     policy is Least Recently Used (specified as LRU). Other policies available -
                                     First In First Out (specified as FIFO) and Less Frequently Used
                                     (specified as LFU)
    -->

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />

    <!-- We want eternal="true" and no timeToIdle or timeToLive settings because Shiro manages session
         expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
         ehcache would evict sessions without Shiro's knowledge, which would cause many problems
        (e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
               Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)

        diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
        even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           overflowToDisk="true"
           eternal="true"
           timeToLiveSeconds="0"
           timeToIdleSeconds="0"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="org.apache.shiro.realm.text.PropertiesRealm-0-accounts"
           maxElementsInMemory="1000"
           eternal="true"
           overflowToDisk="true"/>

</ehcache>

A custom configuration file (ehcache.xml) can be used in place of this in order to set specific caching configuration.

In order to set the ehcache.xml file to use for a particular topology, set the following parameter in the configuration for the ShiroProvider:

<param>
    <name>main.cacheManager.cacheManagerConfigFile</name>
    <value>classpath:ehcache.xml</value>
</param>

In the above example, place the ehcache.xml file under {GATEWAY_HOME}/conf and restart the gateway server.

LDAP Group Lookup

Knox can be configured to look up LDAP groups that the authenticated user belong to. Knox can look up both Static LDAP Groups and Dynamic LDAP Groups. The looked up groups are populated as Principal(s) in the Java Subject of authenticated user. Therefore service authorization rules can be defined in terms of LDAP groups looked up from a LDAP directory.

To look up LDAP groups of authenticated user from LDAP, you have to use org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm in Shiro configuration.

Please see below a sample Shiro configuration snippet from a topology file that was tested looking LDAP groups.

<provider>
    <role>authentication</role>
    <name>ShiroProvider</name>
    <enabled>true</enabled>
    <!-- 
    session timeout in minutes,  this is really idle timeout,
    defaults to 30mins, if the property value is not defined,, 
    current client authentication would expire if client idles continuously for more than this value
    -->
    <!-- defaults to: 30 minutes
    <param>
        <name>sessionTimeout</name>
        <value>30</value>
    </param>
    -->

    <!--
      Use single KnoxLdapRealm to do authentication and ldap group look up
    -->
    <param>
        <name>main.ldapRealm</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
    </param>
    <param>
        <name>main.ldapGroupContextFactory</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory</name>
        <value>$ldapGroupContextFactory</value>
    </param>
    <!-- defaults to: simple
    <param>
        <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
        <value>simple</value>
    </param>
    -->
    <param>
        <name>main.ldapRealm.contextFactory.url</name>
        <value>ldap://localhost:33389</value>
    </param>
    <param>
        <name>main.ldapRealm.userDnTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>

    <param>
        <name>main.ldapRealm.authorizationEnabled</name>
        <!-- defaults to: false -->
        <value>true</value>
    </param>
    <!-- defaults to: simple
    <param>
        <name>main.ldapRealm.contextFactory.systemAuthenticationMechanism</name>
        <value>simple</value>
    </param>
    -->
    <param>
        <name>main.ldapRealm.searchBase</name>
        <value>ou=groups,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <!-- defaults to: groupOfNames
    <param>
        <name>main.ldapRealm.groupObjectClass</name>
        <value>groupOfNames</value>
    </param>
    -->
    <!-- defaults to: member
    <param>
        <name>main.ldapRealm.memberAttribute</name>
        <value>member</value>
    </param>
    -->
    <param>
         <name>main.cacheManager</name>
         <value>org.apache.shiro.cache.MemoryConstrainedCacheManager</value>
    </param>
    <param>
        <name>main.securityManager.cacheManager</name>
        <value>$cacheManager</value>
    </param>
    <param>
        <name>main.ldapRealm.memberAttributeValueTemplate</name>
        <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <!-- the above element is the template for most ldap servers 
        for active directory use the following instead and
        remove the above configuration.
    <param>
        <name>main.ldapRealm.memberAttributeValueTemplate</name>
        <value>cn={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    -->
    <param>
        <name>main.ldapRealm.contextFactory.systemUsername</name>
        <value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
    </param>
    <param>
        <name>main.ldapRealm.contextFactory.systemPassword</name>
        <value>${ALIAS=ldcSystemPassword}</value>
    </param>

    <param>
        <name>urls./**</name> 
        <value>authcBasic</value>
    </param>

</provider>

The configuration shown above would look up Static LDAP groups of authenticated user and populate the group principals in the Java Subject corresponding to authenticated user.

If you want to look up Dynamic LDAP Groups instead of Static LDAP Groups, you would have to specify groupObjectClass and memberAttribute params as shown below:

<param>
    <name>main.ldapRealm.groupObjectClass</name>
    <value>groupOfUrls</value>
</param>
<param>
    <name>main.ldapRealm.memberAttribute</name>
    <value>memberUrl</value>
</param>

Template topology files and LDIF files to try out LDAP Group Look up

Knox bundles some template topology files and ldif files that you can use to try and test LDAP Group Lookup and associated authorization ACLs. All these template files are located under {GATEWAY_HOME}/templates.

LDAP Static Group Lookup Templates, authentication and group lookup from the same directory

To try this out

cd {GATEWAY_HOME}
cp templates/sandbox.knoxrealm1.xml conf/topologies/sandbox.xml
cp templates/users.ldapgroups.ldif conf/users.ldif
java -jar bin/ldap.jar conf
java -Dsandbox.ldcSystemPassword=guest-password -jar bin/gateway.jar -persist-master

Following call to WebHDFS should report HTTP/1.1 401 Unauthorized As guest is not a member of group “analyst”, authorization provider states user should be member of group “analyst”

curl  -i -v  -k -u guest:guest-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

Following call to WebHDFS should report: {“Path”:“/user/sam”} As sam is a member of group “analyst”, authorization provider states user should be member of group “analyst”

curl  -i -v  -k -u sam:sam-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

LDAP Static Group Lookup Templates, authentication and group lookup from different directories

To try this out

cd {GATEWAY_HOME}
cp templates/sandbox.knoxrealm2.xml conf/topologies/sandbox.xml
cp templates/users.ldapgroups.ldif conf/users.ldif
java -jar bin/ldap.jar conf
java -Dsandbox.ldcSystemPassword=guest-password -jar bin/gateway.jar -persist-master

Following call to WebHDFS should report HTTP/1.1 401 Unauthorized As guest is not a member of group “analyst”, authorization provider states user should be member of group “analyst”

curl  -i -v  -k -u guest:guest-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

Following call to WebHDFS should report: {“Path”:“/user/sam”} As sam is a member of group “analyst”, authorization provider states user should be member of group “analyst”

curl  -i -v  -k -u sam:sam-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

LDAP Dynamic Group Lookup Templates, authentication and dynamic group lookup from same directory

To try this out

cd {GATEWAY_HOME}
cp templates/sandbox.knoxrealmdg.xml conf/topologies/sandbox.xml
cp templates/users.ldapdynamicgroups.ldif conf/users.ldif
java -jar bin/ldap.jar conf
java -Dsandbox.ldcSystemPassword=guest-password -jar bin/gateway.jar -persist-master

Please note that user.ldapdynamicgroups.ldif also loads necessary schema to create dynamic groups in Apache DS.

Following call to WebHDFS should report HTTP/1.1 401 Unauthorized As guest is not a member of dynamic group “directors”, authorization provider states user should be member of group “directors”

curl  -i -v  -k -u guest:guest-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

Following call to WebHDFS should report: {“Path”:“/user/bob”} As bob is a member of dynamic group “directors”, authorization provider states user should be member of group “directors”

curl  -i -v  -k -u sam:sam-password  -X GET https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY

Identity Assertion

The identity assertion provider within Knox plays the critical role of communicating the identity principal to be used within the Hadoop cluster to represent the identity that has been authenticated at the gateway.

The general responsibilities of the identity assertion provider is to interrogate the current Java Subject that has been established by the authentication or federation provider and:

  1. determine whether it matches any principal mapping rules and apply them appropriately
  2. determine whether it matches any group principal mapping rules and apply them
  3. if it is determined that the principal will be impersonating another through a principal mapping rule then a Subject.doAS is required in order for providers farther downstream can determine the appropriate effective principal name and groups for the user

Default Identity Assertion Provider

The following configuration is required for asserting the users identity to the Hadoop cluster using Pseudo or Simple “authentication” and for using kerberos/SPNEGO for secure clusters.

<provider>
    <role>identity-assertion</role>
    <name>Default</name>
    <enabled>true</enabled>
</provider>

This particular configuration indicates that the Default identity assertion provider is enabled and that there are no principal mapping rules to apply to identities flowing from the authentication in the gateway to the backend Hadoop cluster services. The primary principal of the current subject will therefore be asserted via a query parameter or as a form parameter - ie. ?user.name={primaryPrincipal}

<provider>
    <role>identity-assertion</role>
    <name>Default</name>
    <enabled>true</enabled>
    <param>
        <name>principal.mapping</name>
        <value>guest=hdfs;</value>
    </param>
    <param>
        <name>group.principal.mapping</name>
        <value>*=users;hdfs=admin</value>
    </param>
</provider>

This configuration identifies the same identity assertion provider but does provide principal and group mapping rules. In this case, when a user is authenticated as “guest” his identity is actually asserted to the Hadoop cluster as “hdfs”. In addition, since there are group principal mappings defined, he will also be considered as a member of the groups “users” and “admin”. In this particular example the wildcard "*“ is used to indicate that all authenticated users need to be considered members of the ”users“ group and that only the user ”hdfs“ is mapped to be a member of the ”admin" group.

NOTE: These group memberships are currently only meaningful for Service Level Authorization using the AclsAuthorization provider. The groups are not currently asserted to the Hadoop cluster at this time. See the Authorization section within this guide to see how this is used.

The principal mapping aspect of the identity assertion provider is important to understand in order to fully utilize the authorization features of this provider.

This feature allows us to map the authenticated principal to a runas or impersonated principal to be asserted to the Hadoop services in the backend.

When a principal mapping is defined that results in an impersonated principal, this impersonated principal is then the effective principal.

If there is no mapping to another principal then the authenticated or primary principal is then the effective principal.

Principal Mapping

<param>
    <name>principal.mapping</name>
    <value>{primaryPrincipal}[,...]={impersonatedPrincipal}[;...]</value>
</param>

For instance:

<param>
    <name>principal.mapping</name>
    <value>guest=hdfs</value>
</param>

For multiple mappings:

<param>
    <name>principal.mapping</name>
    <value>guest,alice=hdfs;mary=alice2</value>
</param>

Group Principal Mapping

<param>
    <name>group.principal.mapping</name>
    <value>{userName[,*|userName...]}={groupName[,groupName...]}[,...]</value>
</param>

For instance:

<param>
    <name>group.principal.mapping</name>
    <value>*=users;hdfs=admin</value>
</param>

this configuration indicates that all (*) authenticated users are members of the “users” group and that user “hdfs” is a member of the admin group. Group principal mapping has been added along with the authorization provider described in this document.

Concat Identity Assertion Provider

The Concat identity assertion provider allows for composition of a new user principal through the concatenation of optionally configured prefix and/or suffix provider parameters. This is a useful assertion provider for converting an incoming identity into a disambiguated identity within the Hadoop cluster based on what topology is used to access Hadoop.

The following configuration would convert the user principal into a value that represents a domain specific identity where the identities used inside the Hadoop cluster represent this same separation.

<provider>
    <role>identity-assertion</role>
    <name>Concat</name>
    <enabled>true</enabled>
    <param>
        <name>concat.suffix</name>
        <value>_domain1</value>
    </param>
</provider>

The above configuration will result in all user interactions through that topology to have their principal communicated to the Hadoop cluster with a domain designator concatenated to the username. Possibly useful for multi-tenant deployment scenarios.

In addition to the concat.suffix parameter, the provider supports the setting of a prefix through a concat.prefix parameter.

SwitchCase Identity Assertion Provider

The SwitchCase identity assertion provider solves issues where down stream ecosystem components require user and group principal names to be a specific case. An example of how this provider is enabled and configured within the <gateway> section of a topology file is shown below. This particular example will switch user principals names to lower case and group principal names to upper case.

<provider>
    <role>identity-assertion</role>
    <name>SwitchCase</name>
    <param>
        <name>principal.case</name>
        <value>lower</value>
    </param>
    <param>
        <name>group.principal.case</name>
        <value>upper</value>
    </param>
    <enabled>true</enabled>
</provider>

These are the configuration parameters used to control the behavior of the provider.

Param Description
principal.case The case mapping of user principal names. Choices are: lower, upper, none. Defaults to lower.
group.principal.case The case mapping of group principal names. Choices are: lower, upper, none. Defaults to setting of principal.case.

If no parameters are provided the full defaults will results in both user and group principal names being switched to lower case. A setting of “none” or anything other than “upper” or “lower” leaves the case of the principal name unchanged.

Regular Expression Identity Assertion Provider

The regular expression identity assertion provider allows incoming identities to be translated using a regular expression, template and lookup table. This will probably be most useful in conjunction with the HeaderPreAuth federation provider.

There are three configuration parameters used to control the behavior of the provider.

Param Description
input This is a regular expression that will be applied to the incoming identity. The most critical part of the regular expression is the group notation within the expression. In regular expressions, groups are expressed within parenthesis. For example in the regular expression “(.*)@(.*?)..*” there are two groups. When this regular expression is applied to “nobody@us.imaginary.tld” group 1 matches “nobody” and group 2 matches “us”.
output This is a template that assembles the result identity. The result is assembled from the static text and the matched groups from the input regular expression. In addition, the matched group values can be looked up in the lookup table. An output value of “{1}_{2}” of will result in “nobody_us”.
lookup This lookup table provides a simple (albeit limited) way to translate text in the incoming identities. This configuration takes the form of “=” separated name values pairs separated by “;”. For example a lookup setting is “us=USA;ca=CANADA”. The lookup is invoked in the output setting by surrounding the desired group number in square brackets (i.e. []). Putting it all together, output setting of “{1}_[{2}]” combined with input of “(.*)@(.*?)..*” and lookup of “us=USA;ca=CANADA” will turn “nobody@us.imaginary.tld” into "nobody@USA".

Within the topology file the provider configuration might look like this.

<provider>
    <role>identity-assertion</role>
    <name>Regex</name>
    <enabled>true</enabled>
    <param>
        <name>input</name>
        <value>(.*)@(.*?)\..*</value>
    </param>
    <param>
        <name>output</name>
        <value>{1}_{[2]}</value>
    </param>
    <param>
        <name>lookup</name>
        <value>us=USA;ca=CANADA</value>
    </param>
</provider>  

Using curl with this type of configuration might produce the following results.

curl -k --header "SM_USER: nobody@us.imaginary.tld" 'https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY'

{"Path":"/user/member_USA"}

url -k --header "SM_USER: nobody@ca.imaginary.tld" 'https://localhost:8443/gateway/sandbox/webhdfs/v1?op=GETHOMEDIRECTORY'

{"Path":"/user/member_CANADA"}

Authorization

Service Level Authorization

The Knox Gateway has an out-of-the-box authorization provider that allows administrators to restrict access to the individual services within a Hadoop cluster.

This provider utilizes a simple and familiar pattern of using ACLs to protect Hadoop resources by specifying users, groups and ip addresses that are permitted access.

Configuration

ACLs are bound to services within the topology descriptors by introducing the authorization provider with configuration like:

<provider>
    <role>authorization</role>
    <name>AclsAuthz</name>
    <enabled>true</enabled>
</provider>

The above configuration enables the authorization provider but does not indicate any ACLs yet and therefore there is no restriction to accessing the Hadoop services. In order to indicate the resources to be protected and the specific users, groups or ip’s to grant access, we need to provide parameters like the following:

<param>
    <name>{serviceName}.acl</name>
    <value>username[,*|username...];group[,*|group...];ipaddr[,*|ipaddr...]</value>
</param>

where {serviceName} would need to be the name of a configured Hadoop service within the topology.

NOTE: ipaddr is unique among the parts of the ACL in that you are able to specify a wildcard within an ipaddr to indicate that the remote address must being with the String prior to the asterisk within the ipaddr acl. For instance:

<param>
    <name>{serviceName}.acl</name>
    <value>*;*;192.168.*</value>
</param>

This indicates that the request must come from an IP address that begins with ‘192.168.’ in order to be granted access.

Note also that configuration without any ACLs defined is equivalent to:

<param>
    <name>{serviceName}.acl</name>
    <value>*;*;*</value>
</param>

meaning: all users, groups and IPs have access. Each of the elements of the acl param support multiple values via comma separated list and the * wildcard to match any.

For instance:

<param>
    <name>webhdfs.acl</name>
    <value>hdfs;admin;127.0.0.2,127.0.0.3</value>
</param>

this configuration indicates that ALL of the following have to be satisfied to be granted access:

  1. The user name must be “hdfs” AND
  2. the user must be in the group “admin” AND
  3. the user must come from either 127.0.0.2 or 127.0.0.3

This allows us to craft policy that restricts the members of a large group to a subset that should have access. The user being removed from the group will allow access to be denied even though their username may have been in the ACL.

An additional configuration element may be used to alter the processing of the ACL to be OR instead of the default AND behavior:

<param>
    <name>{serviceName}.acl.mode</name>
    <value>OR</value>
</param>

this processing behavior requires that the effective user satisfy one of the parts of the ACL definition in order to be granted access. For instance:

<param>
    <name>webhdfs.acl</name>
    <value>hdfs,guest;admin;127.0.0.2,127.0.0.3</value>
</param>

You may also set the ACL processing mode at the top level for the topology. This essentially sets the default for the managed cluster. It may then be overridden at the service level as well.

<param>
    <name>acl.mode</name>
    <value>OR</value>
</param>

this configuration indicates that ONE of the following must be satisfied to be granted access:

  1. The user is “hdfs” or “guest” OR
  2. the user is in “admin” group OR
  3. the request is coming from 127.0.0.2 or 127.0.0.3

Following are a few concrete examples on how to use this feature.

Note: In the examples below {serviceName} represents a real service name (e.g. WEBHDFS) and would be replaced with these values in an actual configuration.

Usecases
USECASE-1: Restrict access to specific Hadoop services to specific Users
<param>
    <name>{serviceName}.acl</name>
    <value>guest;*;*</value>
</param>
USECASE-2: Restrict access to specific Hadoop services to specific Groups
<param>
    <name>{serviceName}.acls</name>
    <value>*;admins;*</value>
</param>
USECASE-3: Restrict access to specific Hadoop services to specific Remote IPs
<param>
    <name>{serviceName}.acl</name>
    <value>*;*;127.0.0.1</value>
</param>
USECASE-4: Restrict access to specific Hadoop services to specific Users OR users within specific Groups
<param>
    <name>{serviceName}.acl.mode</name>
    <value>OR</value>
</param>
<param>
    <name>{serviceName}.acl</name>
    <value>guest;admin;*</value>
</param>
USECASE-5: Restrict access to specific Hadoop services to specific Users OR users from specific Remote IPs
<param>
    <name>{serviceName}.acl.mode</name>
    <value>OR</value>
</param>
<param>
    <name>{serviceName}.acl</name>
    <value>guest;*;127.0.0.1</value>
</param>
USECASE-6: Restrict access to specific Hadoop services to users within specific Groups OR from specific Remote IPs
<param>
    <name>{serviceName}.acl.mode</name>
    <value>OR</value>
</param>
<param>
    <name>{serviceName}.acl</name>
    <value>*;admin;127.0.0.1</value>
</param>
USECASE-7: Restrict access to specific Hadoop services to specific Users OR users within specific Groups OR from specific Remote IPs
<param>
    <name>{serviceName}.acl.mode</name>
    <value>OR</value>
</param>
<param>
    <name>{serviceName}.acl</name>
    <value>guest;admin;127.0.0.1</value>
</param>
USECASE-8: Restrict access to specific Hadoop services to specific Users AND users within specific Groups
<param>
    <name>{serviceName}.acl</name>
    <value>guest;admin;*</value>
</param>
USECASE-9: Restrict access to specific Hadoop services to specific Users AND users from specific Remote IPs
<param>
    <name>{serviceName}.acl</name>
    <value>guest;*;127.0.0.1</value>
</param>
USECASE-10: Restrict access to specific Hadoop services to users within specific Groups AND from specific Remote IPs
<param>
    <name>{serviceName}.acl</name>
    <value>*;admins;127.0.0.1</value>
</param>
USECASE-11: Restrict access to specific Hadoop services to specific Users AND users within specific Groups AND from specific Remote IPs
<param>
    <name>{serviceName}.acl</name>
    <value>guest;admins;127.0.0.1</value>
</param>
USECASE-12: Full example including identity assertion/principal mapping

The principal mapping aspect of the identity assertion provider is important to understand in order to fully utilize the authorization features of this provider.

This feature allows us to map the authenticated principal to a runas or impersonated principal to be asserted to the Hadoop services in the backend. It is fully documented in the Identity Assertion section of this guide.

These additional mapping capabilities are used together with the authorization ACL policy. An example of a full topology that illustrates these together is below.

<topology>
    <gateway>
        <provider>
            <role>authentication</role>
            <name>ShiroProvider</name>
            <enabled>true</enabled>
            <param>
                <name>main.ldapRealm</name>
                <value>org.apache.shiro.realm.ldap.JndiLdapRealm</value>
            </param>
            <param>
                <name>main.ldapRealm.userDnTemplate</name>
                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
            </param>
            <param>
                <name>main.ldapRealm.contextFactory.url</name>
                <value>ldap://localhost:33389</value>
            </param>
            <param>
                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
                <value>simple</value>
            </param>
            <param>
                <name>urls./**</name>
                <value>authcBasic</value>
            </param>
        </provider>
        <provider>
            <role>identity-assertion</role>
            <name>Default</name>
            <enabled>true</enabled>
            <param>
                <name>principal.mapping</name>
                <value>guest=hdfs;</value>
            </param>
            <param>
                <name>group.principal.mapping</name>
                <value>*=users;hdfs=admin</value>
            </param>
        </provider>
        <provider>
            <role>authorization</role>
            <name>AclsAuthz</name>
            <enabled>true</enabled>
            <param>
                <name>acl.mode</name>
                <value>OR</value>
            </param>
            <param>
                <name>webhdfs.acl.mode</name>
                <value>AND</value>
            </param>
            <param>
                <name>webhdfs.acl</name>
                <value>hdfs;admin;127.0.0.2,127.0.0.3</value>
            </param>
            <param>
                <name>webhcat.acl</name>
                <value>hdfs;admin;127.0.0.2,127.0.0.3</value>
            </param>
        </provider>
        <provider>
            <role>hostmap</role>
            <name>static</name>
            <enabled>true</enabled>
            <param>
                <name>localhost</name>
                <value>sandbox,sandbox.hortonworks.com</value>
            </param>
        </provider>
    </gateway>

    <service>
        <role>JOBTRACKER</role>
        <url>rpc://localhost:8050</url>
    </service>

    <service>
        <role>WEBHDFS</role>
        <url>http://localhost:50070/webhdfs</url>
    </service>

    <service>
        <role>WEBHCAT</role>
        <url>http://localhost:50111/templeton</url>
    </service>

    <service>
        <role>OOZIE</role>
        <url>http://localhost:11000/oozie</url>
    </service>

    <service>
        <role>WEBHBASE</role>
        <url>http://localhost:8080</url>
    </service>

    <service>
        <role>HIVE</role>
        <url>http://localhost:10001/cliservice</url>
    </service>
</topology>

Secure Clusters

See the Hadoop documentation for setting up a secure Hadoop cluster http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SecureMode.html

Once you have a Hadoop cluster that is using Kerberos for authentication, you have to do the following to configure Knox to work with that cluster.

Create Unix account for Knox on Hadoop master nodes

useradd -g hadoop knox

Create Kerberos principal, keytab for Knox

One way of doing this, assuming your KDC realm is EXAMPLE.COM, is to ssh into your host running KDC and execute kadmin.local That will result in an interactive session in which you can execute commands.

ssh into your host running KDC

kadmin.local
add_principal -randkey knox/knox@EXAMPLE.COM
ktadd -k knox.service.keytab -norandkey knox/knox@EXAMPLE.COM
exit

Copy knox keytab to Knox host

Add unix account for the knox user on Knox host

useradd -g hadoop knox

Copy knox.service.keytab created on KDC host on to your Knox host {GATEWAY_HOME}/conf/knox.service.keytab

chown knox knox.service.keytab
chmod 400 knox.service.keytab

Update krb5.conf at {GATEWAY_HOME}/conf/krb5.conf on Knox host

You could copy the {GATEWAY_HOME}/templates/krb5.conf file provided in the Knox binary download and customize it to suit your cluster.

Update krb5JAASLogin.conf at /etc/knox/conf/krb5JAASLogin.conf on Knox host

You could copy the {GATEWAY_HOME}/templates/krb5JAASLogin.conf file provided in the Knox binary download and customize it to suit your cluster.

Update gateway-site.xml on Knox host

Update conf/gateway-site.xml in your Knox installation and set the value of gateway.hadoop.kerberos.secured to true.

Restart Knox

After you do the above configurations and restart Knox, Knox would use SPNego to authenticate with Hadoop services and Oozie. There is no change in the way you make calls to Knox whether you use Curl or Knox DSL.

High Availability

This describes how Knox itself can be made highly available.

Configure Knox instances

All Knox instances must be synced to use the same topology credential keystores. These files are located under {GATEWAY_HOME}/conf/security/keystores/{TOPOLOGY_NAME}-credentials.jceks. They are generated after the first topology deployment. Currently these files need to be synced manually. Here are the steps to sync topologies credentials keystores:

  1. Choose a Knox instance that will be the source for topology credential keystores. Let’s call it keystores master
  2. Replace the topology credential keystores in the other Knox instances with topology credential keystores from the keystores master
  3. Restart Knox instances

High Availability with Apache HTTP Server + mod_proxy + mod_proxy_balancer

1 - Requirements
openssl-devel

openssl-devel is required for Apache Module mod_ssl.

sudo yum install openssl-devel
Apache HTTP Server

Apache HTTP Server 2.4.6 or later is required. See this document for installing and setting up Apache HTTP Server: http://httpd.apache.org/docs/2.4/install.html

Hint: pass --enable-ssl to the ./configure command to enable the generation of the Apache Module mod_ssl.

Apache Module mod_proxy

See this document for setting up Apache Module mod_proxy: http://httpd.apache.org/docs/2.4/mod/mod_proxy.html

Apache Module mod_proxy_balancer

See this document for setting up Apache Module mod_proxy_balancer: http://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html

Apache Module mod_ssl

See this document for setting up Apache Module mod_ssl: http://httpd.apache.org/docs/2.4/mod/mod_ssl.html

2 - Configuration example
Generate certificate for Apache HTTP Server

See this document for an example: http://www.akadia.com/services/ssh_test_certificate.html

By convention, Apache HTTP Server and Knox certificates are put into /etc/apache2/ssl/ folder.

Update Apache HTTP Server configuration file

This file is located under {APACHE_HOME}/conf/httpd.conf.

Following directives have to be added or uncommented in the configuration file:

Also following lines have to be added to file. Replace placeholders (${…}) with real data:

Listen 443
<VirtualHost *:443>
   SSLEngine On
   SSLProxyEngine On
   SSLCertificateFile ${PATH_TO_CERTIFICATE_FILE}
   SSLCertificateKeyFile ${PATH_TO_CERTIFICATE_KEY_FILE}
   SSLProxyCACertificateFile ${PATH_TO_PROXY_CA_CERTIFICATE_FILE}

   ProxyRequests Off
   ProxyPreserveHost Off

   RequestHeader set X-Forwarded-Port "443"
   Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
   <Proxy balancer://mycluster>
     BalancerMember ${HOST_#1} route=1
     BalancerMember ${HOST_#2} route=2
     ...
     BalancerMember ${HOST_#N} route=N

     ProxySet failontimeout=On lbmethod=${LB_METHOD} stickysession=ROUTEID 
   </Proxy>

   ProxyPass / balancer://mycluster/
   ProxyPassReverse / balancer://mycluster/
</VirtualHost>

Note:

Start/stop Apache HTTP Server
APACHE_HOME/bin/apachectl -k start
APACHE_HOME/bin/apachectl -k stop
Verify

Use Knox samples.

Web App Security Provider

Knox is a Web API (REST) Gateway for Hadoop. The fact that REST interactions are HTTP based means that they are vulnerable to a number of web application security vulnerabilities. This project introduces a web application security provider for plugging in various protection filters.

There are two aspects of web application security that are handled now: Cross Site Request Forgery (CSRF) and Cross Origin Resource Sharing. Others will be added in future releases.

CSRF

Cross site request forgery (CSRF) attacks attempt to force an authenticated user to execute functionality without their knowledge. By presenting them with a link or image that when clicked invokes a request to another site with which the user may have already established an active session.

CSRF is entirely a browser based attack. Some background knowledge of how browsers work enables us to provide a filter that will prevent CSRF attacks. HTTP requests from a web browser performed via form, image, iframe, etc are unable to set custom HTTP headers. The only way to create a HTTP request from a browser with a custom HTTP header is to use a technology such as Javascript XMLHttpRequest or Flash. These technologies can set custom HTTP headers, but have security policies built in to prevent web sites from sending requests to each other unless specifically allowed by policy.

This means that a website www.bad.com cannot send a request to http://bank.example.com with the custom header X-XSRF-Header unless they use a technology such as a XMLHttpRequest. That technology would prevent such a request from being made unless the bank.example.com domain specifically allowed it. This then results in a REST endpoint that can only be called via XMLHttpRequest (or similar technology).

NOTE: by enabling this protection within the topology, this custom header will be required for all clients that interact with it - not just browsers.

CORS

For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts. For example, XMLHttpRequest follows the same-origin policy. So, a web application using XMLHttpRequest could only make HTTP requests to its own domain. To improve web applications, developers asked browser vendors to allow XMLHttpRequest to make cross-domain requests.

Cross Origin Resource Sharing is a way to explicitly alter the same-origin policy for a given application or API. In order to allow for applications to make cross domain requests through Apache Knox, we need to configure the CORS filter of the WebAppSec provider.

Configuration

Overview

As with all providers in the Knox gateway, the web app security provider is configured through provider params. Unlike many other providers, the web app security provider may actually host multiple vulnerability/security filters. Currently, we only have implementations for CSRF and CORS but others will follow and you may be interested in creating your own.

Because of this one-to-many provider/filter relationship, there is an extra configuration element for this provider per filter. As you can see in the sample below, the actual filter configuration is defined entirely within the params of the WebAppSec provider.

<provider>
    <role>webappsec</role>
    <name>WebAppSec</name>
    <enabled>true</enabled>
    <param><name>csrf.enabled</name><value>true</value></param>
    <param><name>csrf.customHeader</name><value>X-XSRF-Header</value></param>
    <param><name>csrf.methodsToIgnore</name><value>GET,OPTIONS,HEAD</value></param>
    <param><name>cors.enabled</name><value>true</value></param>
    <param><name>xframe-options.enabled</name><value>true</value></param>
</provider>

Descriptions

The following tables describes the configuration options for the web app security provider:

CSRF
Config
Name Description Default
csrf.enabled This param enables the CSRF protection capabilities false
csrf.customHeader This is an optional param that indicates the name of the header to be used in order to determine that the request is from a trusted source. It defaults to the header name described by the NSA in its guidelines for dealing with CSRF in REST. X-XSRF-Header
csrf.methodsToIgnore This is also an optional param that enumerates the HTTP methods to allow through without the custom HTTP header. This is useful for allowing things like GET requests from the URL bar of a browser but it assumes that the GET request adheres to REST principals in terms of being idempotent. If this cannot be assumed then it would be wise to not include GET in the list of methods to ignore. GET,OPTIONS,HEAD
REST Invocation

The following curl command can be used to request a directory listing from HDFS while passing in the expected header X-XSRF-Header.

curl -k -i --header "X-XSRF-Header: valid" -v -u guest:guest-password https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS

Omitting the –header “X-XSRF-Header: valid” above should result in an HTTP 400 bad_request.

Disabling the provider will then allow a request that is missing the header through.

CORS
Config
Name Description Default
cors.enabled This param enables the CORS capabilities false
cors.allowGenericHttpRequests {true|false} defaults to true. If true generic HTTP requests will be allowed to pass through the filter, else only valid and accepted CORS requests will be allowed (strict CORS filtering). true
cors.allowOrigin {“*”|origin-list} defaults to “*”. Whitespace-separated list of origins that the CORS filter must allow. Requests from origins not included here will be refused with an HTTP 403 “Forbidden” response. If set to * (asterisk) any origin will be allowed. “*”
cors.allowSubdomains {true|false} defaults to false. If true the CORS filter will allow requests from any origin which is a subdomain origin of the allowed origins. A subdomain is matched by comparing its scheme and suffix (host name / IP address and optional port number). false
cors.supportedMethods {method-list} defaults to GET, POST, HEAD, OPTIONS. List of the supported HTTP methods. These are advertised through the Access-Control-Allow-Methods header and must also be implemented by the actual CORS web service. Requests for methods not included here will be refused by the CORS filter with an HTTP 405 “Method not allowed” response. GET, POST, HEAD, OPTIONS
cors.supportedHeaders {“*”|header-list} defaults to *. The names of the supported author request headers. These are advertised through the Access-Control-Allow-Headers header. If the configuration property value is set to * (asterisk) any author request header will be allowed. The CORS Filter implements this by simply echoing the requested value back to the browser. *
cors.exposedHeaders {header-list} defaults to empty list. List of the response headers other than simple response headers that the browser should expose to the author of the cross-domain request through the XMLHttpRequest.getResponseHeader() method. The CORS filter supplies this information through the Access-Control-Expose-Headers header. empty
cors.supportsCredentials {true|false} defaults to true. Indicates whether user credentials, such as cookies, HTTP authentication or client-side certificates, are supported. The CORS filter uses this value in constructing the Access-Control-Allow-Credentials header. true
cors.maxAge {int} defaults to -1 (unspecified). Indicates how long the results of a preflight request can be cached by the web browser, in seconds. If -1 unspecified. This information is passed to the browser via the Access-Control-Max-Age header. -1
cors.tagRequests {true|false} defaults to false (no tagging). Enables HTTP servlet request tagging to provide CORS information to downstream handlers (filters and/or servlets). false
X-Frame-Options

Cross Frame Scripting and Clickjacking are attackes that can be prevented by controlling the ability for a third-party to embed an application or resource within a Frame, IFrame or Object html element. This can be done adding the X-Frame-Options HTTP header to responses.

Config
Name Description Default
xframe-options.enabled This param enables the X-Frame-Options capabilities false
xframe-options.value This param specifies a particular value for the X-Frame-Options header. Most often the default value of DENY will be most appropriate. You can also use SAMEORIGIN or ALLOW-FROM uri DENY

HadoopAuth Authentication Provider

The HadoopAuth authentication provider for Knox integrates the use of the Apache Hadoop module for SPNEGO and delegation token based authentication. This introduces the same authentication pattern used across much of the Hadoop ecosystem to Apache Knox and allows clients to using the strong authentication and SSO capabilities of Kerberos.

Configuration

Overview

As with all providers in the Knox gateway, the HadoopAuth provider is configured through provider params. The configuration parameters are the same parameters used within Apache Hadoop for the same capabilities. In this section, we provide an example configuration and description of each of the parameters. We do encourage the reader to refer to the Hadoop documentation for this as well. (see http://hadoop.apache.org/docs/current/hadoop-auth/Configuration.html)

One of the interesting things to note about this configuration is the use of the config.prefix parameter. In Hadoop there may be multiple components with their own specific configuration values for these parameters and since they may get mixed into the same Configuration object - there needs to be a way to identify the component specific values. The config.prefix parameter is used for this and is prepended to each of the configuration parameters for this provider. Below, you see an example configuration where the value for config.prefix happens to be ‘hadoop.auth.config’. You will also notice that this same value is prepended to the name of the rest of the configuration parameters.

<provider>
  <role>authentication</role>
  <name>HadoopAuth</name>
  <enabled>true</enabled>
  <param>
    <name>config.prefix</name>
    <value>hadoop.auth.config</value>
  </param>
  <param>
    <name>hadoop.auth.config.signature.secret</name>
    <value>knox-signature-secret</value>
  </param>
  <param>
    <name>hadoop.auth.config.type</name>
    <value>kerberos</value>
  </param>
  <param>
    <name>hadoop.auth.config.simple.anonymous.allowed</name>
    <value>false</value>
  </param>
  <param>
    <name>hadoop.auth.config.token.validity</name>
    <value>1800</value>
  </param>
  <param>
    <name>hadoop.auth.config.cookie.domain</name>
    <value>novalocal</value>
  </param>
  <param>
    <name>hadoop.auth.config.cookie.path</name>
    <value>gateway/default</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.principal</name>
    <value>HTTP/lmccay-knoxft-24m-r6-sec-160422-1327-2.novalocal@EXAMPLE.COM</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.keytab</name>
    <value>/etc/security/keytabs/spnego.service.keytab</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.name.rules</name>
    <value>DEFAULT</value>
  </param>
</provider>

Descriptions

The following tables describes the configuration parameters for the HadoopAuth provider:

Config
Name Description Default
config.prefix If specified, all other configuration parameter names must start with the prefix. none
signature.secret This is the secret used to sign the delegation token in the hadoop.auth cookie. This same secret needs to be used across all instances of the Knox gateway in a given cluster. Otherwise, the delegation token will fail validation and authentication will be repeated each request. a simple random number
type This parameter needs to be set to kerberos. none, would throw exception
simple.anonymous.allowed This should always be false for a secure deployment. true
token.validity The validity -in seconds- of the generated authentication token. This is also used for the rollover interval when signer.secret.provider is set to random or zookeeper. 36000 seconds
cookie.domain domain to use for the HTTP cookie that stores the authentication token null
cookie.path path to use for the HTTP cookie that stores the authentication token null
kerberos.principal The web-application Kerberos principal name. The Kerberos principal name must start with HTTP/…. For example: HTTP/localhost@LOCALHOST null
kerberos.keytab The path to the keytab file containing the credentials for the kerberos principal. For example: /Users/lmccay/lmccay.keytab null
kerberos.name.rules The name of the ruleset for extracting the username from the kerberos principal. DEFAULT
REST Invocation

Once a user logs in with kinit then their kerberos session may be used across client requests with things like curl. The following curl command can be used to request a directory listing from HDFS while authenticating with SPNEGO via the –negotiate flag

curl -k -i --negotiate -u https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS

Preauthenticated SSO Provider

A number of SSO solutions provide mechanisms for federating an authenticated identity across applications. These mechanisms are at times simple HTTP Header type tokens that can be used to propagate the identity across process boundaries.

Knox Gateway needs a pluggable mechanism for consuming these tokens and federating the asserted identity through an interaction with the Hadoop cluster.

CAUTION: The use of this provider requires that proper network security and identity provider configuration and deployment does not allow requests directly to the Knox gateway. Otherwise, this provider will leave the gateway exposed to identity spoofing.

Configuration

Overview

This provider was designed for use with identity solutions such as those provided by CA’s SiteMinder and IBM’s Tivoli Access Manager. While direct testing with these products has not been done, there has been extensive unit and functional testing that ensure that it should work with such providers.

The HeaderPreAuth provider is configured within the topology file and has a minimal configuration that assumes SM_USER for CA SiteMinder. The following example is the bare minimum configuration for SiteMinder (with no IP address validation).

<provider>
    <role>federation</role>
    <name>HeaderPreAuth</name>
    <enabled>true</enabled>
</provider>

The following table describes the configuration options for the web app security provider:

Descriptions
Name Description Default
preauth.validation.method Optional parameter that indicates the type of trust validation to perform on incoming requests. Possible values are: null, preauth.ip.validation (others will be added in future releases). Failure results in a 403 forbidden HTTP status response. null - which means no validation will be performed and that we are assuming that the network security and external authentication system is sufficient.
preauth.ip.addresses Optional parameter that indicates the list of trusted ip addresses. When preauth.ip.validation is indicated as the validation method this parameter must be provided to indicate the trusted ip address set. Wildcarded IPs may be used to indicate subnet level trust. ie. 127.0.* null - which means that no validation will be performed.
preauth.custom.header Required parameter for indicating a custom header to use for extracting the preauthenticated principal. The value extracted from this header is utilized as the PrimaryPrincipal within the established Subject. An incoming request that is missing the configured header will be refused with a 401 unauthorized HTTP status. SM_USER for SiteMinder usecase
preauth.custom.group.header Optional parameter for indicating a HTTP header name that contains a comma separated list of groups. These are added to the authenticated Subject as group principals. A missing group header will result in no groups being extracted from the incoming request and a log entry but processing will continue. null - which means that there will be no group principals extracted from the request and added to the established Subject.

NOTE: Mutual authentication can be used to establish a strong trust relationship between clients and servers while using the Preauthenticated SSO provider. See the configuration for Mutual Authentication with SSL in this document.

Configuration for SiteMinder

The following is an example of a configuration of the preauthenticated sso provider that leverages the default SM_USER header name - assuming use with CA SiteMinder. It further configures the validation based on the IP address from the incoming request.

<provider>
    <role>federation</role>
    <name>HeaderPreAuth</name>
    <enabled>true</enabled>
    <param><name>preauth.validation.method</name><value>preauth.ip.validation</value></param>
    <param><name>preauth.ip.addresses</name><value>127.0.0.2,127.0.0.1</value></param>
</provider>
REST Invocation for SiteMinder

The following curl command can be used to request a directory listing from HDFS while passing in the expected header SM_USER.

curl -k -i --header "SM_USER: guest" -v https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS

Omitting the –header “SM_USER: guest” above will result in a rejected request.

Configuration for IBM Tivoli AM

As an example for configuring the preauthenticated SSO provider for another SSO provider, the following illustrates the values used for IBM’s Tivoli Access Manager:

<provider>
    <role>federation</role>
    <name>HeaderPreAuth</name>
    <enabled>true</enabled>
    <param><name>preauth.custom.header</name><value>iv_user</value></param>
    <param><name>preauth.custom.group.header</name><value>iv_group</value></param>
    <param><name>preauth.validation.method</name><value>preauth.ip.validation</value></param>
    <param><name>preauth.ip.addresses</name><value>127.0.0.2,127.0.0.1</value></param>
</provider>
REST Invocation for Tivoli AM

The following curl command can be used to request a directory listing from HDFS while passing in the expected headers of iv_user and iv_group. Note that the iv_group value in this command matches the expected ACL for webhdfs in the above topology file. Changing this from “admin” to “admin2” should result in a 401 unauthorized response.

curl -k -i --header "iv_user: guest" --header "iv_group: admin" -v https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS

Omitting the –header “iv_user: guest” above will result in a rejected request.

Pac4j Provider - CAS / OAuth / SAML / OpenID Connect

pac4j is a Java security engine to authenticate users, get their profiles and manage their authorizations in order to secure Java web applications.

It supports many authentication mechanisms for UI and web services and is implemented by many frameworks and tools.

For Knox, it is used as a federation provider to support the OAuth, CAS, SAML and OpenID Connect protocols. It must be used for SSO, in association with the KnoxSSO service and optionally with the SSOCookieProvider for access to REST APIs.

Configuration

SSO topology

To enable SSO for REST API access through the Knox gateway, you need to protect your Hadoop services with the the SSOCookieProvider configured to use the KnoxSSO service (sandbox.xml topology):

<gateway>
  <provider>
    <role>webappsec</role>
    <name>WebAppSec</name>
    <enabled>true</enabled>
    <param>
      <name>cors.enabled</name>
      <value>true</value>
    </param>
  </provider>
  <provider>
    <role>federation</role>
    <name>SSOCookieProvider</name>
    <enabled>true</enabled>
    <param>
      <name>sso.authentication.provider.url</name>
      <value>https://127.0.0.1:8443/gateway/knoxsso/api/v1/websso</value>
    </param>
  </provider>
  <provider>
    <role>identity-assertion</role>
    <name>Default</name>
    <enabled>true</enabled>
  </provider>
</gateway>

<service>
  <role>NAMENODE</role>
  <url>hdfs://localhost:8020</url>
</service>

...

and protect the KnoxSSO service by the pac4j provider (knoxsso.xml topology):

<gateway>
  <provider>
    <role>federation</role>
    <name>pac4j</name>
    <enabled>true</enabled>
    <param>
      <name>pac4j.callbackUrl</name>
      <value>https://127.0.0.1:8443/gateway/knoxsso/api/v1/websso</value>
    </param>
    <param>
      <name>cas.loginUrl</name>
      <value>https://casserverpac4j.herokuapp.com/login</value>
    </param>
  </provider>
  <provider>
    <role>identity-assertion</role>
    <name>Default</name>
    <enabled>true</enabled>
  </provider>
</gateway>

<service>
  <role>KNOXSSO</role>
  <param>
    <name>knoxsso.cookie.secure.only</name>
    <value>true</value>
  </param>
  <param>
    <name>knoxsso.token.ttl</name>
    <value>100000</value>
  </param>
  <param>
     <name>knoxsso.redirect.whitelist.regex</name>
     <value>^https?:\/\/(localhost|127\.0\.0\.1|0:0:0:0:0:0:0:1|::1):[0-9].*$</value>
  </param>
</service>

Notice that the pac4j callback url is the KnoxSSO url (pac4j.callbackUrl parameter). An additional pac4j.cookie.domain.suffix parameter allows you to define the domain suffix for the pac4j cookies.

In this example, the pac4j provider is configured to authenticate users via a CAS server hosted at: https://casserverpac4j.herokuapp.com/login.

Parameters

You can define the identity provider client/s to be used for authentication with the appropriate parameters - as defined below. When configuring any pac4j identity provider client there is a mandatory parameter that must be defined to indicate the order in which the providers should be engaged with the first in the comma separated list being the default. Consuming applications may indicate their desire to use one of the configured clients with a query parameter called client_name. When there is no client_name specified, the default (first) provider is selected.

<param>
  <name>clientName</name>
  <value>CLIENTNAME[,CLIENTNAME]</value>
</param>

Valid client names are: FacebookClient, TwitterClient, CasClient, SAML2Client or OidcClient

For tests only, you can use a basic authentication where login equals password by defining the following configuration:

<param>
  <name>clientName</name>
  <value>testBasicAuth</value>
</param>

NOTE: This is NOT a secure mechanism and must NOT be used in production deployments.

Otherwise, you can use Facebook, Twitter, a CAS server, a SAML IdP or an OpenID Connect provider by using the following parameters:

For OAuth support:
Name Value
facebook.id Identifier of the OAuth Facebook application
facebook.secret Secret of the OAuth Facebook application
facebook.scope Requested scope at Facebook login
facebook.fields Fields returned by Facebook
twitter.id Identifier of the OAuth Twitter application
twitter.secret Secret of the OAuth Twitter application
For CAS support:
Name Value
cas.loginUrl Login url of the CAS server
cas.protocol CAS protocol (CAS10, CAS20, CAS20_PROXY, CAS30, CAS30_PROXY, SAML)
For SAML support:
Name Value
saml.keystorePassword Password of the keystore (storepass)
saml.privateKeyPassword Password for the private key (keypass)
saml.keystorePath Path of the keystore
saml.identityProviderMetadataPath Path of the identity provider metadata
saml.maximumAuthenticationLifetime Maximum lifetime for authentication
saml.serviceProviderEntityId Identifier of the service provider
saml.serviceProviderMetadataPath Path of the service provider metadata

Get more details on the pac4j wiki.

The SSO url in your SAML 2 provider config will need to include a special query parameter that lets the pac4j provider know that the request is coming back from the provider rather than from a redirect from a KnoxSSO participating application. This query parameter is “pac4jCallback=true”.

This results in a URL that looks something like:

https://hostname:8443/gateway/knoxsso/api/v1/websso?pac4jCallback=true&client_name=SAML2Client

This also means that the SP Entity ID should also include this query parameter as appropriate for your provider. Often something like the above URL is used for both the SSO url and SP Entity ID.

For OpenID Connect support:
Name Value
oidc.id Identifier of the OpenID Connect provider
oidc.secret Secret of the OpenID Connect provider
oidc.discoveryUri Direcovery URI of the OpenID Connect provider
oidc.useNonce Whether to use nonce during login process
oidc.preferredJwsAlgorithm Preferred JWS algorithm
oidc.maxClockSkew Max clock skew during login process
oidc.customParamKey1 Key of the first custom parameter
oidc.customParamValue1 Value of the first custom parameter
oidc.customParamKey2 Key of the second custom parameter
oidc.customParamValue2 Value of the second custom parameter

Get more details on the pac4j wiki.

In fact, you can even define several identity providers at the same time, the first being chosen by default unless you define a client_name parameter to specify it (FacebookClient, TwitterClient, CasClient, SAML2Client or OidcClient).

UI invocation

In a browser, when calling your Hadoop service (for example: https://127.0.0.1:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS), you are redirected to the identity provider for login. Then, after a successful authentication, your are redirected back to your originally requested url and your KnoxSSO session is initialized.

KnoxSSO Setup and Configuration

Introduction


Authentication of the Hadoop component UIs, and those of the overall ecosystem, is usually limited to Kerberos (which requires SPNEGO to be configured for the user’s browser) and simple/psuedo. This often results in the UIs not being secured - even in secured clusters. This is where KnoxSSO provides value by providing WebSSO capabilities to the Hadoop cluster.

By leveraging the hadoop-auth module in Hadoop common, we have introduced the ability to consume a common SSO cookie for web UIs while retaining the non-web browser authentication through kerberos/SPNEGO. We do this by extending the AltKerberosAuthenticationHandler class which provides the useragent based multiplexing.

We also provide integration guidance within the developers guide for other applications to be able to participate in these SSO capabilities.

The flexibility of the Apache Knox authentication and federation providers allows KnoxSSO to provide a normalization of authentication events through token exchange resulting in a common JWT (JSON WebToken) based token.

KnoxSSO provides an abstraction for integrating any number of authentication systems and SSO solutions and enables participating web applications to scale to those solutions more easily. Without the token exchange capabilities offered by KnoxSSO each component UI would need to integrate with each desired solution on its own. With KnoxSSO they only need to integrate with the single solution and common token.

This document describes the overall setup requirements for KnoxSSO and participating applications.

KnoxSSO Setup

knoxsso.xml Topology

To enable KnoxSSO, we use the KnoxSSO topology for exposing an API that can be used to abstract the use of any number of enterprise or customer IDPs. By default, the knoxsso.xml file is configured for using the simple KnoxAuth application for form-based authentication against LDAP/AD. By swapping the Shiro authentication provider that is there out-of-the-box with another authentication or federation provider, an admin may leverage many of the existing providers for SSO for the UI components that participate in KnoxSSO.

Just as with any Knox service, the KNOXSSO service is protected by the gateway providers defined above it. In this case, the ShiroProvider is taking care of HTTP Basic Auth against LDAP for us. Once the user authenticates the request processing continues to the KNOXSSO service that will create the required cookie and do the necessary redirects.

The knoxsso.xml topology will result in a KnoxSSO URL that looks something like:

https://{gateway_host}:{gateway_port}/gateway/knoxsso/api/v1/websso

This URL is needed when configuring applications that participate in KnoxSSO for a given deployment. We will refer to this as the Provider URL.

KnoxSSO Configuration Parameters

Parameter Description Default
knoxsso.cookie.secure.only This determines whether the browser is allowed to send the cookie over unsecured channels. This should always be set to true in production systems. If during development a relying party is not running ssl then you can turn this off. Running with it off exposes the cookie and underlying token for capture and replay by others. true
knoxsso.cookie.max.age optional: This indicates that a cookie can only live for a specified amount of time - in seconds. This should probably be left to the default which makes it a session cookie. Session cookies are discarded once the browser session is closed. session
knoxsso.cookie.domain.suffix optional: This indicates the portion of the request hostname that represents the domain to be used for the cookie domain. For single host development scenarios the default behavior should be fine. For production deployments, the expected domain should be set and all configured URLs that are related to SSO should use this domain. Otherwise, the cookie will not be presented by the browser to mismatched URLs. Default cookie domain or a domain derived from a hostname that includes more than 2 dots.
knoxsso.token.ttl This indicates the lifespan of the token within the cookie. Once it expires a new cookie must be acquired from KnoxSSO. This is in milliseconds. The 36000000 in the topology above gives you 10 hrs. 30000 That is 30 seconds.
knoxsso.token.audiences This is a comma separated list of audiences to add to the JWT token. This is used to ensure that a token received by a participating application knows that the token was intended for use with that application. It is optional. In the event that an application has expected audiences and they are not present the token must be rejected. In the event where the token has audiences and the application has none expected then the token is accepted. OPEN ISSUE - not currently being populated in WebSSOResource. empty
knoxsso.redirect.whitelist.regex A semicolon separated list of regex expressions. The incoming originalUrl must match one of the expressions in order for KnoxSSO to redirect to it after authentication. Defaults to only relative paths and localhost with or without SSL for development usecases. This needs to be opened up for production use and actual participating applications. Note that cookie use is still constrained to redirect destinations in the same domain as the KnoxSSO service - regardless of the expressions specified here. ^/.*$;^https?://localhost:\d{0,9}/.*$

Participating Application Configuration

Hadoop Configuration Example

The following is used as the KnoxSSO configuration in the Hadoop JWTRedirectAuthenticationHandler implementation. Any participating application will need similar configuration. Since JWTRedirectAuthenticationHandler extends the AltKerberosAuthenticationHandler, the typical Kerberos configuration parameters for authentication are also required.

<property>
    <name>hadoop.http.authentication.type</name
    <value>org.apache.hadoop.security.authentication.server.JWTRedirectAuthenticationHandler</value>
</property>

This is the handler classname in Hadoop auth for JWT token (KnoxSSO) support.

<property>
    <name>hadoop.http.authentication.authentication.provider.url</name>
    <value>https://c6401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso</value>
</property>

The above property is the SSO provider URL that points to the knoxsso endpoint.

<property>
    <name>hadoop.http.authentication.public.key.pem</name>
    <value>MIICVjCCAb+gAwIBAgIJAPPvOtuTxFeiMA0GCSqGSIb3DQEBBQUAMG0xCzAJBgNV
  BAYTAlVTMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ8wDQYDVQQKEwZI
  YWRvb3AxDTALBgNVBAsTBFRlc3QxIDAeBgNVBAMTF2M2NDAxLmFtYmFyaS5hcGFj
  aGUub3JnMB4XDTE1MDcxNjE4NDcyM1oXDTE2MDcxNTE4NDcyM1owbTELMAkGA1UE
  BhMCVVMxDTALBgNVBAgTBFRlc3QxDTALBgNVBAcTBFRlc3QxDzANBgNVBAoTBkhh
  ZG9vcDENMAsGA1UECxMEVGVzdDEgMB4GA1UEAxMXYzY0MDEuYW1iYXJpLmFwYWNo
  ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMFs/rymbiNvg8lDhsdA
  qvh5uHP6iMtfv9IYpDleShjkS1C+IqId6bwGIEO8yhIS5BnfUR/fcnHi2ZNrXX7x
  QUtQe7M9tDIKu48w//InnZ6VpAqjGShWxcSzR6UB/YoGe5ytHS6MrXaormfBg3VW
  tDoy2MS83W8pweS6p5JnK7S5AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEANyVg6EzE
  2q84gq7wQfLt9t047nYFkxcRfzhNVL3LB8p6IkM4RUrzWq4kLA+z+bpY2OdpkTOe
  wUpEdVKzOQd4V7vRxpdANxtbG/XXrJAAcY/S+eMy1eDK73cmaVPnxPUGWmMnQXUi
  TLab+w8tBQhNbq6BOQ42aOrLxA8k/M4cV1A=</value>
</property>

The above property holds the KnoxSSO server’s public key for signature verification. Adding it directly to the config like this is convenient and is easily done through Ambari to existing config files that take custom properties. Config is generally protected as root access only as well - so it is a pretty good solution.

Individual UIs within the Hadoop ecosystem will have similar configuration for participating in the KnoxSSO websso capabilities.

Blogs will be provided on the Apache Knox project site for these usecases as they become available.

Mutual Authentication with SSL

To establish a stronger trust relationship between client and server, we provide mutual authentication with SSL via client certs. This is particularly useful in providing additional validation for Preauthenticated SSO with HTTP Headers. Rather than just ip address validation, connections will only be accepted by Knox from clients presenting trusted certificates.

This behavior is configured for the entire gateway instance within the gateway-site.xml file. All topologies deployed within the gateway instance with mutual authentication enabled will require incoming connections to present trusted client certificates during the SSL handshake. Otherwise, connections will be refused.

The following table describes the configuration elements related to mutual authentication and their defaults:

Configuration Element Description
gateway.client.auth.needed True|False - indicating the need for client authentication. Default is False.
gateway.truststore.path Fully qualified path to the trust store to use. Default is the gateway.jks.
gateway.truststore.type Keystore type of the trust store. Default is JKS.
gateway.trust.all.certs Allows for all certificates to be trusted. Default is false.

By only indicating that it is needed with gateway.client.auth.needed, the {GATEWAY_HOME}/data/security/keystores/gateway.jks keystore is used. This is the identity keystore for the server and can also be used as the truststore. We can specify the path to a dedicated truststore via gateway.truststore.path. If the truststore password is different from the gateway master secret then it can be set using

knoxcli.sh create-alias gateway-truststore-password --value {pwd} 

Otherwise, the master secret will be used. If the truststore is not a JKS type then it can be set via gateway.truststore.type.

Audit

The Audit facility within the Knox Gateway introduces functionality for tracking actions that are executed by Knox per user’s request or that are produced by Knox internal events like topology deploy, etc. The Knox Audit module is based on Apache log4j.

Configuration needed

Out of the box, the Knox Gateway includes preconfigured auditing capabilities. To change its configuration please read the following sections.

Where audit logs go

The Audit module is preconfigured to write audit records to the log file {GATEWAY_HOME}/log/gateway-audit.log.

This behavior can be changed in the {GATEWAY_HOME}/conf/gateway-log4j.properties file. app.audit.file can be used to change the location. The log4j.appender.auditfile.* properties can be used for further customization. For detailed information read the Apache log4j documentation.

Audit format

Out of the box, the audit record format is defined by org.apache.hadoop.gateway.audit.log4j.layout.AuditLayout. Its structure is as follows:

EVENT_PUBLISHING_TIME ROOT_REQUEST_ID|PARENT_REQUEST_ID|REQUEST_ID|LOGGER_NAME|TARGET_SERVICE_NAME|USER_NAME|PROXY_USER_NAME|SYSTEM_USER_NAME|ACTION|RESOURCE_TYPE|RESOURCE_NAME|OUTCOME|LOGGING_MESSAGE

The audit record format can be changed by setting log4j.appender.auditfile.layout property in {GATEWAY_HOME}/conf/gateway-log4j.properties to another class that extends org.apache.log4j.Layout or its subclasses.

For detailed information read Apache log4j.

How to interpret audit log
Component Description
EVENT_PUBLISHING_TIME Time when audit record was published.
ROOT_REQUEST_ID The root request ID if this is a sub-request. Currently it is empty.
PARENT_REQUEST_ID The parent request ID if this is a sub-request. Currently it is empty.
REQUEST_ID A unique value representing the current, active request. If the current request id value is different from the current parent request id value then the current request id value is moved to the parent request id before it is replaced by the provided request id. If the root request id is not set it will be set with the first non-null value of either the parent request id or the passed request id.
LOGGER_NAME The name of the logger
TARGET_SERVICE_NAME Name of Hadoop service. Can be empty if audit record is not linked to any Hadoop service, for example, audit record for topology deployment.
USER_NAME Name of user that initiated session with Knox
PROXY_USER_NAME Mapped user name. For detailed information read Identity Assertion.
SYSTEM_USER_NAME Currently is empty.
ACTION Type of action that was executed. Following actions are defined: authentication, authorization, redeploy, deploy, undeploy, identity-mapping, dispatch, access.
RESOURCE_TYPE Type of resource for which action was executed. Following resource types are defined: uri, topology, principal.
RESOURCE_NAME Name of resource. For resource of type topology it is name of topology. For resource of type uri it is inbound or dispatch request path. For resource of type principal it is a name of mapped user.
OUTCOME Action result type. Following outcomes are defined: success, failure, unavailable.
LOGGING_MESSAGE Logging message. Contains additional tracking information.

Audit log rotation

Audit logging is preconfigured with org.apache.log4j.DailyRollingFileAppender. Apache log4j contains information about other Appenders.

How to change the audit level or disable it

All audit messages are logged at INFO level and this behavior can’t be changed.

Disabling auditing can be done by decreasing the log level for the Audit appender or setting it to OFF.

Client Details

Hadoop requires a client that can be used to interact remotely with the services provided by Hadoop cluster. This will also be true when using the Apache Knox Gateway to provide perimeter security and centralized access for these services. The two primary existing clients for Hadoop are the CLI (i.e. Command Line Interface, hadoop) and Hue (i.e. Hadoop User Experience). For several reasons however, neither of these clients can currently be used to access Hadoop services via the Apache Knox Gateway.

This led to thinking about a very simple client that could help people use and evaluate the gateway. The list below outlines the general requirements for such a client.

The result is a very simple DSL (Domain Specific Language) of sorts that is used via Groovy scripts. Here is an example of a command that copies a file from the local file system to HDFS.

Note: The variables session, localFile and remoteFile are assumed to be defined.

Hdfs.put(session).file(localFile).to(remoteFile).now()

This work is in very early development but is already very useful in its current state. We are very interested in receiving feedback about how to improve this feature and the DSL in particular.

A note of thanks to REST-assured which provides a Fluent interface style DSL for testing REST services. It served as the initial inspiration for the creation of this DSL.

Assumptions

This document assumes a few things about your environment in order to simplify the examples.

Basics

The DSL requires a shell to interpret the Groovy script. The shell can either be used interactively or to execute a script file. To simplify use, the distribution contains an embedded version of the Groovy shell.

The shell can be run interactively. Use the command exit to exit.

java -jar bin/shell.jar

When running interactively it may be helpful to reduce some of the output generated by the shell console. Use the following command in the interactive shell to reduce that output. This only needs to be done once as these preferences are persisted.

set verbosity QUIET
set show-last-result false

Also when running interactively use the exit command to terminate the shell. Using ^C to exit can sometimes leaves the parent shell in a problematic state.

The shell can also be used to execute a script by passing a single filename argument.

java -jar bin/shell.jar samples/ExampleWebHdfsPutGet.groovy

Examples

Once the shell can be launched the DSL can be used to interact with the gateway and Hadoop. Below is a very simple example of an interactive shell session to upload a file to HDFS.

java -jar bin/shell.jar
knox:000> session = Hadoop.login( "https://localhost:8443/gateway/sandbox", "guest", "guest-password" )
knox:000> Hdfs.put( session ).file( "README" ).to( "/tmp/example/README" ).now()

The knox:000> in the example above is the prompt from the embedded Groovy console. If you output doesn’t look like this you may need to set the verbosity and show-last-result preferences as described above in the Usage section.

If you recieve an error HTTP/1.1 403 Forbidden it may be because that file already exists. Try deleting it with the following command and then try again.

knox:000> Hdfs.rm(session).file("/tmp/example/README").now()

Without using some other tool to browse HDFS it is hard to tell that this command did anything. Execute this to get a bit more feedback.

knox:000> println "Status=" + Hdfs.put( session ).file( "README" ).to( "/tmp/example/README2" ).now().statusCode
Status=201

Notice that a different filename is used for the destination. Without this an error would have resulted. Of course the DSL also provides a command to list the contents of a directory.

knox:000> println Hdfs.ls( session ).dir( "/tmp/example" ).now().string
{"FileStatuses":{"FileStatus":[{"accessTime":1363711366977,"blockSize":134217728,"group":"hdfs","length":19395,"modificationTime":1363711366977,"owner":"guest","pathSuffix":"README","permission":"644","replication":1,"type":"FILE"},{"accessTime":1363711375617,"blockSize":134217728,"group":"hdfs","length":19395,"modificationTime":1363711375617,"owner":"guest","pathSuffix":"README2","permission":"644","replication":1,"type":"FILE"}]}}

It is a design decision of the DSL to not provide type safe classes for various request and response payloads. Doing so would provide an undesirable coupling between the DSL and the service implementation. It also would make adding new commands much more difficult. See the Groovy section below for a variety capabilities and tools for working with JSON and XML to make this easy. The example below shows the use of JsonSlurper and GPath to extract content from a JSON response.

knox:000> import groovy.json.JsonSlurper
knox:000> text = Hdfs.ls( session ).dir( "/tmp/example" ).now().string
knox:000> json = (new JsonSlurper()).parseText( text )
knox:000> println json.FileStatuses.FileStatus.pathSuffix
[README, README2]

In the future, “built-in” methods to slurp JSON and XML may be added to make this a bit easier. This would allow for the following type of single line interaction:

println Hdfs.ls(session).dir("/tmp").now().json().FileStatuses.FileStatus.pathSuffix

Shell sessions should always be ended with shutting down the session. The examples above do not touch on it but the DSL supports the simple execution of commands asynchronously. The shutdown command attempts to ensures that all asynchronous commands have completed before existing the shell.

knox:000> session.shutdown()
knox:000> exit

All of the commands above could have been combined into a script file and executed as a single line.

java -jar bin/shell.jar samples/ExampleWebHdfsPutGet.groovy

This would be the content of that script.

import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.hadoop.gateway.shell.hdfs.Hdfs
import groovy.json.JsonSlurper

gateway = "https://localhost:8443/gateway/sandbox"
username = "guest"
password = "guest-password"
dataFile = "README"

session = Hadoop.login( gateway, username, password )
Hdfs.rm( session ).file( "/tmp/example" ).recursive().now()
Hdfs.put( session ).file( dataFile ).to( "/tmp/example/README" ).now()
text = Hdfs.ls( session ).dir( "/tmp/example" ).now().string
json = (new JsonSlurper()).parseText( text )
println json.FileStatuses.FileStatus.pathSuffix
session.shutdown()
exit

Notice the Hdfs.rm command. This is included simply to ensure that the script can be rerun. Without this an error would result the second time it is run.

Futures

The DSL supports the ability to invoke commands asynchronously via the later() invocation method. The object returned from the later() method is a java.util.concurrent.Future parameterized with the response type of the command. This is an example of how to asynchronously put a file to HDFS.

future = Hdfs.put(session).file("README").to("/tmp/example/README").later()
println future.get().statusCode

The future.get() method will block until the asynchronous command is complete. To illustrate the usefulness of this however multiple concurrent commands are required.

readmeFuture = Hdfs.put(session).file("README").to("/tmp/example/README").later()
licenseFuture = Hdfs.put(session).file("LICENSE").to("/tmp/example/LICENSE").later()
session.waitFor( readmeFuture, licenseFuture )
println readmeFuture.get().statusCode
println licenseFuture.get().statusCode

The session.waitFor() method will wait for one or more asynchronous commands to complete.

Closures

Futures alone only provide asynchronous invocation of the command. What if some processing should also occur asynchronously once the command is complete. Support for this is provided by closures. Closures are blocks of code that are passed into the later() invocation method. In Groovy these are contained within {} immediately after a method. These blocks of code are executed once the asynchronous command is complete.

Hdfs.put(session).file("README").to("/tmp/example/README").later(){ println it.statusCode }

In this example the put() command is executed on a separate thread and once complete the println it.statusCode block is executed on that thread. The it variable is automatically populated by Groovy and is a reference to the result that is returned from the future or now() method. The future example above can be rewritten to illustrate the use of closures.

readmeFuture = Hdfs.put(session).file("README").to("/tmp/example/README").later() { println it.statusCode }
licenseFuture = Hdfs.put(session).file("LICENSE").to("/tmp/example/LICENSE").later() { println it.statusCode }
session.waitFor( readmeFuture, licenseFuture )

Again, the session.waitFor() method will wait for one or more asynchronous commands to complete.

Constructs

In order to understand the DSL there are three primary constructs that need to be understood.

Session

This construct encapsulates the client side session state that will be shared between all command invocations. In particular it will simplify the management of any tokens that need to be presented with each command invocation. It also manages a thread pool that is used by all asynchronous commands which is why it is important to call one of the shutdown methods.

The syntax associated with this is expected to change. We expect that credentials will not need to be provided to the gateway. Rather it is expected that some form of access token will be used to initialize the session.

Services

Services are the primary extension point for adding new suites of commands. The current built-in examples are: Hdfs, Job and Workflow. The desire for extensibility is the reason for the slightly awkward Hdfs.ls(session) syntax. Certainly something more like session.hdfs().ls() would have been preferred but this would prevent adding new commands easily. At a minimum it would result in extension commands with a different syntax from the “built-in” commands.

The service objects essentially function as a factory for a suite of commands.

Commands

Commands provide the behavior of the DSL. They typically follow a Fluent interface style in order to allow for single line commands. There are really three parts to each command: Request, Invocation, Response

Request

The request is populated by all of the methods following the “verb” method and the “invoke” method. For example in Hdfs.rm(session).ls(dir).now() the request is populated between the “verb” method rm() and the “invoke” method now().

Invocation

The invocation method controls how the request is invoked. Currently supported synchronous and asynchronous invocation. The now() method executes the request and returns the result immediately. The later() method submits the request to be executed later and returns a future from which the result can be retrieved. In addition later() invocation method can optionally be provided a closure to execute when the request is complete. See the Futures and Closures sections below for additional detail and examples.

Response

The response contains the results of the invocation of the request. In most cases the response is a thin wrapper over the HTTP response. In fact many commands will share a single BasicResponse type that only provides a few simple methods.

public int getStatusCode()
public long getContentLength()
public String getContentType()
public String getContentEncoding()
public InputStream getStream()
public String getString()
public byte[] getBytes()
public void close();

Thanks to Groovy these methods can be accessed as attributes. In the some of the examples the staticCode was retrieved for example.

println Hdfs.put(session).rm(dir).now().statusCode

Groovy will invoke the getStatusCode method to retrieve the statusCode attribute.

The three methods getStream(), getBytes() and getString deserve special attention. Care must be taken that the HTTP body is fully read once and only once. Therefore one of these methods (and only one) must be called once and only once. Calling one of these more than once will cause an error. Failing to call one of these methods once will result in lingering open HTTP connections. The close() method may be used if the caller is not interested in reading the result body. Most commands that do not expect a response body will call close implicitly. If the body is retrieved via getBytes() or getString(), the close() method need not be called. When using getStream(), care must be taken to consume the entire body otherwise lingering open HTTP connections will result. The close() method may be called after reading the body partially to discard the remainder of the body.

Services

The built-in supported client DSL for each Hadoop service can be found in the Service Details section.

Extension

Extensibility is a key design goal of the KnoxShell and client DSL. There are two ways to provide extended functionality for use with the shell. The first is to simply create Groovy scripts that use the DSL to perform a useful task. The second is to add new services and commands. In order to add new service and commands new classes must be written in either Groovy or Java and added to the classpath of the shell. Fortunately there is a very simple way to add classes and JARs to the shell classpath. The first time the shell is executed it will create a configuration file in the same directory as the JAR with the same base name and a .cfg extension.

bin/shell.jar
bin/shell.cfg

That file contains both the main class for the shell as well as a definition of the classpath. Currently that file will by default contain the following.

main.class=org.apache.hadoop.gateway.shell.Shell
class.path=../lib; ../lib/*.jar; ../ext; ../ext/*.jar

Therefore to extend the shell you should copy any new service and command class either to the ext directory or if they are packaged within a JAR copy the JAR to the ext directory. The lib directory is reserved for JARs that may be delivered with the product.

Below are samples for the service and command classes that would need to be written to add new commands to the shell. These happen to be Groovy source files but could - with very minor changes - be Java files. The easiest way to add these to the shell is to compile them directly into the ext directory. Note: This command depends upon having the Groovy compiler installed and available on the execution path.

groovy -d ext -cp bin/shell.jar samples/SampleService.groovy \
    samples/SampleSimpleCommand.groovy samples/SampleComplexCommand.groovy

These source files are available in the samples directory of the distribution but are included here for convenience.

Sample Service (Groovy)

import org.apache.hadoop.gateway.shell.Hadoop

class SampleService {

    static String PATH = "/webhdfs/v1"

    static SimpleCommand simple( Hadoop session ) {
        return new SimpleCommand( session )
    }

    static ComplexCommand.Request complex( Hadoop session ) {
        return new ComplexCommand.Request( session )
    }

}

Sample Simple Command (Groovy)

import org.apache.hadoop.gateway.shell.AbstractRequest
import org.apache.hadoop.gateway.shell.BasicResponse
import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.utils.URIBuilder

import java.util.concurrent.Callable

class SimpleCommand extends AbstractRequest<BasicResponse> {

    SimpleCommand( Hadoop session ) {
        super( session )
    }

    private String param
    SimpleCommand param( String param ) {
        this.param = param
        return this
    }

    @Override
    protected Callable<BasicResponse> callable() {
        return new Callable<BasicResponse>() {
            @Override
            BasicResponse call() {
                URIBuilder uri = uri( SampleService.PATH, param )
                addQueryParam( uri, "op", "LISTSTATUS" )
                HttpGet get = new HttpGet( uri.build() )
                return new BasicResponse( execute( get ) )
            }
        }
    }

}

Sample Complex Command (Groovy)

import com.jayway.jsonpath.JsonPath
import org.apache.hadoop.gateway.shell.AbstractRequest
import org.apache.hadoop.gateway.shell.BasicResponse
import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.utils.URIBuilder

import java.util.concurrent.Callable

class ComplexCommand {

    static class Request extends AbstractRequest<Response> {

        Request( Hadoop session ) {
            super( session )
        }

        private String param;
        Request param( String param ) {
            this.param = param;
            return this;
        }

        @Override
        protected Callable<Response> callable() {
            return new Callable<Response>() {
                @Override
                Response call() {
                    URIBuilder uri = uri( SampleService.PATH, param )
                    addQueryParam( uri, "op", "LISTSTATUS" )
                    HttpGet get = new HttpGet( uri.build() )
                    return new Response( execute( get ) )
                }
            }
        }

    }

    static class Response extends BasicResponse {

        Response(HttpResponse response) {
            super(response)
        }

        public List<String> getNames() {
            return JsonPath.read( string, "\$.FileStatuses.FileStatus[*].pathSuffix" )
        }

    }

}

Groovy

The shell included in the distribution is basically an unmodified packaging of the Groovy shell. The distribution does however provide a wrapper that makes it very easy to setup the class path for the shell. In fact the JARs required to execute the DSL are included on the class path by default. Therefore these command are functionally equivalent if you have Groovy installed. See below for a description of what is required for JARs required by the DSL from lib and dep directories.

java -jar bin/shell.jar samples/ExampleWebHdfsPutGet.groovy
groovy -classpath {JARs required by the DSL from lib and dep} samples/ExampleWebHdfsPutGet.groovy

The interactive shell isn’t exactly equivalent. However the only difference is that the shell.jar automatically executes some additional imports that are useful for the KnoxShell client DSL. So these two sets of commands should be functionality equivalent. However there is currently a class loading issue that prevents the groovysh command from working properly.

java -jar bin/shell.jar

groovysh -classpath {JARs required by the DSL from lib and dep}
import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.hadoop.gateway.shell.hdfs.Hdfs
import org.apache.hadoop.gateway.shell.job.Job
import org.apache.hadoop.gateway.shell.workflow.Workflow
import java.util.concurrent.TimeUnit

Alternatively, you can use the Groovy Console which does not appear to have the same class loading issue.

groovyConsole -classpath {JARs required by the DSL from lib and dep}

import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.hadoop.gateway.shell.hdfs.Hdfs
import org.apache.hadoop.gateway.shell.job.Job
import org.apache.hadoop.gateway.shell.workflow.Workflow
import java.util.concurrent.TimeUnit

The JARs currently required by the client DSL are

lib/gateway-shell-{GATEWAY_VERSION}.jar
dep/httpclient-4.3.6.jar
dep/httpcore-4.3.3.jar
dep/commons-lang3-3.4.jar
dep/commons-codec-1.7.jar

So on Linux/MacOS you would need this command

groovy -cp lib/gateway-shell-0.9.0.jar:dep/httpclient-4.3.6.jar:dep/httpcore-4.3.3.jar:dep/commons-lang3-3.4.jar:dep/commons-codec-1.7.jar samples/ExampleWebHdfsPutGet.groovy

and on Windows you would need this command

groovy -cp lib/gateway-shell-0.9.0.jar;dep/httpclient-4.3.6.jar;dep/httpcore-4.3.3.jar;dep/commons-lang3-3.4.jar;dep/commons-codec-1.7.jar samples/ExampleWebHdfsPutGet.groovy

The exact list of required JARs is likely to change from release to release so it is recommended that you utilize the wrapper bin/shell.jar.

In addition because the DSL can be used via standard Groovy, the Groovy integrations in many popular IDEs (e.g. IntelliJ, Eclipse) can also be used. This makes it particularly nice to develop and execute scripts to interact with Hadoop. The code-completion features in modern IDEs in particular provides immense value. All that is required is to add the gateway-shell-{GATEWAY_VERSION}.jar to the projects class path.

There are a variety of Groovy tools that make it very easy to work with the standard interchange formats (i.e. JSON and XML). In Groovy the creation of XML or JSON is typically done via a “builder” and parsing done via a “slurper”. In addition once JSON or XML is “slurped” the GPath, an XPath like feature build into Groovy can be used to access data.

Service Details

In the sections that follow the integrations currently available out of the box with the gateway will be described. In general these sections will include examples that demonstrate how to access each of these services via the gateway. In many cases this will include both the use of cURL as a REST API client as well as the use of the Knox Client DSL. You may notice that there are some minor differences between using the REST API of a given service via the gateway. In general this is necessary in order to achieve the goal of leaking internal Hadoop cluster details to the client.

Keep in mind that the gateway uses a plugin model for supporting Hadoop services. Check back with the Apache Knox site for the latest news on plugin availability. You can also create your own custom plugin to extend the capabilities of the gateway.

These are the current Hadoop services with built-in support.

Assumptions

This document assumes a few things about your environment in order to simplify the examples.

Customization

Using these samples with other Hadoop installations will require changes to the steps describe here as well as changes to referenced sample scripts. This will also likely require changes to the gateway’s default configuration. In particular host names, ports user names and password may need to be changed to match your environment. These changes may need to be made to gateway configuration and also the Groovy sample script files in the distribution. All of the values that may need to be customized in the sample scripts can be found together at the top of each of these files.

cURL

The cURL HTTP client command line utility is used extensively in the examples for each service. In particular this form of the cURL command line is used repeatedly.

curl -i -k -u guest:guest-password ...

The option -i (aka –include) is used to output HTTP response header information. This will be important when the content of the HTTP Location header is required for subsequent requests.

The option -k (aka –insecure) is used to avoid any issues resulting from the use of demonstration SSL certificates.

The option -u (aka –user) is used to provide the credentials to be used when the client is challenged by the gateway.

Keep in mind that the samples do not use the cookie features of cURL for the sake of simplicity. Therefore each request via cURL will result in an authentication.

WebHDFS

REST API access to HDFS in a Hadoop cluster is provided by WebHDFS. The WebHDFS REST API documentation is available online. WebHDFS must be enabled in the hdfs-site.xml configuration file. In the sandbox this configuration file is located at /etc/hadoop/conf/hdfs-site.xml. Note the properties shown below as they are related to configuration required by the gateway. Some of these represent the default values and may not actually be present in hdfs-site.xml.

<property>
    <name>dfs.webhdfs.enabled</name>
    <value>true</value>
</property>
<property>
    <name>dfs.namenode.rpc-address</name>
    <value>sandbox.hortonworks.com:8020</value>
</property>
<property>
    <name>dfs.namenode.http-address</name>
    <value>sandbox.hortonworks.com:50070</value>
</property>
<property>
    <name>dfs.https.namenode.https-address</name>
    <value>sandbox.hortonworks.com:50470</value>
</property>

The values above need to be reflected in each topology descriptor file deployed to the gateway. The gateway by default includes a sample topology descriptor file {GATEWAY_HOME}/deployments/sandbox.xml. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>NAMENODE</role>
    <url>hdfs://localhost:8020</url>
</service>
<service>
    <role>WEBHDFS</role>
    <url>http://localhost:50070/webhdfs</url>
</service>

The URL provided for the role NAMENODE does not result in an endpoint being exposed by the gateway. This information is only required so that other URLs can be rewritten that reference the Name Node’s RPC address. This prevents clients from needed to be aware of the internal cluster details.

By default the gateway is configured to use the HTTP endpoint for WebHDFS in the Sandbox. This could alternatively be configured to use the HTTPS endpoint by provided the correct address.

WebHDFS URL Mapping

For Name Node URLs, the mapping of Knox Gateway accessible WebHDFS URLs to direct WebHDFS URLs is simple.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/webhdfs
Cluster http://{webhdfs-host}:50070/webhdfs

However, there is a subtle difference to URLs that are returned by WebHDFS in the Location header of many requests. Direct WebHDFS requests may return Location headers that contain the address of a particular DataNode. The gateway will rewrite these URLs to ensure subsequent requests come back through the gateway and internal cluster details are protected.

A WebHDFS request to the NameNode to retrieve a file will return a URL of the form below in the Location header.

http://{datanode-host}:{data-node-port}/webhdfs/v1/{path}?...

Note that this URL contains the network location of a DataNode. The gateway will rewrite this URL to look like the URL below.

https://{gateway-host}:{gateway-port}/{gateway-path}/{custer-name}/webhdfs/data/v1/{path}?_={encrypted-query-parameters}

The {encrypted-query-parameters} will contain the {datanode-host} and {datanode-port} information. This information along with the original query parameters are encrypted so that the internal Hadoop details are protected.

WebHDFS Examples

The examples below upload a file, download the file and list the contents of the directory.

WebHDFS via client DSL

You can use the Groovy example scripts and interpreter provided with the distribution.

java -jar bin/shell.jar samples/ExampleWebHdfsPutGet.groovy
java -jar bin/shell.jar samples/ExampleWebHdfsLs.groovy

You can manually type the client DSL script into the KnoxShell interactive Groovy interpreter provided with the distribution. The command below starts the KnoxShell in interactive mode.

java -jar bin/shell.jar

Each line below could be typed or copied into the interactive shell and executed. This is provided as an example to illustrate the use of the client DSL.

// Import the client DSL and a useful utilities for working with JSON.
import org.apache.hadoop.gateway.shell.Hadoop
import org.apache.hadoop.gateway.shell.hdfs.Hdfs
import groovy.json.JsonSlurper

// Setup some basic config.
gateway = "https://localhost:8443/gateway/sandbox"
username = "guest"
password = "guest-password"

// Start the session.
session = Hadoop.login( gateway, username, password )

// Cleanup anything leftover from a previous run.
Hdfs.rm( session ).file( "/user/guest/example" ).recursive().now()

// Upload the README to HDFS.
Hdfs.put( session ).file( "README" ).to( "/user/guest/example/README" ).now()

// Download the README from HDFS.
text = Hdfs.get( session ).from( "/user/guest/example/README" ).now().string
println text

// List the contents of the directory.
text = Hdfs.ls( session ).dir( "/user/guest/example" ).now().string
json = (new JsonSlurper()).parseText( text )
println json.FileStatuses.FileStatus.pathSuffix

// Cleanup the directory.
Hdfs.rm( session ).file( "/user/guest/example" ).recursive().now()

// Clean the session.
session.shutdown()
WebHDFS via cURL

Use can use cURL to directly invoke the REST APIs via the gateway.

Optionally cleanup the sample directory in case a previous example was run without cleaning up.
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'
Register the name for a sample file README in /user/guest/example.
curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/README?op=CREATE'
Upload README to /user/guest/example. Use the README in {GATEWAY_HOME}.
curl -i -k -u guest:guest-password -T README -X PUT \
    '{Value of Location header from command above}'
List the contents of the directory /user/guest/example.
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=LISTSTATUS'
Request the content of the README file in /user/guest/example.
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/README?op=OPEN'
Read the content of the file.
curl -i -k -u guest:guest-password -X GET \
    '{Value of Location header from command above}'
Optionally cleanup the example directory.
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'
WebHDFS client DSL
get() - Get a file from HDFS (OPEN).
ls() - Query the contents of a directory (LISTSTATUS)
mkdir() - Create a directory in HDFS (MKDIRS)
put() - Write a file into HDFS (CREATE)
rm() - Delete a file or directory (DELETE)

WebHDFS HA

Knox provides basic failover and retry functionality for REST API calls made to WebHDFS when HDFS HA has been configured and enabled.

To enable HA functionality for WebHDFS in Knox the following configuration has to be added to the topology file.

<provider>
   <role>ha</role>
   <name>HaProvider</name>
   <enabled>true</enabled>
   <param>
       <name>WEBHDFS</name>
       <value>maxFailoverAttempts=3;failoverSleep=1000;maxRetryAttempts=300;retrySleep=1000;enabled=true</value>
   </param>
</provider>

The role and name of the provider above must be as shown. The name in the ‘param’ section must match that of the service role name that is being configured for HA and the value in the ‘param’ section is the configuration for that particular service in HA mode. In this case the name is ‘WEBHDFS’.

The various configuration parameters are described below:

And for the service configuration itself the additional URLs should be added to the list. The active URL (at the time of configuration) should ideally be added to the top of the list.

<service>
    <role>WEBHDFS</role>
    <url>http://{host1}:50070/webhdfs</url>
    <url>http://{host2}:50070/webhdfs</url>
</service>

WebHCat

WebHCat (also called Templeton) is a related but separate service from HiveServer2. As such it is installed and configured independently. The WebHCat wiki pages describe this processes. In sandbox this configuration file for WebHCat is located at /etc/hadoop/hcatalog/webhcat-site.xml. Note the properties shown below as they are related to configuration required by the gateway.

<property>
    <name>templeton.port</name>
    <value>50111</value>
</property>

Also important is the configuration of the JOBTRACKER RPC endpoint. For Hadoop 2 this can be found in the yarn-site.xml file. In Sandbox this file can be found at /etc/hadoop/conf/yarn-site.xml. The property yarn.resourcemanager.address within that file is relevant for the gateway’s configuration.

<property>
    <name>yarn.resourcemanager.address</name>
    <value>sandbox.hortonworks.com:8050</value>
</property>

See WebHDFS for details about locating the Hadoop configuration for the NAMENODE endpoint.

The gateway by default includes a sample topology descriptor file {GATEWAY_HOME}/deployments/sandbox.xml. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>NAMENODE</role>
    <url>hdfs://localhost:8020</url>
</service>
<service>
    <role>JOBTRACKER</role>
    <url>rpc://localhost:8050</url>
</service>
<service>
    <role>WEBHCAT</role>
    <url>http://localhost:50111/templeton</url>
</service>

The URLs provided for the role NAMENODE and JOBTRACKER do not result in an endpoint being exposed by the gateway. This information is only required so that other URLs can be rewritten that reference the appropriate RPC address for Hadoop services. This prevents clients from needed to be aware of the internal cluster details. Note that for Hadoop 2 the JOBTRACKER RPC endpoint is provided by the Resource Manager component.

By default the gateway is configured to use the HTTP endpoint for WebHCat in the Sandbox. This could alternatively be configured to use the HTTPS endpoint by provided the correct address.

WebHCat URL Mapping

For WebHCat URLs, the mapping of Knox Gateway accessible URLs to direct WebHCat URLs is simple.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/templeton
Cluster http://{webhcat-host}:{webhcat-port}/templeton}

WebHCat via cURL

Use can use cURL to directly invoke the REST APIs via the gateway. For the full list of available REST calls look at the WebHCat documentation. This is a simple curl command to test the connection:

curl -i -k -u guest:guest-password 'https://localhost:8443/gateway/sandbox/templeton/v1/status'

WebHCat Example

This example will submit the familiar WordCount Java MapReduce job to the Hadoop cluster via the gateway using the KnoxShell DSL. There are several ways to do this depending upon your preference.

You can use the “embedded” Groovy interpreter provided with the distribution.

java -jar bin/shell.jar samples/ExampleWebHCatJob.groovy

You can manually type in the KnoxShell DSL script into the “embedded” Groovy interpreter provided with the distribution.

java -jar bin/shell.jar

Each line from the file samples/ExampleWebHCatJob.groovy would then need to be typed or copied into the interactive shell.

WebHCat Client DSL

submitJava() - Submit a Java MapReduce job.
Job.submitJava(session)
    .jar(remoteJarName)
    .app(appName)
    .input(remoteInputDir)
    .output(remoteOutputDir)
    .now()
    .jobId
submitPig() - Submit a Pig job.
submitHive() - Submit a Hive job.
queryQueue() - Return a list of all job IDs registered to the user.
queryStatus() - Check the status of a job and get related job information given its job ID.

WebHCat HA

Please look at Default Service HA support

Oozie

Oozie is a Hadoop component that provides complex job workflows to be submitted and managed. Please refer to the latest Oozie documentation for details.

In order to make Oozie accessible via the gateway there are several important Hadoop configuration settings. These all relate to the network endpoint exposed by various Hadoop services.

The HTTP endpoint at which Oozie is running can be found via the oozie.base.url property in the oozie-site.xml file. In a Sandbox installation this can typically be found in /etc/oozie/conf/oozie-site.xml.

<property>
    <name>oozie.base.url</name>
    <value>http://sandbox.hortonworks.com:11000/oozie</value>
</property>

The RPC address at which the Resource Manager exposes the JOBTRACKER endpoint can be found via the yarn.resourcemanager.address in the yarn-site.xml file. In a Sandbox installation this can typically be found in /etc/hadoop/conf/yarn-site.xml.

<property>
    <name>yarn.resourcemanager.address</name>
    <value>sandbox.hortonworks.com:8050</value>
</property>

The RPC address at which the Name Node exposes its RPC endpoint can be found via the dfs.namenode.rpc-address in the hdfs-site.xml file. In a Sandbox installation this can typically be found in /etc/hadoop/conf/hdfs-site.xml.

<property>
    <name>dfs.namenode.rpc-address</name>
    <value>sandbox.hortonworks.com:8020</value>
</property>

If HDFS has been configured to be in High Availability mode (HA), then instead of the RPC address mentioned above for the Name Node, look up and use the logical name of the service found via dfs.nameservices in hdfs-site.xml. For example,

<property>
    <name>dfs.nameservices</name>
    <value>ha-service</value>
</property>

Please note, only one of the URLs, either the RPC endpoint or the HA service name should be used as the NAMENODE hdfs URL in the gateway topology file.

The information above must be provided to the gateway via a topology descriptor file. These topology descriptor files are placed in {GATEWAY_HOME}/deployments. An example that is setup for the default configuration of the Sandbox is {GATEWAY_HOME}/deployments/sandbox.xml. These values will need to be changed for non-default Sandbox or other Hadoop cluster configuration.

<service>
    <role>NAMENODE</role>
    <url>hdfs://localhost:8020</url>
</service>
<service>
    <role>JOBTRACKER</role>
    <url>rpc://localhost:8050</url>
</service>
<service>
    <role>OOZIE</role>
    <url>http://localhost:11000/oozie</url>
</service>

Oozie URL Mapping

For Oozie URLs, the mapping of Knox Gateway accessible URLs to direct Oozie URLs is simple.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/oozie
Cluster http://{oozie-host}:{oozie-port}/oozie}

Oozie Request Changes

TODO - In some cases the Oozie requests needs to be slightly different when made through the gateway. These changes are required in order to protect the client from knowing the internal structure of the Hadoop cluster.

Oozie Example via Client DSL

This example will also submit the familiar WordCount Java MapReduce job to the Hadoop cluster via the gateway using the KnoxShell DSL. However in this case the job will be submitted via a Oozie workflow. There are several ways to do this depending upon your preference.

You can use the “embedded” Groovy interpreter provided with the distribution.

java -jar bin/shell.jar samples/ExampleOozieWorkflow.groovy

You can manually type in the KnoxShell DSL script into the “embedded” Groovy interpreter provided with the distribution.

java -jar bin/shell.jar

Each line from the file samples/ExampleOozieWorkflow.groovy will need to be typed or copied into the interactive shell.

Oozie Example via cURL

The example below illustrates the sequence of curl commands that could be used to run a “word count” map reduce job via an Oozie workflow.

It utilizes the hadoop-examples.jar from a Hadoop install for running a simple word count job. A copy of that jar has been included in the samples directory for convenience.

In addition a workflow definition and configuration file is required. These have not been included but are available for download. Download workflow-definition.xml and workflow-configuration.xml and store them in the {GATEWAY_HOME} directory. Review the contents of workflow-configuration.xml to ensure that it matches your environment.

Take care to follow the instructions below where replacement values are required. These replacement values are identified with { } markup.

# 0. Optionally cleanup the test directory in case a previous example was run without cleaning up.
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'

# 1. Create the inode for workflow definition file in /user/guest/example
curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/workflow.xml?op=CREATE'

# 2. Upload the workflow definition file.  This file can be found in {GATEWAY_HOME}/templates
curl -i -k -u guest:guest-password -T workflow-definition.xml -X PUT \
    '{Value Location header from command above}'

# 3. Create the inode for hadoop-examples.jar in /user/guest/example/lib
curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/lib/hadoop-examples.jar?op=CREATE'

# 4. Upload hadoop-examples.jar to /user/guest/example/lib.  Use a hadoop-examples.jar from a Hadoop install.
curl -i -k -u guest:guest-password -T samples/hadoop-examples.jar -X PUT \
    '{Value Location header from command above}'

# 5. Create the inode for a sample input file readme.txt in /user/guest/example/input.
curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/input/README?op=CREATE'

# 6. Upload readme.txt to /user/guest/example/input.  Use the readme.txt in {GATEWAY_HOME}.
# The sample below uses this README file found in {GATEWAY_HOME}.
curl -i -k -u guest:guest-password -T README -X PUT \
    '{Value of Location header from command above}'

# 7. Submit the job via Oozie
# Take note of the Job ID in the JSON response as this will be used in the next step.
curl -i -k -u guest:guest-password -H Content-Type:application/xml -T workflow-configuration.xml \
    -X POST 'https://localhost:8443/gateway/sandbox/oozie/v1/jobs?action=start'

# 8. Query the job status via Oozie.
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/oozie/v1/job/{Job ID from JSON body}'

# 9. List the contents of the output directory /user/guest/example/output
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/output?op=LISTSTATUS'

# 10. Optionally cleanup the test directory
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'

Oozie Client DSL

submit() - Submit a workflow job.

status() - Query the status of a workflow job.

Oozie HA

Please look at Default Service HA support

HBase

HBase provides an optional REST API (previously called Stargate). See the HBase REST Setup section below for getting started with the HBase REST API and Knox with the Hortonworks Sandbox environment.

HBase URL Mapping

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/hbase
Cluster http://{hbase-rest-host}:8080/

HBase Examples

The examples below illustrate the set of basic operations with HBase instance using the REST API. Use following link to get more details about HBase REST API: http://hbase.apache.org/book.html#_rest.

Note: Some HBase examples may not work due to enabled Access Control. User may not be granted access for performing operations in the samples. In order to check if Access Control is configured in the HBase instance verify hbase-site.xml for a presence of org.apache.hadoop.hbase.security.access.AccessController in hbase.coprocessor.master.classes and hbase.coprocessor.region.classes properties.
To grant the Read, Write, Create permissions to guest user execute the following command:

echo grant 'guest', 'RWC' | hbase shell

If you are using a cluster secured with Kerberos you will need to have used kinit to authenticate to the KDC.

HBase REST API Setup

Launch REST API

The command below launches the REST daemon on port 8080 (the default)

sudo {HBASE_BIN}/hbase-daemon.sh start rest

Where {HBASE_BIN} is /usr/hdp/current/hbase-master/bin/ in the case of a HDP install.

To use a different port use the -p option:

sudo {HBASE_BIN/hbase-daemon.sh start rest -p 60080

Configure Sandbox port mapping for VirtualBox

  1. Select the VM
  2. Select menu Machine>Settings…
  3. Select tab Network
  4. Select Adapter 1
  5. Press Port Forwarding button
  6. Press Plus button to insert new rule: Name=HBASE REST, Host Port=60080, Guest Port=60080
  7. Press OK to close the rule window
  8. Press OK to Network window save the changes

HBase Restart

If it becomes necessary to restart HBase you can log into the hosts running HBase and use these steps.

sudo {HBASE_BIN}/hbase-daemon.sh stop rest
sudo -u hbase {HBASE_BIN}/hbase-daemon.sh stop regionserver
sudo -u hbase {HBASE_BIN}/hbase-daemon.sh stop master
sudo -u hbase {HBASE_BIN}/hbase-daemon.sh stop zookeeper

sudo -u hbase {HBASE_BIN}/hbase-daemon.sh start regionserver
sudo -u hbase {HBASE_BIN}/hbase-daemon.sh start master
sudo -u hbase {HBASE_BIN}/hbase-daemon.sh start zookeeper
sudo {HBASE_BIN}/hbase-daemon.sh start rest -p 60080

Where {HBASE_BIN} is /usr/hdp/current/hbase-master/bin/ in the case of a HDP Sandbox install.

HBase client DSL

For more details about client DSL usage please look at the chapter about the client DSL in this guide.

After launching the shell, execute the following command to be able to use the snippets below. import org.apache.hadoop.gateway.shell.hbase.HBase;

systemVersion() - Query Software Version.

clusterVersion() - Query Storage Cluster Version.

status() - Query Storage Cluster Status.

table().list() - Query Table List.

table(String tableName).schema() - Query Table Schema.

table(String tableName).create() - Create Table Schema.

HBase.session(session).table(tableName).create()
   .attribute("tb_attr1", "value1")
   .attribute("tb_attr2", "value2")
   .family("family1")
       .attribute("fm_attr1", "value3")
       .attribute("fm_attr2", "value4")
   .endFamilyDef()
   .family("family2")
   .family("family3")
   .endFamilyDef()
   .attribute("tb_attr3", "value5")
   .now()

table(String tableName).update() - Update Table Schema.

HBase.session(session).table(tableName).update()
     .family("family1")
         .attribute("fm_attr1", "new_value3")
     .endFamilyDef()
     .family("family4")
         .attribute("fm_attr3", "value6")
     .endFamilyDef()
     .now()```

table(String tableName).regions() - Query Table Metadata.

table(String tableName).delete() - Delete Table.

table(String tableName).row(String rowId).store() - Cell Store.

HBase.session(session).table(tableName).row("row_id_1").store()
     .column("family1", "col1", "col_value1")
     .column("family1", "col2", "col_value2", 1234567890l)
     .column("family2", null, "fam_value1")
     .now()


HBase.session(session).table(tableName).row("row_id_2").store()
     .column("family1", "row2_col1", "row2_col_value1")
     .now()

table(String tableName).row(String rowId).query() - Cell or Row Query.

HBase.session(session).table(tableName).row("row_id_1")
     .query()
     .now().string


HBase.session(session).table(tableName).row().query().now().string


HBase.session(session).table(tableName).row().query()
     .column("family1", "row2_col1")
     .column("family2")
     .times(0, Long.MAX_VALUE)
     .numVersions(1)
     .now().string

table(String tableName).row(String rowId).delete() - Row, Column, or Cell Delete.

HBase.session(session).table(tableName).row("row_id_1")
     .delete()
     .column("family1", "col1")
     .now()```


HBase.session(session).table(tableName).row("row_id_1")
     .delete()
     .column("family2")
     .time(Long.MAX_VALUE)
     .now()```

table(String tableName).scanner().create() - Scanner Creation.

HBase.session(session).table(tableName).scanner().create()
     .column("family1", "col2")
     .column("family2")
     .startRow("row_id_1")
     .endRow("row_id_2")
     .batch(1)
     .startTime(0)
     .endTime(Long.MAX_VALUE)
     .filter("")
     .maxVersions(100)
     .now()```

table(String tableName).scanner(String scannerId).getNext() - Scanner Get Next.

table(String tableName).scanner(String scannerId).delete() - Scanner Deletion.

HBase via Client DSL

This example illustrates sequence of all basic HBase operations: 1. get system version 2. get cluster version 3. get cluster status 4. create the table 5. get list of tables 6. get table schema 7. update table schema 8. insert single row into table 9. query row by id 10. query all rows 11. delete cell from row 12. delete entire column family from row 13. get table regions 14. create scanner 15. fetch values using scanner 16. drop scanner 17. drop the table

There are several ways to do this depending upon your preference.

You can use the Groovy interpreter provided with the distribution.

java -jar bin/shell.jar samples/ExampleHBase.groovy

You can manually type in the KnoxShell DSL script into the interactive Groovy interpreter provided with the distribution.

java -jar bin/shell.jar

Each line from the file below will need to be typed or copied into the interactive shell.

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.hadoop.gateway.shell.hbase

import org.apache.hadoop.gateway.shell.Hadoop

import static java.util.concurrent.TimeUnit.SECONDS

gateway = "https://localhost:8443/gateway/sandbox"
username = "guest"
password = "guest-password"
tableName = "test_table"

session = Hadoop.login(gateway, username, password)

println "System version : " + HBase.session(session).systemVersion().now().string

println "Cluster version : " + HBase.session(session).clusterVersion().now().string

println "Status : " + HBase.session(session).status().now().string

println "Creating table '" + tableName + "'..."

HBase.session(session).table(tableName).create()  \
    .attribute("tb_attr1", "value1")  \
    .attribute("tb_attr2", "value2")  \
    .family("family1")  \
        .attribute("fm_attr1", "value3")  \
        .attribute("fm_attr2", "value4")  \
    .endFamilyDef()  \
    .family("family2")  \
    .family("family3")  \
    .endFamilyDef()  \
    .attribute("tb_attr3", "value5")  \
    .now()

println "Done"

println "Table List : " + HBase.session(session).table().list().now().string

println "Schema for table '" + tableName + "' : " + HBase.session(session)  \
    .table(tableName)  \
    .schema()  \
    .now().string

println "Updating schema of table '" + tableName + "'..."

HBase.session(session).table(tableName).update()  \
    .family("family1")  \
        .attribute("fm_attr1", "new_value3")  \
    .endFamilyDef()  \
    .family("family4")  \
        .attribute("fm_attr3", "value6")  \
    .endFamilyDef()  \
    .now()

println "Done"

println "Schema for table '" + tableName + "' : " + HBase.session(session)  \
    .table(tableName)  \
    .schema()  \
    .now().string

println "Inserting data into table..."

HBase.session(session).table(tableName).row("row_id_1").store()  \
    .column("family1", "col1", "col_value1")  \
    .column("family1", "col2", "col_value2", 1234567890l)  \
    .column("family2", null, "fam_value1")  \
    .now()

HBase.session(session).table(tableName).row("row_id_2").store()  \
    .column("family1", "row2_col1", "row2_col_value1")  \
    .now()

println "Done"

println "Querying row by id..."

println HBase.session(session).table(tableName).row("row_id_1")  \
    .query()  \
    .now().string

println "Querying all rows..."

println HBase.session(session).table(tableName).row().query().now().string

println "Querying row by id with extended settings..."

println HBase.session(session).table(tableName).row().query()  \
    .column("family1", "row2_col1")  \
    .column("family2")  \
    .times(0, Long.MAX_VALUE)  \
    .numVersions(1)  \
    .now().string

println "Deleting cell..."

HBase.session(session).table(tableName).row("row_id_1")  \
    .delete()  \
    .column("family1", "col1")  \
    .now()

println "Rows after delete:"

println HBase.session(session).table(tableName).row().query().now().string

println "Extended cell delete"

HBase.session(session).table(tableName).row("row_id_1")  \
    .delete()  \
    .column("family2")  \
    .time(Long.MAX_VALUE)  \
    .now()

println "Rows after delete:"

println HBase.session(session).table(tableName).row().query().now().string

println "Table regions : " + HBase.session(session).table(tableName)  \
    .regions()  \
    .now().string

println "Creating scanner..."

scannerId = HBase.session(session).table(tableName).scanner().create()  \
    .column("family1", "col2")  \
    .column("family2")  \
    .startRow("row_id_1")  \
    .endRow("row_id_2")  \
    .batch(1)  \
    .startTime(0)  \
    .endTime(Long.MAX_VALUE)  \
    .filter("")  \
    .maxVersions(100)  \
    .now().scannerId

println "Scanner id=" + scannerId

println "Scanner get next..."

println HBase.session(session).table(tableName).scanner(scannerId)  \
    .getNext()  \
    .now().string

println "Dropping scanner with id=" + scannerId

HBase.session(session).table(tableName).scanner(scannerId).delete().now()

println "Done"

println "Dropping table '" + tableName + "'..."

HBase.session(session).table(tableName).delete().now()

println "Done"

session.shutdown(10, SECONDS)

HBase via cURL

Get software version

Set Accept Header to “text/plain”, “text/xml”, “application/json” or “application/x-protobuf”

%  curl -ik -u guest:guest-password\
 -H "Accept:  application/json"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/version'

Get version information regarding the HBase cluster backing the REST API instance

Set Accept Header to “text/plain”, “text/xml” or “application/x-protobuf”

%  curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/version/cluster'

Get detailed status on the HBase cluster backing the REST API instance.

Set Accept Header to “text/plain”, “text/xml”, “application/json” or “application/x-protobuf”

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/status/cluster'

Get the list of available tables.

Set Accept Header to “text/plain”, “text/xml”, “application/json” or “application/x-protobuf”

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase'

Create table with two column families using xml input

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"   -H "Content-Type: text/xml"\
 -d '<?xml version="1.0" encoding="UTF-8"?><TableSchema name="table1"><ColumnSchema name="family1"/><ColumnSchema name="family2"/></TableSchema>'\
 -X PUT 'https://localhost:8443/gateway/sandbox/hbase/table1/schema'

Create table with two column families using JSON input

curl -ik -u guest:guest-password\
 -H "Accept: application/json"  -H "Content-Type: application/json"\
 -d '{"name":"table2","ColumnSchema":[{"name":"family3"},{"name":"family4"}]}'\
 -X PUT 'https://localhost:8443/gateway/sandbox/hbase/table2/schema'

Get table metadata

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/table1/regions'

Insert single row table

curl -ik -u guest:guest-password\
 -H "Content-Type: text/xml"\
 -H "Accept: text/xml"\
 -d '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CellSet><Row key="cm93MQ=="><Cell column="ZmFtaWx5MTpjb2wx" >dGVzdA==</Cell></Row></CellSet>'\
 -X POST 'https://localhost:8443/gateway/sandbox/hbase/table1/row1'

Insert multiple rows into table

curl -ik -u guest:guest-password\
 -H "Content-Type: text/xml"\
 -H "Accept: text/xml"\
 -d '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CellSet><Row key="cm93MA=="><Cell column=" ZmFtaWx5Mzpjb2x1bW4x" >dGVzdA==</Cell></Row><Row key="cm93MQ=="><Cell column=" ZmFtaWx5NDpjb2x1bW4x" >dGVzdA==</Cell></Row></CellSet>'\
 -X POST 'https://localhost:8443/gateway/sandbox/hbase/table2/false-row-key'

Get all data from table

Set Accept Header to “text/plain”, “text/xml”, “application/json” or “application/x-protobuf”

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/table1/*'

Execute cell or row query

Set Accept Header to “text/plain”, “text/xml”, “application/json” or “application/x-protobuf”

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/table1/row1/family1:col1'

Delete entire row from table

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X DELETE 'https://localhost:8443/gateway/sandbox/hbase/table2/row0'

Delete column family from row

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X DELETE 'https://localhost:8443/gateway/sandbox/hbase/table2/row0/family3'

Delete specific column from row

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X DELETE 'https://localhost:8443/gateway/sandbox/hbase/table2/row0/family3'

Create scanner

Scanner URL will be in Location response header

curl -ik -u guest:guest-password\
 -H "Content-Type: text/xml"\
 -d '<Scanner batch="1"/>'\
 -X PUT 'https://localhost:8443/gateway/sandbox/hbase/table1/scanner'

Get the values of the next cells found by the scanner

curl -ik -u guest:guest-password\
 -H "Accept: application/json"\
 -X GET 'https://localhost:8443/gateway/sandbox/hbase/table1/scanner/13705290446328cff5ed'

Delete scanner

curl -ik -u guest:guest-password\
 -H "Accept: text/xml"\
 -X DELETE 'https://localhost:8443/gateway/sandbox/hbase/table1/scanner/13705290446328cff5ed'

Delete table

curl -ik -u guest:guest-password\
 -X DELETE 'https://localhost:8443/gateway/sandbox/hbase/table1/schema'

HBase REST HA

Please look at Default Service HA support

Hive

The Hive wiki pages describe Hive installation and configuration processes. In sandbox configuration file for Hive is located at /etc/hive/hive-site.xml. Hive Server has to be started in HTTP mode. Note the properties shown below as they are related to configuration required by the gateway.

<property>
    <name>hive.server2.thrift.http.port</name>
    <value>10001</value>
    <description>Port number when in HTTP mode.</description>
</property>

<property>
    <name>hive.server2.thrift.http.path</name>
    <value>cliservice</value>
    <description>Path component of URL endpoint when in HTTP mode.</description>
</property>

<property>
    <name>hive.server2.transport.mode</name>
    <value>http</value>
    <description>Server transport mode. "binary" or "http".</description>
</property>

<property>
    <name>hive.server2.allow.user.substitution</name>
    <value>true</value>
</property>

The gateway by default includes a sample topology descriptor file {GATEWAY_HOME}/deployments/sandbox.xml. The value in this sample is configured to work with an installed Sandbox VM.

<service>
    <role>HIVE</role>
    <url>http://localhost:10001/cliservice</url>
</service>

By default the gateway is configured to use the binary transport mode for Hive in the Sandbox.

Hive JDBC URL Mapping

Gateway jdbc:hive2://{gateway-host}:{gateway-port}/;ssl=true;sslTrustStore={gateway-trust-store-path};trustStorePassword={gateway-trust-store-password};transportMode=http;httpPath={gateway-path}/{cluster-name}/hive
Cluster http://{hive-host}:{hive-port}/{hive-path}

Hive Examples

This guide provides detailed examples for how to do some basic interactions with Hive via the Apache Knox Gateway.

Hive Setup
  1. Make sure you are running the correct version of Hive to ensure JDBC/Thrift/HTTP support.
  2. Make sure Hive Server is running on the correct port.
  3. Make sure Hive Server is running in HTTP mode.
  4. Client side (JDBC):
    1. Hive JDBC in HTTP mode depends on following minimal libraries set to run successfully(must be in the classpath):
      • hive-jdbc-0.14.0-standalone.jar;
      • commons-logging-1.1.3.jar;
    2. Connection URL has to be following: jdbc:hive2://{gateway-host}:{gateway-port}/;ssl=true;sslTrustStore={gateway-trust-store-path};trustStorePassword={gateway-trust-store-password};transportMode=http;httpPath={gateway-path}/{cluster-name}/hive
    3. Look at https://cwiki.apache.org/confluence/display/Hive/GettingStarted#GettingStarted-DDLOperations for examples. Hint: For testing it would be better to execute set hive.security.authorization.enabled=false as the first statement. Hint: Good examples of Hive DDL/DML can be found here http://gettingstarted.hadooponazure.com/hw/hive.html
Customization

This example may need to be tailored to the execution environment. In particular host name, host port, user name, user password and context path may need to be changed to match your environment. In particular there is one example file in the distribution that may need to be customized. Take a moment to review this file. All of the values that may need to be customized can be found together at the top of the file.

Client JDBC Example

Sample example for creating new table, loading data into it from the file system local to the Hive server and querying data from that table.

Java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import java.util.logging.Level;
import java.util.logging.Logger;

public class HiveJDBCSample {

  public static void main( String[] args ) {
    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;

    try {
      String user = "guest";
      String password = user + "-password";
      String gatewayHost = "localhost";
      int gatewayPort = 8443;
      String trustStore = "/usr/lib/knox/data/security/keystores/gateway.jks";
      String trustStorePassword = "knoxsecret";
      String contextPath = "gateway/sandbox/hive";
      String connectionString = String.format( "jdbc:hive2://%s:%d/;ssl=true;sslTrustStore=%s;trustStorePassword=%s?hive.server2.transport.mode=http;hive.server2.thrift.http.path=/%s", gatewayHost, gatewayPort, trustStore, trustStorePassword, contextPath );

      // load Hive JDBC Driver
      Class.forName( "org.apache.hive.jdbc.HiveDriver" );

      // configure JDBC connection
      connection = DriverManager.getConnection( connectionString, user, password );

      statement = connection.createStatement();

      // disable Hive authorization - it could be omitted if Hive authorization
      // was configured properly
      statement.execute( "set hive.security.authorization.enabled=false" );

      // create sample table
      statement.execute( "CREATE TABLE logs(column1 string, column2 string, column3 string, column4 string, column5 string, column6 string, column7 string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' '" );

      // load data into Hive from file /tmp/log.txt which is placed on the local file system
      statement.execute( "LOAD DATA LOCAL INPATH '/tmp/log.txt' OVERWRITE INTO TABLE logs" );

      resultSet = statement.executeQuery( "SELECT * FROM logs" );

      while ( resultSet.next() ) {
        System.out.println( resultSet.getString( 1 ) + " --- " + resultSet.getString( 2 ) + " --- " + resultSet.getString( 3 ) + " --- " + resultSet.getString( 4 ) );
      }
    } catch ( ClassNotFoundException ex ) {
      Logger.getLogger( HiveJDBCSample.class.getName() ).log( Level.SEVERE, null, ex );
    } catch ( SQLException ex ) {
      Logger.getLogger( HiveJDBCSample.class.getName() ).log( Level.SEVERE, null, ex );
    } finally {
      if ( resultSet != null ) {
        try {
          resultSet.close();
        } catch ( SQLException ex ) {
          Logger.getLogger( HiveJDBCSample.class.getName() ).log( Level.SEVERE, null, ex );
        }
      }
      if ( statement != null ) {
        try {
          statement.close();
        } catch ( SQLException ex ) {
          Logger.getLogger( HiveJDBCSample.class.getName() ).log( Level.SEVERE, null, ex );
        }
      }
      if ( connection != null ) {
        try {
          connection.close();
        } catch ( SQLException ex ) {
          Logger.getLogger( HiveJDBCSample.class.getName() ).log( Level.SEVERE, null, ex );
        }
      }
    }
  }
}
Groovy

Make sure that {GATEWAY_HOME/ext} directory contains following libraries for successful execution:

There are several ways to execute this sample depending upon your preference.

You can use the Groovy interpreter provided with the distribution.

java -jar bin/shell.jar samples/hive/groovy/jdbc/sandbox/HiveJDBCSample.groovy

You can manually type in the KnoxShell DSL script into the interactive Groovy interpreter provided with the distribution.

java -jar bin/shell.jar

Each line from the file below will need to be typed or copied into the interactive shell.

import java.sql.DriverManager

user = "guest";
password = user + "-password";
gatewayHost = "localhost";
gatewayPort = 8443;
trustStore = "/usr/lib/knox/data/security/keystores/gateway.jks";
trustStorePassword = "knoxsecret";
contextPath = "gateway/sandbox/hive";
connectionString = String.format( "jdbc:hive2://%s:%d/;ssl=true;sslTrustStore=%s;trustStorePassword=%s?hive.server2.transport.mode=http;hive.server2.thrift.http.path=/%s", gatewayHost, gatewayPort, trustStore, trustStorePassword, contextPath );

// Load Hive JDBC Driver
Class.forName( "org.apache.hive.jdbc.HiveDriver" );

// Configure JDBC connection
connection = DriverManager.getConnection( connectionString, user, password );

statement = connection.createStatement();

// Disable Hive authorization - This can be omitted if Hive authorization is configured properly
statement.execute( "set hive.security.authorization.enabled=false" );

// Create sample table
statement.execute( "CREATE TABLE logs(column1 string, column2 string, column3 string, column4 string, column5 string, column6 string, column7 string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' '" );

// Load data into Hive from file /tmp/log.txt which is placed on the local file system
statement.execute( "LOAD DATA LOCAL INPATH '/tmp/sample.log' OVERWRITE INTO TABLE logs" );

resultSet = statement.executeQuery( "SELECT * FROM logs" );

while ( resultSet.next() ) {
  System.out.println( resultSet.getString( 1 ) + " --- " + resultSet.getString( 2 ) );
}

resultSet.close();
statement.close();
connection.close();

Examples use ‘log.txt’ with content:

2012-02-03 18:35:34 SampleClass6 [INFO] everything normal for id 577725851
2012-02-03 18:35:34 SampleClass4 [FATAL] system problem at id 1991281254
2012-02-03 18:35:34 SampleClass3 [DEBUG] detail for id 1304807656
2012-02-03 18:35:34 SampleClass3 [WARN] missing id 423340895
2012-02-03 18:35:34 SampleClass5 [TRACE] verbose detail for id 2082654978
2012-02-03 18:35:34 SampleClass0 [ERROR] incorrect id  1886438513
2012-02-03 18:35:34 SampleClass9 [TRACE] verbose detail for id 438634209
2012-02-03 18:35:34 SampleClass8 [DEBUG] detail for id 2074121310
2012-02-03 18:35:34 SampleClass0 [TRACE] verbose detail for id 1505582508
2012-02-03 18:35:34 SampleClass0 [TRACE] verbose detail for id 1903854437
2012-02-03 18:35:34 SampleClass7 [DEBUG] detail for id 915853141
2012-02-03 18:35:34 SampleClass3 [TRACE] verbose detail for id 303132401
2012-02-03 18:35:34 SampleClass6 [TRACE] verbose detail for id 151914369
2012-02-03 18:35:34 SampleClass2 [DEBUG] detail for id 146527742
...

Expected output:

2012-02-03 --- 18:35:34 --- SampleClass6 --- [INFO]
2012-02-03 --- 18:35:34 --- SampleClass4 --- [FATAL]
2012-02-03 --- 18:35:34 --- SampleClass3 --- [DEBUG]
2012-02-03 --- 18:35:34 --- SampleClass3 --- [WARN]
2012-02-03 --- 18:35:34 --- SampleClass5 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass0 --- [ERROR]
2012-02-03 --- 18:35:34 --- SampleClass9 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass8 --- [DEBUG]
2012-02-03 --- 18:35:34 --- SampleClass0 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass0 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass7 --- [DEBUG]
2012-02-03 --- 18:35:34 --- SampleClass3 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass6 --- [TRACE]
2012-02-03 --- 18:35:34 --- SampleClass2 --- [DEBUG]
...

HiveServer2 HA

Knox provides basic failover functionality for calls made to Hive Server when more than one HiveServer2 instance is installed in the cluster and registered with the same Zookeeper ensemble. The HA functionality in this case fetches the HiveServer2 URL information from a Zookeeper ensemble, so the user need only supply the necessary Zookeeper configuration and not the Hive connection URLs.

To enable HA functionality for Hive in Knox the following configuration has to be added to the topology file.

<provider>
    <role>ha</role>
    <name>HaProvider</name>
    <enabled>true</enabled>
    <param>
        <name>HIVE</name>
        <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true;zookeeperEnsemble=machine1:2181,machine2:2181,machine3:2181;
       zookeeperNamespace=hiveserver2</value>
   </param>
</provider>

The role and name of the provider above must be as shown. The name in the ‘param’ section must match that of the service role name that is being configured for HA and the value in the ‘param’ section is the configuration for that particular service in HA mode. In this case the name is ‘HIVE’.

The various configuration parameters are described below:

And for the service configuration itself the URLs need not be added to the list. For example.

<service>
    <role>HIVE</role>
</service>

Please note that there is no <url> tag specified here as the URLs for the Hive servers are obtained from Zookeeper.

Yarn

Knox provides gateway functionality for the REST APIs of the ResourceManager. The ResourceManager REST APIs allow the user to get information about the cluster - status on the cluster, metrics on the cluster, scheduler information, information about nodes in the cluster, and information about applications on the cluster. Also as of Hadoop version 2.5.0, the user can submit a new application as well as kill it (or get state) using the ‘Writable’ APIs.

The docs for this can be found here

http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/ResourceManagerRest.html

To enable this functionality, a topology file needs to have the following configuration:

<service>
    <role>RESOURCEMANAGER</role>
    <url>http://<hostname>:<port>/ws</url>
</service>

The default resource manager http port is 8088. If it is configured to some other port, that configuration can be found in yarn-site.xml under the property yarn.resourcemanager.webapp.address.

Yarn URL Mapping

For Yarn URLs, the mapping of Knox Gateway accessible URLs to direct Yarn URLs is the following.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/resourcemanager
Cluster http://{yarn-host}:{yarn-port}/ws}

Yarn Examples via cURL

Some of the various calls that can be made and examples using curl are listed below.

# 0. Getting cluster info

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster'

# 1. Getting cluster metrics

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/metrics'

To get the same information in an xml format

curl -ikv -u guest:guest-password -H Accept:application/xml -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/metrics'

# 2. Getting scheduler information

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/scheduler'

# 3. Getting all the applications listed and their information

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps'

# 4. Getting applications statistics

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/appstatistics'

Also query params can be used as below to filter the results

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/appstatistics?states=accepted,running,finished&applicationTypes=mapreduce'

# 5. To get a specific application (please note, replace the application id with a real value)

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/{application_id}'

# 6. To get the attempts made for a particular application

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/{application_id}/appattempts'

# 7. To get information about the various nodes

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/nodes'

Also to get a specific node, use an id obtained in the response from above (the node id is scrambled) and issue the following

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/nodes/{node_id}'

# 8. To create a new Application

curl -ikv -u guest:guest-password -X POST 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/new-application'

An application id is returned from the request above and this can be used to submit an application.

# 9. To submit an application, put together a request containing the application id received in the above response (please refer to Yarn REST
API documentation).

curl -ikv -u guest:guest-password -T request.json -H Content-Type:application/json -X POST 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps'

Here the request is saved in a file called request.json

#10. To get application state

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/{application_id}/state'

curl -ikv -u guest:guest-password -H Content-Type:application/json -X PUT -T state-killed.json 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/application_1409008107556_0007/state'

# 11. To kill an application that is running issue the below command with the application id of the application that is to be killed.
The contents of the state-killed.json file are :

{
  "state":"KILLED"
}


curl -ikv -u guest:guest-password -H Content-Type:application/json -X PUT -T state-killed.json 'https://localhost:8443/gateway/sandbox/resourcemanager/v1/cluster/apps/{application_id}/state'

Storm

Storm is a distributed realtime computation system. Storm exposes REST APIs for UI functionality that can be used for retrieving metrics data and configuration information as well as management operations such as starting or stopping topologies.

The docs for this can be found here

https://github.com/apache/storm/blob/master/docs/documentation/ui-rest-api.md

To enable this functionality, a topology file needs to have the following configuration:

<service>
    <role>STORM</role>
    <url>http://<hostname>:<port></url>
</service>

The default UI daemon port is 8744. If it is configured to some other port, that configuration can be found in storm.yaml as the value for the property ui.port.

In addition to the storm service configuration above, a STORM-LOGVIEWER service must be configured if the log files are to be retrieved through Knox. The value of the port for the logviewer can be found by the property ‘logviewer.port’ also in the file storm.yaml.

<service>
    <role>STORM-LOGVIEWER</role>
    <url>http://<hostname>:<port></url>
</service>

Storm URL Mapping

For Storm URLs, the mapping of Knox Gateway accessible URLs to direct Storm URLs is the following.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/storm
Cluster http://{storm-host}:{storm-port}

For the log viewer the mapping is as follows

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/storm/logviewer
Cluster http://{storm-logviewer-host}:{storm-logviewer-port}

Storm Examples

Some of the various calls that can be made and examples using curl are listed below.

# 0. Getting cluster configuration

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/cluster/configuration'

# 1. Getting cluster summary information

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/cluster/summary'

# 2. Getting supervisor summary information

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/supervisor/summary'

# 3. topologies summary information

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/topology/summary'

# 4. Getting specific topology information. Substitute {id} with the topology id.

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/topology/{id}'

# 5. To get component level information. Substitute {id} with the topology id and {component} with the component id e.g. 'spout'

curl -ikv -u guest:guest-password -X GET 'https://localhost:8443/gateway/sandbox/storm/api/v1/topology/{id}/component/{component}'

The following POST operations all require a ‘x-csrf-token’ header along with other information that can be stored in a cookie file. In particular the ‘ring-session’ header and ‘JSESSIONID’.

# 6. To activate a topology. Substitute {id} with the topology id and {token-value} with the x-csrf-token value.

curl -ik -b ~/cookiejar.txt -c ~/cookiejar.txt -u guest:guest-password -H 'x-csrf-token:{token-value}' -X POST \
 http://localhost:8744/api/v1/topology/{id}/activate

# 7. To de-activate a topology. Substitute {id} with the topology id and {token-value} with the x-csrf-token value.

curl -ik -b ~/cookiejar.txt -c ~/cookiejar.txt -u guest:guest-password -H 'x-csrf-token:{token-value}' -X POST \
 http://localhost:8744/api/v1/topology/{id}/deactivate

# 8. To rebalance a topology. Substitute {id} with the topology id and {token-value} with the x-csrf-token value.

curl -ik -b ~/cookiejar.txt -c ~/cookiejar.txt -u guest:guest-password -H 'x-csrf-token:{token-value}' -X POST \
 http://localhost:8744/api/v1/topology/{id}/rebalance/0

# 9. To kill a topology. Substitute {id} with the topology id and {token-value} with the x-csrf-token value.

curl -ik -b ~/cookiejar.txt -c ~/cookiejar.txt -u guest:guest-password -H 'x-csrf-token:{token-value}' -X POST \
 http://localhost:8744/api/v1/topology/{id}/kill/0

Common Service Config

It is possible to override a few of the global configuration settings provided in gateway-site.xml at the service level. These overrides are specified as name/value pairs within the <service> elements of a particular service. The overidden settings apply only to that service.

The following table shows the common configuration settings available at the service level via service level parameters. Individual services may support additional service level parameters.

Property Description Default
httpclient.maxConnections The maximum number of connections that a single httpclient will maintain to a single host:port. The default is 32. 32
httpclient.connectionTimeout The amount of time to wait when attempting a connection. The natural unit is milliseconds but a ‘s’ or ‘m’ suffix may be used for seconds or minutes respectively. The default timeout is system dependent. System Dependent
httpclient.socketTimeout The amount of time to wait for data on a socket before aborting the connection. The natural unit is milliseconds but a ‘s’ or ‘m’ suffix may be used for seconds or minutes respectively. The default timeout is system dependent but is likely to be indefinite. System Dependent

The example below demonstrates how these service level parameters are used.

<service>
     <role>HIVE</role>
     <param>
         <name>httpclient.socketTimeout</name>
         <value>180s</value>
     </param>
</service>

Default Service HA support

Knox provides connectivity based failover functionality for service calls that can be made to more than one server instance in a cluster. To enable this functionality HaProvider configuration needs to be enabled for the service and the service itself needs to be configured with more than one URL in the topology file.

The default HA functionality works on a simple round robin algorithm, where the top of the list of URLs is always used to route all of a service’s REST calls until a connection error occurs. The top URL is then put at the bottom of the list and the next URL is attempted. This goes on until the setting of ‘maxFailoverAttempts’ is reached.

At present the following services can use this default High Availability functionality and have been tested for the same:

To enable HA functionality for a service in Knox the following configuration has to be added to the topology file.

<provider>
     <role>ha</role>
     <name>HaProvider</name>
     <enabled>true</enabled>
     <param>
         <name>{SERVICE}</name>
         <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true</value>
     </param>
</provider>

The role and name of the provider above must be as shown. The name in the ‘param’ section i.e. {SERVICE} must match that of the service role name that is being configured for HA and the value in the ‘param’ section is the configuration for that particular service in HA mode. For example, the value of {SERVICE} can be ‘WEBHCAT’, ‘HBASE’ or ‘OOZIE’.

To configure multiple services in HA mode, additional ‘param’ sections can be added.

For example,

<provider>
     <role>ha</role>
     <name>HaProvider</name>
     <enabled>true</enabled>
     <param>
         <name>OOZIE</name>
         <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true</value>
     </param>
     <param>
         <name>HBASE</name>
         <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true</value>
     </param>
     <param>
         <name>WEBHCAT</name>
         <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true</value>
     </param>
</provider>

The various configuration parameters are described below:

And for the service configuration itself the additional URLs should be added to the list.

<service>
    <role>{SERVICE}</role>
    <url>http://host1:port1</url>
    <url>http://host2:port2</url>
</service>

For example,

<service>
    <role>OOZIE</role>
    <url>http://sandbox1:11000/oozie</url>
    <url>http://sandbox2:11000/oozie</url>
</service>

Service Test API

The gateway supports a Service Test API that can be used to test Knox’s ability to connect to each of the different Hadoop services via a simeple HTTP GET request. To be able to access this API one must add the following line into the topology for which you wish to run the service test.

<service>
  <role>SERVICE-TEST</role>
</service>

After adding the above to a topology, you can make a cURL request with the following structure

curl -i -k "https://{gateway-hostname}:{gateway-port}/gateway/path/{topology-name}/service-test?username=guest&password=guest-password"

An alternate method of providing credentials:

curl -i -k -u guest:guest-password https://{gateway-hostname}:{gateway-port}/gateway/path/{topology-name}/service-test

Below is an example response. The gateway is also capable of returning XML if specified in the request’s “Accept” HTTP header.

{
    "serviceTestWrapper": {
     "Tests": {
      "ServiceTest": [
       {
        "serviceName": "WEBHDFS",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/webhdfs/v1/?op=LISTSTATUS",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHCAT",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/templeton/v1/status",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHCAT",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/templeton/v1/version",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHCAT",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/templeton/v1/version/hive",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHCAT",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/templeton/v1/version/hadoop",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "OOZIE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/oozie/v1/admin/build-version",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "OOZIE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/oozie/v1/admin/status",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "OOZIE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/oozie/versions",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHBASE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/hbase/version",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHBASE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/hbase/version/cluster",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHBASE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/hbase/status/cluster",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "WEBHBASE",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/hbase",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "RESOURCEMANAGER",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/resourcemanager/v1/{topology-name}/info",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "RESOURCEMANAGER",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/resourcemanager/v1/{topology-name}/metrics",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "RESOURCEMANAGER",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/resourcemanager/v1/{topology-name}/apps",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "FALCON",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/falcon/api/admin/stack",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "FALCON",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/falcon/api/admin/version",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "FALCON",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/falcon/api/metadata/lineage/serialize",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "FALCON",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/falcon/api/metadata/lineage/vertices/all",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "FALCON",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/falcon/api/metadata/lineage/edges/all",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "STORM",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/storm/api/v1/cluster/configuration",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "STORM",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/storm/api/v1/cluster/summary",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "STORM",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/storm/api/v1/supervisor/summary",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       },
       {
        "serviceName": "STORM",
        "requestURL": "http://{gateway-host}:{gateway-port}/gateway/{topology-name}/storm/api/v1/topology/summary",
        "responseContent": "Content-Length:0,Content-Type: application/json;charset=utf-8",
        "httpCode": 200,
        "message": "Request sucessful."
       }
      ]
     },
     "messages": {
      "message": [

      ]
     }
    }
}

We can see that this service-test makes HTTP requests to each of the services through Knox using the specified topology. The test will only make calls to those services that have entries within the topology file.

Adding and Changing test URLs

URLs for each service are stored in {GATEWAY_HOME}/data/services/{service-name}/{service-version}/service.xml. Each <testURL> element represents a service resource that will be tested if the service is set up in the topology. You can add or remove these from the service.xml file. Just note if you add URLs there is no guarantee in the order they will be tested. All default URLs have been tested and work on various clusters. If a new URL is added and doesn’t respond in a way the user expects then it is up to the user to determine whether the URL is correct or not.

Some important things to note:

UI Service Details

In the sections that follow the integrations for proxying various UIs currently available out of the box with the gateway will be described. These sections will include examples that demonstrate how to access each of these services via the gateway.

These are the current Hadoop services with built-in support for their UIs.

Assumptions

This section assumes an environment setup similar to the one in the REST services section Service Details

Name Node UI

The Name Node UI is available on the same host and port combination that WebHDFS is available on. As mentioned in the WebHDFS REST service configuration section, the values for the host and port can be obtained from the following properties in hdfs-site.xml

<property>
    <name>dfs.namenode.http-address</name>
    <value>sandbox.hortonworks.com:50070</value>
</property>
<property>
    <name>dfs.https.namenode.https-address</name>
    <value>sandbox.hortonworks.com:50470</value>
</property>

The values above need to be reflected in each topology descriptor file deployed to the gateway. The gateway by default includes a sample topology descriptor file {GATEWAY_HOME}/deployments/sandbox.xml. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>HDFSUI</role>
    <url>http://sandbox.hortonworks.com:50070/webhdfs</url>
</service>

In addition to the service configuration for HDFSUI, the REST service configuration for WEBHDFS is also required.

<service>
    <role>NAMENODE</role>
    <url>hdfs://sandbox.hortonworks.com:8020</url>
</service>
<service>
    <role>WEBHDFS</role>
    <url>http://sandbox.hortonworks.com:50070/webhdfs</url>
</service>

By default the gateway is configured to use the HTTP endpoint for WebHDFS in the Sandbox. This could alternatively be configured to use the HTTPS endpoint by provided the correct address.

Name Node UI URL Mapping

For Name Node UI URLs, the mapping of Knox Gateway accessible HDFS UI URLs to direct HDFS UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/hdfs
Cluster http://{webhdfs-host}:50070/

For example to browse the file system using the NameNode UI the URL in a web browser would be:

http://sandbox.hortonworks.com:50070/explorer.html#

And using the gateway to access the same page the URL would be (where the gateway host:port is ‘localhost:8443’)

https://localhost:8443/gateway/sandbox/hdfs/explorer.html#

Job History UI

The Job History UI service can be configured in a topology by adding the following snippet. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>JOBHISTORYUI</role>
    <url>http://sandbox.hortonworks.com:19888</url>
</service>

The values for the host and port can be obtained from the following property in mapred-site.xml

<property>
    <name>mapreduce.jobhistory.webapp.address</name>
    <value>sandbox.hortonworks.com:19888</value>
</property>

Job History UI URL Mapping

For Job History UI URLs, the mapping of Knox Gateway accessible Job History UI URLs to direct Job History UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/jobhistory
Cluster http://{jobhistory-host}:19888/jobhistory

Oozie UI

The Oozie UI service can be configured in a topology by adding the following snippet. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>OOZIEUI</role>
    <url>http://sandbox.hortonworks.com:11000/oozie</url>
</service>

The value for the URL can be obtained from the following property in oozie-site.xml

<property>
    <name>oozie.base.url</name>
    <value>http://sandbox.hortonworks.com:11000/oozie</value>
</property>

Oozie UI URL Mapping

For Oozie UI URLs, the mapping of Knox Gateway accessible Oozie UI URLs to direct Oozie UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/oozie/
Cluster http://{oozie-host}:11000/oozie/

HBase UI

The HBase UI service can be configured in a topology by adding the following snippet. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>HBASEUI</role>
    <url>http://sandbox.hortonworks.com:16010</url>
</service>

The values for the host and port can be obtained from the following property in hbase-site.xml. Below the hostname of the hbase master is used since the bindAddress is 0.0.0.0

<property>
    <name>hbase.master.info.bindAddress</name>
    <value>0.0.0.0</value>
</property>
<property>
    <name>hbase.master.info.port</name>
    <value>16010</value>
</property>

HBase UI URL Mapping

For HBase UI URLs, the mapping of Knox Gateway accessible HBase UI URLs to direct HBase Master UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/hbase/webui/
Cluster http://{hbase-master-host}:16010/

YARN UI

The YARN UI service can be configured in a topology by adding the following snippet. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>YARNUI</role>
    <url>http://sandbox.hortonworks.com:8088</url>
</service>

The values for the host and port can be obtained from the following property in mapred-site.xml

<property>
    <name>yarn.resourcemanager.webapp.address</name>
    <value>sandbox.hortonworks.com:8088</value>
</property>

YARN UI URL Mapping

For Resource Manager UI URLs, the mapping of Knox Gateway accessible Resource Manager UI URLs to direct Resource Manager UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/yarn
Cluster http://{resource-manager-host}:8088/cluster

Spark UI

The Spark History UI service can be configured in a topology by adding the following snippet. The values in this sample are configured to work with an installed Sandbox VM.

<service>
    <role>SPARKHISTORYUI</role>
    <url>http://sandbox.hortonworks.com:18080/</url>
</service>

Spark History UI URL Mapping

For Spark History UI URLs, the mapping of Knox Gateway accessible Spark History UI URLs to direct Spark History UI URLs is:

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/sparkhistory
Cluster http://{spark-history-host}:18080

Ambari UI

Ambari UI has functionality around provisioning and managing services in a hadoop cluster. This UI can now be used behind the Knox gateway.

To enable this functionality, a topology file needs to have the following configuration:

<service>
    <role>AMBARIUI</role>
    <url>http://<hostname>:<port></url>
</service>

The default Ambari http port is 8080. Also please note that the UI service also requires the Ambari REST API service to be enabled to function properly. An example of a more complete topology is given below.

Ambari UI URL Mapping

For Ambari UI URLs, the mapping of Knox Gateway accessible URLs to direct Ambari UI URLs is the following.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/ambari/
Cluster http://{ambari-host}:{ambari-port}/}

Example Topology

The Ambari UI service may require a separate topology file due to its requirements around authentication. Knox passes through authentication challenge and credentials to the service in this case.

<topology>
    <gateway>
        <provider>
            <role>authentication</role>
            <name>Anonymous</name>
            <enabled>true</enabled>
        </provider>
        <provider>
            <role>identity-assertion</role>
            <name>Default</name>
            <enabled>false</enabled>
        </provider>
    </gateway>
    <service>
        <role>AMBARI</role>
        <url>http://localhost:8080</url>
    </service>

    <service>
        <role>AMBARIUI</role>
        <url>http://localhost:8080</url>
    </service>
</topology>

Please look at JIRA issue [KNOX-705] for a known issue with this release.

Ranger Admin Console

The Ranger Admin console can now be used behind the Knox gateway.

To enable this functionality, a topology file needs to have the following configuration:

<service>
    <role>RANGERUI</role>
    <url>http://<hostname>:<port></url>
</service>

The default Ranger http port is 8060. Also please note that the UI service also requires the Ranger REST API service to be enabled to function properly. An example of a more complete topology is given below.

Ranger Admin Console URL Mapping

For Ranger Admin console URLs, the mapping of Knox Gateway accessible URLs to direct Ranger Admin console URLs is the following.

Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/ranger/
Cluster http://{ranger-host}:{ranger-port}/}

Example Topology

The Ranger UI service may require a separate topology file due to its requirements around authentication. Knox passes through authentication challenge and credentials to the service in this case.

<topology>
    <gateway>
        <provider>
            <role>authentication</role>
            <name>Anonymous</name>
            <enabled>true</enabled>
        </provider>
        <provider>
            <role>identity-assertion</role>
            <name>Default</name>
            <enabled>false</enabled>
        </provider>
    </gateway>
    <service>
        <role>RANGER</role>
        <url>http://localhost:8060</url>
    </service>

    <service>
        <role>RANGERUI</role>
        <url>http://localhost:8060</url>
    </service>
</topology>

Limitations

Secure Oozie POST/PUT Request Payload Size Restriction

With one exception there are no known size limits for requests or responses payloads that pass through the gateway. The exception involves POST or PUT request payload sizes for Oozie in a Kerberos secured Hadoop cluster. In this one case there is currently a 4Kb payload size limit for the first request made to the Hadoop cluster. This is a result of how the gateway negotiates a trust relationship between itself and the cluster via SPNego. There is an undocumented configuration setting to modify this limit’s value if required. In the future this will be made more easily configurable and at that time it will be documented.

Group Membership Propagation

Groups that are acquired via Shiro Group Lookup and/or Identity Assertion Group Principal Mapping are not propagated to the Hadoop services. Therefore, groups used for Service Level Authorization policy may not match those acquired within the cluster via GroupMappingServiceProvider plugins.

Troubleshooting

Finding Logs

When things aren’t working the first thing you need to do is examine the diagnostic logs. Depending upon how you are running the gateway these diagnostic logs will be output to different locations.

java -jar bin/gateway.jar

When the gateway is run this way the diagnostic output is written directly to the console. If you want to capture that output you will need to redirect the console output to a file using OS specific techniques.

java -jar bin/gateway.jar > gateway.log

bin/gateway.sh start

When the gateway is run this way the diagnostic output is written to {GATEWAY_HOME}/log/knox.out and {GATEWAY_HOME}/log/knox.err. Typically only knox.out will have content.

Increasing Logging

The log4j.properties files {GATEWAY_HOME}/conf can be used to change the granularity of the logging done by Knox. The Knox server must be restarted in order for these changes to take effect. There are various useful loggers pre-populated but commented out.

log4j.logger.org.apache.hadoop.gateway=DEBUG # Use this logger to increase the debugging of Apache Knox itself.
log4j.logger.org.apache.shiro=DEBUG          # Use this logger to increase the debugging of Apache Shiro.
log4j.logger.org.apache.http=DEBUG           # Use this logger to increase the debugging of Apache HTTP components.
log4j.logger.org.apache.http.client=DEBUG    # Use this logger to increase the debugging of Apache HTTP client component.
log4j.logger.org.apache.http.headers=DEBUG   # Use this logger to increase the debugging of Apache HTTP header.
log4j.logger.org.apache.http.wire=DEBUG      # Use this logger to increase the debugging of Apache HTTP wire traffic.

LDAP Server Connectivity Issues

If the gateway cannot contact the configured LDAP server you will see errors in the gateway diagnostic output.

13/11/15 16:30:17 DEBUG authc.BasicHttpAuthenticationFilter: Attempting to execute login with headers [Basic Z3Vlc3Q6Z3Vlc3QtcGFzc3dvcmQ=]
13/11/15 16:30:17 DEBUG ldap.JndiLdapRealm: Authenticating user 'guest' through LDAP
13/11/15 16:30:17 DEBUG ldap.JndiLdapContextFactory: Initializing LDAP context using URL    [ldap://localhost:33389] and principal [uid=guest,ou=people,dc=hadoop,dc=apache,dc=org] with pooling disabled
13/11/15 16:30:17 DEBUG servlet.SimpleCookie: Added HttpServletResponse Cookie [rememberMe=deleteMe; Path=/gateway/vaultservice; Max-Age=0; Expires=Thu, 14-Nov-2013 21:30:17 GMT]
13/11/15 16:30:17 DEBUG authc.BasicHttpAuthenticationFilter: Authentication required: sending 401 Authentication challenge response.

The client should see something along the lines of:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: BASIC realm="application"
Content-Length: 0
Server: Jetty(8.1.12.v20130726)

Resolving this will require ensuring that the LDAP server is running and that connection information is correct. The LDAP server connection information is configured in the cluster’s topology file (e.g. {GATEWAY_HOME}/deployments/sandbox.xml).

Hadoop Cluster Connectivity Issues

If the gateway cannot contact one of the services in the configured Hadoop cluster you will see errors in the gateway diagnostic output.

13/11/18 18:49:45 WARN hadoop.gateway: Connection exception dispatching request: http://localhost:50070/webhdfs/v1/?user.name=guest&op=LISTSTATUS org.apache.http.conn.HttpHostConnectException: Connection to http://localhost:50070 refused
org.apache.http.conn.HttpHostConnectException: Connection to http://localhost:50070 refused
  at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:190)
  at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
  at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:645)
  at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
  at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
  at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
  at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
  at org.apache.hadoop.gateway.dispatch.HttpClientDispatch.executeRequest(HttpClientDispatch.java:99)

The resulting behavior on the client will differ by client. For the client DSL executing the {GATEWAY_HOME}/samples/ExampleWebHdfsLs.groovy the output will look like this.

Caught: org.apache.hadoop.gateway.shell.HadoopException: org.apache.hadoop.gateway.shell.ErrorResponse: HTTP/1.1 500 Server Error
org.apache.hadoop.gateway.shell.HadoopException: org.apache.hadoop.gateway.shell.ErrorResponse: HTTP/1.1 500 Server Error
  at org.apache.hadoop.gateway.shell.AbstractRequest.now(AbstractRequest.java:72)
  at org.apache.hadoop.gateway.shell.AbstractRequest$now.call(Unknown Source)
  at ExampleWebHdfsLs.run(ExampleWebHdfsLs.groovy:28)

When executing commands requests via cURL the output might look similar to the following example.

Set-Cookie: JSESSIONID=16xwhpuxjr8251ufg22f8pqo85;Path=/gateway/sandbox;Secure
Content-Type: text/html;charset=ISO-8859-1
Cache-Control: must-revalidate,no-cache,no-store
Content-Length: 21856
Server: Jetty(8.1.12.v20130726)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 500 Server Error</title>
</head>
<body><h2>HTTP ERROR 500</h2>

Resolving this will require ensuring that the Hadoop services are running and that connection information is correct. Basic Hadoop connectivity can be evaluated using cURL as described elsewhere. Otherwise the Hadoop cluster connection information is configured in the cluster’s topology file (e.g. {GATEWAY_HOME}/deployments/sandbox.xml).

HTTP vs HTTPS protocol issues

When Knox is configured to accept requests over SSL and is presented with a request over plain HTTP, the client is presented with an error such as seen in the following:

curl -i -k -u guest:guest-password -X GET 'http://localhost:8443/gateway/sandbox/webhdfs/v1/?op=LISTSTATUS'
the following error is returned
curl: (52) Empty reply from server

This is the default behavior for Jetty SSL listener. While the credentials to the default authentication provider continue to be username and password, we do not want to encourage sending these in clear text. Since preemptively sending BASIC credentials is a common pattern with REST APIs it would be unwise to redirect to a HTTPS listener thus allowing clear text passwords.

To resolve this issue, we have two options:

  1. change the scheme in the URL to https and deal with any trust relationship issues with the presented server certificate
  2. Disabling SSL in gateway-site.xml - this is not encouraged due to the reasoning described above.

Check Hadoop Cluster Access via cURL

When you are experiencing connectivity issue it can be helpful to “bypass” the gateway and invoke the Hadoop REST APIs directly. This can easily be done using the cURL command line utility or many other REST/HTTP clients. Exactly how to use cURL depends on the configuration of your Hadoop cluster. In general however you will use a command line the one that follows.

curl -ikv -X GET 'http://namenode-host:50070/webhdfs/v1/?op=LISTSTATUS'

If you are using Sandbox the WebHDFS or NameNode port will be mapped to localhost so this command can be used.

curl -ikv -X GET 'http://localhost:50070/webhdfs/v1/?op=LISTSTATUS'

If you are using a cluster secured with Kerberos you will need to have used kinit to authenticate to the KDC. Then the command below should verify that WebHDFS in the Hadoop cluster is accessible.

curl -ikv --negotiate -u : -X 'http://localhost:50070/webhdfs/v1/?op=LISTSTATUS'

Authentication Issues

The following log information is available when you enable debug level logging for shiro. This can be done within the conf/log4j.properties file. Not the “Password not correct for user” message.

13/11/15 16:37:15 DEBUG authc.BasicHttpAuthenticationFilter: Attempting to execute login with headers [Basic Z3Vlc3Q6Z3Vlc3QtcGFzc3dvcmQw]
13/11/15 16:37:15 DEBUG ldap.JndiLdapRealm: Authenticating user 'guest' through LDAP
13/11/15 16:37:15 DEBUG ldap.JndiLdapContextFactory: Initializing LDAP context using URL [ldap://localhost:33389] and principal [uid=guest,ou=people,dc=hadoop,dc=apache,dc=org] with pooling disabled
2013-11-15 16:37:15,899 INFO  Password not correct for user 'uid=guest,ou=people,dc=hadoop,dc=apache,dc=org'
2013-11-15 16:37:15,899 INFO  Authenticator org.apache.directory.server.core.authn.SimpleAuthenticator@354c78e3 failed to authenticate: BindContext for DN 'uid=guest,ou=people,dc=hadoop,dc=apache,dc=org', credentials <0x67 0x75 0x65 0x73 0x74 0x2D 0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64 0x30 >
2013-11-15 16:37:15,899 INFO  Cannot bind to the server
13/11/15 16:37:15 DEBUG servlet.SimpleCookie: Added HttpServletResponse Cookie [rememberMe=deleteMe; Path=/gateway/vaultservice; Max-Age=0; Expires=Thu, 14-Nov-2013 21:37:15 GMT]
13/11/15 16:37:15 DEBUG authc.BasicHttpAuthenticationFilter: Authentication required: sending 401 Authentication challenge response.

The client will likely see something along the lines of:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: BASIC realm="application"
Content-Length: 0
Server: Jetty(8.1.12.v20130726)

Using ldapsearch to verify LDAP connectivity and credentials

If your authentication to Knox fails and you believe your are using correct credentials, you could try to verify the connectivity and credentials using ldapsearch, assuming you are using LDAP directory for authentication.

Assuming you are using the default values that came out of box with Knox, your ldapsearch command would be like the following

ldapsearch -h localhost -p 33389 -D "uid=guest,ou=people,dc=hadoop,dc=apache,dc=org" -w guest-password -b "uid=guest,ou=people,dc=hadoop,dc=apache,dc=org" "objectclass=*"

This should produce output like the following

# extended LDIF

LDAPv3
base <uid=guest,ou=people,dc=hadoop,dc=apache,dc=org> with scope subtree
filter: objectclass=*
requesting: ALL


# guest, people, hadoop.apache.org
dn: uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
uid: guest
cn: Guest
sn: User
userpassword:: Z3Vlc3QtcGFzc3dvcmQ=

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

In a more general form the ldapsearch command would be

ldapsearch -h {HOST} -p {PORT} -D {DN of binding user} -w {bind password} -b {DN of binding user} "objectclass=*}

Hostname Resolution Issues

The deployments/sandbox.xml topology file has the host mapping feature enabled. This is required due to the way networking is setup in the Sandbox VM. Specifically the VM’s internal hostname is sandbox.hortonworks.com. Since this hostname cannot be resolved to the actual VM Knox needs to map that hostname to something resolvable.

If for example host mapping is disabled but the Sandbox VM is still used you will see an error in the diagnostic output similar to the below.

13/11/18 19:11:35 WARN hadoop.gateway: Connection exception dispatching request: http://sandbox.hortonworks.com:50075/webhdfs/v1/user/guest/example/README?op=CREATE&namenoderpcaddress=sandbox.hortonworks.com:8020&user.name=guest&overwrite=false java.net.UnknownHostException: sandbox.hortonworks.com
java.net.UnknownHostException: sandbox.hortonworks.com
  at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)

On the other hand if you are migrating from the Sandbox based configuration to a cluster you have deployment you may see a similar error. However in this case you may need to disable host mapping. This can be done by modifying the topology file (e.g. deployments/sandbox.xml) for the cluster.

...
<provider>
    <role>hostmap</role>
    <name>static</name>
    <enabled>false</enabled>
    <param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>
</provider>
....

Job Submission Issues - HDFS Home Directories

If you see error like the following in your console while submitting a Job using groovy shell, it is likely that the authenticated user does not have a home directory on HDFS.

Caught: org.apache.hadoop.gateway.shell.HadoopException: org.apache.hadoop.gateway.shell.ErrorResponse: HTTP/1.1 403 Forbidden
org.apache.hadoop.gateway.shell.HadoopException: org.apache.hadoop.gateway.shell.ErrorResponse: HTTP/1.1 403 Forbidden

You would also see this error if you try file operation on the home directory of the authenticating user.

The error would look a little different as shown below if you are attempting to the operation with cURL.

{"RemoteException":{"exception":"AccessControlException","javaClassName":"org.apache.hadoop.security.AccessControlException","message":"Permission denied: user=tom, access=WRITE, inode=\"/user\":hdfs:hdfs:drwxr-xr-x"}}* 

Resolution

Create the home directory for the user on HDFS. The home directory is typically of the form /user/{userid} and should be owned by the user. user ‘hdfs’ can create such a directory and make the user owner of the directory.

Job Submission Issues - OS Accounts

If the Hadoop cluster is not secured with Kerberos, the user submitting a job need not have an OS account on the Hadoop Nodemanagers.

If the Hadoop cluster is secured with Kerberos, the user submitting the job should have an OS account on Hadoop Nodemanagers.

In either case if the user does not have such OS account, his file permissions are based on user ownership of files or “other” permission in “ugo” posix permission. The user does not get any file permission as a member of any group if you are using default hadoop.security.group.mapping.

TODO: add sample error message from running test on secure cluster with missing OS account

HBase Issues

If you experience problems running the HBase samples with the Sandbox VM it may be necessary to restart HBase and the HBASE REST API. This can sometimes occur with the Sandbox VM is restarted from a saved state. If the client hangs after emitting the last line in the sample output below you are most likely affected.

System version : {...}
Cluster version : 0.96.0.2.0.6.0-76-hadoop2
Status : {...}
Creating table 'test_table'...

HBase and the HBASE REST API can be restarted using the following commands on the Hadoop Sandbox VM. You will need to ssh into the VM in order to run these commands.

sudo -u hbase /usr/lib/hbase/bin/hbase-daemon.sh stop master
sudo -u hbase /usr/lib/hbase/bin/hbase-daemon.sh start master
sudo -u hbase /usr/lib/hbase/bin/hbase-daemon.sh restart rest

SSL Certificate Issues

Clients that do not trust the certificate presented by the server will behave in different ways. A browser will typically warn you of the inability to trust the received certificate and give you an opportunity to add an exception for the particular certificate. Curl will present you with the follow message and instructions for turning of certificate verification:

curl performs SSL certificate verification by default, using a "bundle" 
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented 
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

SPNego Authentication Issues

Calls from Knox to Secure Hadoop Cluster fails, with SPNego authentication problems, if there was a TGT for Knox in disk cache when Knox was started.

You are likely to run into this situation on developer machines where the developer could have kinited for some testing.

Work Around: clear TGT of Knox from disk cache (calling kdestroy would do it), before starting Knox

Filing Bugs

Bugs can be filed using Jira. Please include the results of this command below in the Environment section. Also include the version of Hadoop being used in the same section.

cd {GATEWAY_HOME}
java -jar bin/gateway.jar -version

Export Controls

Apache Knox Gateway includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country’s laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See http://www.wassenaar.org for more information.

The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.

The following provides more details on the included cryptographic software:

Trademarks

Apache Knox, Apache Knox Gateway, Apache, the Apache feather logo and the Apache Knox Gateway project logos are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.

License

Apache Knox uses the standard Apache license.

Privacy Policy

Apache Knox uses the standard Apache privacy policy.

Information about your use of this website is collected using server access logs and a tracking cookie. The collected information consists of the following:

Part of this information is gathered using a tracking cookie set by the Google Analytics service. Google’s policy for the use of this information is described in their privacy policy. See your browser’s documentation for instructions on how to disable the cookie if you prefer not to share this data with Google.

We use the gathered information to help us make our site more useful to visitors and to better understand how and when our site is used. We do not track or collect personally identifiable information or associate gathered data with any personally identifying information from other sources.

By using this website, you consent to the collection of this data in the manner and for the purpose described above.