blob: a0a37915df49c9bce791f93f207885304c011cb1 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>Security</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="generator" content="Jekyll v3.7.3">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic,900">
<link rel="stylesheet" href="/avatica/css/screen.css">
<link rel="icon" type="image/x-icon" href="/avatica/favicon.ico">
<!--[if lt IE 9]>
<script src="/js/html5shiv.min.js"></script>
<script src="/js/respond.min.js"></script>
<![endif]-->
</head>
<body class="wrap">
<header role="banner">
<nav class="mobile-nav show-on-mobiles">
<ul>
<li class="">
<a href="/avatica/">Home</a>
</li>
<li class="">
<a href="/avatica/downloads/">Download</a>
</li>
<li class="">
<a href="/avatica/community/">Community</a>
</li>
<li class="">
<a href="/avatica/develop/">Develop</a>
</li>
<li class="">
<a href="/avatica/news/">News</a>
</li>
<li class="current">
<a href="/avatica/docs/">Docs</a>
</li>
</ul>
</nav>
<div class="grid">
<div class="unit one-third center-on-mobiles">
<h1>
<a href="/avatica/">
<span class="sr-only">Apache Calcite Avatica</span>
<img src="/avatica/img/logo.png" width="226" height="140" alt="Calcite Logo">
</a>
</h1>
</div>
<nav class="main-nav unit two-thirds hide-on-mobiles">
<ul>
<li class="">
<a href="/avatica/">Home</a>
</li>
<li class="">
<a href="/avatica/downloads/">Download</a>
</li>
<li class="">
<a href="/avatica/community/">Community</a>
</li>
<li class="">
<a href="/avatica/develop/">Develop</a>
</li>
<li class="">
<a href="/avatica/news/">News</a>
</li>
<li class="current">
<a href="/avatica/docs/">Docs</a>
</li>
</ul>
</nav>
</div>
</header>
<section class="docs">
<div class="grid">
<div class="docs-nav-mobile unit whole show-on-mobiles">
<select onchange="if (this.value) window.location.href=this.value">
<option value="">Navigate the docs…</option>
<optgroup label="Overview">
</optgroup>
<optgroup label="Avatica Reference">
</optgroup>
<optgroup label="Avatica Go Client Reference">
</optgroup>
<optgroup label="Avatica Meta">
</optgroup>
<optgroup label="Avatica Go Client Meta">
</optgroup>
</select>
</div>
<div class="unit four-fifths">
<article>
<h1>Security</h1>
<!--
-->
<p>Security is an important topic between clients and the Avatica server. Most JDBC
drivers and databases implement some level of authentication and authorization
for limit what actions clients are allowed to perform.</p>
<p>Similarly, Avatica must limit what users are allowed to connect and interact
with the server. Avatica must primarily deal with authentication while authorization
is deferred to the underlying database. By default, Avatica provides no authentication.
Avatica does have the ability to perform client authentication using Kerberos,
HTTP Basic, and HTTP Digest.</p>
<p>The authentication and authorization provided by Avatica are designed for use
<em>instead</em> of the authentication and authorization provided by the underlying database.
The typical <code class="highlighter-rouge">user</code> and <code class="highlighter-rouge">password</code> JDBC properties are <strong>always</strong> passed through to
the Avatica server which will cause the server to enforce those credentials. As such,
Avatica’s authentication types mentioned here only have relevance when the underlying database’s authentication
and authorization features are not used. (The Kerberos/SPNEGO integration is one difference as the impersonation feature
is specifically designed to allow the Kerberos identity to be passed to the database -
new advanced implementations could also follow this same approach if desired).</p>
<h2 id="table-of-contents">Table of Contents</h2>
<ul>
<li><a href="#http-basic-authentication">HTTP Basic</a></li><li><a href="#http-digest-authentication">HTTP Digest</a></li><li><a href="#kerberos-with-spnego-authentication">Kerberos with SPNEGO</a></li><li><a href="#custom-authentication">Custom Authentication</a></li><li><a href="#client-implementation">Client implementation</a></li>
</ul>
<h2 id="http-basic-authentication">HTTP Basic Authentication</h2>
<p>Avatica supports authentication over <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">HTTP Basic</a>.
This is simple username-password based authentication which is ultimately insecure when
operating over an untrusted network. Basic authentication is only secure when the transport
is encrypted (e.g. TLS) as the credentials are passed in the clear. This authentication is
supplementary to the provided JDBC authentication. If credentials are passed to the database
already, this authentication is unnecessary.</p>
<h3 id="enabling-basic-authentication">Enabling Basic Authentication</h3>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">String</span> <span class="n">propertiesFile</span> <span class="o">=</span> <span class="s">"/path/to/jetty-users.properties"</span><span class="o">;</span>
<span class="c1">// All roles allowed</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">allowedRoles</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[]</span> <span class="o">{</span><span class="s">"*"</span><span class="o">};</span>
<span class="c1">// Only specific roles are allowed</span>
<span class="n">allowedRoles</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[]</span> <span class="o">{</span> <span class="s">"users"</span><span class="o">,</span> <span class="s">"admins"</span> <span class="o">};</span>
<span class="n">HttpServer</span> <span class="n">server</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpServer</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">8765</span><span class="o">)</span>
<span class="o">.</span><span class="na">withHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">LocalService</span><span class="o">(),</span> <span class="n">Driver</span><span class="o">.</span><span class="na">Serialization</span><span class="o">.</span><span class="na">PROTOBUF</span><span class="o">)</span>
<span class="o">.</span><span class="na">withBasicAuthentication</span><span class="o">(</span><span class="n">propertiesFile</span><span class="o">,</span> <span class="n">allowedRoles</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span></code></pre></figure>
<p>The properties file must be in a form consumable by Jetty. Each line in this
file is of the form: <code class="highlighter-rouge">username: password[,rolename ...]</code></p>
<p>For example:</p>
<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">bob</span><span class="p">:</span> <span class="s">b0b5pA55w0rd,users</span>
<span class="py">steve</span><span class="p">:</span> <span class="s">5teve5pA55w0rd,users</span>
<span class="py">alice</span><span class="p">:</span> <span class="s">Al1cepA55w0rd,admins</span></code></pre></figure>
<p>Passwords can also be obfuscated as MD5 hashes or oneway cryptography (“CRYPT”).
For more information, see the <a href="http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html">official Jetty documentation</a>.</p>
<h2 id="http-digest-authentication">HTTP Digest Authentication</h2>
<p>Avatica also supports <a href="https://en.wikipedia.org/wiki/Digest_access_authentication">HTTP Digest</a>.
This is desirable for Avatica as it does not require the use of TLS to secure communication
between the Avatica client and server. It is configured very similarly to HTTP Basic
authentication. This authentication is supplementary to the provided JDBC authentication.
If credentials are passed to the database already, this authentication is unnecessary.</p>
<h3 id="enabling-digest-authentication">Enabling Digest Authentication</h3>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">String</span> <span class="n">propertiesFile</span> <span class="o">=</span> <span class="s">"/path/to/jetty-users.properties"</span><span class="o">;</span>
<span class="c1">// All roles allowed</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">allowedRoles</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[]</span> <span class="o">{</span><span class="s">"*"</span><span class="o">};</span>
<span class="c1">// Only specific roles are allowed</span>
<span class="n">allowedRoles</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[]</span> <span class="o">{</span> <span class="s">"users"</span><span class="o">,</span> <span class="s">"admins"</span> <span class="o">};</span>
<span class="n">HttpServer</span> <span class="n">server</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpServer</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">8765</span><span class="o">)</span>
<span class="o">.</span><span class="na">withHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">LocalService</span><span class="o">(),</span> <span class="n">Driver</span><span class="o">.</span><span class="na">Serialization</span><span class="o">.</span><span class="na">PROTOBUF</span><span class="o">)</span>
<span class="o">.</span><span class="na">withDigestAuthentication</span><span class="o">(</span><span class="n">propertiesFile</span><span class="o">,</span> <span class="n">allowedRoles</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span></code></pre></figure>
<p>The properties file must be in a form consumable by Jetty. Each line in this
file is of the form: <code class="highlighter-rouge">username: password[,rolename ...]</code></p>
<p>For example:</p>
<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">bob</span><span class="p">:</span> <span class="s">b0b5pA55w0rd,users</span>
<span class="py">steve</span><span class="p">:</span> <span class="s">5teve5pA55w0rd,users</span>
<span class="py">alice</span><span class="p">:</span> <span class="s">Al1cepA55w0rd,admins</span></code></pre></figure>
<p>Passwords can also be obfuscated as MD5 hashes or oneway cryptography (“CRYPT”).
For more information, see the <a href="http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html">official Jetty documentation</a>.</p>
<h2 id="kerberos-with-spnego-authentication">Kerberos with SPNEGO Authentication</h2>
<p>Because Avatica operates over an HTTP interface, the simple and protected GSSAPI
negotiation mechanism (<a href="https://en.wikipedia.org/wiki/SPNEGO">SPNEGO</a>) is a logical
choice. This mechanism makes use of the “HTTP Negotiate” authentication extension to
communicate with the Kerberos Key Distribution Center (KDC) to authenticate a client.</p>
<h3 id="enabling-spnegokerberos-authentication-in-servers">Enabling SPNEGO/Kerberos Authentication in servers</h3>
<p>The Avatica server can operate either by performing the login using
a JAAS configuration file or login programmatically. By default, authenticated clients
will have queries executed as the Avatica server’s kerberos user. <a href="#impersonation">Impersonation</a>
is the feature which enables actions to be run in the server as the actual end-user.</p>
<p>As a note, it is required that the Kerberos principal in use by the Avatica server
<strong>must</strong> have an primary of <code class="highlighter-rouge">HTTP</code> (where Kerberos principals are of the form
<code class="highlighter-rouge">primary[/instance]@REALM</code>). This is specified by <a href="https://tools.ietf.org/html/rfc4559">RFC-4559</a>.</p>
<h4 id="programmatic-login">Programmatic Login</h4>
<p>This approach requires no external file configurations and only requires a
keytab file for the principal.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">HttpServer</span> <span class="n">server</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpServer</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">8765</span><span class="o">)</span>
<span class="o">.</span><span class="na">withHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">LocalService</span><span class="o">(),</span> <span class="n">Driver</span><span class="o">.</span><span class="na">Serialization</span><span class="o">.</span><span class="na">PROTOBUF</span><span class="o">)</span>
<span class="o">.</span><span class="na">withSpnego</span><span class="o">(</span><span class="s">"HTTP/host.domain.com@DOMAIN.COM"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withAutomaticLogin</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">File</span><span class="o">(</span><span class="s">"/etc/security/keytabs/avatica.spnego.keytab"</span><span class="o">))</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span></code></pre></figure>
<h4 id="jaas-configuration-file-login">JAAS Configuration File Login</h4>
<p>A JAAS configuration file can be set via the system property <code class="highlighter-rouge">java.security.auth.login.config</code>.
The user must set this property when launching their Java application invoking the Avatica server.
The presence of this file will automatically perform login as necessary in the first use
of the Avatica server. The invocation is nearly the same as the programmatic login.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">HttpServer</span> <span class="n">server</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpServer</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">8765</span><span class="o">)</span>
<span class="o">.</span><span class="na">withHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">LocalService</span><span class="o">(),</span> <span class="n">Driver</span><span class="o">.</span><span class="na">Serialization</span><span class="o">.</span><span class="na">PROTOBUF</span><span class="o">)</span>
<span class="o">.</span><span class="na">withSpnego</span><span class="o">(</span><span class="s">"HTTP/host.domain.com@DOMAIN.COM"</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span></code></pre></figure>
<p>The contents of the JAAS configuration file are very specific:</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">com</span><span class="o">.</span><span class="na">sun</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">jgss</span><span class="o">.</span><span class="na">accept</span> <span class="o">{</span>
<span class="n">com</span><span class="o">.</span><span class="na">sun</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">auth</span><span class="o">.</span><span class="na">module</span><span class="o">.</span><span class="na">Krb5LoginModule</span> <span class="n">required</span>
<span class="n">storeKey</span><span class="o">=</span><span class="kc">true</span>
<span class="n">useKeyTab</span><span class="o">=</span><span class="kc">true</span>
<span class="n">keyTab</span><span class="o">=/</span><span class="n">etc</span><span class="o">/</span><span class="n">security</span><span class="o">/</span><span class="n">keytabs</span><span class="o">/</span><span class="n">avatica</span><span class="o">.</span><span class="na">spnego</span><span class="o">.</span><span class="na">keyTab</span>
<span class="n">principal</span><span class="o">=</span><span class="n">HTTP</span><span class="o">/</span><span class="n">host</span><span class="o">.</span><span class="na">domain</span><span class="o">.</span><span class="na">com</span><span class="nd">@DOMAIN</span><span class="o">.</span><span class="na">COM</span><span class="o">;</span>
<span class="o">};</span></code></pre></figure>
<p>Ensure the <code class="highlighter-rouge">keyTab</code> and <code class="highlighter-rouge">principal</code> attributes are set correctly for your system.</p>
<h3 id="impersonation">Impersonation</h3>
<p>Impersonation is a feature of the Avatica server which allows the Avatica clients
to execute the server-side calls (e.g. the underlying JDBC calls). Because the details
on what it means to execute such an operation are dependent on the actual system, a
callback is exposed for downstream integrators to implement.</p>
<p>For example, the following is an example for creating an Apache Hadoop <code class="highlighter-rouge">UserGroupInformation</code>
“proxy user”. This example takes a <code class="highlighter-rouge">UserGroupInformation</code> object representing the Avatica server’s
identity, creates a “proxy user” with the client’s username, and performs the action as that
client but using the server’s identity.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PhoenixDoAsCallback</span> <span class="kd">implements</span> <span class="n">DoAsRemoteUserCallback</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">UserGroupInformation</span> <span class="n">serverUgi</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">PhoenixDoAsCallback</span><span class="o">(</span><span class="n">UserGroupInformation</span> <span class="n">serverUgi</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">serverUgi</span> <span class="o">=</span> <span class="n">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">serverUgi</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">T</span> <span class="nf">doAsRemoteUser</span><span class="o">(</span><span class="n">String</span> <span class="n">remoteUserName</span><span class="o">,</span> <span class="n">String</span> <span class="n">remoteAddress</span><span class="o">,</span> <span class="kd">final</span> <span class="n">Callable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">action</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="c1">// Proxy this user on top of the server's user (the real user)</span>
<span class="n">UserGroupInformation</span> <span class="n">proxyUser</span> <span class="o">=</span> <span class="n">UserGroupInformation</span><span class="o">.</span><span class="na">createProxyUser</span><span class="o">(</span><span class="n">remoteUserName</span><span class="o">,</span> <span class="n">serverUgi</span><span class="o">);</span>
<span class="c1">// Check if this user is allowed to be impersonated.</span>
<span class="c1">// Will throw AuthorizationException if the impersonation as this user is not allowed</span>
<span class="n">ProxyUsers</span><span class="o">.</span><span class="na">authorize</span><span class="o">(</span><span class="n">proxyUser</span><span class="o">,</span> <span class="n">remoteAddress</span><span class="o">);</span>
<span class="c1">// Execute the actual call as this proxy user</span>
<span class="k">return</span> <span class="n">proxyUser</span><span class="o">.</span><span class="na">doAs</span><span class="o">(</span><span class="k">new</span> <span class="n">PrivilegedExceptionAction</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">T</span> <span class="nf">run</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">action</span><span class="o">.</span><span class="na">call</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<h4 id="remote-user-extraction">Remote user extraction</h4>
<p>In some cases, it may be desirable to execute some queries on behalf of another user. For example,
<a href="https://knox.apache.org">Apache Knox</a> has a gateway service which can act as a proxy for all requests
to the backend Avatica server. In this case, we don’t want to run the queries as the Knox user, instead
the real user communicating with Knox.</p>
<p>There are presently two options to extract the “real” user from HTTP requests:</p>
<ul>
<li>The authenticated user from the HTTP request, <code class="highlighter-rouge">org.apache.calcite.avatica.server.HttpRequestRemoteUserExtractor</code> (default)</li>
<li>The value of a parameter in the HTTP query string, <code class="highlighter-rouge">org.apache.calcite.avatica.server.HttpQueryStringParameterRemoteUserExtractor</code> (e.g “doAs”)</li>
</ul>
<p>Implementations of Avatica can configure this using the <code class="highlighter-rouge">AvaticaServerConfiguration</code> and providing
an implementation of <code class="highlighter-rouge">RemoteUserExtractor</code>. There are two implementations provided as listed above.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">config</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AvaticaServerConfiguration</span><span class="o">()</span> <span class="o">{</span>
<span class="cm">/* ... */</span>
<span class="nd">@Override</span> <span class="kd">public</span> <span class="n">RemoteUserExtractor</span> <span class="nf">getRemoteUserExtractor</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// We extract the "real" user via the "doAs" query string parameter</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">HttpQueryStringParameterRemoteUserExtractor</span><span class="o">(</span><span class="s">"doAs"</span><span class="o">);</span>
<span class="o">}</span>
<span class="cm">/* ... */</span>
<span class="o">};</span></code></pre></figure>
<h2 id="custom-authentication">Custom Authentication</h2>
<p>Avatica server allows users to plugin their Custom Authentication
mechanism through the HTTPServer Builder. This is useful if users
want to combine features of various authentication types. Examples
include combining basic authentication with impersonation or adding
mutual authentication with impersonation. More Examples are available
in <code class="highlighter-rouge">CustomAuthHttpServerTest</code> class.</p>
<p>Note: Users need to configure their own <code class="highlighter-rouge">ServerConnectors</code> and
<code class="highlighter-rouge">Handlers</code> with the help of <code class="highlighter-rouge">ServerCustomizers</code>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">AvaticaServerConfiguration</span> <span class="n">configuration</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ExampleAvaticaServerConfiguration</span><span class="o">();</span>
<span class="n">HttpServer</span> <span class="n">server</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpServer</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withCustomAuthentication</span><span class="o">(</span><span class="n">configuration</span><span class="o">)</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">8765</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span></code></pre></figure>
<h2 id="client-implementation">Client implementation</h2>
<p>Many HTTP client libraries, such as <a href="https://hc.apache.org/">Apache Commons HttpComponents</a>, already have
support for performing Basic, Digest, and SPNEGO authentication. When in doubt, refer to one of
these implementations as it is likely correct.</p>
<h3 id="spnego">SPNEGO</h3>
<p>For information on building SPNEGO support by hand, consult <a href="https://tools.ietf.org/html/rfc4559">RFC-4559</a>
which describes how the authentication handshake, through use of the “WWW-authenticate=Negotiate”
HTTP header, is used to authenticate a client.</p>
<h3 id="password-based">Password-based</h3>
<p>For both HTTP Basic and Digest authentication, the <a href="/avatica/docs/client_reference.html#avatica-user">avatica_user</a>
and <a href="/avatica/docs/client_reference.html#avatica-password">avatica_password</a>
properties are used to identify the client with the server. If the underlying database
(the JDBC driver inside the Avatica server) require their own user and password combination,
these are set via the traditional “user” and “password” properties in the Avatica
JDBC driver. This also implies that adding HTTP-level authentication in Avatica is likely
superfluous.</p>
<div class="section-nav">
<div class="left align-right">
<a href="/avatica/docs/howto.html" class="prev">Previous</a>
</div>
<div class="right align-left">
<a href="/avatica/docs/compatibility.html" class="next">Next</a>
</div>
</div>
<div class="clear"></div>
</article>
</div>
<div class="unit one-fifth hide-on-mobiles">
<aside>
<h4>Overview</h4>
<ul>
<li class=""><a href="/avatica/docs/index.html">Background</a></li>
<li class=""><a href="/avatica/docs/roadmap.html">Roadmap</a></li>
</ul>
<h4>Avatica Reference</h4>
<ul>
<li class=""><a href="/avatica/docs/client_reference.html">Client Reference</a></li>
<li class=""><a href="/avatica/docs/json_reference.html">JSON Reference</a></li>
<li class=""><a href="/avatica/docs/protobuf_reference.html">Protobuf Reference</a></li>
<li class=""><a href="/avatica/docs/howto.html">HOWTO</a></li>
<li class="current"><a href="/avatica/docs/security.html">Security</a></li>
<li class=""><a href="/avatica/docs/compatibility.html">Compatibility</a></li>
<li class=""><a href="/avatica/docs/custom_client_artifacts.html">Custom Client Artifacts</a></li>
<li class=""><a href="/avatica/docs/docker.html">Docker Images</a></li>
<li class=""><a href="/avatica/docs/protocol_testing.html">Protocol Testing</a></li>
</ul>
<h4>Avatica Go Client Reference</h4>
<ul>
<li class=""><a href="/avatica/docs/go_client_reference.html">Go Client Reference</a></li>
<li class=""><a href="/avatica/docs/go_howto.html">HOWTO</a></li>
</ul>
<h4>Avatica Meta</h4>
<ul>
<li class=""><a href="/avatica/docs/history.html">History</a></li>
<li class=""><a href="/avatica/docs/api/">API</a></li>
<li class=""><a href="/avatica/docs/testapi/">Test API</a></li>
</ul>
<h4>Avatica Go Client Meta</h4>
<ul>
<li class=""><a href="/avatica/docs/go_history.html">Go Client History</a></li>
</ul>
</aside>
</div>
<div class="clear"></div>
</div>
</section>
<footer role="contentinfo">
<div id="poweredby">
<a href="http://www.apache.org/">
<span class="sr-only">Apache</span>
<img src="/avatica/img/feather.png" width="190" height="77" alt="Apache Logo"></a>
</div>
<div id="copyright">
<p>The contents of this website are &copy;&nbsp;2019
<a href="https://www.apache.org/">Apache Software Foundation</a>
under the terms of
the <a href="https://www.apache.org/licenses/LICENSE-2.0.html">
Apache&nbsp;License&nbsp;v2</a>. Apache Calcite and its logo are
trademarks of the Apache Software Foundation.</p>
</div>
</footer>
<script>
var anchorForId = function (id) {
var anchor = document.createElement("a");
anchor.className = "header-link";
anchor.href = "#" + id;
anchor.innerHTML = "<span class=\"sr-only\">Permalink</span><i class=\"fa fa-link\"></i>";
anchor.title = "Permalink";
return anchor;
};
var linkifyAnchors = function (level, containingElement) {
var headers = containingElement.getElementsByTagName("h" + level);
for (var h = 0; h < headers.length; h++) {
var header = headers[h];
if (typeof header.id !== "undefined" && header.id !== "") {
header.appendChild(anchorForId(header.id));
}
}
};
document.onreadystatechange = function () {
if (this.readyState === "complete") {
var contentBlock = document.getElementsByClassName("docs")[0] || document.getElementsByClassName("news")[0];
if (!contentBlock) {
return;
}
for (var level = 1; level <= 6; level++) {
linkifyAnchors(level, contentBlock);
}
}
};
</script>
</body>
</html>