Running registry and streamline web-services securely¶
SPNEGO¶
This module is intended to be used by registry and streamline web-services so that they can enable http client authentication via SPNEGO. The supported authentication mechanism is Kerberos. The code for this module has been borrowed from the hadoop-auth(2.7.3) module in Hadoop project and slightly modified. The reasons for doing so are to avoid having a dependency on hadoop-auth module which brings in some other modules, avoid conflicts with other versions of hadoop-auth module and having more control over the changes needed in future. Some text for this document has been borrowed from SECURITY.md of Apache Storm.
By default, registry and streamline web-services are running with authentication disabled and therefore anyone can access the web-services from ui/client as far as they know the url and can access the web-server from the client machine. To enable client authentication, webservice needs to add a servlet filter from this module. The webservice module will need to declare a dependency on this module. One way of adding a servlet filter in code is as follows.
List<ServletFilterConfiguration> servletFilterConfigurations = registryConfiguration.getServletFilters();
if (servletFilterConfigurations != null && !servletFilterConfigurations.isEmpty()) {
for (ServletFilterConfiguration servletFilterConfiguration: servletFilterConfigurations) {
try {
FilterRegistration.Dynamic dynamic = environment.servlets().addFilter(servletFilterConfiguration.getClassName(), (Class<? extends Filter>)
Class.forName(servletFilterConfiguration.getClassName()));
dynamic.setInitParameters(servletFilterConfiguration.getParams());
dynamic.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
} catch (Exception e) {
LOG.error("Error registering servlet filter {}", servletFilterConfiguration);
throw new RuntimeException(e);
}
}
}
In the above code, ServletFilterConfiguration is a Java object representing the servlet filter specified using the registry YAML file as show in the example below. However the general idea is that one needs to add com.hortonworks.registries.auth.server.AuthenticationFilter for enabling authentication
The filter configuration is passed using the params property in the YAML file, as follows:
servletFilters:
- className: "com.hortonworks.registries.auth.server.AuthenticationFilter"
params:
type: "kerberos"
kerberos.principal: "HTTP/web-service-host.com"
kerberos.keytab: "/path/to/keytab"
kerberos.name.rules: "RULE:[2:$1@$0]([jt]t@.*EXAMPLE.COM)s/.*/$MAPRED_USER/ RULE:[2:$1@$0]([nd]n@.*EXAMPLE.COM)s/.*/$HDFS_USER/DEFAULT"
token.validity: 36000
enable.trusted.proxy: true
proxyuser.knox.hosts: 102.22.22.22
proxyuser.haproxy.hosts: 102.22.22.21, 102.22.22.20
The servlet filter uses the principal HTTP/{hostname}
to
login(hostname must be the host where the web-service runs) . Make sure
that principal is created as part of Kerberos setup
Once configured, the user must do kinit
on client side using the
principal declared before accessing the web-service via the browser or
some other client. This principal also needs to be created first during
Kerberos setup
Trusted Proxy Pattern can be enabled by setting the property ‘enable.trusted.proxy’ to true. You can provide the list of proxyusers and the allowed hosts in the format given below.
proxyuser.``proxy-user``.hosts=<ip_address1, ip_address2>
Here’s an example on how to access the web-service after the setup above:
curl -i --negotiate -u:anyUser -b ~/cookiejar.txt -c ~/cookiejar.txt http://<web-service-host>:<port>/api/v1/
- Firefox: Go to
about:config
and search fornetwork.negotiate-auth.trusted-uris
double-click to add value “http://:” - Google-chrome: start from command line with:
google-chrome --auth-server-whitelist="*web-service-hostname" --auth-negotiate-delegate-whitelist="*web-service-hostname"
- IE: Configure trusted websites to include “web-service-hostname” and allow negotiation for that website
Caution: In AD MIT Kerberos setup, the key size is bigger than the default UI jetty server request header size. If using MIT Kerberos with jettty server, make sure you set HTTP header buffer bytes to 65536
SPNEGO+BASIC¶
SPNEGO Authentication handler can be extended to support Kerberos credentials based Basic Authentication as long as the incoming HTTP request is secure and the HTTP method is POST. If a user provides user credentials in a HTTPS, POST call under Authorization Header, then a Kerberos login is attempted. In the authentication failure scenario, the SPNEGO sequence is invoked.
This mechanism can be enabled by adding a property(login.enabled) to the existing Kerberos configuration. Below is an example.
servletFilters:
- className: "com.hortonworks.registries.auth.server.AuthenticationFilter"
params:
type: "kerberos"
kerberos.principal: "HTTP/web-service-host.com"
kerberos.keytab: "/path/to/keytab"
kerberos.name.rules: "RULE:[2:$1@$0]([jt]t@.*EXAMPLE.COM)s/.*/$MAPRED_USER/ RULE:[2:$1@$0]([nd]n@.*EXAMPLE.COM)s/.*/$HDFS_USER/DEFAULT"
token.validity: 36000
login.enabled: "true"
Here’s an example of how a login call would like(“Z3VydTI6Z3VydTI=” is base64 encoded username and password):
curl -k -X POST -H "Authorization: Basic Z3VydTI6Z3VydTI=" https://host-172-22-74-66.example.com:8587/api/v1/admin/auth/login
SPNEGO authentication sequence is by default attempted, however, it can be skipped by adding another property.
servletFilters:
- className: "com.hortonworks.registries.auth.server.AuthenticationFilter"
params:
type: "kerberos"
kerberos.name.rules: "RULE:[2:$1@$0]([jt]t@.*EXAMPLE.COM)s/.*/$MAPRED_USER/ RULE:[2:$1@$0]([nd]n@.*EXAMPLE.COM)s/.*/$HDFS_USER/DEFAULT"
token.validity: 36000
login.enabled: "true"
spnego.enabled: "false"
SSL¶
This section talks about enabling SSL for Registry Server. Below steps mention about how to generate self signed certificates and use them with Registry Server.
Run the following to create a self-signed entry in the keystore.jks. The alias selfsigned can be anything you want.
# keytool -genkey -alias selfsigned -keyalg RSA -keystore keystore.jks -keysize 2048
Export the certificate to selfsigned.crt with:
# keytool -export -alias selfsigned -file selfsigned.crt -keystore keystore.jks
Import that certificate into your cacerts, the default Java keystore. You may need to do this as root, or with sudo. Go to the /jre/lib/security directory, and run:
# keytool -import -trustcacerts -alias selfsigned -file selfsigned.crt -keystore <path_to_java>/cacerts
Registry config for the server can be configured like below.
server: applicationConnectors: - type: https port: 8443 keyStorePath: ./conf/keystore.jks keyStorePassword: test12 validateCerts: false validatePeers: false adminConnectors: - type: https port: 8444 keyStorePath: ./conf/keystore.jks keyStorePassword: test12 validateCerts: false validatePeers: falseWhen you start the server, you can access via https on the port 8443.