Adding Custom Plugins in SolrCloud Mode
In SolrCloud mode, custom plugins need to be shared across all nodes of the cluster.
When running Solr in SolrCloud mode and you want to use custom code (such as custom analyzers, tokenizers, query parsers, and other plugins), it can be cumbersome to add jars to the classpath on all nodes in your cluster. Using the Blob Store API and special commands with the Config API, you can upload jars to a special system-level collection and dynamically load plugins from them at runtime without needing to restart any nodes.
This Feature is Disabled By Default
In addition to requiring that Solr is running in SolrCloud mode, this feature is also disabled by default unless all Solr nodes are run with the Before enabling this feature, users should carefully consider the issues discussed in the Securing Runtime Libraries section below. |
Uploading Jar Files
Use your own service to host the jars or you can use Solr itself to host the jars.
Use the Blob Store API to upload your jar files to Solr. This will to put your jars in the .system
collection and distribute them across your SolrCloud nodes. These jars are added to a separate classloader and only accessible to components that are configured with the property runtimeLib=true
. These components are loaded lazily because the .system
collection may not be loaded when a particular core is loaded.
Config API Commands to use Jars as Runtime Libraries
The runtime library feature uses a special set of commands for the Config API to add, update, or remove jar files currently available in the blob store to the list of runtime libraries.
The following commands are used to manage runtime libs:
add-runtimelib
update-runtimelib
delete-runtimelib
V1 API
curl http://localhost:8983/solr/techproducts/config -H 'Content-type:application/json' -d '{
"add-runtimelib": { "name":"jarblobname", "version":2 },
"update-runtimelib": { "name":"jarblobname", "version":3 },
"delete-runtimelib": "jarblobname"
}'
V2 API
curl http://localhost:8983/api/collections/techproducts/config -H 'Content-type:application/json' -d '{
"add-runtimelib": { "name":"jarblobname", "version":2 },
"update-runtimelib": { "name":"jarblobname", "version":3 },
"delete-runtimelib": "jarblobname"
}'
The name to use is the name of the blob that you specified when you uploaded your jar to the blob store. You should also include the version of the jar found in the blob store that you want to use. These details are added to configoverlay.json
.
The default SolrResourceLoader
does not have visibility to the jars that have been defined as runtime libraries. There is a classloader that can access these jars which is made available only to those components which are specially annotated.
Every pluggable component can have an optional extra attribute called runtimeLib=true
, which means that the components are not loaded at core load time. Instead, they will be loaded on demand. If all the dependent jars are not available when the component is loaded, an error is thrown.
This example shows creating a ValueSourceParser using a jar that has been loaded to the Blob store.
V1 API
curl http://localhost:8983/solr/techproducts/config -H 'Content-type:application/json' -d '{
"create-valuesourceparser": {
"name": "nvl",
"runtimeLib": true,
"class": "solr.org.apache.solr.search.function.NvlValueSourceParser,
"nvlFloatValue": 0.0 }
}'
V2 API
curl http://localhost:8983/api/collections/techproducts/config -H 'Content-type:application/json' -d '{
"create-valuesourceparser": {
"name": "nvl",
"runtimeLib": true,
"class": "solr.org.apache.solr.search.function.NvlValueSourceParser,
"nvlFloatValue": 0.0 }
}'
Example: Using external service to host your jars
Hosting your jars externally is more convenient if you have a reliable server to host your jars. There is no need to create and manage a .system
collection.
Step 1: Download a jar from github to the current directory
curl -o runtimelibs.jar -LO https://github.com/apache/lucene-solr/blob/master/solr/core/src/test-files/runtimecode/runtimelibs.jar.bin?raw=true
Step 2: Get the sha512
hash of the jar
openssl dgst -sha512 runtimelibs.jar
Step 3 : Start solr with runtime lib enabled
bin/solr start -e cloud -a "-Denable.runtime.lib=true" -noprompt
Step 4: Run a local server. Skip this step if you have another place to host your jars. Ensure that the url is set appropriately
python -m SimpleHTTPServer 8000 &
Step 5: Add the jar to your collection gettingstarted
curl http://localhost:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d '{
"add-runtimelib": { "name" : "testjar",
"url":"http://localhost:8000/runtimelibs.jar" ,
"sha512" : "d01b51de67ae1680a84a813983b1de3b592fc32f1a22b662fc9057da5953abd1b72476388ba342cad21671cd0b805503c78ab9075ff2f3951fdf75fa16981420"}
}'
Step 6 : Create a new request handler '/test' for the collection 'gettingstarted' from the jar we just added
curl http://localhost:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d '{
"create-requesthandler": { "name" : "/test",
'class': 'org.apache.solr.core.RuntimeLibReqHandler', 'runtimeLib' : true}
}'
Step 7: Test your request handler
curl http://localhost:8983/solr/gettingstarted/test
output:
{
"responseHeader":{
"status":0,
"QTime":0},
"params":{},
"context":{
"webapp":"/solr",
"path":"/test",
"httpMethod":"GET"},
"class":"org.apache.solr.core.RuntimeLibReqHandler",
"loader":"org.apache.solr.core.MemClassLoader"}
Updating remote jars
Example:
- Host the new jar to a new url. eg: http://localhost:8000/runtimelibs_v2.jar
- get the
sha512
hash of the new jar - run the update-runtime lib command
curl http://localhost:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d '{
"update-runtimelib": { "name" : "testjar",
"url":"http://localhost:8000/runtimelibs_v2.jar" ,
"sha512" : "<replace-the-new-sha512-digest-here>"}
}'
Always upload your jar to a new url as the Solr cluster is still referring to the old jar. If the existing jar is modified it can cause errors as the hash may not match |
Securing Runtime Libraries
A drawback of this feature is that it could be used to load malicious executable code into the system. However, it is possible to restrict the system to load only trusted jars using PKI to verify that the executables loaded into the system are trustworthy.
The following steps will allow you enable security for this feature. The instructions assume you have started all your Solr nodes with the -Denable.runtime.lib=true
.
Step 1: Generate an RSA Private Key
The first step is to generate an RSA private key. The example below uses a 512-bit key, but you should use the strength appropriate to your needs.
$ openssl genrsa -out priv_key.pem 512
Step 2: Output the Public Key
The public portion of the key should be output in DER format so Java can read it.
$ openssl rsa -in priv_key.pem -pubout -outform DER -out pub_key.der
Step 3: Load the Key to ZooKeeper
The .der
files that are output from Step 2 should then be loaded to ZooKeeper under a node /keys/exe
so they are available throughout every node. You can load any number of public keys to that node and all are valid. If a key is removed from the directory, the signatures of that key will cease to be valid. So, before removing the a key, make sure to update your runtime library configurations with valid signatures with the update-runtimelib
command.
At the current time, you can only use the ZooKeeper zkCli.sh
(or zkCli.cmd
on Windows) script to issue these commands (the Solr version has the same name, but is not the same). If you have your own ZooKeeper ensemble running already, you can find the script in $ZK_INSTALL/bin/zkCli.sh
(or zkCli.cmd
if you are using Windows).
If you are running the embedded ZooKeeper that is included with Solr, you do not have this script already; in order to use it, you will need to download a copy of ZooKeeper v${org.apache.zookeeper.version} from http://zookeeper.apache.org/. Don’t worry about configuring the download, you’re just trying to get the command line utility script. When you start the script, you will connect to the embedded ZooKeeper. |
To load the keys, you will need to connect to ZooKeeper with zkCli.sh
, create the directories, and then create the key file, as in the following example.
# Connect to ZooKeeper
# Replace the server location below with the correct ZooKeeper connect string for your installation.
$ .bin/zkCli.sh -server localhost:9983
# After connection, you will interact with the ZK prompt.
# Create the directories
[zk: localhost:9983(CONNECTED) 5] create /keys
[zk: localhost:9983(CONNECTED) 5] create /keys/exe
# Now create the public key file in ZooKeeper
# The second path is the path to the .der file on your local machine
[zk: localhost:9983(CONNECTED) 5] create /keys/exe/pub_key.der /myLocal/pathTo/pub_key.der
After this, any attempt to load a jar will fail. All your jars must be signed with one of your private keys for Solr to trust it. The process to sign your jars and use the signature is outlined in Steps 4-6.
Step 4: Sign the jar File
Next you need to sign the sha1 digest of your jar file and get the base64 string.
$ openssl dgst -sha1 -sign priv_key.pem myjar.jar | openssl enc -base64
The output of this step will be a string that you will need to add the jar to your classpath in Step 6 below.
Step 5: Load the jar to the Blob Store
Load your jar to the Blob store, using the Blob Store API. This step does not require a signature; you will need the signature in Step 6 to add it to your classpath.
curl -X POST -H 'Content-Type: application/octet-stream' --data-binary @{filename}
http://localhost:8983/solr/.system/blob/{blobname}
The blob name that you give the jar file in this step will be used as the name in the next step.
Step 6: Add the jar to the Classpath
Finally, add the jar to the classpath using the Config API as detailed above. In this step, you will need to provide the signature of the jar that you got in Step 4.
V1 API
curl http://localhost:8983/solr/techproducts/config -H 'Content-type:application/json' -d '{
"add-runtimelib": {
"name":"blobname",
"version":2,
"sig":"mW1Gwtz2QazjfVdrLFHfbGwcr8xzFYgUOLu68LHqWRDvLG0uLcy1McQ+AzVmeZFBf1yLPDEHBWJb5KXr8bdbHN/
PYgUB1nsr9pk4EFyD9KfJ8TqeH/ijQ9waa/vjqyiKEI9U550EtSzruLVZ32wJ7smvV0fj2YYhrUaaPzOn9g0=" }
}'
V2 API
curl http://localhost:8983/api/collections/techproducts/config -H 'Content-type:application/json' -d '{
"add-runtimelib": {
"name":"blobname",
"version":2,
"sig":"mW1Gwtz2QazjfVdrLFHfbGwcr8xzFYgUOLu68LHqWRDvLG0uLcy1McQ+AzVmeZFBf1yLPDEHBWJb5KXr8bdbHN/
PYgUB1nsr9pk4EFyD9KfJ8TqeH/ijQ9waa/vjqyiKEI9U550EtSzruLVZ32wJ7smvV0fj2YYhrUaaPzOn9g0=" }
}'