OpenSearch is an open-source fork of Elasticsearch and Kibana developed by Amazon. It is a real-time, distributed, and scalable search engine that allows for full-text search as well as analytics. It is used to index and search through large amounts of data. It is commonly deployed alongside OpenSearch Dashboards (a fork of Kibana), a powerful data visualization dashboard for OpenSearch. OpenSearch Dashboards allows you to explore the OpenSearch log data, and build dashboards and queries to gain insight into your application.

This tutorial will show you how to install OpenSearch on a Debian 12 server. You will also learn how to secure your installation with custom TLS certificates and enable authentication on OpenSearch.

Prerequisites

  • A server running Debian 12 with a minimum of 4GB of RAM.

  • A non-root user with sudo privileges.

  • The uncomplicated Firewall(UFW) is enabled and running.

  • A Fully Qualified Domain Name (FQDN) like opensearch.example.com pointing to your server. This is beneficial if you want to serve OpenSearch Dashboards via SSL using a proxy server.

  • Everything is updated.

    $ sudo apt update && sudo apt upgrade
    
  • A few essential packages are required for the tutorial and Craft CMS to run. Some of these will already be on your server.

    $ sudo apt install curl wget nano software-properties-common dirmngr apt-transport-https ca-certificates lsb-release debian-archive-keyring gnupg2 ufw unzip -y
    

Step 1 – Configure Firewall

The first step before installing any packages is to configure the firewall to allow HTTP and HTTPS connections.

Check the status of the firewall.

$ sudo ufw status

You should see something like the following.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

Allow HTTP and HTTPs ports.

$ sudo ufw allow http
$ sudo ufw allow https

Check the status again to confirm.

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

Step 2 – Prepare System

Before installing OpenSearch, we need to prepare and optimize our system for it. There are several steps involved which include disabling any swap memory if you have it, setting the number of memory maps for your system, and setting up the system hostname.

First, check if you have swap memory enabled.

$ free -m

If you have swap enabled, then you should see a similar output.

               total        used        free      shared  buff/cache   available
Mem:            7945        1083        6492          34         636        6861
Swap:           4242           0        4242

Run the following commands to disable swap space permanently.

$ sudo swapoff -a
$ sudo sed -i '/ swap / s/^(.*)$/#1/g' /etc/fstab

Check for swap memory again and it should show the following.

               total        used        free      shared  buff/cache   available
Mem:            7945        1113        6449          37         651        6831
Swap:              0           0           0

OpenSearch uses a mapfs directory by default to store its indices. The default operating system limits on nmap counts are too low which can cause out of memory errors.

Check the existing value for the vm.max_map_count variable.

$ sysctl -n vm.max_map_count
65530

To increase the number of memory maps used by each process, run the following command.

$ sudo sysctl -w vm.max_map_count=262144

This will work only till the system is restarted. To make this value permanent, add the line vm.max_map_count=262144 in the /etc/sysctl.conf file using the following command.

$ echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

You can make the change permanent by either rebooting the system or running the following command.

$ sudo sysctl -p

The final step is setting up the hostname. Depending upon where you are hosting your server, some cloud companies allow you to set your hostname directly via their control panel. However, it is better to do it via the terminal.

Run the following command to set the hostname.

$ sudo hostnamectl set-hostname opensearch

Add the FQDN (Fully qualified domain name) to the /etc/hosts file. In here, we map the hostname opensearch to the domain name opensearch.example.com and point it to the server IP address. Replace in the command below with the IP address of your server.

$ echo "  opensearch.example.com  opensearch" | sudo tee -a /etc/hosts

Check the hostname and the FQDN using the following commands.

$ hostname
opensearch
$ hostname -f
opensearch.example.com

This finishes the process of optimizing our system to work with OpenSearch.

Step 3 – Install OpenSearch

Import the OpenSearch GPG key used for signing the installation package.

$  curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/opensearch-keyring

Create the OpenSearch APT repository file.

$ echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring] https://artifacts.opensearch.org/releases/bundle/opensearch/2.x/apt stable main" | sudo tee /etc/apt/sources.list.d/opensearch-2.x.list

Update the system repository list.

$ sudo apt update

Check all the available versions of OpenSearch available.

$ sudo apt list -a opensearch
Listing... Done
opensearch/stable 2.12.0 amd64
opensearch/stable 2.11.1 amd64
opensearch/stable 2.11.0 amd64
opensearch/stable 2.10.0 amd64
opensearch/stable 2.9.0 amd64
opensearch/stable 2.8.0 amd64
opensearch/stable 2.7.0 amd64
opensearch/stable 2.6.0 amd64
opensearch/stable 2.5.0 amd64

The latest version of OpenSearch v2.12.0 requires you to set the admin demo password before the installation or it will fail.

You can install the latest version of OpenSearch using the following command. We can set the admin demo password using the environment variable in the installation command itself. Choose a strong password.

$ sudo env OPENSEARCH_INITIAL_ADMIN_PASSWORD= apt install opensearch

If you see the following warning during the installation, you can safely ignore it.

......
chown: warning: '.' should be ':': ‘opensearch.opensearch’
chown: warning: '.' should be ':': ‘opensearch.opensearch’
chown: warning: '.' should be ':': ‘opensearch.opensearch’
chown: warning: '.' should be ':': ‘opensearch.opensearch’
chown: warning: '.' should be ':': ‘opensearch.opensearch’
......

Or you can install a specific version.

$ sudo apt install opensearch=2.11.1

Reload the service daemon.

$ sudo systemctl daemon-reload

Enable the OpenSearch service.

$ sudo systemctl enable opensearch

Start the OpenSearch service.

$ sudo systemctl start opensearch

Verify the status of the OpenSearch service.

$ sudo systemctl status opensearch

You should get the following output.

? opensearch.service - OpenSearch
     Loaded: loaded (/lib/systemd/system/opensearch.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-03-02 06:37:01 UTC; 5s ago
       Docs: https://opensearch.org/
   Main PID: 31109 (java)
      Tasks: 75 (limit: 4652)
     Memory: 1.3G
        CPU: 1min 3.128s
     CGroup: /system.slice/opensearch.service
             ??31109 /usr/share/opensearch/jdk/bin/java -Xshare:auto -Dopensearch.networkaddress.cache.ttl=60 -Dopensearch.networkaddress.cache.negative.ttl=10 -XX: AlwaysPreTouch -Xss1m -Djava.awt.headless=true...

Step 4 – Test OpenSearch

Before proceeding any further, let us check if the installation is working fine or not. Run the following command to verify if OpenSearch is running as intended.

$ curl -X GET https://localhost:9200 -u 'admin:' --insecure

You should get a similar output.

{
  "name" : "opensearch",
  "cluster_name" : "opensearch",
  "cluster_uuid" : "3oFvYn96St2MOpDP3Sf29g",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.12.0",
    "build_type" : "deb",
    "build_hash" : "2c355ce1a427e4a528778d4054436b5c4b756221",
    "build_date" : "2024-02-20T02:18:31.541484890Z",
    "build_snapshot" : false,
    "lucene_version" : "9.9.2",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

Query the OpenSearch plugins endpoint to see the list of available plugins.

$ curl -X GET https://localhost:9200/_cat/plugins?v -u 'admin:' --insecure

You can also use the following command for the same.

$ /usr/share/opensearch/bin/opensearch-plugin list

You should get a similar output.

name       component                            version
opensearch opensearch-alerting                  2.12.0.0
opensearch opensearch-anomaly-detection         2.12.0.0
opensearch opensearch-asynchronous-search       2.12.0.0
opensearch opensearch-cross-cluster-replication 2.12.0.0
opensearch opensearch-custom-codecs             2.12.0.0
opensearch opensearch-flow-framework            2.12.0.0
opensearch opensearch-geospatial                2.12.0.0
opensearch opensearch-index-management          2.12.0.0
opensearch opensearch-job-scheduler             2.12.0.0
opensearch opensearch-knn                       2.12.0.0
opensearch opensearch-ml                        2.12.0.0
opensearch opensearch-neural-search             2.12.0.0
opensearch opensearch-notifications             2.12.0.0
opensearch opensearch-notifications-core        2.12.0.0
opensearch opensearch-observability             2.12.0.0
opensearch opensearch-performance-analyzer      2.12.0.0
opensearch opensearch-reports-scheduler         2.12.0.0
opensearch opensearch-security                  2.12.0.0
opensearch opensearch-security-analytics        2.12.0.0
opensearch opensearch-skills                    2.12.0.0
opensearch opensearch-sql                       2.12.0.0

You can install additional plugins using the following command.

$ /usr/share/opensearch/bin/opensearch-plugin install 

You can find the list of available OpenSearch plugins on their official website.

Step 5 – Configure OpenSearch

OpenSearch’s default settings are not fit for a production environment. By default, OpenSearch is not bound to a network interface and is unreachable from outside. Furthermore, default user names and passwords are being used which can cause security issues if used directly in a production environment.

OpenSearch stores its configuration in the /etc/opensearch/opensearch.yml file. Open it for editing.

$ sudo nano /etc/opensearch/opensearch.yml

Find the #network.host: 192.168.0.1 line and uncomment it by removing the hash (#) in front of it and changing its value as shown. This binds OpenSearch to all available interfaces. You can also choose to enter a specific IP to bind it to a specific interface.

# Set the bind address to a specific IP (IPv4 or IPv6):
#
network.host: 0.0.0.0

Add the following lines at the bottom of the file.

# Unless you have already configured a cluster, you should set
# discovery.type to single-node, or the bootstrap checks will
# fail when you try to start the service.
discovery.type: single-node

# If you previously disabled the Security plugin in opensearch.yml,
# be sure to re-enable it. Otherwise you can skip this setting.
plugins.security.disabled: false

Since we are installing on a single server, we have set discovery.type to single-node. Setting the plugins.security.disabled to false enables OpenSearch security plugin.

Save the file by pressing Ctrl X and entering Y when prompted.

We also need to specify initial and maximum JVM (Java Virtual Memory) heap sizes. This setting depends on your server’s RAM size. For example, we are using a server with 4GB of RAM so we are setting the heap size to 3GB.

Create and open the heapsize.options in the /etc/opensearch/jvm.options.d directory for editing. It will overwrite the default value set in the /etc/opensearch/jvm.options file.

$ sudo nano /etc/opensearch/jvm.options.d/heapsize.options

Add the following lines to it.

-Xms3g
-Xmx3g

Save the file by pressing Ctrl X and entering Y when prompted.

Restart the OpenSearch service to implement the changes.

$ sudo systemctl restart opensearch

You can check the current heap size and maximum memory using the following command.

$ curl -sS "https://localhost:9200/_cat/nodes?h=heap*&v" -u 'admin:' --insecure

You should get the following output.

heap.current heap.percent heap.max
     345.5mb           11      3gb

Step 6 – Configure TLS for OpenSearch

Here we will be generating certificates to secure OpenSearch. TLS certificates provide security by allowing clients to confirm the identity of hosts and encrypt traffic between a client and the host. We will generate the following certificates

  1. Root CA Certificates – These are the primary certificates that will sign all other certificates.
  2. Admin certificates – These certificates are used to gain elevated rights for performing administrative tasks related to the Security plugin.
  3. Node certificates – These certificates are used for communication between a node and the client.

All the OpenSearch certificates are stored in the /etc/opensearch directory. The first step is to remove the default certificates set during OpenSearch’s installation.

Switch to the OpenSearch directory.

$ cd /etc/opensearch

Remove the default certificates.

$ sudo rm -f *.pem

Open the OpenSearch configuration file for editing.

$ sudo nano opensearch.yml

Comment out the following code at the bottom of the file by putting a hash in front of each line inside the OpenSearch Security Demo Configuration section as shown below.

######## Start OpenSearch Security Demo Configuration ########
# WARNING: revise all the lines below before you go into production
#plugins.security.ssl.transport.pemcert_filepath: esnode.pem
#plugins.security.ssl.transport.pemkey_filepath: esnode-key.pem
#plugins.security.ssl.transport.pemtrustedcas_filepath: root-ca.pem
#plugins.security.ssl.transport.enforce_hostname_verification: false
#plugins.security.ssl.http.enabled: true
#plugins.security.ssl.http.pemcert_filepath: esnode.pem
#plugins.security.ssl.http.pemkey_filepath: esnode-key.pem
#plugins.security.ssl.http.pemtrustedcas_filepath: root-ca.pem
#plugins.security.allow_unsafe_democertificates: true
#plugins.security.allow_default_init_securityindex: true
#plugins.security.authcz.admin_dn: ['CN=kirk,OU=client,O=client,L=test,C=de']
#plugins.security.audit.type: internal_opensearch
#plugins.security.enable_snapshot_restore_privilege: true
#plugins.security.check_snapshot_restore_write_privileges: true
#plugins.security.restapi.roles_enabled: [all_access, security_rest_api_access]
#plugins.security.system_indices.enabled: true
#plugins.security.system_indices.indices: [.plugins-ml-config, .plugins-ml-connector,
#  .plugins-ml-model-group, .plugins-ml-model, .plugins-ml-task, .plugins-ml-conversation-meta,
#  .plugins-ml-conversation-interactions, .plugins-ml-memory-meta, .plugins-ml-memory-message,
#  .opendistro-alerting-config, .opendistro-alerting-alert*, .opendistro-anomaly-results*,
#  .opendistro-anomaly-detector*, .opendistro-anomaly-checkpoints, .opendistro-anomaly-detection-state,
#  .opendistro-reports-*, .opensearch-notifications-*, .opensearch-notebooks, .opensearch-observability,
#  .ql-datasources, .opendistro-asynchronous-search-response*, .replication-metadata-store,
#  .opensearch-knn-models, .geospatial-ip2geo-data*, .plugins-flow-framework-config,
#  .plugins-flow-framework-templates, .plugins-flow-framework-state]
#node.max_local_storage_nodes: 3
######## End OpenSearch Security Demo Configuration ########

Save the file by pressing Ctrl X and entering Y when prompted.

Next, create a directory to store certificates inside the /etc/opensearch directory.

$ sudo mkdir /etc/opensearch/certs

Change the permission of the directory to the currently logged-in user to avoid using sudo while generating the certificates.

$ sudo chown $USER:$USER /etc/opensearch/certs -R

Switch to the certificate directory.

$ cd /etc/opensearch/certs

Generate Root Certificates

Generate a private key for the root certificate.

$ openssl genrsa -out root-ca-key.pem 2048

Generate a self-signed root CA certificate. We are using the -subj parameter to provide information that is embedded in the certificate. If you don’t use the -subj flag, you will be asked for all this information.

$ openssl req -new -x509 -sha256 -key root-ca-key.pem -subj "https://www.howtoforge.com/C=CA/ST=ONTARIO/L=TORONTO/O=ORG/OU=UNIT/CN=ROOT" -out root-ca.pem -days 730

You can change the information to suit your specific host.

The variables in the -subj parameter mean the following.

C = Country Name (2 letter code), ST = State or Province Name (Full name), L = Locality Name (e.g, city), O = Organization Name, OU = Organizational Unit Name (e.g, section), CN = Common Name (e.g., server FQDN)

Your root certificate is now live.

Generate Admin Certificates

Generate the private key for the Admin certificate using the following command.

$ openssl genrsa -out admin-key-temp.pem 2048

Convert the private key to the PKCS#8 format for use in Java using a PKCS#12 compatible algorithm(3DES).

$ openssl pkcs8 -inform PEM -outform PEM -in admin-key-temp.pem -topk8 -nocrypt -v1 PBE-SHA1-3DES -out admin-key.pem

Generate the Admin CSR (Certificate Signing Request) from the private key. Since this certificate is not tied to any hosts and is used for authenticating elevated access, we are using a value of A for the Common Name (CN).

$ openssl req -new -key admin-key.pem -subj "https://www.howtoforge.com/C=CA/ST=ONTARIO/L=TORONTO/O=ORG/OU=UNIT/CN=A" -out admin.csr

Sign the Admin CSR with the root certificate and private key you just created.

$ openssl x509 -req -in admin.csr -CA root-ca.pem -CAkey root-ca-key.pem -CAcreateserial -sha256 -out admin.pem -days 730
Certificate request self-signature ok
subject=C = CA, ST = ONTARIO, L = TORONTO, O = ORG, OU = UNIT, CN = A

Generate Node Certificates

Generate a private key for the node certificate.

$ openssl genrsa -out node1-key-temp.pem 2048

Convert the node private key to the PKCS#8 format.

$ openssl pkcs8 -inform PEM -outform PEM -in node1-key-temp.pem -topk8 -nocrypt -v1 PBE-SHA1-3DES -out node1-key.pem

Create a new CSR for the Node certificate from the private key. Make sure to change the value of CN variable with the hostname of your node. This certificate is tied to your hosts and it should match with the hostname or the IP address of the node.

$ openssl req -new -key node1-key.pem -subj "https://www.howtoforge.com/C=CA/ST=ONTARIO/L=TORONTO/O=ORG/OU=UNIT/CN=opensearch.example.com" -out node1.csr

Create a SAN (Subject Alternative Name) extension file that contains the node hostname or FQDN or IP address.

$ sh -c 'echo subjectAltName=DNS:opensearch.example.com > node1.ext'

Sign the node certificate CSR with the root certificate and private key you just created.

$ openssl x509 -req -in node1.csr -CA root-ca.pem -CAkey root-ca-key.pem -CAcreateserial -sha256 -out node1.pem -days 730 -extfile node1.ext
Certificate request self-signature ok
subject=C = CA, ST = ONTARIO, L = TORONTO, O = ORG, OU = UNIT, CN = opensearch.example.com

Preparing Certificates for Installation

Remove any temporary certificate, CSR, and the SAN extension file.

$ rm -f *temp.pem *csr *ext

Next, we will install the root CA certificate on the server. For that, convert the root CA certificate to the .crt format.

$ openssl x509 -outform der -in root-ca.pem -out root-ca.crt

Copy the root-ca.crt file to the /usr/local/share/ca-certificates/ directory.

$ sudo cp root-ca.crt /usr/local/share/ca-certificates/

Add the root CA certificate to your Debian server using the following command.

$ sudo update-ca-certificates

You should see the following output ( 1 added) confirming that the root CA certificate is added to your system.

Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
rehash: warning: skipping root-ca.pem,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.

Change the ownership of the directory back to the opensearch user.

$ sudo chown opensearch:opensearch /etc/opensearch/certs -R

Change the permission on the directory to 0700.

$ sudo chmod 0700 /etc/opensearch/certs

Change the permission on the certificates to 0600.

$ sudo chmod 0600 /etc/opensearch/certs/{admin-key,admin,node1-key,node1,root-ca-key,root-ca}.pem
$ sudo chmod 0600 /etc/opensearch/certs/root-ca.crt

Step 7 – Install TLS to OpenSearch

Now that all the certificates are generated and set up, it is time to install and add the certificates to your OpenSearch installation.

The first step is to create a bash script that will add all the created certificates and TLS security plugin settings to the OpenSearch configuration file (/etc/opensearch/opensearch.yml).

Create a file named add-opensearch-tls.sh and open it for editing.

$ cd ~
$ nano add-opensearch-tls.sh

Paste the following code in it.

#! /bin/bash

# Before running this script, make sure to replace the CN in the 
# node's distinguished name with a real DNS A record.

echo "plugins.security.ssl.transport.pemcert_filepath: /etc/opensearch/certs/node1.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.transport.pemkey_filepath: /etc/opensearch/certs/node1-key.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.http.enabled: true" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.http.pemcert_filepath: /etc/opensearch/certs/node1.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.http.pemkey_filepath: /etc/opensearch/certs/node1-key.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.ssl.http.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.allow_default_init_securityindex: true" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.authcz.admin_dn:" | sudo tee -a /etc/opensearch/opensearch.yml
echo "  - 'CN=A,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.nodes_dn:" | sudo tee -a /etc/opensearch/opensearch.yml
echo "  - 'CN=opensearch.example.com,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.audit.type: internal_opensearch" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.enable_snapshot_restore_privilege: true" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.check_snapshot_restore_write_privileges: true" | sudo tee -a /etc/opensearch/opensearch.yml
echo "plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"]" | sudo tee -a /etc/opensearch/opensearch.yml

Save the file by pressing Ctrl X and entering Y when prompted.

Make the file executable.

$ chmod  x add-opensearch-tls.sh

Run the script.

$ ./add-opensearch-tls.sh

You should get the following output.

plugins.security.ssl.transport.pemcert_filepath: /etc/opensearch/certs/node1.pem
plugins.security.ssl.transport.pemkey_filepath: /etc/opensearch/certs/node1-key.pem
plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem
plugins.security.ssl.http.enabled: true
plugins.security.ssl.http.pemcert_filepath: /etc/opensearch/certs/node1.pem
plugins.security.ssl.http.pemkey_filepath: /etc/opensearch/certs/node1-key.pem
plugins.security.ssl.http.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem
plugins.security.allow_default_init_securityindex: true
plugins.security.authcz.admin_dn:
  - 'CN=A,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'
plugins.security.nodes_dn:
  - 'CN=opensearch.example.com,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'
plugins.security.audit.type: internal_opensearch
plugins.security.enable_snapshot_restore_privilege: true
plugins.security.check_snapshot_restore_write_privileges: true
plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"]

Open the file /etc/opensearch/opensearch.yml file to check whether your settings have been applied.

$ sudo nano /etc/opensearch/opensearch.yml

Scroll to the bottom of the file and you should see the following.

# If you previously disabled the Security plugin in opensearch.yml,
# be sure to re-enable it. Otherwise you can skip this setting.
plugins.security.disabled: false
....
plugins.security.ssl.transport.pemcert_filepath: /etc/opensearch/certs/node1.pem
plugins.security.ssl.transport.pemkey_filepath: /etc/opensearch/certs/node1-key.pem
plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem
plugins.security.ssl.http.enabled: true
plugins.security.ssl.http.pemcert_filepath: /etc/opensearch/certs/node1.pem
plugins.security.ssl.http.pemkey_filepath: /etc/opensearch/certs/node1-key.pem
plugins.security.ssl.http.pemtrustedcas_filepath: /etc/opensearch/certs/root-ca.pem
plugins.security.allow_default_init_securityindex: true
plugins.security.authcz.admin_dn:
  - 'CN=A,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'
plugins.security.nodes_dn:
  - 'CN=opensearch.example.com,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA'
plugins.security.audit.type: internal_opensearch
plugins.security.enable_snapshot_restore_privilege: true
plugins.security.check_snapshot_restore_write_privileges: true
plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"]

Once you are satisfied, save the file by pressing Ctrl X.

Step 8 – Creating OpenSearch User

In this step, we will remove all the demo users except for the admin user and replace the default admin password using a script.

Switch to the OpenSearch security plugins tools directory.

$ cd /usr/share/opensearch/plugins/opensearch-security/tools

Run the hash.sh script to generate a new password.

$ ./hash

The script will fail and you will get the following error because the path to the Java executable (JDK) has not been defined.

**************************************************************************
** This tool will be deprecated in the next major release of OpenSearch **
** https://github.com/opensearch-project/security/issues/1755           **
**************************************************************************
WARNING: nor OPENSEARCH_JAVA_HOME nor JAVA_HOME is set, will use 
./hash.sh: line 35: java: command not found

Run the command again by declaring the environment variable OPENSEARCH_JAVA_HOME with the location of the JDK.

$ OPENSEARCH_JAVA_HOME=/usr/share/opensearch/jdk ./hash.sh

You will get a similar output.

**************************************************************************
** This tool will be deprecated in the next major release of OpenSearch **
** https://github.com/opensearch-project/security/issues/1755           **
**************************************************************************
[Password:]
$2y$12$4NL0LqEnN4FXaUdQQC.gOupeCQT2IuMo9gN0b5d3nxdMVbLX088f2

It will generate the password hash which we will need later.

Open the OpenSearch User configuration file internal_users.yml in the /etc/opensearch/opensearch-security/ directory for editing.

$ sudo nano /etc/opensearch/opensearch-security/internal_users.yml

Comment out all the demo users by adding hash in front of them as shown.

---
# This is the internal user database
# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh

_meta:
  type: "internalusers"
  config_version: 2

# Define your internal users here

## Demo users

#admin:
#  hash: "$2y$12$Twy84uxOX0SQ1QMQ68og8O/i1MvqWItt/phZX3DiJMjpBoT9u9KX6"
#  reserved: true
#  backend_roles:
#  - "admin"
#  description: "Demo admin user"

#anomalyadmin:

Add the admin user with the hash you generated above as shown.

---
# This is the internal user database
# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh

_meta:
  type: "internalusers"
  config_version: 2

# Define your internal users here

admin:
  hash: "$2y$12$4NL0LqEnN4FXaUdQQC.gOupeCQT2IuMo9gN0b5d3nxdMVbLX088f2"
  reserved: true
  backend_roles:
  - "admin"
  description: "Admin user"

## Demo users

#admin:

Once finished, save the file by pressing Ctrl X and entering Y when prompted.

Now that the TLS certificates are installed and the demo users removed or assigned new passwords, it is time to apply the configuration changes. To apply the changes, we will invoke the securityadmin.sh script inside the /usr/share/opensearch/plugins/opensearch-security/tools directory. The script requires the OpenSearch service to run.

Restart the OpenSearch service.

$ sudo systemctl restart opensearch

Switch to the /usr/share/opensearch/plugins/opensearch-security/tools directory.

$ cd /usr/share/opensearch/plugins/opensearch-security/tools

Run the script using the following command.

$ sudo OPENSEARCH_JAVA_HOME=/usr/share/opensearch/jdk ./securityadmin.sh -h opensearch.nspeaks.com -p 9200 -cd /etc/opensearch/opensearch-security/ -cacert /etc/opensearch/certs/root-ca.pem -cert /etc/opensearch/certs/admin.pem -key /etc/opensearch/certs/admin-key.pem -icl -nhnv

The security plugin stores its configuration – including users, permissions, roles, and backend settings in a system index on the OpenSearch cluster. Storing these settings in the cluster allows you to change settings without restarting the cluster and eliminates the need to edit the configuration file on every node. This is accomplished by running the securityadmin.sh script.

  • The -cd option specifies where the security plugin configuration files are located.
  • The -icl (--ignore-clustername) option tells the security plugin to upload the configuration regardless of the cluster name. You can also specify the cluster name instead of using the -cn (--clustername) option.
  • Since the certificates are self-signed, we use the -nhnv (--disable-host-name-verification) option.
  • The --cacert, --cert, and --key options define the location of the root certificate, the admin certificate, and the private key for the admin certificate. If the private key has a password, you can define it using the -keypass option.

You can learn more about the security plugin configuration in the OpenSearch documentation.

You should have gotten a similar output as shown below with the above command.

**************************************************************************
** This tool will be deprecated in the next major release of OpenSearch **
** https://github.com/opensearch-project/security/issues/1755           **
**************************************************************************
Security Admin v7
Will connect to opensearch:9200 ... done
Connected as "CN=A,OU=UNIT,O=ORG,L=TORONTO,ST=ONTARIO,C=CA"
OpenSearch Version: 2.12.0
Contacting opensearch cluster 'opensearch' and wait for YELLOW clusterstate ...
Clustername: opensearch
Clusterstate: GREEN
Number of nodes: 1
Number of data nodes: 1
.opendistro_security index already exists, so we do not need to create one.
Populate config from /etc/opensearch/opensearch-security/
Will update '/config' with /etc/opensearch/opensearch-security/config.yml 
   SUCC: Configuration for 'config' created or updated
Will update '/roles' with /etc/opensearch/opensearch-security/roles.yml 
   SUCC: Configuration for 'roles' created or updated
Will update '/rolesmapping' with /etc/opensearch/opensearch-security/roles_mapping.yml 
   SUCC: Configuration for 'rolesmapping' created or updated
Will update '/internalusers' with /etc/opensearch/opensearch-security/internal_users.yml 
   SUCC: Configuration for 'internalusers' created or updated
Will update '/actiongroups' with /etc/opensearch/opensearch-security/action_groups.yml 
   SUCC: Configuration for 'actiongroups' created or updated
Will update '/tenants' with /etc/opensearch/opensearch-security/tenants.yml 
   SUCC: Configuration for 'tenants' created or updated
Will update '/nodesdn' with /etc/opensearch/opensearch-security/nodes_dn.yml 
   SUCC: Configuration for 'nodesdn' created or updated
Will update '/whitelist' with /etc/opensearch/opensearch-security/whitelist.yml 
   SUCC: Configuration for 'whitelist' created or updated
Will update '/audit' with /etc/opensearch/opensearch-security/audit.yml 
   SUCC: Configuration for 'audit' created or updated
Will update '/allowlist' with /etc/opensearch/opensearch-security/allowlist.yml 
   SUCC: Configuration for 'allowlist' created or updated
SUCC: Expected 10 config types for node {"updated_config_types":["allowlist","tenants","rolesmapping","nodesdn","audit","roles","whitelist","internalusers","actiongroups","config"],"updated_config_size":10,"message":null} is 10 (["allowlist","tenants","rolesmapping","nodesdn","audit","roles","whitelist","internalusers","actiongroups","config"]) due to: null
Done with success

Verify the new credentials using the following command.

During the previous test, we directed requests to localhost. Now that the TLS certificates are set up and the new certificates refer to the host’s actual DNS record, the requests to localhost will fail the CN check, and the certificate will be considered invalid. Therefore, add your actual DNS record in the command below to verify.

$ curl https://your.host.address:9200 -u admin:yournewpassword -k

You should see the following response.

{
  "name" : "opensearch",
  "cluster_name" : "opensearch",
  "cluster_uuid" : "3oFvYn96St2MOpDP3Sf29g",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.12.0",
    "build_type" : "deb",
    "build_hash" : "2c355ce1a427e4a528778d4054436b5c4b756221",
    "build_date" : "2024-02-20T02:18:31.541484890Z",
    "build_snapshot" : false,
    "lucene_version" : "9.9.2",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

Step 9 – Install OpenSearch Dashboards

Since we installed OpenSearch already, we can skip the step of importing the OpenSearch GPG key.

To install OpenSearch dashboards, run the following command to create an APT repository for it.

$ echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring] https://artifacts.opensearch.org/releases/bundle/opensearch-dashboards/2.x/apt stable main" | sudo tee /etc/apt/sources.list.d/opensearch-dashboards-2.x.list

Update the system repository list.

$ sudo apt update

List all the available versions of OpenSearch Dashboards.

$ sudo apt list -a opensearch-dashboards

You should see a similar output.

Listing... Done
opensearch-dashboards/stable 2.12.0 amd64
opensearch-dashboards/stable 2.11.1 amd64
opensearch-dashboards/stable 2.11.0 amd64
opensearch-dashboards/stable 2.10.0 amd64
opensearch-dashboards/stable 2.9.0 amd64
opensearch-dashboards/stable 2.8.0 amd64
opensearch-dashboards/stable 2.7.0 amd64
opensearch-dashboards/stable 2.6.0 amd64
opensearch-dashboards/stable 2.5.0 amd64

Install the latest version of OpenSearch Dashboard.

$ sudo apt install opensearch-dashboards

Or install a specific version of OpenSearch Dashboard.

$ sudo apt install opensearch-dashboards=2.12.0

The version of OpenSearch dashboards and OpenSearch should be the same otherwise you can face multiple issues.

Reload the service daemon.

$ sudo systemctl daemon-reload

Enable the OpenSearch Dashboards service.

$ sudo systemctl enable opensearch-dashboards

Start the OpenSearch Dashboards service.

$ sudo systemctl start opensearch-dashboards

Verify the status of the OpenSearch Dashboards service.

$ sudo systemctl status opensearch-dashboards

You should see the following output.

? opensearch-dashboards.service - "OpenSearch Dashboards"
     Loaded: loaded (/lib/systemd/system/opensearch-dashboards.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-03-02 16:01:25 UTC; 4s ago
   Main PID: 7098 (node)
      Tasks: 11 (limit: 9484)
     Memory: 175.4M
        CPU: 5.389s
     CGroup: /system.slice/opensearch-dashboards.service
             ??7098 /usr/share/opensearch-dashboards/node/bin/node /usr/share/opensearch-dashboards/src/cli/dist

Mar 02 16:01:25 opensearch systemd[1]: Started opensearch-dashboards.service - "OpenSearch Dashboards".

Step 10 – Configure OpenSearch Dashboards

OpenSearch Dashboards stores its configuration in the /etc/opensearch-dashboards/opensearch_dashboards.yml file. Open it for editing.

$ sudo nano /etc/opensearch-dashboards/opensearch_dashboards.yml

Find the line # server.host: "localhost" and uncomment it by removing the hash in front of it and changing its value as shown.

# Specifies the address to which the OpenSearch Dashboards server will bind. IP addresses and host names are both valid values.
# The default is 'localhost', which usually means remote machines will not be able to connect.
# To allow connections from remote users, set this parameter to a non-loopback address.
server.host: 0.0.0.0

Setting the value of server.host to 0.0.0.0 binds OpenSearch Dashboard to any external IP which makes it accessible over the World Wide Web. If you want to serve the Dashboards over SSL using a reverse proxy, then leave this value unchanged.

Scroll down to find the following section.

opensearch.hosts: [https://localhost:9200]
opensearch.ssl.verificationMode: none
opensearch.username: kibanaserver
opensearch.password: kibanaserver
opensearch.requestHeadersWhitelist: [authorization, securitytenant]

Change the values as configured for our OpenSearch installation.

opensearch.hosts: [https://opensearch:9200]
opensearch.ssl.verificationMode: none
opensearch.username: admin
opensearch.password: 
opensearch.requestHeadersWhitelist: [authorization, securitytenant]

Once finished, save the file by pressing Ctrl X and entering Y when prompted.

Restart the OpenDashboards service.

$  sudo systemctl restart opensearch-dashboards

Open port 5601 in the UFW firewall.

$ sudo ufw allow 5601

Open the URL http://:5601 or http://opensearch.example.com:5601 to launch the OpenSearch Dashboards website.

<img alt="OpenSearch Dashboards Login" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-login.png65eb5e58e78b7.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="418" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”491″>

If you are content accessing OpenSearch Dashboards over HTTP and with a port number in the URL, you can stop here. However, it is better to serve Dashboards over SSL. For this, we will install Nginx as a reverse proxy and serve SSL over it.

Step 11 – Install Nginx

Debian 12 ships with an older version of Nginx. You need to download the official Nginx repository to install the latest version.

Import Nginx’s signing key.

$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor 
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Add the repository for Nginx’s mainline version.

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] 
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" 
    | sudo tee /etc/apt/sources.list.d/nginx.list

Update the system repositories.

$ sudo apt update

Install Nginx.

$ sudo apt install nginx

Verify the installation. On Debian systems, the following command will only work with sudo.

$ sudo nginx -v
nginx version: nginx/1.25.4

Start the Nginx server.

$ sudo systemctl start nginx

Check the service status.

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-03-02 12:53:57 UTC; 4s ago
       Docs: https://nginx.org/en/docs/
    Process: 3976 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 3977 (nginx)
      Tasks: 3 (limit: 9484)
     Memory: 2.7M
        CPU: 9ms
     CGroup: /system.slice/nginx.service
             ??3977 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??3978 "nginx: worker process"
             ??3979 "nginx: worker process"

Mar 02 12:53:57 opensearch systemd[1]: Starting nginx.service - nginx - high performance web server...
Mar 02 12:53:57 opensearch systemd[1]: Started nginx.service - nginx - high performance web server.

Step 12 – Install SSL

We need to install Certbot to generate the SSL certificate. You can either install Certbot using Debian’s repository or grab the latest version using the Snapd tool. We will be using the Snapd version.

Debian 12 comes doesn’t come with Snapd installed. Install Snapd package.

$ sudo apt install snapd

Run the following commands to ensure that your version of Snapd is up to date.

$ sudo snap install core && sudo snap refresh core

Install Certbot.

$ sudo snap install --classic certbot

Use the following command to ensure that the Certbot command can be run by creating a symbolic link to the /usr/bin directory.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Verify if Certbot is functioning correctly.

$ certbot --version
certbot 2.9.0

Run the following command to generate an SSL Certificate.

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d craftcms.example.com

The above command will download a certificate to the /etc/letsencrypt/live/craftcms.example.com directory on your server.

Generate a Diffie-Hellman group certificate.

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

Check the Certbot renewal scheduler service.

$ sudo systemctl list-timers

You will find snap.certbot.renew.service as one of the services scheduled to run.

NEXT                        LEFT           LAST                        PASSED    UNIT                         ACTIVATES                   ---------------------------------------------------------------------------------------------------------------------------------------  
Sat 2024-03-02 16:25:00 UTC 3h 27min left  -                           -         snap.certbot.renew.timer     snap.certbot.renew.service
Sat 2024-03-02 23:00:51 UTC 10h left       Sat 2024-03-02 06:29:02 UTC 6h ago    apt-daily.timer              apt-daily.service
Sun 2024-03-03 00:00:00 UTC 11h left       -                           -         dpkg-db-backup.timer         dpkg-db-backup.service
.....

Do a dry run of the process to check whether the SSL renewal is working fine.

$ sudo certbot renew --dry-run

If you see no errors, you are all set. Your certificate will renew automatically.

Step 13 – Configure Nginx

Open the file /etc/nginx/nginx.conf for editing.

$ sudo nano /etc/nginx/nginx.conf

Add the following line before the line include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size 64;

Save the file by pressing Ctrl X and entering Y when prompted.

Create and open the file /etc/nginx/conf.d/opensearch.conf for editing.

$ sudo nano /etc/nginx/conf.d/opensearch.conf

Paste the following code in it. Replace opensearch.example.com with your domain name.

server {

    listen 443 ssl;
    listen [::]:443 ssl;

    http2 on;
    http3 on;
    quic_retry on;

    server_name opensearch.example.com;

    access_log  /var/log/nginx/opensearch.access.log;
    error_log   /var/log/nginx/opensearch.error.log;

    ssl_certificate      /etc/letsencrypt/live/opensearch.example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/opensearch.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/opensearch.example.com/chain.pem;

    ssl_session_timeout  5m;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;
    ssl_early_data on;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    location / {
        proxy_pass http://localhost:5601;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
# enforce HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name  opensearch.example.com;
    return 301   https://$host$request_uri;
}

Save the file by pressing Ctrl X and entering Y when prompted.

Verify your Nginx configuration.

$ sudo nginx -t

Restart the Nginx server.

$ sudo systemctl restart nginx

Visit https://opensearch.example.com in your browser to launch OpenSearch Dashboards.

Step 14 – Access and use OpenSearch Dashboards

If you used Nginx, then access the Dashboards via the URL https://opensearch.example.com or use http://opensearch.example.com:5601 and you should get the login page.

<img alt="OpenSearch Dashboards Login Credentials" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-login-credentials.png65eb5e59167d1.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="444" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”511″>

Enter admin and the custom admin password you configured for OpenSearch in step 4 and click the Log in button to proceed.

<img alt="OpenSearch Dashboards Add Data Page" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-add-data-page.png65eb5e593379e.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="750" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”585″>

Click the Add data button to start adding data directly or click the Explore on my own link if you want to browse first. We will browse first and it should take you to a popup that talks about OpenSearch Dashboards’ dark mode.

<img alt="OpenSearch Dashboards Theme Popup" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-theme-popup.png65eb5e5955041.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="473" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

Click the Dismiss button to proceed. Next, you will be asked to choose a tenant.

<img alt="OpenSearch Dashboards Tenant Selection screen" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-tenant-selection-screen.png65eb5e59729f3.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="530" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, dashboards, and other OpenSearch Dashboards objects. Each user can create multiple tenants for multiple uses. Tenants are useful for safely sharing your work with other OpenSearch Dashboards’ users. OpenSearch gives you the option of choosing between three options.

  • Global – This tenant is shared between every OpenSearch Dashboards user.
  • Private – This tenant is exclusive to each user and can’t be shared.
  • Custom – Administrators can create custom tenants and assign them to specific roles. Once created, you can use these tenants to provide spaces to a specific set of users.

For now, we will stick with the default option of a Private tenant. Click the Confirm button to proceed. You will be greeted with the following page.

<img alt="OpenSearch Dashboards Homepage" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-homepage.png65eb5e59b772a.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="457" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

Next, we will verify the connection between OpenSearch and the OpenSearch Dashboards.

Click the Dev Tools button on the top right and you will be shown a mini popup about the console UI for the OpenSearch Dashboards.

<img alt="OpenSearch Dashboards Dev Tools Console Popup" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-dev-tools-console-popup.png65eb5e59d52be.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="750" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”548″>

Click the Dismiss button, input the query GET / in the console on the left, and click the small play button in the middle. You should see the following output on the right.

<img alt="OpenSearch Dashboards Console Connection Verification" data-ezsrc="https://kirelos.com/wp-content/uploads/2024/03/echo/opensearch-dashboards-console-connection-verify.png65eb5e59ef22c.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="360" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

This confirms that OpenSearch Dashboards is receiving data from OpenSearch and both are working fine. You can start adding data from here on.

Conclusion

This concludes the tutorial on installing OpenSearch on a Debian 12 server. If you have any questions, post them in the comments below.