Author: tkurc
Date: Wed Feb 17 02:15:29 2016
New Revision: 1730775
URL: http://svn.apache.org/viewvc?rev=1730775&view=rev
Log:
NIFI-1379: Updating docs
Added:
nifi/site/trunk/docs/nifi-docs/html/images/allow-weak-crypto.png (with
props)
nifi/site/trunk/docs/nifi-docs/html/images/bcrypt-salt.png (with props)
nifi/site/trunk/docs/nifi-docs/html/images/iconDetails.png (with props)
nifi/site/trunk/docs/nifi-docs/html/images/nifi-connection-bend-points.png
(with props)
nifi/site/trunk/docs/nifi-docs/html/images/nifi-legacy-salt.png (with
props)
nifi/site/trunk/docs/nifi-docs/html/images/openssl-salt.png (with props)
nifi/site/trunk/docs/nifi-docs/html/images/pbkdf2-salt.png (with props)
nifi/site/trunk/docs/nifi-docs/html/images/scrypt-salt.png (with props)
Modified:
nifi/site/trunk/docs/nifi-docs/html/administration-guide.html
nifi/site/trunk/docs/nifi-docs/html/developer-guide.html
nifi/site/trunk/docs/nifi-docs/html/expression-language-guide.html
nifi/site/trunk/docs/nifi-docs/html/getting-started.html
nifi/site/trunk/docs/nifi-docs/html/overview.html
nifi/site/trunk/docs/nifi-docs/html/user-guide.html
Modified: nifi/site/trunk/docs/nifi-docs/html/administration-guide.html
URL:
http://svn.apache.org/viewvc/nifi/site/trunk/docs/nifi-docs/html/administration-guide.html?rev=1730775&r1=1730774&r2=1730775&view=diff
==============================================================================
--- nifi/site/trunk/docs/nifi-docs/html/administration-guide.html (original)
+++ nifi/site/trunk/docs/nifi-docs/html/administration-guide.html Wed Feb 17
02:15:29 2016
@@ -461,7 +461,23 @@ body.book #toc,body.book #preamble,body.
<li><a href="#security-configuration">Security Configuration</a></li>
<li><a href="#user-authentication">User Authentication</a></li>
<li><a href="#controlling-levels-of-access">Controlling Levels of
Access</a></li>
+<li><a href="#encryption">Encryption Configuration</a>
+<ul class="sectlevel2">
+<li><a href="#key-derivation-functions">Key Derivation Functions</a></li>
+<li><a href="#salt-and-iv-encoding">Salt and IV Encoding</a></li>
+<li><a
href="#java-cryptography-extension-jce-limited-strength-jurisdiction-policies">Java
Cryptography Extension (JCE) Limited Strength Jurisdiction Policies</a></li>
+<li><a href="#allow-insecure-cryptographic-modes">Allow Insecure Cryptographic
Modes</a></li>
+</ul>
+</li>
<li><a href="#clustering">Clustering Configuration</a></li>
+<li><a href="#state_management">State Management</a>
+<ul class="sectlevel2">
+<li><a href="#state_providers">Configuring State Providers</a></li>
+<li><a href="#embedded_zookeeper">Embedded ZooKeeper Server</a></li>
+<li><a href="#zk_access_control">ZooKeeper Access Control</a></li>
+<li><a href="#securing_zookeeper">Securing ZooKeeper</a></li>
+</ul>
+</li>
<li><a href="#bootstrap_properties">Bootstrap Properties</a></li>
<li><a href="#notification_services">Notification Services</a></li>
<li><a href="#system_properties">System Properties</a></li>
@@ -521,7 +537,13 @@ body.book #toc,body.book #preamble,body.
</ul>
</div>
<div class="paragraph">
-<p>Note that there is a known issue in Internet Explorer (IE) 10 and 11 that
can cause problems when moving items on the NiFi graph. If you encounter this
problem, we suggest using a browser other than IE. This known issue is
described here: <a
href="https://connect.microsoft.com/IE/Feedback/Details/1050422"
class="bare">https://connect.microsoft.com/IE/Feedback/Details/1050422</a>.</p>
+<p><strong>Note</strong> There is a known issue in Internet Explorer (IE) 10
and 11 that can cause problems when moving items on the NiFi graph. If you
encounter this problem, we suggest using a browser other than IE. This known
issue is described here: <a
href="https://connect.microsoft.com/IE/Feedback/Details/1050422"
class="bare">https://connect.microsoft.com/IE/Feedback/Details/1050422</a>.</p>
+</div>
+<div class="paragraph">
+<p><strong>Note</strong> Java 7 default perm gen sizing can result in <em>out
of memory errors</em> due to the amount of classes loaded by NiFi. See the <a
href="#bootstrap_properties">Bootstrap Properties</a> section for more
information.</p>
+</div>
+<div class="paragraph">
+<p><strong>Note</strong> Under sustained and extremely high throughput the
CodeCache settings may need to be tuned to avoid sudden performance loss. See
the <a href="#bootstrap_properties">Bootstrap Properties</a> section for more
information.</p>
</div>
</div>
</div>
@@ -1170,6 +1192,399 @@ cluster, s/he can grant it to the group
</div>
</div>
<div class="sect1">
+<h2 id="encryption"><a class="anchor" href="#encryption"></a>Encryption
Configuration</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>This section provides an overview of the capabilities of NiFi to encrypt
and decrypt data.</p>
+</div>
+<div class="paragraph">
+<p>The <code>EncryptContent</code> processor allows for the encryption and
decryption of data, both internal to NiFi and integrated with external systems,
such as <code>openssl</code> and other data sources and consumers.</p>
+</div>
+<div class="sect2">
+<h3 id="key-derivation-functions"><a class="anchor"
href="#key-derivation-functions"></a>Key Derivation Functions</h3>
+<div class="paragraph">
+<p>Key Derivation Functions (KDF) are mechanisms by which human-readable
information, usually a password or other secret information, is translated into
a cryptographic key suitable for data protection. For further information, read
<a href="https://en.wikipedia.org/wiki/Key_derivation_function">the Wikipedia
entry on Key Derivation Functions</a>.
+Currently, KDFs are ingested by <code>CipherProvider</code> implementations
and return a fully-initialized <code>Cipher</code> object to be used for
encryption or decryption. Due to the use of a
<code>CipherProviderFactory</code>, the KDFs are not customizable at this time.
Future enhancements will include the ability to provide custom cost parameters
to the KDF at initialization time. As a work-around,
<code>CipherProvider</code> instances can be initialized with custom cost
parameters in the constructor but this is not currently supported by the
<code>CipherProviderFactory</code>.
+Here are the KDFs currently supported by NiFi (primarily in the
<code>EncryptContent</code> processor for password-based encryption (PBE)) and
relevant notes:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>NiFi Legacy KDF</p>
+<div class="ulist">
+<ul>
+<li>
+<p>The original KDF used by NiFi for internal key derivation for PBE, this is
1000 iterations of the MD5 digest over the concatenation of the password and 8
or 16 bytes of random salt (the salt length depends on the selected cipher
block size).</p>
+</li>
+<li>
+<p>This KDF is <strong>deprecated as of NiFi 0.5.0</strong> and should only be
used for backwards compatibility to decrypt data that was previously encrypted
by a legacy version of NiFi.</p>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p>OpenSSL PKCS#5 v1.5 EVP_BytesToKey</p>
+<div class="ulist">
+<ul>
+<li>
+<p>This KDF was added in v0.4.0.</p>
+</li>
+<li>
+<p>This KDF is provided for compatibility with data encrypted using
OpenSSL’s default PBE, known as <code>EVP_BytesToKey</code>. This is a
single iteration of MD5 over the concatenation of the password and 8 bytes of
random ASCII salt. OpenSSL recommends using <code>PBKDF2</code> for key
derivation but does not expose the library method necessary to the command-line
tool, so this KDF is still the de facto default for command-line encryption.</p>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p>Bcrypt</p>
+<div class="ulist">
+<ul>
+<li>
+<p>This KDF was added in v0.5.0.</p>
+</li>
+<li>
+<p><a href="https://en.wikipedia.org/wiki/Bcrypt">Bcrypt</a> is an adaptive
function based on the <a
href="https://en.wikipedia.org/wiki/Blowfish_(cipher)">Blowfish</a> cipher.
This KDF is strongly recommended as it automatically incorporates a random 16
byte salt, configurable cost parameter (or "work factor"), and is hardened
against brute-force attacks using <a
href="https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units">GPGPU</a>
(which share memory between cores) by requiring access to "large" blocks of
memory during the key derivation. It is less resistant to <a
href="https://en.wikipedia.org/wiki/Field-programmable_gate_array">FPGA</a>
brute-force attacks where the gate arrays have access to individual embedded
RAM blocks.</p>
+</li>
+<li>
+<p>Because the length of a Bcrypt-derived key is always 184 bits, the complete
output is then fed to a <code>SHA-512</code> digest and truncated to the
desired key length. This provides the benefit of the avalanche effect on the
formatted input.</p>
+</li>
+<li>
+<p>The recommended minimum work factor is 12 (2<sup>12</sup> key derivation
rounds) (as of 2/1/2016 on commodity hardware) and should be increased to the
threshold at which legitimate systems will encounter detrimental delays (see
schedule below or use
<code>BcryptCipherProviderGroovyTest#testDefaultConstructorShouldProvideStrongWorkFactor()</code>
to calculate safe minimums).</p>
+</li>
+<li>
+<p>The salt format is <code>$2a$10$ABCDEFGHIJKLMNOPQRSTUV</code>. The salt is
delimited by <code>$</code> and the three sections are as follows:</p>
+<div class="ulist">
+<ul>
+<li>
+<p><code>2a</code> - the version of the format. An extensive explanation can
be found <a
href="http://blog.ircmaxell.com/2012/12/seven-ways-to-screw-up-bcrypt.html">here</a>.
NiFi currently uses <code>2a</code> for all salts generated internally.</p>
+</li>
+<li>
+<p><code>10</code> - the work factor. This is actually the log<sub>2</sub>
value, so the total iteration count would be 2<sup>10</sup> in this case.</p>
+</li>
+<li>
+<p><code>ABCDEFGHIJKLMNOPQRSTUV</code> - the 22 character, Base64-encoded,
unpadded, raw salt value. This decodes to a 16 byte salt used in the key
derivation.</p>
+</li>
+</ul>
+</div>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p>Scrypt</p>
+<div class="ulist">
+<ul>
+<li>
+<p>This KDF was added in v0.5.0.</p>
+</li>
+<li>
+<p><a href="https://en.wikipedia.org/wiki/Scrypt">Scrypt</a> is an adaptive
function designed in response to <code>bcrypt</code>. This KDF is recommended
as it requires relatively large amounts of memory for each derivation, making
it resistant to hardware brute-force attacks.</p>
+</li>
+<li>
+<p>The recommended minimum cost is <code>N</code>=2<sup>14</sup>,
<code>r</code>=8, <code>p</code>=1 (as of 2/1/2016 on commodity hardware) and
should be increased to the threshold at which legitimate systems will encounter
detrimental delays (see schedule below or use
<code>ScryptCipherProviderGroovyTest#testDefaultConstructorShouldProvideStrongParameters()</code>
to calculate safe minimums).</p>
+</li>
+<li>
+<p>The salt format is <code>$s0$e0101$ABCDEFGHIJKLMNOPQRSTUV</code>. The salt
is delimited by <code>$</code> and the three sections are as follows:</p>
+<div class="ulist">
+<ul>
+<li>
+<p><code>s0</code> - the version of the format. NiFi currently uses
<code>s0</code> for all salts generated internally.</p>
+</li>
+<li>
+<p><code>e0101</code> - the cost parameters. This is actually a hexadecimal
encoding of <code>N</code>, <code>r</code>, <code>p</code> using shifts. This
can be formed/parsed using <code>Scrypt#encodeParams()</code> and
<code>Scrypt#parseParameters()</code>.</p>
+<div class="ulist">
+<ul>
+<li>
+<p>Some external libraries encode <code>N</code>, <code>r</code>, and
<code>p</code> separately in the form <code>$400$1$1$</code>. A utility method
is available at <code>ScryptCipherProvider#translateSalt()</code> which will
convert the external form to the internal form.</p>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p><code>ABCDEFGHIJKLMNOPQRSTUV</code> - the 12-44 character, Base64-encoded,
unpadded, raw salt value. This decodes to a 8-32 byte salt used in the key
derivation.</p>
+</li>
+</ul>
+</div>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p>PBKDF2</p>
+<div class="ulist">
+<ul>
+<li>
+<p>This KDF was added in v0.5.0.</p>
+</li>
+<li>
+<p><a href="https://en.wikipedia.org/wiki/PBKDF2">Password-Based Key
Derivation Function 2</a> is an adaptive derivation function which uses an
internal pseudorandom function (PRF) and iterates it many times over a password
and salt (at least 16 bytes).</p>
+</li>
+<li>
+<p>The PRF is recommended to be <code>HMAC/SHA-256</code> or
<code>HMAC/SHA-512</code>. The use of an HMAC cryptographic hash function
mitigates a length extension attack.</p>
+</li>
+<li>
+<p>The recommended minimum number of iterations is 160,000 (as of 2/1/2016 on
commodity hardware). This number should be doubled every two years (see
schedule below or use
<code>PBKDF2CipherProviderGroovyTest#testDefaultConstructorShouldProvideStrongIterationCount()</code>
to calculate safe minimums).</p>
+</li>
+<li>
+<p>This KDF is not memory-hard (can be parallelized massively with commodity
hardware) but is still recommended as sufficient by <a
href="http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf">NIST
SP 800-132 (PDF)</a> and many cryptographers (when used with a proper
iteration count and HMAC cryptographic hash function).</p>
+</li>
+</ul>
+</div>
+</li>
+<li>
+<p>None</p>
+<div class="ulist">
+<ul>
+<li>
+<p>This KDF was added in v0.5.0.</p>
+</li>
+<li>
+<p>This KDF performs no operation on the input and is a marker to indicate the
raw key is provided to the cipher. The key must be provided in hexadecimal
encoding and be of a valid length for the associated cipher/algorithm.</p>
+</li>
+</ul>
+</div>
+</li>
+</ul>
+</div>
+<div class="sect3">
+<h4 id="additional-resources"><a class="anchor"
href="#additional-resources"></a>Additional Resources</h4>
+<div class="ulist">
+<ul>
+<li>
+<p><a href="http://stackoverflow.com/a/30308723/70465">Explanation of optimal
scrypt cost parameters and relationships</a></p>
+</li>
+<li>
+<p><a
href="http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf">NIST
Special Publication 800-132</a></p>
+</li>
+<li>
+<p><a
href="https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet#Work_Factor">OWASP
Password Storage Work Factor Calculations</a></p>
+</li>
+<li>
+<p><a href="http://security.stackexchange.com/a/3993/16485">PBKDF2 rounds
calculations</a></p>
+</li>
+<li>
+<p><a
href="http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html">Scrypt
as KDF vs password storage vulnerabilities</a></p>
+</li>
+<li>
+<p><a href="http://security.stackexchange.com/a/26253/16485">Scrypt vs. Bcrypt
(as of 2010)</a></p>
+</li>
+<li>
+<p><a href="http://security.stackexchange.com/a/6415/16485">Bcrypt vs
PBKDF2</a></p>
+</li>
+<li>
+<p><a
href="http://wildlyinaccurate.com/bcrypt-choosing-a-work-factor/">Choosing a
work factor for Bcrypt</a></p>
+</li>
+<li>
+<p><a
href="https://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/crypto/bcrypt/BCrypt.html">Spring
Security Bcrypt</a></p>
+</li>
+<li>
+<p><a
href="https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html">OpenSSL
EVP BytesToKey PKCS#1v1.5</a></p>
+</li>
+<li>
+<p><a
href="https://www.openssl.org/docs/manmaster/crypto/PKCS5_PBKDF2_HMAC.html">OpenSSL
PBKDF2 KDF</a></p>
+</li>
+<li>
+<p><a href="http://security.stackexchange.com/a/29139/16485">OpenSSL KDF flaws
description</a></p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="salt-and-iv-encoding"><a class="anchor"
href="#salt-and-iv-encoding"></a>Salt and IV Encoding</h3>
+<div class="paragraph">
+<p>Initially, the <code>EncryptContent</code> processor had a single method of
deriving the encryption key from a user-provided password. This is now referred
to as <code>NiFiLegacy</code> mode, effectively <code>MD5 digest, 1000
iterations</code>. In v0.4.0, another method of deriving the key, <code>OpenSSL
PKCS#5 v1.5 EVP_BytesToKey</code> was added for compatibility with content
encrypted outside of NiFi using the <code>openssl</code> command-line tool.
Both of these <a href="#key-derivation-functions">Key Derivation Functions</a>
(KDF) had hard-coded digest functions and iteration counts, and the salt format
was also hard-coded. With v0.5.0, additional KDFs are introduced with variable
iteration counts, work factors, and salt formats. In addition, <em>raw keyed
encryption</em> was also introduced. This required the capacity to encode
arbitrary salts and Initialization Vectors (IV) into the cipher stream in order
to be recovered by NiFi or a follow-on system to decrypt these mess
ages.</p>
+</div>
+<div class="paragraph">
+<p>For the existing KDFs, the salt format has not changed.</p>
+</div>
+<div class="sect3">
+<h4 id="nifi-legacy"><a class="anchor" href="#nifi-legacy"></a>NiFi Legacy</h4>
+<div class="paragraph">
+<p>The first 8 or 16 bytes of the input are the salt. The salt length is
determined based on the selected algorithm’s cipher block length. If the
cipher block size cannot be determined (such as with a stream cipher like
<code>RC4</code>), the default value of 8 bytes is used. On decryption, the
salt is read in and combined with the password to derive the encryption key and
IV.</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="./images/nifi-legacy-salt.png" alt="NiFi
Legacy Salt Encoding"></span></p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="openssl-pkcs-5-v1-5-evp_bytestokey"><a class="anchor"
href="#openssl-pkcs-5-v1-5-evp_bytestokey"></a>OpenSSL PKCS#5 v1.5
EVP_BytesToKey</h4>
+<div class="paragraph">
+<p>OpenSSL allows for salted or unsalted key derivation. <em>*Unsalted key
derivation is a security risk and is not recommended.*</em> If a salt is
present, the first 8 bytes of the input are the ASCII string
"<code>Salted__</code>" (<code>0x53 61 6C 74 65 64 5F 5F</code>) and the next 8
bytes are the ASCII-encoded salt. On decryption, the salt is read in and
combined with the password to derive the encryption key and IV. If there is no
salt header, the entire input is considered to be the cipher text.</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="./images/openssl-salt.png" alt="OpenSSL Salt
Encoding"></span></p>
+</div>
+<div class="paragraph">
+<p>For new KDFs, each of which allow for non-deterministic IVs, the IV must be
stored alongside the cipher text. This is not a vulnerability, as the IV is not
required to be secret, but simply to be unique for messages encrypted using the
same key to reduce the success of cryptographic attacks. For these KDFs, the
output consists of the salt, followed by the salt delimiter, UTF-8 string
"<code>NiFiSALT</code>" (<code>0x4E 69 46 69 53 41 4C 54</code>) and then the
IV, followed by the IV delimiter, UTF-8 string "<code>NiFiIV</code>"
(<code>0x4E 69 46 69 49 56</code>), followed by the cipher text.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="bcrypt-scrypt-pbkdf2"><a class="anchor"
href="#bcrypt-scrypt-pbkdf2"></a>Bcrypt, Scrypt, PBKDF2</h4>
+<div class="paragraph">
+<p><span class="image"><img src="./images/bcrypt-salt.png" alt="Bcrypt Salt
& IV Encoding"></span></p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="./images/scrypt-salt.png" alt="Scrypt Salt
& IV Encoding"></span></p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="./images/pbkdf2-salt.png" alt="PBKDF2 Salt
& IV Encoding"></span></p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3
id="java-cryptography-extension-jce-limited-strength-jurisdiction-policies"><a
class="anchor"
href="#java-cryptography-extension-jce-limited-strength-jurisdiction-policies"></a>Java
Cryptography Extension (JCE) Limited Strength Jurisdiction Policies</h3>
+<div class="paragraph">
+<p>Because of US export regulations, default JVMs have <a
href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#importlimits">limits
imposed on the strength of cryptographic operations</a> available to them. For
example, AES operations are limited to <code>128 bit keys</code> by default.
While <code>AES-128</code> is cryptographically safe, this can have unintended
consequences, specifically on Password-based Encryption (PBE).</p>
+</div>
+<div class="paragraph">
+<p>PBE is the process of deriving a cryptographic key for encryption or
decryption from <em>user-provided secret material</em>, usually a password.
Rather than a human remembering a (random-appearing) 32 or 64 character
hexadecimal string, a password or passphrase is used.</p>
+</div>
+<div class="paragraph">
+<p>A number of PBE algorithms provided by NiFi impose strict limits on the
length of the password due to the underlying key length checks. Below is a
table listing the maximum password length on a JVM with limited cryptographic
strength.</p>
+</div>
+<table class="tableblock frame-all grid-all spread">
+<caption class="title">Table 1. Maximum Password Length on Limited
Cryptographic Strength JVM</caption>
+<colgroup>
+<col style="width: 50%;">
+<col style="width: 50%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Algorithm</th>
+<th class="tableblock halign-left valign-top">Max Password Length</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHMD5AND128BITAES-CBC-OPENSSL</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHMD5AND192BITAES-CBC-OPENSSL</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHMD5AND256BITAES-CBC-OPENSSL</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHMD5ANDDES</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHMD5ANDRC2</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHA1ANDRC2</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHA1ANDDES</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">16</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND128BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND192BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND256BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND40BITRC2-CBC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND128BITRC2-CBC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND40BITRC4</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND128BITRC4</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHA256AND128BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHA256AND192BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHA256AND256BITAES-CBC-BC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND2-KEYTRIPLEDES-CBC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAAND3-KEYTRIPLEDES-CBC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>PBEWITHSHAANDTWOFISH-CBC</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td>
+</tr>
+</tbody>
+</table>
+</div>
+<div class="sect2">
+<h3 id="allow-insecure-cryptographic-modes"><a class="anchor"
href="#allow-insecure-cryptographic-modes"></a>Allow Insecure Cryptographic
Modes</h3>
+<div class="paragraph">
+<p>By default, the <code>Allow Insecure Cryptographic Modes</code> property in
<code>EncryptContent</code> processor settings is set to
<code>not-allowed</code>. This means that if a password of fewer than
<code>10</code> characters is provided, a validation error will occur. 10
characters is a conservative estimate and does not take into consideration full
entropy calculations, patterns, etc.</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="./images/allow-weak-crypto.png" alt="Allow
Insecure Cryptographic Modes" width="940"></span></p>
+</div>
+<div class="paragraph">
+<p>On a JVM with limited strength cryptography, some PBE algorithms limit the
maximum password length to 7, and in this case it will not be possible to
provide a "safe" password. It is recommended to install the JCE Unlimited
Strength Jurisdiction Policy files for the JVM to mitigate this issue.</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><a
href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JCE
Unlimited Strength Jurisdiction Policy files for Java 7</a></p>
+</li>
+<li>
+<p><a
href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JCE
Unlimited Strength Jurisdiction Policy files for Java 8</a></p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>If on a system where the unlimited strength policies cannot be installed,
it is recommended to switch to an algorithm that supports longer passwords (see
table above).</p>
+</div>
+<div class="admonitionblock warning">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-warning" title="Warning"></i>
+</td>
+<td class="content">
+<div class="title">Allowing Weak Crypto</div>
+<div class="paragraph">
+<p>If it is not possible to install the unlimited strength jurisdiction
policies, the <code>Allow Weak Crypto</code> setting can be changed to
<code>allowed</code>, but <strong>this is <em>not</em> recommended</strong>.
Changing this setting explicitly acknowledges the inherent risk in using weak
cryptographic configurations.</p>
+</div>
+</td>
+</tr>
+</table>
+</div>
+<div class="paragraph">
+<p>It is preferable to request upstream/downstream systems to switch to <a
href="https://cwiki.apache.org/confluence/display/NIFI/Encryption+Information">keyed
encryption</a> or use a "strong" <a
href="https://cwiki.apache.org/confluence/display/NIFI/Key+Derivation+Function+Explanations">Key
Derivation Function (KDF) supported by NiFi</a>.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
<h2 id="clustering"><a class="anchor" href="#clustering"></a>Clustering
Configuration</h2>
<div class="sectionbody">
<div class="paragraph">
@@ -1284,6 +1699,9 @@ the NiFi Cluster Manager (NCM), and the
<p>Under the Web Properties, set either the http or https port that you want
Node 1 to run on. If the NCM is running on the same server, choose a different
web port for Node 1. Also, consider whether you need to set the http or https
host property.</p>
</li>
<li>
+<p>Under the State Management section, set the
<code>nifi.state.management.provider.cluster</code> property to the identifier
of the Cluster State Provider. Ensure that the Cluster State Provider has been
configured in the <em>state-management.xml</em> file. See <a
href="#state_providers">Configuring State Providers</a> for more
information.</p>
+</li>
+<li>
<p>Under Cluster Node Properties, set the following:</p>
<div class="ulist">
<ul>
@@ -1316,6 +1734,9 @@ the NiFi Cluster Manager (NCM), and the
<p>Under the Web Properties, set either the http or https port that you want
Node 2 to run on. Also, consider whether you need to set the http or https host
property.</p>
</li>
<li>
+<p>Under the State Management section, set the
<code>nifi.state.management.provider.cluster</code> property to the identifier
of the Cluster State Provider. Ensure that the Cluster State Provider has been
configured in the <em>state-management.xml</em> file. See <a
href="#state_providers">Configuring State Providers</a> for more
information.</p>
+</li>
+<li>
<p>Under the Cluster Node Properties, set the following:</p>
<div class="ulist">
<ul>
@@ -1362,6 +1783,391 @@ additivity="false">
</div>
</div>
<div class="sect1">
+<h2 id="state_management"><a class="anchor" href="#state_management"></a>State
Management</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>NiFi provides a mechanism for Processors, Reporting Tasks, Controller
Services, and the framework itself to persist state. This
+allows a Processor, for example, to resume from the place where it left off
after NiFi is restarted. Additionally, it allows for
+a Processor to store some piece of information so that the Processor can
access that information from all of the different nodes
+in the cluster. This allows one node to pick up where another node left off,
or to coordinate across all of the nodes in a cluster.</p>
+</div>
+<div class="sect2">
+<h3 id="state_providers"><a class="anchor"
href="#state_providers"></a>Configuring State Providers</h3>
+<div class="paragraph">
+<p>When a component decides to store or retrieve state, it does so by
providing a "Scope" - either Node-local or Cluster-wide. The
+mechanism that is used to store and retrieve this state is then determined
based on this Scope, as well as the configured State
+Providers. The <em>nifi.properties</em> file contains three different
properties that are relevant to configuring these State Providers.
+The first is the <code>nifi.state.management.configuration.file</code>
property specifies an external XML file that is used for configuring
+the local and cluster-wide State Providers. This XML file may contain
configurations for multiple providers, so the
+<code>nifi.state.management.provider.local</code> property provides the
identifier of the local State Provider configured in this XML file.
+Similarly, the <code>nifi.state.management.provider.cluster</code> property
provides the identifier of the cluster-wide State Provider
+configured in this XML file.</p>
+</div>
+<div class="paragraph">
+<p>This XML file consists of a top-level <code>state-management</code>
element, which has one or more <code>local-provider</code> and zero or more
+<code>cluster-provider</code> elements. Each of these elements then contains
an <code>id</code> element that is used to specify the identifier that can
+be referenced in the <em>nifi.properties</em> file, as well as a
<code>class</code> element that specifies the fully-qualified class name to use
+in order to instantiate the State Provider. Finally, each of these elements
may have zero or more <code>property</code> elements. Each
+<code>property</code> element has an attribute, <code>name</code> that is the
name of the property that the State Provider supports. The textual content
+of the <code>property</code> element is the value of the property.</p>
+</div>
+<div class="paragraph">
+<p>Once these State Providers have been configured in the
<em>state-management.xml</em> file (or whatever file is configured), those
Providers
+may be referenced by their identifiers. By default, the Local State Provider
is configured to be a <code>WriteAheadLocalStateProvider</code> that
+persists the data to the <em>$NIFI_HOME/state</em> directory. The default
Cluster State Provider is configured to be a
<code>ZooKeeperStateProvider</code>.
+The default ZooKeeper-based provider must have its <code>Connect String</code>
property populated before it can be used. It is also advisable,
+if multiple NiFi instances will use the same ZooKeeper instance, that the
value of the <code>Root Node</code> property be changed. For instance,
+one might set the value to <code>/nifi/<team name>/production</code>. A
<code>Connect String</code> takes the form of comma separated
<host>:<port> tuples,
+such as my-zk-server1:2181,my-zk-server2:2181,my-zk-server3:2181. In the event
a port is not specified for any of the hosts, the ZooKeeper
+default of 2181 is assumed.</p>
+</div>
+<div class="paragraph">
+<p>When adding data to ZooKeeper, there are two options for Access Control:
<code>Open</code> and <code>CreatorOnly</code>. If the <code>Access
Control</code> property is
+set to <code>Open</code>, then anyone is allowed to log into ZooKeeper and
have full permissions to see, change, delete, or administer the data.
+If <code>CreatorOnly</code> is specified, then only the user that created the
data is allowed to read, change, delete, or administer the data.
+In order to use the <code>CreatorOnly</code> option, NiFi must provide some
form of authentication. See the <a href="#zk_access_control">ZooKeeper Access
Control</a>
+section below for more information on how to configure authentication.</p>
+</div>
+<div class="paragraph">
+<p>If NiFi is configured to run in a standalone mode, the
<code>cluster-provider</code> element need not be populated in the
<em>state-management.xml</em>
+file and will actually be ignored if they are populated. However, the
<code>local-provider</code> element must always be present and populated.
+Additionally, if NiFi is run in a cluster, each node must also have the
<code>cluster-provider</code> element present and properly configured.
+Otherwise, NiFi will fail to startup.</p>
+</div>
+<div class="paragraph">
+<p>While there are not many properties that need to be configured for these
providers, they were externalized into a separate <em>state-management.xml</em>
+file, rather than being configured via the <em>nifi.properties</em> file,
simply because different implementations may require different properties,
+and it is easier to maintain and understand the configuration in an XML-based
file such as this, than to mix the properties of the Provider
+in with all of the other NiFi framework-specific properties.</p>
+</div>
+<div class="paragraph">
+<p>It should be noted that if Processors and other components save state using
the Clustered scope, the Local State Provider will be used
+if the instance is a standalone instance (not in a cluster) or is disconnected
from the cluster. This also means that if a standalone instance
+is migrated to become a cluster, then that state will no longer be available,
as the component will begin using the Clustered State Provider
+instead of the Local State Provider.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="embedded_zookeeper"><a class="anchor"
href="#embedded_zookeeper"></a>Embedded ZooKeeper Server</h3>
+<div class="paragraph">
+<p>As mentioned above, the default State Provider for cluster-wide state is
the <code>ZooKeeperStateProvider</code>. At the time of this writing, this is
the
+only State Provider that exists for handling cluster-wide state. What this
means is that NiFi has dependencies on ZooKeeper in order to
+behave as a cluster. However, there are many environments in which NiFi is
deployed where there is no existing ZooKeeper ensemble being maintained.
+In order to avoid the burden of forcing administrators to also maintain a
separate ZooKeeper instance, NiFi provides the option of starting an
+embedded ZooKeeper server.</p>
+</div>
+<div class="paragraph">
+<p>This can be accomplished by setting the
<code>nifi.state.management.embedded.zookeeper.start</code> property in
<em>nifi.properties</em> to <code>true</code> on those nodes
+that should run the embedded ZooKeeper server. Generally, it is advisable to
run ZooKeeper on either 3 or 5 nodes. Running on fewer than 3 nodes
+provides less durability in the face of failure. Running on more than 5 nodes
generally produces more network traffic than is necessary. Additionally,
+running ZooKeeper on 4 nodes provides no more benefit than running on 3 nodes,
ZooKeeper requires a majority of nodes be active in order to function.
+However, it is up to the administrator to determine the number of nodes most
appropriate to the particular deployment of NiFi.</p>
+</div>
+<div class="paragraph">
+<p>If the <code>nifi.state.management.embedded.zookeeper.start</code> property
is set to <code>true</code>, the
<code>nifi.state.management.embedded.zookeeper.properties</code> property
+in <em>nifi.properties</em> also becomes relevant. This specifies the
ZooKeeper properties file to use. At a minimum, this properties file needs to
be populated
+with the list of ZooKeeper servers. The servers are specified as properties in
the form of <code>server.1</code>, <code>server.2</code>, to
<code>server.n</code>. Each of these servers is
+configured as <hostname>:<quorum port>[:<leader election
port>]. For example, <code>myhost:2888:3888</code>. This list of nodes
should be the same nodes in the NiFi
+cluster that have the
<code>nifi.state.management.embedded.zookeeper.start</code> property set to
<code>true</code>. Also note that because ZooKeeper will be listening on these
+ports, the firewall may need to be configured to open these ports for incoming
traffic, at least between nodes in the cluster. Additionally, the port to
+listen on for client connections must be opened in the firewall. The default
value for this is <em>2181</em> but can be configured via the
<em>clientPort</em> property
+in the <em>zookeeper.properties</em> file.</p>
+</div>
+<div class="paragraph">
+<p>When using an embedded ZooKeeper, the <em>conf/zookeeper.properties</em>
file has a property named <code>dataDir</code>. By default, this value is set
to <code>./state/zookeeper</code>.
+If more than one NiFi node is running an embedded ZooKeeper, it is important
to tell the server which one it is. This is accomplished by creating a file
named
+<em>myid</em> and placing it in ZooKeeper’s data directory. The contents
of this file should be the index of the server as specific by the
<code>server.<number></code>. So for
+one of the ZooKeeper servers, we will accomplish this by performing the
following commands:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>cd $NIFI_HOME
+mkdir state
+mkdir state/zookeeper
+echo 1 > state/zookeeper/myid</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For the next NiFi Node that will run ZooKeeper, we can accomplish this by
performing the following commands:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>cd $NIFI_HOME
+mkdir state
+mkdir state/zookeeper
+echo 2 > state/zookeeper/myid</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>And so on.</p>
+</div>
+<div class="paragraph">
+<p>For more information on the properties used to administer ZooKeeper, see the
+<a
href="https://zookeeper.apache.org/doc/current/zookeeperAdmin.html">ZooKeeper
Admin Guide</a>.</p>
+</div>
+<div class="paragraph">
+<p>For information on securing the embedded ZooKeeper Server, see the <a
href="#securing_zookeeper">Securing ZooKeeper</a> section below.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="zk_access_control"><a class="anchor"
href="#zk_access_control"></a>ZooKeeper Access Control</h3>
+<div class="paragraph">
+<p>ZooKeeper provides Access Control to its data via an Access Control List
(ACL) mechanism. When data is written to ZooKeeper, NiFi will provide an ACL
+that indicates that any user is allowed to have full permissions to the data,
or an ACL that indicates that only the user that created the data is
+allowed to access the data. Which ACL is used depends on the value of the
<code>Access Control</code> property for the
<code>ZooKeeperStateProvider</code> (see the
+<a href="#state_providers">Configuring State Providers</a> section for more
information).</p>
+</div>
+<div class="paragraph">
+<p>In order to use an ACL that indicates that only the Creator is allowed to
access the data, we need to tell ZooKeeper who the Creator is. There are two
+mechanisms for accomplishing this. The first mechanism is to provide
authentication using Kerberos. See <a href="#zk_kerberos_client">Kerberizing
NiFi’s ZooKeeper Client</a> for more information.</p>
+</div>
+<div class="paragraph">
+<p>The second option is to use a user name and password. This is configured by
specifying a value for the <code>Username</code> and a value for the
<code>Password</code> properties
+for the <code>ZooKeeperStateProvider</code> (see the <a
href="#state_providers">Configuring State Providers</a> section for more
information). The important thing to keep in mind here, though, is that
ZooKeeper
+will pass around the password in plain text. This means that using a user name
and password should not be used unless ZooKeeper is running on localhost as a
+one-instance cluster, or if communications with ZooKeeper occur only over
encrypted communications, such as a VPN or an SSL connection. ZooKeeper will be
+providing support for SSL connections in version 3.5.0.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="securing_zookeeper"><a class="anchor"
href="#securing_zookeeper"></a>Securing ZooKeeper</h3>
+<div class="paragraph">
+<p>When NiFi communicates with ZooKeeper, all communications, by default, are
non-secure, and anyone who logs into ZooKeeper is able to view and manipulate
all
+of the NiFi state that is stored in ZooKeeper. To prevent this, we can use
Kerberos to manage the authentication. At this time, ZooKeeper does not provide
+support for encryption via SSL. Support for SSL in ZooKeeper is being actively
developed and is expected to be available in the 3.5.x release version.</p>
+</div>
+<div class="paragraph">
+<p>In order to secure the communications, we need to ensure that both the
client and the server support the same configuration. Instructions for
configuring the
+NiFi ZooKeeper client and embedded ZooKeeper server to use Kerberos are
provided below.</p>
+</div>
+<div class="sect3">
+<h4 id="zk_kerberos_client"><a class="anchor"
href="#zk_kerberos_client"></a>Kerberizing NiFi’s ZooKeeper Client</h4>
+<div class="paragraph">
+<p>The preferred mechanism for authenticating users with ZooKeeper is to use
Kerberos. In order to use Kerberos to authenticate, we must configure a few
+system properties, so that the ZooKeeper client knows who the user is and
where the KeyTab file is. All nodes configured to store cluster-wide state
+using <code>ZooKeeperStateProvider</code> and using Kerberos should follow
these steps.</p>
+</div>
+<div class="paragraph">
+<p>First, we must create the Principal that we will use when communicating
with ZooKeeper. This is generally done via the <code>kadmin</code> tool:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kadmin: addprinc "[email protected]"</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>A Kerberos Principal is made up of three parts: the primary, the instance,
and the realm. Here, we are creating a Principal with the primary
<code>nifi</code>,
+no instance, and the realm <code>EXAMPLE.COM</code>. The primary
(<code>nifi</code>, in this case) is the identifier that will be used to
identify the user when authenticating
+via Kerberos.</p>
+</div>
+<div class="paragraph">
+<p>After we have created our Principal, we will need to create a KeyTab for
the Principal:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kadmin: xst -k nifi.keytab
[email protected]</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This will create a file in the current directory named
<code>nifi.keytab</code>. We can now copy that file into the
<em>$NIFI_HOME/conf/</em> directory. We should ensure
+that only the user that will be running NiFi is allowed to read this file.</p>
+</div>
+<div class="paragraph">
+<p>Next, we need to configure NiFi to use this KeyTab for authentication.
Since ZooKeeper uses the Java Authentication and Authorization Service (JAAS),
we need to
+create a JAAS-compatible file. In the <code>$NIFI_HOME/conf/</code> directory,
create a file named <code>zookeeper-jaas.conf</code> and add to it the
following snippet:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>Client {
+ com.sun.security.auth.module.Krb5LoginModule required
+ useKeyTab=true
+ keyTab="./conf/nifi.keytab"
+ storeKey=true
+ useTicketCache=false
+ principal="[email protected]";
+};</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Finally, we need to tell NiFi to use this as our JAAS configuration. This
is done by setting a JVM System Property, so we will edit the
<em>conf/bootstrap.conf</em> file.
+We add the following line anywhere in this file in order to tell the NiFi JVM
to use this configuration:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre
class="highlight"><code>java.arg.15=-Djava.security.auth.login.config=./conf/zookeeper-jaas.conf</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>We can initialize our Kerberos ticket by running the following command:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kinit nifi</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Note, the above <code>kinit</code> command requires that Kerberos client
libraries be installed. This is accomplished in Fedora-based Linux
distributions via:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>yum install krb5-workstation krb5-libs
krb5-auth-dialog</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Once this is complete, the /etc/krb5.conf will need to be configured
appropriately for your organization’s Kerberos envrionment.</p>
+</div>
+<div class="paragraph">
+<p>Now, when we start NiFi, it will use Kerberos to authentication as the
<code>nifi</code> user when communicating with ZooKeeper.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="zk_kerberos_server"><a class="anchor"
href="#zk_kerberos_server"></a>Kerberizing Embedded ZooKeeper Server</h4>
+<div class="paragraph">
+<p>When using the embedded ZooKeeper server, we may choose to secure the
server by using Kerberos. All nodes configured to launch an embedded ZooKeeper
+and using Kerberos should follow these steps.</p>
+</div>
+<div class="paragraph">
+<p>If Kerberos is not already setup in your environment, you can find
information on installing and setting up a Kerberos Server at
+<a
href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Managing_Smart_Cards/Configuring_a_Kerberos_5_Server.html">https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Managing_Smart_Cards/Configuring_a_Kerberos_5_Server.html</a>
+. This guide assumes that Kerberos already has been installed in the
environment in which NiFi is running.</p>
+</div>
+<div class="paragraph">
+<p>In order to use Kerberos, we first need to generate a Kerberos Principal
for our ZooKeeper server. This is accomplished via the <code>kadmin</code>
tool:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kadmin: addprinc
"zookeeper/[email protected]"</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here, we are creating a Principal with the primary
<code>zookeeper/myHost.example.com</code>, using the realm
<code>EXAMPLE.COM</code>. We need to use a Principal whose
+name is <code><service name>/<instance name></code>. In this case,
the service is <code>zookeeper</code> and the instance name is
<code>myHost.example.com</code> (the fully qualified name of our host).</p>
+</div>
+<div class="paragraph">
+<p>Next, we will need to create a KeyTab for this Principal:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kadmin: xst -k zookeeper-server.keytab
zookeeper/[email protected]</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This will create a file in the current directory named
<code>zookeeper-server.keytab</code>. We can now copy that file into the
<code>$NIFI_HOME/conf/</code> directory. We should ensure
+that only the user that will be running NiFi is allowed to read this file.</p>
+</div>
+<div class="paragraph">
+<p>We will need to repeat the above steps for each of the instances of NiFi
that will be running the embedded ZooKeeper server, being sure to replace
<em>myHost.example.com</em> with
+<em>myHost2.example.com</em>, or whatever fully qualified hostname the
ZooKeeper server will be run on.</p>
+</div>
+<div class="paragraph">
+<p>Now that we have our KeyTab for each of the servers that will be running
NiFi, we will need to configure NiFi’s embedded ZooKeeper server to use
this configuration.
+ZooKeeper uses the Java Authentication and Authorization Service (JAAS), so we
need to create a JAAS-compatible file In the <code>$NIFI_HOME/conf/</code>
directory, create a file
+named <code>zookeeper-jaas.conf</code> (this file will already exist if the
Client has already been configured to authenticate via Kerberos. That’s
okay, just add to the file).
+We will add to this file, the following snippet:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>Server {
+ com.sun.security.auth.module.Krb5LoginModule required
+ useKeyTab=true
+ keyTab="./conf/zookeeper-server.keytab"
+ storeKey=true
+ useTicketCache=false
+ principal="zookeeper/[email protected]";
+};</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Be sure to replace the value of <em>principal</em> above with the
appropriate Principal, including the fully qualified domain name of the
server.</p>
+</div>
+<div class="paragraph">
+<p>Next, we need to tell NiFi to use this as our JAAS configuration. This is
done by setting a JVM System Property, so we will edit the
<code>conf/bootstrap.conf</code> file.
+If the Client has already been configured to use Kerberos, this is not
necessary, as it was done above. Otherwise, we will add the following line to
our <em>bootstrap.conf</em> file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre
class="highlight"><code>java.arg.15=-Djava.security.auth.login.config=./conf/zookeeper-jaas.conf</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>We will want to initialize our Kerberos ticket by running the following
command:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>kinit
"zookeeper/[email protected]"</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Again, be sure to replace the Principal with the appropriate value,
including your realm and your fully qualified hostname.</p>
+</div>
+<div class="paragraph">
+<p>Finally, we need to tell the Kerberos server to use the SASL Authentication
Provider. To do this, we edit the
<code>$NIFI_HOME/conf/zookeeper.properties</code> file and add the following
+lines:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre
class="highlight"><code>authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
+jaasLoginRenew=3600000
+requireClientAuthScheme=sasl</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The last line is optional but specifies that clients MUST use Kerberos to
communicate with our ZooKeeper instance.</p>
+</div>
+<div class="paragraph">
+<p>Now, we can start NiFi, and the embedded ZooKeeper server will use Kerberos
as the authentication mechanism.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="troubleshooting_kerberos"><a class="anchor"
href="#troubleshooting_kerberos"></a>Troubleshooting Kerberos Configuration</h4>
+<div class="paragraph">
+<p>When using Kerberos, it is import to use fully-qualified domain names and
not use <em>localhost</em>. Please ensure that the fully qualified hostname of
each server is used
+in the following locations:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><em>conf/zookeeper.properties</em> file should use FQDN for
<code>server.1</code>, <code>server.2</code>, …​,
<code>server.N</code> values.</p>
+</li>
+<li>
+<p>The <code>Connect String</code> property of the ZooKeeperStateProvider</p>
+</li>
+<li>
+<p>The /etc/hosts file should also resolve the FQDN to an IP address that is
<strong>not</strong> <em>127.0.0.1</em>.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Failure to do so, may result in errors similar to the following:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>2016-01-08 16:08:57,888 ERROR
[pool-26-thread-1-SendThread(localhost:2181)]
o.a.zookeeper.client.ZooKeeperSaslClient An error:
(java.security.PrivilegedActionException: javax.security.sasl.SaslException:
GSS initiate failed [Caused by GSSException: No valid credentials provided
(Mechanism level: Server not found in Kerberos database (7) -
LOOKING_UP_SERVER)]) occurred when evaluating Zookeeper Quorum Member's
received SASL token. Zookeeper Client will go to AUTH_FAILED state.</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If there are problems communicating or authenticating with Kerberos,
+<a
href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/Troubleshooting.html">this
Troubleshooting Guide</a> may be of value.</p>
+</div>
+<div class="paragraph">
+<p>One of the most important notes in the above Troubleshooting guide is the
mechanism for turning on Debug output for Kerberos.
+This is done by setting the <code>sun.security.krb5.debug</code> environment
variable.
+In NiFi, this is accomplished by adding the following line to the
_$NIFI_HOME/conf/bootstrap.conf` file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre
class="highlight"><code>java.arg.16=-Dsun.security.krb5.debug=true</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This will cause the debug output to be written to the NiFi Bootstrap log
file. By default, this is located at
<em>$NIFI_HOME/logs/nifi-bootstrap.log</em>.
+This output can be rather verbose but provides extremely valuable information
for troubleshooting Kerberos failures.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
<h2 id="bootstrap_properties"><a class="anchor"
href="#bootstrap_properties"></a>Bootstrap Properties</h2>
<div class="sectionbody">
<div class="paragraph">
@@ -1441,6 +2247,34 @@ take effect only after NiFi has been sto
</tr>
</tbody>
</table>
+<div class="paragraph">
+<p><strong>Java 7 PermGen Sizing</strong>
+The provided <em>bootstrap.conf</em> file may include a line such as</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>#java.arg.11=-XX:PermSize=128M
+#java.arg.12=-XX:MaxPermSize=128M</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If running in Java 7 it is recommended to uncomment those lines to ensure
the PermGen size and maximum can be larger than is available by default. This
is important because NiFi
+can load a significant number of classes which will result in OutOfMemoryError
due to PermGen being full. You might choose a value larger than 128MB as
well.</p>
+</div>
+<div class="paragraph">
+<p><strong>Java 7 and 8 handling of codecache</strong>
+It has been observed in both Java 7 and Java 8 runtime environments that
performance can suddenly drop by more than an order of magnitude after days or
weeks of otherwise ideal
+behavior. This has only been observed under extremely high load and in cases
where considerable Just in Time (JIT) compilation occurs. The core problem is
the CodeCache becomes
+full and is seemingly not properly garbage collected or grown. When this
occurs JIT seems to no longer occur or involve considerable delays and
performance drops.
+This is easily overcome by ensuring the following lines are available in the
<em>boostrap.conf</em>. By default they are there but commented. Uncomment
them for maximum sustained throughput.</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>#java.arg.7=-XX:ReservedCodeCacheSize=256m
+#java.arg.8=-XX:CodeCacheFlushingMinimumFreeSpace=10m
+#java.arg.9=-XX:+UseCodeCacheFlushing</pre>
+</div>
+</div>
</div>
</div>
<div class="sect1">
@@ -1690,6 +2524,45 @@ only consider if <code>nifi.security.use
</tbody>
</table>
<div class="paragraph">
+<p><strong>State Management</strong><br></p>
+</div>
+<div class="paragraph">
+<p>The State Management section of the Properties file provides a mechanism
for configuring local and cluster-wide mechanisms
+for components to persist state. See the <a href="#state_management">State
Management</a> section for more information on how this is used.</p>
+</div>
+<table class="tableblock frame-all grid-all spread">
+<colgroup>
+<col style="width: 50%;">
+<col style="width: 50%;">
+</colgroup>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><strong>Property</strong></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><strong>Description</strong></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.state.management.configuration.file</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The XML
file that contains configuration for the local and cluster-wide State
Providers. The default value is <em>./conf/state-management.xml</em></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.state.management.provider.local</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The ID of
the Local State Provider to use. This value must match the value of the
<code>id</code> element of one of the <code>local-provider</code> elements in
the <em>state-management.xml</em> file.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.state.management.provider.cluster</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The ID of
the Cluster State Provider to use. This value must match the value of the
<code>id</code> element of one of the <code>cluster-provider</code> elements in
the <em>state-management.xml</em> file. This value is ignored if not clustered
but is required for nodes in a cluster.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.state.management.embedded.zookeeper.start</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies
whether or not this instance of NiFi should start an embedded ZooKeeper Server.
This is used in conjunction with the ZooKeeperStateProvider.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.state.management.embedded.zookeeper.properties</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies
a properties file that contains the configuration for the embedded ZooKeeper
Server that is started (if the `</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
<p><strong>H2 Settings</strong><br></p>
</div>
<div class="paragraph">
@@ -1999,6 +2872,13 @@ Providing three total locations, includi
<p>The Component Status Repository contains the information for the Component
Status History tool in the User Interface. These
properties govern how that tool works.</p>
</div>
+<div class="paragraph">
+<p>The buffer.size and snapshot.frequency work together to determine the
amount of historical data to retain. As an example to
+configure two days worth of historical data with a data point snapshot
occurring every 5 minutes you would configure
+snapshot.frequency to be "5 mins" and the buffer.size to be "576". To further
explain this example for every 60 minutes there
+are 12 (60 / 5) snapshot windows for that time period. To keep that data for
48 hours (12 * 48) you end up with a buffer size
+of 576.</p>
+</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
@@ -2015,11 +2895,11 @@ properties govern how that tool works.</
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.components.status.repository.buffer.size</p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies
the buffer size for the Component Status Repository. The default value is
288.</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies
the buffer size for the Component Status Repository. The default value is
1440.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.components.status.snapshot.frequency</p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">This value
indicates how often to present a snapshot of the components' status history.
The default value is 5 mins.</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">This value
indicates how often to present a snapshot of the components' status history.
The default value is 1 min.</p></td>
</tr>
</tbody>
</table>
@@ -2118,11 +2998,11 @@ Security Configuration section of this A
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.sensitive.props.key</p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">This is
the password used to encrypt any sensitive property values that are configured
in processors. By default, it is blank, but the system administrator should
provide a value for it. It can be a string of any length. Be aware that once
this password is set and one or more sensitive processor properties have been
configured, this password should not be changed.</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">This is
the password used to encrypt any sensitive property values that are configured
in processors. By default, it is blank, but the system administrator should
provide a value for it. It can be a string of any length, although the
recommended minimum length is 10 characters. Be aware that once this password
is set and one or more sensitive processor properties have been configured,
this password should not be changed.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.sensitive.props.algorithm</p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">The
algorithm used to encrypt sensitive properties. The default value is
PBEWITHMD5AND256BITAES-CBC-OPENSSL.</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The
algorithm used to encrypt sensitive properties. The default value is
<code>PBEWITHMD5AND256BITAES-CBC-OPENSSL</code>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p
class="tableblock">nifi.sensitive.props.provider</p></td>
@@ -2410,7 +3290,7 @@ If multicast is used, the following nifi
</div>
<div id="footer">
<div id="footer-text">
-Last updated 2015-12-08 13:53:16 -05:00
+Last updated 2016-02-16 10:19:45 EST
</div>
</div>
</body>
Modified: nifi/site/trunk/docs/nifi-docs/html/developer-guide.html
URL:
http://svn.apache.org/viewvc/nifi/site/trunk/docs/nifi-docs/html/developer-guide.html?rev=1730775&r1=1730774&r2=1730775&view=diff
==============================================================================
--- nifi/site/trunk/docs/nifi-docs/html/developer-guide.html (original)
+++ nifi/site/trunk/docs/nifi-docs/html/developer-guide.html Wed Feb 17
02:15:29 2016
@@ -462,6 +462,7 @@ body.book #toc,body.book #preamble,body.
<li><a href="#supporting_api">Supporting API</a></li>
<li><a href="#AbstractProcessor">AbstractProcessor API</a></li>
<li><a href="#component-lifecycle">Component Lifecycle</a></li>
+<li><a href="#state_manager">State Manager</a></li>
<li><a href="#reporting-processor-activity">Reporting Processor
Activity</a></li>
</ul>
</li>
@@ -800,11 +801,13 @@ the <code>build</code> method.</p>
<div class="sect3">
<h4 id="validator"><a class="anchor" href="#validator"></a>Validator</h4>
<div class="paragraph">
-<p>A PropertyDescriptor may specify one or more Validators that can be
+<p>A PropertyDescriptor MUST specify one or more Validators that can be
used to ensure that the user-entered value
for a property is valid. If a Validator indicates that a property
value is invalid, the Component will not be
-able to be run or used until the property becomes valid.</p>
+able to be run or used until the property becomes valid. If a
+Validator is not specified, the Component will be assumed invalid and
+NiFi will report that the property is not supported.</p>
</div>
</div>
<div class="sect3">
@@ -839,6 +842,16 @@ to fill in the details of the Relationsh
</div>
</div>
<div class="sect3">
+<h4 id="supporting_api_state_manager"><a class="anchor"
href="#supporting_api_state_manager"></a>StateManager</h4>
+<div class="paragraph">
+<p>The StateManager provides Processors, Reporting Tasks, and Controller
Services a mechanism
+for easily storing and retrieving state. The API is similar to that of
ConcurrentHashMap
+but requires a Scope for each operation. The Scope indicates whether the state
is to be
+retrieved/stored locally or in a cluster-wide manner. For more information,
see the
+<a href="#state_manager">State Manager</a> section.</p>
+</div>
+</div>
+<div class="sect3">
<h4 id="processor_initialization_context"><a class="anchor"
href="#processor_initialization_context"></a>ProcessorInitializationContext</h4>
<div class="paragraph">
<p>After a Processor is created, its <code>initialize</code> method will be
called
@@ -1219,6 +1232,74 @@ relied upon for critical business logic.
</div>
</div>
<div class="sect2">
+<h3 id="state_manager"><a class="anchor" href="#state_manager"></a>State
Manager</h3>
+<div class="paragraph">
+<p>From the ProcessContext, ReportingContext, and
ControllerServiceInitializationContext, components are
+able to call the <code>getStateManager()</code> method. This State Manager is
responsible for providing a simple API
+for storing and retrieving state. As such, the API is designed to be quite
similar to the ConcurrentMap
+API, which most Java developers are already familiar with.</p>
+</div>
+<div class="sect3">
+<h4 id="state_scope"><a class="anchor" href="#state_scope"></a>Scope</h4>
+<div class="paragraph">
+<p>One very notable difference between the StateManager API and the
ConcurrentMap API, however, is the presence
+of a Scope object on each method call of the StateManager. This Scope will
either be <code>Scope.NODE</code> or <code>Scope.CLUSTER</code>.
+If NiFi is run in a cluster, this Scope provides important information to the
framework about how the operation should
+occur.</p>
+</div>
+<div class="paragraph">
+<p>If state as stored using <code>Scope.CLUSTER</code>, then all nodes in the
cluster will be communicating with the same
+state storage mechanism, as if all nodes were to share a single ConcurrentMap.
If state is stored and retrieved using
+<code>Scope.NODE</code>, then each node will see a different representation of
the state.</p>
+</div>
+<div class="paragraph">
+<p>It is also worth noting that if NiFi is configured to run as a standalone
instance, rather than running in a cluster,
+a scope of <code>Scope.NODE</code> is always used. This is done in order to
allow the developer of a NiFi component to write the code
+in one consistent way, without worrying about whether or not the NiFi instance
is clustered. The developer should instead assume
+that the instance is clustered and write the code accordingly.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="storing-and-retrieving-state"><a class="anchor"
href="#storing-and-retrieving-state"></a>Storing and Retrieving State</h4>
+<div class="paragraph">
+<p>State is stored using the StateManager’s <code>set</code>,
<code>replace</code>, <code>putIfAbsent</code>, <code>remove</code>, and
<code>clear</code> methods. All of these methods,
+with the exception of <code>clear</code> take as the first argument the key to
be set. The key that is used is unique only to the same
+instance of the component and for the same Scope. That is, if two Processors
store a value using the key <em>My Key</em>, those Processors
+will not conflict with each other, even if both Processors are of the same
type (e.g., both are of type ListFile) and scope. Furthermore,
+if a Processor stores a value with the key of <em>My Key</em> using the
<code>Scope.CLUSTER</code> scope, and then attempts to retrieve the value
+using the <code>Scope.NODE</code> scope, the value retrieved will be
<code>null</code>. Each Processor’s state, then, is stored in isolation
from other
+Processors' state. A unique key can be thought of as a triple of <Processor
Instance, Key, Scope>.</p>
+</div>
+<div class="paragraph">
+<p>It follows, then, that two Processors cannot share the same state. There
are, however, some circumstances in which it is very
+necessary to share state between two Processors of different types, or two
Processors of the same type. This can be accomplished
+by using a Controller Service. By storing and retrieving state from a
Controller Service, multiple Processors can use the same
+Controller Service and the state can be exposed via the Controller
Service’s API.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="unit-tests"><a class="anchor" href="#unit-tests"></a>Unit Tests</h4>
+<div class="paragraph">
+<p>NiFi’s Mock Framework provides an extensive collection of tools to
perform unit testing of Processors. Processor unit tests typically
+begin with the <code>TestRunner</code> class. As a result, the
<code>TestRunner</code> class contains a <code>getStateManager</code> method of
its own. The StateManager
+that is returned, however, is of a specific type:
<code>MockStateManager</code>. This implementation provides several methods in
addition to those
+defined by the <code>StateManager</code> interface, that help developers to
more easily develop unit tests.</p>
+</div>
+<div class="paragraph">
+<p>First, the <code>MockStateManager</code> implements the
<code>StateManager</code> interface, so all of the state can be examined from
within a unit test.
+Additionally, the <code>MockStateManager</code> exposes a handful of
<code>assert*</code> methods to perform assertions that the State is set as
expected.
+There are times, however, that state could be updated multiple times during
the run of a single invocation of a Processor’s <code>onTrigger</code>
+method. In this case, inspecting the values after running the Processor may
not be sufficient. Additionally, we must always remember at each
+step to check the value of the stored state, which can become error-prone and
burdensome for the developer.</p>
+</div>
+<div class="paragraph">
+<p>For these reasons, the <code>MockStateManager</code> provides an additional
method, named <code>failIfStateSet</code>. This method instructs the State
Manager that
+the unit test should immediately fail if the state for a given key is ever
set, or if it is set to a specific value. The <code>doNotFailIfStateSet</code>
+method can then be used to instruct the Mock Framework to clear this state and
allow state to be set to any value.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
<h3 id="reporting-processor-activity"><a class="anchor"
href="#reporting-processor-activity"></a>Reporting Processor Activity</h3>
<div class="paragraph">
<p>Processors are responsible for reporting their activity so that users
@@ -1396,7 +1477,7 @@ public final class InvokeHTTP extends Ab
<div class="sect2">
<h3 id="documenting-related-components"><a class="anchor"
href="#documenting-related-components"></a>Documenting Related Components</h3>
<div class="paragraph">
-<p>Often Processors and ControllerServices are related to one another.
Sometimes its a put/get relation as in <code>PutFile</code> and
<code>GetFile</code>.
+<p>Often Processors and ControllerServices are related to one another.
Sometimes it is a put/get relation as in <code>PutFile</code> and
<code>GetFile</code>.
Sometimes a Processor uses a ControllerService like <code>InvokeHTTP</code>
and <code>StandardSSLContextService</code>. Sometimes one ControllerService
uses another
like <code>DistributedMapCacheClientService</code> and
<code>DistributedMapCacheServer</code>. Developers of these extension points
may relate these
different components using the <code>SeeAlso</code> tag. This annotation
links these components in the documentation.
@@ -2557,7 +2638,7 @@ that references a Controller Service as
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">public
static final PropertyDescriptor SSL_CONTEXT_SERVICE = new
PropertyDescriptor.Builder()
.name("SSL Context Service")
- .details("Specified the SSL Context Service that can be used to create
secure connections")
+ .description("Specified the SSL Context Service that can be used to create
secure connections")
.required(true)
.identifiesControllerService(SSLContextService.class)
.build();</code></pre>
@@ -3102,7 +3183,7 @@ worry about bothering us. Just ping the
</div>
<div id="footer">
<div id="footer-text">
-Last updated 2015-12-08 13:53:16 -05:00
+Last updated 2016-02-16 10:19:45 EST
</div>
</div>
</body>