<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.bidhankhatri.com.np/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.bidhankhatri.com.np/" rel="alternate" type="text/html" /><updated>2026-01-05T17:22:36+00:00</updated><id>https://www.bidhankhatri.com.np/feed.xml</id><title type="html">bidhankhatri.com.np</title><subtitle>Blog from System Administrator</subtitle><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><entry><title type="html">Inside Linux TCP: From Handshake to Reset or Close</title><link href="https://www.bidhankhatri.com.np/system/Inside-Linux-TCP/" rel="alternate" type="text/html" title="Inside Linux TCP: From Handshake to Reset or Close" /><published>2026-01-04T02:16:41+00:00</published><updated>2026-01-04T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/Inside-Linux-TCP</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/Inside-Linux-TCP/"><![CDATA[<p>TCP is the backbone of network communication in Linux systems. It’s a connection-oriented protocol that ensures reliable data exchange between a sender and a receiver over a network. Operating at Layer 4 (the Transport Layer) of the OSI model, TCP guarantees that data is delivered in the correct order and without loss. <br />
Understanding TCP is not just theoretical. It’s critical for real-world troubleshooting. For example, when an application fails to connect, or data transfer stalls, knowing how TCP establishes, maintains, and closes connections helps you pinpoint issues like dropped packets, RSTs, or handshake failures. This insight can save hours when diagnosing network problems, firewall misconfigurations, or application-level errors. <br />
In this post, we’ll explore real-world Linux scenarios using tools such as <b>ncat, ss,</b> and <b>tcpdump</b> to observe TCP connections from start to finish, from SYN to FIN or RST. By understanding the basic concepts of TCP, troubleshooting becomes much faster and more effective.  <br />
<br />
<b>TCP state vs TCP flags:</b> <br />
<span style="color:#e83e8c">TCP state:</span> In networking, a TCP state refers to the current condition of a Transmission Control Protocol (TCP) connection. Since TCP is a connection-oriented protocol, it must track whether a connection is being opened, actively transferring data, or being closed. This process is managed by a Finite State Machine (FSM). Both the client and the server move through these states independently based on the packets they send or receive. If you want to observe the TCP state then you can monitor it via <b>ss -at</b> or <b>netstat -tn</b> in a linux.<br />
<b>ESTABLISHED, TIME-WAIT, FIN-WAIT-2</b> are few states of the TCP. 
<br /> <br />
<span style="color:#e83e8c">TCP flags:</span> TCP flags are single-bit control signals in TCP packets that manage the state of a connection, indicating events such as connection setup (SYN), acknowledgment (ACK), termination (FIN), or connection reset (RST). These flags can be seen in packet captures using tcpdump or Wireshark. TCP flags are crucial for network troubleshooting, monitoring, and security because they indicate exactly what a TCP connection is doing at any given moment.<br />
Common TCP Flags are: <b>SYN, ACK, FIN, RST, PSH</b>.</p>

<p><br />
<b>What is TCP Handshake?</b><br /> 
A TCP handshake is the process used to establish a reliable TCP connection between a client and a server before any actual data is transferred. It’s also called a <span style="color:#e83e8c">three-way handshake</span> because it involves three steps exchanged between the two sides. SYN, SYN-ACK and ACK.<br />
<br /></p>

<p>In this example, I will send <span style="color:#e83e8c">“hello”</span> data from <span style="color:#e83e8c">client(10.32.10.21)</span> to the <span style="color:#e83e8c">server(10.32.3.69)</span>, which is listening on port <span style="color:#e83e8c">9600</span>. We will analyze the packet flow between the client and the server. We will be using the <span style="color:#e83e8c">ncat</span> command on the client side to send the data and <span style="color:#e83e8c">tcpdump</span> on the server side to monitor the TCP packets.<br />
<br />
For reference:<br />
TCP Flags:</p>

<table>
  <thead>
    <tr>
      <th>Flag(s)</th>
      <th>Meaning</th>
      <th>Typical Use / Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>[S]</strong></td>
      <td>SYN</td>
      <td>Start of TCP handshake (client initiates connection)</td>
    </tr>
    <tr>
      <td><strong>[S.]</strong></td>
      <td>SYN + ACK</td>
      <td>Second step of handshake (server acknowledges SYN)</td>
    </tr>
    <tr>
      <td><strong>[.]</strong></td>
      <td>ACK</td>
      <td>Acknowledgment of received data or handshake completion</td>
    </tr>
    <tr>
      <td><strong>[F]</strong></td>
      <td>FIN</td>
      <td>Graceful connection close — sender finished sending data</td>
    </tr>
    <tr>
      <td><strong>[F.]</strong></td>
      <td>FIN + ACK</td>
      <td>Graceful close acknowledgment (both sides exchange)</td>
    </tr>
    <tr>
      <td><strong>[R]</strong></td>
      <td>RST</td>
      <td>Reset connection — abrupt/abnormal termination</td>
    </tr>
    <tr>
      <td><strong>[R.]</strong></td>
      <td>RST + ACK</td>
      <td>Reset with acknowledgment</td>
    </tr>
    <tr>
      <td><strong>[P]</strong></td>
      <td>PSH</td>
      <td>Push data immediately to the application</td>
    </tr>
    <tr>
      <td><strong>[P.]</strong></td>
      <td>PSH + ACK</td>
      <td>Data push with acknowledgment</td>
    </tr>
    <tr>
      <td><strong>[U]</strong></td>
      <td>URG</td>
      <td>Urgent data flag</td>
    </tr>
    <tr>
      <td><strong>[E]</strong></td>
      <td>ECE</td>
      <td>ECN Echo — congestion notification</td>
    </tr>
    <tr>
      <td><strong>[C]</strong></td>
      <td>CWR</td>
      <td>Congestion Window Reduced — ECN flow control</td>
    </tr>
    <tr>
      <td><strong>[.] / [P.]</strong></td>
      <td>ACK + PSH</td>
      <td>Normal data acknowledgment plus immediate delivery</td>
    </tr>
  </tbody>
</table>

<p><br />
Step 1: Testing TCP Connectivity with <b>ncat</b></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">echo "hello" | ncat 10.32.3.69 </span><span class="m">9600</span>
</code></pre></div></div>
<p><b>Tcpdump</b> output on server side.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">1766716972.073906 IP 10.32.10.21.57270 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [S], seq 2335128560, win 64240, options [mss 1460,sackOK,TS val 2523678873 ecr 0,nop,wscale 9], length 0</span> 
<span class="na">1766716972.074026 IP 10.32.3.69.9600 &gt; 10.32.10.21.57270</span><span class="pi">:</span> <span class="s">Flags [S.], seq 1265705932, ack 2335128561, win 28960, options [mss 1460,sackOK,TS val 3672955275 ecr 2523678873,nop,wscale 7], length 0</span> 
<span class="na">1766716972.074184 IP 10.32.10.21.57270 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [.], ack 1, win 126, options [nop,nop,TS val 2523678874 ecr 3672955275], length </span><span class="m">0</span>
</code></pre></div></div>

<p>First line, Client initiates TCP connection to port 9600. <span style="color:#e83e8c">SYN [S]</span> Flags can be seen as the 1st packet.<br />
Second line, Server acknowledges SYN and sends its own sequence number. <span style="color:#e83e8c">SYN+ACK [S.]</span> in 2nd packet.<br />
Third line, Client sends the <span style="color:#e83e8c">ACK [.]</span> packet to the server.<br />
Now this completes the 3 way handshake. Connection is now open for data transfer and you will see TCP state as <span style="color:#e83e8c">“ESTABLISHED”</span> in the linux box.<br />
<br />
If you check through <span style="color:#e83e8c">ss</span> command then you will see <span style="color:#e83e8c">“ESTAB”</span>. This confirms that the session between client and the server is active.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">ss -pnt | grep 9600</span> 
<span class="s">ESTAB 0 0 10.32.3.69:9600 10.32.10.21:39518 users:(("java",pid=33120,fd=84))</span>
</code></pre></div></div>

<p>Now below line shows how data transfer packet looks like.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">1766716972.075239 IP 10.32.10.21.57270 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [P.], seq 1:7, ack 1, win 126, options [nop,nop,TS val 2523678875 ecr 3672955275], length 6</span> 
<span class="na">1766716972.075288 IP 10.32.3.69.9600 &gt; 10.32.10.21.57270</span><span class="pi">:</span> <span class="s">Flags [.], ack 7, win 227, options [nop,nop,TS val 3672955276 ecr 2523678875], length </span><span class="m">0</span>
</code></pre></div></div>
<p>First line, Client sends data, <span style="color:#e83e8c">[P.] = PSH + ACK</span> push 6 bytes to server immediately. Payload size is 6 bytes. <br />
Second line, Server <span style="color:#e83e8c">Ack[.]</span> it and confirms that I have received the 6 bytes (5 bytes for hello and 1 byte for newline) data. By default, echo adds a newline (\n) at the end. Notice the server’s ACK number is now 7. In TCP, the ACK number tells the sender the next sequence number the receiver expects. Since the client sent 6 bytes starting at sequence 1, the server ACKs with 7 (1 + 6 = 7), effectively saying: “I have received everything up to byte 6; please send byte 7 next.”</p>

<p>Usually server will also send data back to the clients and you will see similary kind of packets. This is actually a normal TCP data exchange packet flow between client and the server.<br />
<br />
After the data exchange is completed, TCP teardown process will begin.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">00:38:40.501716 IP 10.32.10.21.55952 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [F.], seq 7, ack 1151, win 131, options [nop,nop,TS val 2522122312 ecr 3671385878], length 0</span> 
<span class="na">00:38:40.501748 IP 10.32.3.69.9600 &gt; 10.32.10.21.55952</span><span class="pi">:</span> <span class="s">Flags [.], ack 8, win 227, options [nop,nop,TS val 3671398702 ecr 2522122312], length </span><span class="m">0</span>
<span class="na">1766716972.075352 IP 10.32.10.21.57270 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [F.], seq 7, ack 1, win 126, options [nop,nop,TS val 2523678875 ecr 3672955275], length 0</span> 
<span class="na">1766716972.081386 IP 10.32.3.69.9600 &gt; 10.32.10.21.57270</span><span class="pi">:</span> <span class="s">Flags [F.], seq 1, ack 8, win 227, options [nop,nop,TS val 3672955282 ecr 2523678875], length 0</span> 
<span class="na">1766716972.081531 IP 10.32.10.21.57270 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [.], ack 2, win 126, options [nop,nop,TS val 2523678881 ecr 3672955282], length </span><span class="m">0</span>
</code></pre></div></div>
<p>First Packet, Client sends <span style="color:#e83e8c">FIN + ACK [F.]</span> which means, I have finished sending my data. I want to close my side of the connection. and it also acknowledges all the data received from the server side.<br />
Second Packet, I received your request to close. I’m acknowledging it. Server <span style="color:#e83e8c">ACK [.]</span> the client <span style="color:#e83e8c">FIN</span> correctly.<br />
Third Packet, Client sends a <span style="color:#e83e8c">FIN</span> to the server.<br />
Fourth packet, Server also sends its <span style="color:#e83e8c">FIN and ACK</span> to the client.  <br />
Fifth packet, The client sends the final <span style="color:#e83e8c">ACK</span>. <br />
Now the TCP connection is fully closed. This is a graceful connection close. You can observe a similar TCP teardown packet flow whenever a connection is closed.<br />
<br />
Now to test the <span style="color:#e83e8c">RST</span> behaviour. Lets stop the service listening on <span style="color:#e83e8c">Port 9600</span> on the server and we send the request on the same port from the client.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">echo "hello" | ncat 10.32.3.69 </span><span class="m">9600</span>
<span class="na">Ncat</span><span class="pi">:</span> <span class="s">Connection refused.</span>
</code></pre></div></div>
<p>If we do that, the client will receive a <span style="color:#e83e8c">“Connection refused”</span> response from the server. If you observe the packet flow, it will look like this or a similar pattern. This is a connection rejection from the server side.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">1766895916.006405 IP 10.32.10.21.56518 &gt; 10.32.3.69.9600</span><span class="pi">:</span> <span class="s">Flags [S], seq 3541810289, win 64240, options [mss 1460,sackOK,TS val 2702622704 ecr 0,nop,wscale 9], length 0</span> 
<span class="na">1766895916.006514 IP 10.32.3.69.9600 &gt; 10.32.10.21.56518</span><span class="pi">:</span> <span class="s">Flags [R.], seq 0, ack 3541810290, win 0, length </span><span class="m">0</span>
</code></pre></div></div>
<p>1st Packet, client send a <span style="color:#e83e8c">SYN</span> packet. <br />
2nd Packet, <span style="color:#e83e8c">RST + ACK</span>. This is the “Connection Refused” signal. The server acknowledges the request but immediately kills it with a Reset.</p>

<p>This tcpdump output shows a connection attempt that was immediately rejected with a <span style="color:#e83e8c">TCP RST</span>. <br />
<br />
This kind of packet flow analysis can be very helpful during application troubleshooting from the network-level. Usually, if we see RST packets, it may be because the service that should be listening on that port is no longer listening or is not working properly. In such cases, the kernel sends an RST. RST packets can also be generated when the maximum number of connections is reached, causing new connection attempts to be forcefully reset. They may also occur when a service is killed, an incorrect protocol is used, or an RST is sent from the client side. Similarly, RST packets may be sent by firewall rules if such rules are configured.<br />
<br />
In some cases, we want to allow connections only from specific networks and block connections from outside. For security reasons, we can configure firewall rules to send RST packets in those scenarios.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">iptables -A INPUT -p tcp --dport 9600 -j REJECT --reject-with tcp-reset</span>
</code></pre></div></div>
<p>This iptables rule will reject incoming request with RST on the port 9600.</p>

<p><br />
The table below shows the different states of TCP communication from the client’s perspective while sending data. It describes which types of packets are sent and received, and what each of them means.</p>
<h4 id="-tcp-client-state--client-perspective-sending-data-to-server-packet-flow-sequential-view"><b> TCP Client State → Client Perspective (Sending Data to Server), Packet Flow (Sequential View)</b></h4>
<p>When a client initiates a TCP connection and sends data, the connection can go through multiple states:
<br /><br />
<b>Handshake</b></p>

<table>
  <thead>
    <tr>
      <th>Client State</th>
      <th>Packet Sent / Received</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CLOSED</td>
      <td>–</td>
      <td>Initial state; no connection exists yet.</td>
    </tr>
    <tr>
      <td>SYN-SENT</td>
      <td>SYN → Server</td>
      <td>Client initiates connection by sending SYN. Waiting for server reply.</td>
    </tr>
    <tr>
      <td>ESTABLISHED</td>
      <td>SYN+ACK ← Server, ACK → Server</td>
      <td>Three-way handshake completes. Client can now send/receive data.</td>
    </tr>
  </tbody>
</table>

<p><b>Data Transfer</b></p>

<table>
  <thead>
    <tr>
      <th>Client State</th>
      <th>Packet Sent / Received</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ESTABLISHED (data)</td>
      <td>DATA → Server, DATA ← Server</td>
      <td>Application data is exchanged between client and server.</td>
    </tr>
  </tbody>
</table>

<p><b>Connection Teardown</b></p>

<table>
  <thead>
    <tr>
      <th>Client State</th>
      <th>Packet Sent / Received</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>FIN-WAIT-1</td>
      <td>FIN → Server</td>
      <td>Client finishes sending data and calls <code class="language-plaintext highlighter-rouge">close()</code>. Sends FIN to server.</td>
    </tr>
    <tr>
      <td>FIN-WAIT-2</td>
      <td>ACK ← Server</td>
      <td>Server acknowledges FIN. Client waits for server to send its FIN.</td>
    </tr>
    <tr>
      <td>TIME-WAIT</td>
      <td>FIN ← Server, ACK → Server</td>
      <td>Server closes its side. Client ACKs and waits 2×MSL before fully closing.</td>
    </tr>
    <tr>
      <td>CLOSE-WAIT</td>
      <td>FIN ← Server</td>
      <td>Client receives FIN from server but application hasn’t closed socket yet.</td>
    </tr>
    <tr>
      <td>LAST-ACK</td>
      <td>FIN → Server, ACK ← Server</td>
      <td>Client sends FIN after receiving server’s FIN and waits for final ACK.</td>
    </tr>
    <tr>
      <td>CLOSED (again)</td>
      <td>–</td>
      <td>Connection fully terminated. Socket resources released.</td>
    </tr>
  </tbody>
</table>

<p><br />
TCP connections often move so fast (milliseconds) that catching every state in real-time can be challenging.
<br />
<br /></p>
<div> 
<script src="/assets/js/mermaid.min.js"></script>
<div class="mermaid">
sequenceDiagram
    participant Client
    participant Server

    %% 3-way handshake (Connection setup)
    Client-&gt;&gt;Server: SYN (seq=x)
    Server-&gt;&gt;Client: SYN-ACK (seq=y, ack=x+1)
    Client-&gt;&gt;Server: ACK (ack=y+1)

    %% Data transfer
    Client-&gt;&gt;Server: DATA (seq=x+1)
    Server-&gt;&gt;Client: ACK (ack=x+len(DATA)+1)
    Server-&gt;&gt;Client: DATA (seq=y+1)
    Client-&gt;&gt;Server: ACK (ack=y+len(DATA)+1)

    %% Graceful connection termination (4-way handshake)
    Client-&gt;&gt;Server: FIN (seq=a)
    Server-&gt;&gt;Client: ACK (ack=a+1)
    Server-&gt;&gt;Client: FIN (seq=b)
    Client-&gt;&gt;Server: ACK (ack=b+1)

    %% Connection fully closed
</div>


<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>


<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>


<br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>
</div>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="System" /><category term="TCP" /><summary type="html"><![CDATA[TCP is the backbone of network communication in Linux systems. It’s a connection-oriented protocol that ensures reliable data exchange between a sender and a receiver over a network. Operating at Layer 4 (the Transport Layer) of the OSI model, TCP guarantees that data is delivered in the correct order and without loss. Understanding TCP is not just theoretical. It’s critical for real-world troubleshooting. For example, when an application fails to connect, or data transfer stalls, knowing how TCP establishes, maintains, and closes connections helps you pinpoint issues like dropped packets, RSTs, or handshake failures. This insight can save hours when diagnosing network problems, firewall misconfigurations, or application-level errors. In this post, we’ll explore real-world Linux scenarios using tools such as ncat, ss, and tcpdump to observe TCP connections from start to finish, from SYN to FIN or RST. By understanding the basic concepts of TCP, troubleshooting becomes much faster and more effective. TCP state vs TCP flags: TCP state: In networking, a TCP state refers to the current condition of a Transmission Control Protocol (TCP) connection. Since TCP is a connection-oriented protocol, it must track whether a connection is being opened, actively transferring data, or being closed. This process is managed by a Finite State Machine (FSM). Both the client and the server move through these states independently based on the packets they send or receive. If you want to observe the TCP state then you can monitor it via ss -at or netstat -tn in a linux. ESTABLISHED, TIME-WAIT, FIN-WAIT-2 are few states of the TCP. TCP flags: TCP flags are single-bit control signals in TCP packets that manage the state of a connection, indicating events such as connection setup (SYN), acknowledgment (ACK), termination (FIN), or connection reset (RST). These flags can be seen in packet captures using tcpdump or Wireshark. TCP flags are crucial for network troubleshooting, monitoring, and security because they indicate exactly what a TCP connection is doing at any given moment. Common TCP Flags are: SYN, ACK, FIN, RST, PSH.]]></summary></entry><entry><title type="html">RabbitMQ Monitoring: Pushing Queue Metrics to Elasticsearch with Python script</title><link href="https://www.bidhankhatri.com.np/system/RabbitMQ-Queue-Monitoring-With-Python-script/" rel="alternate" type="text/html" title="RabbitMQ Monitoring: Pushing Queue Metrics to Elasticsearch with Python script" /><published>2025-12-10T02:16:41+00:00</published><updated>2025-12-10T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/RabbitMQ-Queue-Monitoring-With-Python-script</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/RabbitMQ-Queue-Monitoring-With-Python-script/"><![CDATA[<p>Monitoring RabbitMQ queues is critical for maintaining the health and performance of the RabbitMQ distributed system. This post is about a Python script that will collect the RabbitMQ queue metrics through it’s API and send them to an Elasticsearch in a data stream. I wrote the script so that I can keep the record of consumers nodes history for the queues.  <br />
<br />
Technologies I used to monitor.<br />
<span style="color:#e83e8c">RabbitMQ:</span> The message broker providing the metrics via its Management API.<br />
<span style="color:#e83e8c">Python (requests, json, logging):</span> For fetching, processing, and ingesting the data.<br />
<span style="color:#e83e8c">Elasticsearch (ES):</span> The robust search and analytics engine used for storing and querying data.<br />
<br />
The process, orchestrated by the <span style="color:#e83e8c">metrics_rabbitmq()</span> function, begins by defining the target RabbitMQ Management Hosts and Elasticsearch Nodes. The script then iterates through each RabbitMQ host, first retrieving a list of all queues using the Management API’s <span style="color:#e83e8c">/api/queues</span> endpoint. Next, for every queue identified, it makes a subsequent call to the detailed queue endpoint <span style="color:#e83e8c">(/api/queues/{vhost}/{queues})</span> querying every vhosts to fetch comprehensive metrics, including message counts, consumer information, and other metrics. This data is further processed by adding a standardized <span style="color:#e83e8c">@timestamp</span>, and adding user friendly field names <span style="color:#e83e8c">node_name,</span> <span style="color:#e83e8c">queue_name</span> and <span style="color:#e83e8c">consumer_ip</span>. Finally, the processed document is ingested into the Elasticsearch through Data Stream. To push the metrics, API key is used for the Elasticsearch authentication and basic authentication credentials to authenticate the RabbitMQ. They all are defined in the <span style="color:#e83e8c">.env</span> file.<br />
<br />
Create a <b>.env</b> file in the project directory first.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">ES_API_KEY="&lt;your ES KEY&gt;"</span>
<span class="s">RABBIT_USER="admin"</span>
<span class="s">RABBITT_PASS="pass"</span>
</code></pre></div></div>
<p>main.yml file. 
<br />
<script src="https://gist.github.com/bidhanahdib/2768ad2d1870b8514e18fb6ab0697fc2.js"></script></p>

<p>Output of the script:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">2025-12-10 13:22:58 [INFO] Fetching queues from http://srv01.abc.com:15672</span> 
<span class="s">2025-12-10 13:22:58 [INFO] Successfully fetched 365 queues from http://srv01.abc.com:15672</span> 
<span class="s">2025-12-10 13:23:41 [INFO] Fetching queues from http://srv02.abc.com:15672</span> 
<span class="s">2025-12-10 13:23:41 [ERROR] Failed to fetch queues from http://srv02.abc.com:15672: 401 Client Error</span><span class="na">: Unauthorized for url</span><span class="pi">:</span> <span class="s">http://srv02.abc.com:15672/api/queues</span> 
<span class="s">2025-12-10 13:23:41 [INFO] Fetching queues from http://srv03.abc.com:15672</span> 
<span class="s">2025-12-10 13:23:41 [INFO] Successfully fetched 28 queues from http://srv03.abc.com:15672</span>
</code></pre></div></div>
<p><br />
Screenshot from the Kibana.</p>

<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/es_rabbitmq_queue.png" width="550" height="450" style="object-fit: contain;" />
</p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<p><br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script></p>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="RabbitMQ" /><category term="ELK" /><summary type="html"><![CDATA[Monitoring RabbitMQ queues is critical for maintaining the health and performance of the RabbitMQ distributed system. This post is about a Python script that will collect the RabbitMQ queue metrics through it’s API and send them to an Elasticsearch in a data stream. I wrote the script so that I can keep the record of consumers nodes history for the queues. Technologies I used to monitor. RabbitMQ: The message broker providing the metrics via its Management API. Python (requests, json, logging): For fetching, processing, and ingesting the data. Elasticsearch (ES): The robust search and analytics engine used for storing and querying data. The process, orchestrated by the metrics_rabbitmq() function, begins by defining the target RabbitMQ Management Hosts and Elasticsearch Nodes. The script then iterates through each RabbitMQ host, first retrieving a list of all queues using the Management API’s /api/queues endpoint. Next, for every queue identified, it makes a subsequent call to the detailed queue endpoint (/api/queues/{vhost}/{queues}) querying every vhosts to fetch comprehensive metrics, including message counts, consumer information, and other metrics. This data is further processed by adding a standardized @timestamp, and adding user friendly field names node_name, queue_name and consumer_ip. Finally, the processed document is ingested into the Elasticsearch through Data Stream. To push the metrics, API key is used for the Elasticsearch authentication and basic authentication credentials to authenticate the RabbitMQ. They all are defined in the .env file. Create a .env file in the project directory first. ES_API_KEY="&lt;your ES KEY&gt;" RABBIT_USER="admin" RABBITT_PASS="pass" main.yml file.]]></summary></entry><entry><title type="html">Scalable CI/CD with Jenkins and Docker Cloud Agents</title><link href="https://www.bidhankhatri.com.np/system/Jenkins-and-Docker-Cloud-Agents/" rel="alternate" type="text/html" title="Scalable CI/CD with Jenkins and Docker Cloud Agents" /><published>2025-03-14T02:16:41+00:00</published><updated>2025-03-14T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/Jenkins-and-Docker-Cloud-Agents</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/Jenkins-and-Docker-Cloud-Agents/"><![CDATA[<p>A Jenkins cloud agent using Docker refers to dynamically provisioning Jenkins build agents (also called slaves or nodes) in Docker containers—typically on demand—rather than using pre-provisioned static nodes. This allows Jenkins to scale efficiently and cleanly by creating isolated environments for each build. Today, I will show you how to configure Docker cloud on Jenkins.</p>

<p>Be cautious when using Docker on the same host as the Jenkins master—consider using a remote Docker daemon or sandboxing strategies to avoid privilege escalation. I will show you both approaches here.<br />
<br />
First, spin up Jenkins. We will run Jenkins as a container. The Jenkins container runs as <b>UID 1000</b> by default, so your host Jenkins directory must be writable by UID 1000.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">mkdir /var/Docker/jenkins</span>
<span class="s">chown -R 1000:1000 /var/Docker/jenkins</span>
<span class="s">docker network create jenkins</span>
</code></pre></div></div>
<p>If UID 1000 does not exist on the host machine, reserve UID 1000 for the Jenkins user.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">sudo useradd -u 1000 -m -s /bin/bash jenkins</span>
</code></pre></div></div>
<p>Run the Jenkins container with persistent storage, Docker access, jdk21 and port mappings.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker run -d \</span>
  <span class="s">--name jenkins \</span>
  <span class="s">--network jenkins \</span>
  <span class="s">--restart=always \</span>
  <span class="s">-p 8080:8080 -p 50000:50000 \</span>
  <span class="s">-v /var/Docker/jenkins:/var/jenkins_home \</span>
  <span class="s">-v /var/run/docker.sock:/var/run/docker.sock \</span>
  <span class="s">jenkins/jenkins:2.504.1-lts-jdk21</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker ps</span>                                                                                                                                                                      
<span class="s">CONTAINER ID   IMAGE                               COMMAND                  CREATED        STATUS        PORTS                                                                                      NAMES</span>
<span class="s">0970e84f4ffb   jenkins/jenkins:2.504.1-lts-jdk21   "/usr/bin/tini -- /u…"   1 hours ago   Up 2 minutes   0.0.0.0:8080-&gt;8080/tcp, :::8080-&gt;8080/tcp, 0.0.0.0:50000-&gt;50000/tcp, :::50000-&gt;50000/tcp   jenkins</span>
</code></pre></div></div>
<p>Get the initial admin password for jenkins</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword</span>
<span class="s">de9ae273099f4a09979e4c54fe0b1a20</span>
</code></pre></div></div>
<p>Enter that password at Jenkins URL http://localhost:8080.  <br />
<span style="color:#e83e8c">
Install suggested plugins » Create your first admmin user.
Dashboard » Manage Jenkins » System configuration » Nodes » clouds » Install a plugin ( Docker ) » Install » Tick on Restart Jenkins when installation is complete. </span><br />
Now jenkins will restart enabling the docker plugin.</p>

<h4 id="docker-cloud-configuration">Docker cloud configuration</h4>
<p><i>setting up Jenkins to dynamically provision Docker containers as build agents.</i><br />
<span style="color:#e83e8c">Manage Jenkins » Nodes » Clouds » New cloud</span><br />
Give a name to your Docker cloud. I used <b>‘docker_cloud’</b> and select ‘Docker Type’ from the options.<br />
Once you click on “create” now you will see 2 options:</p>
<ol>
  <li>Docker Cloud details</li>
  <li>Docker Agent templates</li>
</ol>

<p><b>1. Docker Cloud details:</b> <br />
<i>This is where you define the Docker environment that Jenkins will use to spin up containers.</i><br />
<span style="color:#e83e8c">Name:</span>	A unique identifier for this Docker cloud.<br />
<span style="color:#e83e8c">Docker Host URI:</span> URI to connect to Docker. E.g., <b>unix:///var/run/docker.sock</b> or <b>tcp://…</b> <br />
<span style="color:#e83e8c">Server Credentials:</span>	(Optional) TLS certs if Docker is secured with TLS (rare on local setups). In our case, we are not using any credentials. <br />
<span style="color:#e83e8c">Test Connection:</span>	Verifies Jenkins can reach the Docker daemon. <br />
I’m trying to connect to the Docker daemon running on the same host where the Jenkins container is running, so I’m using <b>docker.sock</b> to connect to it.</p>
<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/docker_cloud_test.png" width="750" height="650" style="object-fit: contain;" />
</p>

<p>If jenkins ia able to connect docker then you will see the docker version and api version detials.
This confirms that Jenkins is correctly detecting the Docker engine version and API version when connecting to the Docker socket.<br />
Version = 20.10.7, API Version = 1.41 
Click on “Enabled”
Leave other section as it is.<br />
If you see errors like <b>“java.net.BindException: Permission denied “</b> then you should add jenkins user to host docker group and restart it.<br />
Get the docker group id from the host machine.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">getent group docker</span>
<span class="s">docker:x:986:bdn</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker exec -u0 -it jenkins bash</span>
<span class="s">groupadd -g 986 docker</span>
<span class="s">usermod -aG docker jenkins</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker restart jenkins</span>
</code></pre></div></div>

<p><b>2. Docker Agent Templates:</b> <br />
<i>Each template defines how a container should be spun up to act as a Jenkins agent.</i> <br />
<span style="color:#e83e8c">Labels</span>:	Labels used by jobs/pipelines to request this agent type.  Click on Enabled.<br />
<span style="color:#e83e8c">Name</span>:	Internal name (can be anything).  use same as labels.<br />
<span style="color:#e83e8c">Docker Image:</span>	Image to use (e.g., jenkins/inbound-agent, maven:3.8-jdk-11 or your custom immage)<br />
<span style="color:#e83e8c">Instance Capacity:</span>	Max number of containers Jenkins can launch from this template.<br />
<span style="color:#e83e8c">Remote File System Root:</span>	Where the Jenkins workspace lives inside the container (e.g., /home/jenkins).<br />
<span style="color:#e83e8c">Usage:</span> Use this node as much as possible<br />
<span style="color:#e83e8c">Connect method:</span> Attach Docker container.<br />
(All the images should have Java installed. Use the same Java version as the master. The Docker image CMD must either be empty or simply keep the container running indefinitely, e.g., /bin/bash. The Jenkins remote agent code will be copied into the container and then run using the Java installed in the container.)<br />
<span style="color:#e83e8c">Pull strategy:</span> Never pull<br />
(Pull all images every time if you’re pulling them from Docker Hub. If you have a local image, choose ‘Never pull.’)<br />
In my case I’m choosing ‘Never pull’ as my image is a custom one. 
<br /> <br />
<b>Dashboard » Manage Jenkins » Clouds » docker_cloud » Configure</b><br />
<br /></p>
<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/docker_agent_template.png" width="700" height="550" style="object-fit: contain;" />
</p>
<p><br />
Here is the <b>Dockerfile</b> for a custom image which includes Java 21(OpenJDK) runtime, python 3 and ansible all based on Alpine linux.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">FROM alpine:3.19</span>

<span class="c1"># Install base packages</span>
<span class="s">RUN apk update &amp;&amp; apk add --no-cache \</span>
    <span class="s">bash \</span>
    <span class="s">curl \</span>
    <span class="s">openssh \</span>
    <span class="s">git \</span>
    <span class="s">python3 \</span>
    <span class="s">py3-pip \</span>
    <span class="s">ansible \</span>
    <span class="s">libc6-compat \</span>
    <span class="s">sshpass</span>

<span class="s">RUN mkdir -p /opt/java &amp;&amp; \</span>
    <span class="s">curl -L -o /tmp/openjdk.tar.gz https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2+13/OpenJDK21U-jdk_x64_alpine-linux_hotspot_21.0.2_13.tar.gz &amp;&amp; \</span>
    <span class="s">tar -xzf /tmp/openjdk.tar.gz -C /opt/java &amp;&amp; \</span>
    <span class="s">mv /opt/java/jdk-* /opt/java/openjdk &amp;&amp; \</span>
    <span class="s">rm -rf /tmp/openjdk.tar.gz</span>


<span class="c1"># Install Java 21 (Alpine-specific Temurin build)</span>
<span class="s">ENV JAVA_HOME=/opt/java/openjdk</span>
<span class="s">ENV PATH="$JAVA_HOME/bin:$PATH"</span>


<span class="c1"># Verify that Java has been installed correctly</span>
<span class="s">RUN java -version</span>

<span class="c1"># Create non-root user</span>
<span class="s">RUN adduser -D jenkins</span>
<span class="s">USER jenkins</span>

<span class="s">WORKDIR /home/jenkins</span>

<span class="c1"># Default command</span>
<span class="s">CMD ["/bin/bash"]</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker build -t jenkins-agent:alp-j21-py3-ans .</span>
</code></pre></div></div>
<p><br /></p>
<h4 id="now-create-a-new-freestyle-job"><b>Now create a new freestyle job</b></h4>
<p>I have created one freestyle job which is <span style="color:#e83e8c">“Hello-World”</span><br />
Click on <span style="color:#e83e8c">“Configuration”</span> and in General, click <span style="color:#e83e8c">“Restrict where this project can be run”</span> field. put <b>“jenkins-agent”</b></p>
<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/jenkins_label_exp.png" width="750" height="650" style="object-fit: contain;" />
</p>
<p>In build section, add build step, Execute shell 
echo “Hello World”
Now run the job.</p>

<p>Here, what will happen is that Jenkins dynamically launches new containers as agents <span style="color:#e83e8c">(jenkins-agent:alp-j21-py3-ans)</span> on demand to run jobs<span style="color:#e83e8c">(“Hello-World”).</span> These containers are ephemeral and automatically destroyed after the job finishes. It’s a best-practice approach in Jenkins to use ephemeral Docker containers for running jobs.</p>
<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/jenkins_job_output.png" width="750" height="650" style="object-fit: contain;" />
</p>

<p>That’s it. Now you can run as much as ephemeral containers to run your Jenkins Jobs.<br />
If you want to run the job on the same host where Docker is installed, then the <span style="color:#e83e8c">/var/run/docker.sock</span> can be used. However, if you want to run the job on a remote server container, you first need to expose Docker over TCP. After that, your Jenkins controller instance can connect to the remote Docker server via TCP.</p>

<p>You can either modify the Docker configuration to enable TCP access, or, if you prefer not to touch the existing docker config, you can use an image like socat which will act as a relay between TCP and the Unix socket, which will expose Docker over TCP without altering the configuration.  To connect from Jenkins controller you should define <span style="color:#e83e8c">tcp://&lt;RemoteIP:2375&gt;</span> in <span style="color:#e83e8c">Docker Host URI</span> field while configuring cloud.<br />
Execute in remote server.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker run -d \</span>                                                                                                                                                                      
  <span class="s">--name socat-docker \</span>
  <span class="s">-v /var/run/docker.sock:/var/run/docker.sock \</span>
  <span class="s">-p 2375:2375 \</span>
  <span class="s">alpine/socat \</span>
  <span class="s">tcp-listen:2375,fork,reuseaddr unix-connect:/var/run/docker.sock</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker ps</span>                                                                                                                                                                     
<span class="s">CONTAINER ID   IMAGE                               COMMAND                  CREATED        STATUS        PORTS                                                                                      NAMES</span>
<span class="s">9a3510394186   alpine/socat                        "socat tcp-listen:23…"   1 days ago     Up 1 second   0.0.0.0:2375-&gt;2375/tcp, :::2375-&gt;2375/tcp</span>
</code></pre></div></div>
<p>Important: Make sure Docker is exposed securely to prevent unauthorized access. The above setup exposes your Docker daemon without authentication, which means anyone who can access port 2375 can control your Docker engine (run containers, delete images, etc.). Therefore, it is strongly recommended to configure TLS for secure access.</p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<p><br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script></p>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="Jenkins" /><category term="Docker" /><summary type="html"><![CDATA[A Jenkins cloud agent using Docker refers to dynamically provisioning Jenkins build agents (also called slaves or nodes) in Docker containers—typically on demand—rather than using pre-provisioned static nodes. This allows Jenkins to scale efficiently and cleanly by creating isolated environments for each build. Today, I will show you how to configure Docker cloud on Jenkins.]]></summary></entry><entry><title type="html">Implementing Mutual TLS (mTLS) Authentication with OpenSSL: A Step-by-Step Guide</title><link href="https://www.bidhankhatri.com.np/system/Implementing-Mutal-TLS-Authentication-with-OpenSSL/" rel="alternate" type="text/html" title="Implementing Mutual TLS (mTLS) Authentication with OpenSSL: A Step-by-Step Guide" /><published>2025-03-09T02:16:41+00:00</published><updated>2025-03-09T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/Implementing-Mutal-TLS-Authentication-with-OpenSSL</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/Implementing-Mutal-TLS-Authentication-with-OpenSSL/"><![CDATA[<p>This article explores mutual Transport Layer Security (mTLS) authentication and how OpenSSL can facilitate its implementation. Also known as client-server authentication, mTLS is a robust security mechanism that requires both the client and server to present valid digital certificates before establishing a secure connection. This additional layer of authentication ensures that only trusted entities can access protected resources.</p>

<p>We will set up mutual TLS (mTLS) using two intermediate CAs—one for server certificates and another for client certificates—both signed by the same root CA.<br />
<br /><br />
<b>Steps Overview:</b></p>
<ol>
  <li>Create Root CA</li>
  <li>Create Two Intermediate CAs (one for server, one for client)</li>
  <li>Generate Server Certificate (signed by Server Intermediate CA)</li>
  <li>Generate Client Certificate (signed by Client Intermediate CA)</li>
  <li>Start OpenSSL Server with mTLS</li>
  <li>Test with curl</li>
</ol>
<div> 
<script src="/assets/js/mermaid.min.js"></script>
<div class="mermaid">

sequenceDiagram
    participant Client
    participant Server
    participant CA as Certificate Authority (CA)

    Client-&gt;&gt;Server: Client Hello (Supported Cipher Suites, TLS Version)
    Server-&gt;&gt;Client: Server Hello + Server Certificate
    Client-&gt;&gt;CA: Validate Server Certificate
    CA--&gt;&gt;Client: Valid / Invalid Response

    alt Server Certificate Valid
        Server-&gt;&gt;Client: Request Client Certificate
        Client-&gt;&gt;Server: Send Client Certificate
        Server-&gt;&gt;CA: Validate Client Certificate
        CA--&gt;&gt;Server: Valid / Invalid Response

        alt Client Certificate Valid
            Server-&gt;&gt;Client: TLS Handshake Complete (Secure Session Established)
            Client-&gt;&gt;Server: Encrypted Data Exchange Begins
        else Client Certificate Invalid
            Server--&gt;&gt;Client: Terminate Connection
        end
    else Server Certificate Invalid
        Client--&gt;&gt;Server: Terminate Connection
    end


</div></div>

<p><br />
<b>Step 1: Create the Root CA</b><br />
A Root Certificate Authority (Root CA) is the top-level trust anchor in a Public Key Infrastructure (PKI) that issues and signs digital certificates. It is self-signed and is used to verify and establish trust in all certificates issued under it.<br />
First commmand generate the private key as a file rootCA.key and second will creates a self-signed Root CA certificate (rootCA.crt) valid for 10 years.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl genrsa -out rootCA.key 4096</span> 
<span class="s">openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt -subj "/C=US/ST=State/L=City/O=RootCA/OU=IT/CN=root-ca"</span>  
</code></pre></div></div>
<p>🔹 <span style="color:#e83e8c">genrsa</span> → Generates a RSA private key.<br />
🔹 <span style="color:#e83e8c">-out rootCA.key</span> → Saves the key as rootCA.key.<br />
🔹 <span style="color:#e83e8c">4096</span> → Specifies the key size (4096 bits) for strong security.<br />
🔹 <span style="color:#e83e8c">req</span> → Runs OpenSSL’s Certificate Signing Request (CSR) command.<br />
🔹 <span style="color:#e83e8c">-x509</span> → Creates a self-signed certificate instead of a CSR.<br />
🔹 <span style="color:#e83e8c">-new</span> → Generates a new certificate request.<br />
🔹 <span style="color:#e83e8c">-nodes</span> → No password protection on the private key (useful for automation).<br />
🔹 <span style="color:#e83e8c">-key rootCA.key</span> → Uses the previously generated private key.<br />
🔹 <span style="color:#e83e8c">-sha256</span> → Uses SHA-256 as the hashing algorithm for the signature.<br />
🔹 <span style="color:#e83e8c">-days 3650</span> → Sets the certificate’s validity period to 3650 days (10 years).<br />
🔹 <span style="color:#e83e8c">-out rootCA.crt</span> → Saves the Root CA certificate as rootCA.crt.<br />
🔹 <span style="color:#e83e8c">-subj “/C=US/ST=State/L=City/O=RootCA/OU=IT/CN=root-ca”</span>  <br />
/C=US → Country (US)<br />
/ST=State → State name<br />
/L=City → City name<br />
/O=RootCA → Organization (Root CA)<br />
/OU=IT → Organizational Unit (IT)<br />
/CN=root-ca → Common Name (Root CA’s name)<br />
<br />
<br /></p>

<p><b>Step 2: Create Two Intermediate CAs</b><br />
Create a Server Intermediate Certificate that is signed by the Root CA. <br />
<b>CA:TRUE</b> in Basic Constraints is required for a CA certificate, so let’s create a file to add Basic Constraints to the intermediate certificates.<br />
<span style="color:#e83e8c">vim intermediate_ca_ext.cnf</span></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">basicConstraints=CA:TRUE,pathlen:0</span>
<span class="s">keyUsage = critical, keyCertSign, cRLSign</span>
<span class="s">subjectKeyIdentifier=hash</span>
<span class="s">authorityKeyIdentifier=keyid:always,issuer</span>
</code></pre></div></div>

<p><span style="color:#4a6ee0">Server Intermediate CA</span></p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl genrsa -out server-intermediateCA.key </span><span class="m">4096</span>
<span class="s">openssl req -new -key server-intermediateCA.key -out server-intermediateCA.csr -subj "/C=US/ST=State/L=City/O=ServerCA/OU=IT/CN=server-intermediate-ca"</span>
<span class="s">openssl x509 -req -in server-intermediateCA.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server-intermediateCA.crt -days 3650 -sha256 -extfile intermediate_ca_ext.cnf</span>
</code></pre></div></div>
<p>First, This private key will be used to sign server certificates issued by this intermediate CA.<br />
Second, This step generates a CSR that will be signed by the Root CA.<br />
Third, This step creates a Server Intermediate CA certificate, signed by the Root CA. <br />
🔹 <span style="color:#e83e8c">server-intermediateCA.key</span> → Private key for the Server Intermediate CA.<br />
🔹 <span style="color:#e83e8c">server-intermediateCA.csr</span> &gt; → CSR for the Server Intermediate CA.<br />
🔹 <span style="color:#e83e8c">server-intermediateCA.crt</span> &gt; → Intermediate CA certificate signed by the Root CA.<br />
🔹 <span style="color:#e83e8c">rootCA.srl</span> &gt; → Serial number file for tracking issued certificates.<br />
<br />
<span style="color:#4a6ee0">Client Intermediate CA</span><br />
Create a Client Intermediate Certificate Authority that is signed by the Root CA.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl genrsa -out client-intermediateCA.key </span><span class="m">4096</span>
<span class="s">openssl req -new -key client-intermediateCA.key -out client-intermediateCA.csr -subj "/C=US/ST=State/L=City/O=ClientCA/OU=IT/CN=client-intermediate-ca"</span>
<span class="s">openssl x509 -req -in client-intermediateCA.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client-intermediateCA.crt -days 3650 -sha256 -extfile intermediate_ca_ext.cnf</span>
</code></pre></div></div>
<p>🔹 <span style="color:#e83e8c">client-intermediateCA.key</span> → Private key for the Client Intermediate CA.<br />
🔹 <span style="color:#e83e8c">client-intermediateCA.csr</span> &gt; → CSR for the Client Intermediate CA.<br />
🔹 <span style="color:#e83e8c">client-intermediateCA.crt</span> &gt; → Intermediate CA certificate signed by the Root CA.<br />
🔹 <span style="color:#e83e8c">rootCA.srl</span> &gt; → Serial number file for tracking issued certificates.<br />
<br /></p>

<p><b>Step 3: Generate Server Certificate (Signed by Server Intermediate CA)</b><br />
Generates a server certificate signed by the Server Intermediate Certificate Authority (CA).</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl genrsa -out server.key </span><span class="m">4096</span>
<span class="s">openssl req -new -key server.key -out server.csr -subj "/C=US/ST=State/L=City/O=MyServer/OU=IT/CN=server.local"</span>
<span class="s">openssl x509 -req -in server.csr -CA server-intermediateCA.crt -CAkey server-intermediateCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile intermediate_ca_ext.cnf</span>
</code></pre></div></div>
<p>Server Certificate Chain:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">Root CA</span>
  <span class="s">├── Server Intermediate CA</span>
       <span class="s">├── Server Certificate (server.crt)</span>
</code></pre></div></div>
<p><b>Step 4: Generate Client Certificate (Signed by Client Intermediate CA)</b><br />
Generates a client certificate signed by the Client Intermediate Certificate Authority (CA).</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl genrsa -out client.key </span><span class="m">4096</span>
<span class="s">openssl req -new -key client.key -out client.csr -subj "/C=US/ST=State/L=City/O=MyClient/OU=IT/CN=client.local"</span>
<span class="s">openssl x509 -req -in client.csr -CA client-intermediateCA.crt -CAkey client-intermediateCA.key -CAcreateserial -out client.crt -days 3650 -sha256</span>
</code></pre></div></div>
<p>Client Certificate Chain:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">Root CA</span>
  <span class="s">├── Client Intermediate CA</span>
       <span class="s">├── Client Certificate (client.crt)</span>
</code></pre></div></div>
<p>Check the Basic Constraints: CA:TRUE in the Basic Constraints extension of an X.509 certificate indicates that the certificate is a Certificate Authority (CA), so it must be present in both the root and intermediate certificates.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl x509 -in server-intermediateCA.crt -noout -text | grep -A1 "Basic Constraints"</span>              

            <span class="s">X509v3 Basic Constraints</span><span class="err">:</span>
                <span class="s">CA:TRUE, pathlen:0</span>

<span class="s">openssl x509 -in client-intermediateCA.crt -noout -text | grep -A1 "Basic Constraints"</span> 

            <span class="s">X509v3 Basic Constraints</span><span class="err">:</span>
                <span class="s">CA:TRUE, pathlen:0</span>
</code></pre></div></div>

<p><span style="color:#e83e8c">Create CA bundle:</span>
Concating both Intermediate certs and root CA into a single file.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">cat server-intermediateCA.crt client-intermediateCA.crt rootCA.crt &gt; ca-bundle.crt</span>
</code></pre></div></div>
<p><b>Verify certificates:</b></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl verify -CAfile ca-bundle.crt server.crt</span>
<span class="na">server.crt</span><span class="pi">:</span> <span class="s">OK</span>

<span class="s">openssl verify -CAfile ca-bundle.crt client.crt</span>
<span class="na">client.crt</span><span class="pi">:</span> <span class="s">OK</span>
</code></pre></div></div>
<p>The output of these commands indicates that both server.crt and client.crt are valid and properly signed by a Certificate Authority (CA).</p>

<h5 id="step-5-start-openssl-server-with-mtls">Step 5: Start OpenSSL Server with mTLS</h5>
<p>Run the server, requiring client authentication:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl s_server -accept 8443 -cert server.crt -key server.key -CAfile ca-bundle.crt -Verify </span><span class="m">2</span>
</code></pre></div></div>
<p>🔹 <span style="color:#e83e8c">s_server</span> → Starts an SSL/TLS server.<br />
🔹 <span style="color:#e83e8c">-accept 8443</span>  → The server listens on port 8443 for incoming TLS connections.<br />
🔹 <span style="color:#e83e8c">-cert server.crt</span>  → Uses server.crt as the server’s certificate.<br />
🔹 <span style="color:#e83e8c">-key server.key</span>  → Uses server.key as the private key for the server.<br />
🔹 <span style="color:#e83e8c">-CAfile ca-bundle.crt</span>  → Specifies a file containing trusted CA certificates (intermediate + root CAs) to verify client certificates.<br />
🔹 <span style="color:#e83e8c">-Verify 2</span>  → Requires mutual TLS (mTLS), meaning: The server will request a client certificate. The depth 2 means it will accept client certificates issued up to 2 levels deep in the CA hierarchy (e.g., <b>Root CA → Intermediate CA → Client Certificate</b>).</p>

<h5 id="step-6-test-with-curl">Step 6: Test with curl</h5>
<p>Use curl with client authentication:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -v --cert client.crt --key client.key --cacert ca-bundle.crt https://server.local:8443</span>
</code></pre></div></div>
<p>🔹 <span style="color:#e83e8c"> -v</span> → Enables verbose mode, showing detailed SSL handshake and request/response details.<br />
🔹 <span style="color:#e83e8c"> –cert client.crt</span>  → Specifies the client certificate to authenticate with the server.<br />
🔹 <span style="color:#e83e8c"> –key client.key</span>  → Specifies the private key for the client certificate.<br />
🔹 <span style="color:#e83e8c"> –cacert ca-bundle.crt</span>  → Specifies the trusted CA certificate bundle to verify the server’s certificate.<br />
🔹 <span style="color:#e83e8c">https://server.local:8443 </span> → The target URL of the server, using HTTPS on port 8443.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">openssl s_server -accept 8443 -cert server.crt -key server.key -CAfile full-chain.crt -Verify </span><span class="m">2</span>

<span class="s">verify depth is 2, must return a certificate</span>
<span class="s">Using auto DH parameters</span>
<span class="s">ACCEPT</span>

<span class="s">depth=2 C = US, ST = State, L = City, O = RootCA, OU = IT, CN = root-ca</span>
<span class="s">verify return:1</span>
<span class="s">depth=1 C = US, ST = State, L = City, O = ClientCA, OU = IT, CN = client-intermediate-ca</span>
<span class="s">verify return:1</span>
<span class="s">depth=0 C = US, ST = State, L = City, O = MyClient, OU = IT, CN = client.local</span>
<span class="s">verify return:1</span>
<span class="s">Write BLOCK</span>
<span class="s">GET / HTTP/1.1</span>
<span class="na">Host</span><span class="pi">:</span> <span class="s">server.local:8443</span>
<span class="na">User-Agent</span><span class="pi">:</span> <span class="s">curl/8.7.1</span>
<span class="na">Accept</span><span class="pi">:</span> <span class="err">*</span><span class="s">/*</span>

<span class="s">DONE</span>
<span class="s">shutting down SSL</span>
<span class="s">CONNECTION CLOSED</span>
<span class="s">ACCEPT</span>
</code></pre></div></div>
<p>First, Server verified the Root CA (which issued the intermediate CA). The server verified the Client Intermediate CA. The client’s certificate was issued by this intermediate CA.And, verify return:1 means verification was successful.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -v --cert client.crt --key client.key --cacert full-chain.crt https://server.local:8443</span>

<span class="err">*</span> <span class="s">Host server.local:8443 was resolved.</span>
<span class="na">* IPv6</span><span class="pi">:</span> <span class="s">(none)</span>
<span class="na">* IPv4</span><span class="pi">:</span> <span class="s">127.0.0.1</span>
<span class="err">*</span>   <span class="s">Trying 127.0.0.1:8443...</span>
<span class="err">*</span> <span class="s">Connected to server.local (127.0.0.1) port </span><span class="m">8443</span>
<span class="na">* ALPN</span><span class="pi">:</span> <span class="s">curl offers h2,http/1.1</span>
<span class="err">*</span> <span class="s">(304) (OUT), TLS handshake, Client hello (1)</span><span class="err">:</span>
<span class="na">*  CAfile</span><span class="pi">:</span> <span class="s">full-chain.crt</span>
<span class="na">*  CApath</span><span class="pi">:</span> <span class="s">none</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, Server hello (2)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, Unknown (8)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, Request CERT (13)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, Certificate (11)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, CERT verify (15)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (IN), TLS handshake, Finished (20)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (OUT), TLS handshake, Certificate (11)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (OUT), TLS handshake, CERT verify (15)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">(304) (OUT), TLS handshake, Finished (20)</span><span class="err">:</span>
<span class="err">*</span> <span class="s">SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF</span>
<span class="na">* ALPN</span><span class="pi">:</span> <span class="s">server did not agree on a protocol. Uses default.</span>
<span class="na">* Server certificate</span><span class="pi">:</span>
<span class="na">*  subject</span><span class="pi">:</span> <span class="s">C=US; ST=State; L=City; O=MyServer; OU=IT; CN=server.local</span>
<span class="na">*  start date</span><span class="pi">:</span> <span class="s">Mar 10 17:18:54 2025 GMT</span>
<span class="na">*  expire date</span><span class="pi">:</span> <span class="s">Mar  8 17:18:54 2035 GMT</span>
<span class="na">*  common name</span><span class="pi">:</span> <span class="s">server.local (matched)</span>
<span class="na">*  issuer</span><span class="pi">:</span> <span class="s">C=US; ST=State; L=City; O=ServerCA; OU=IT; CN=server-intermediate-ca</span>
<span class="err">*</span>  <span class="s">SSL certificate verify ok.</span>
<span class="err">*</span> <span class="s">using HTTP/1.x</span>
<span class="pi">&gt;</span> <span class="err">GET</span> <span class="err">/</span> <span class="err">HTTP/1.1</span>
<span class="err">&gt;</span><span class="s"> Host: server.local:8443</span>
<span class="err">&gt;</span><span class="s"> User-Agent: curl/8.7.1</span>
<span class="err">&gt;</span><span class="s"> Accept: */*</span>
<span class="err">&gt;</span>
<span class="err">*</span><span class="s"> Request completely sent off</span>
</code></pre></div></div>
<p>Curl command successfully established a mutual TLS (mTLS) connection with the server. mTLS was successfully established. The server authenticated itself to the client.The client authenticated itself to the server.A secure session was created, and data exchange began.</p>

<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/configured-mutual-mtls-authentication.svg" width="650" height="650" style="object-fit: contain;" />
</p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<p><br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script></p>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="mTLS" /><category term="OpenSSL" /><summary type="html"><![CDATA[This article explores mutual Transport Layer Security (mTLS) authentication and how OpenSSL can facilitate its implementation. Also known as client-server authentication, mTLS is a robust security mechanism that requires both the client and server to present valid digital certificates before establishing a secure connection. This additional layer of authentication ensures that only trusted entities can access protected resources.]]></summary></entry><entry><title type="html">Unassigned shards in Elasticsearch 7 and 8</title><link href="https://www.bidhankhatri.com.np/system/unassigned-shards-in-elasticsearch/" rel="alternate" type="text/html" title="Unassigned shards in Elasticsearch 7 and 8" /><published>2024-06-27T02:16:41+00:00</published><updated>2024-06-27T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/unassigned-shards-in-elasticsearch</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/unassigned-shards-in-elasticsearch/"><![CDATA[<p>There are multiple reasons why shards might get unassigned, ranging from misconfigured allocation settings to lack of disk space.</p>

<p>To reassign all unassigned shards in Elasticsearch, you can use the following steps:<br />
<br /></p>

<p><b>STEPS:</b></p>
<ol>
  <li>Check the status of unassigned shards</li>
  <li>Check cluster allocation explanation</li>
  <li>Automatic reallocation</li>
  <li>Force reallocation of shards</li>
</ol>

<p><b>Check current cluster status</b></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XGET -u elastic:password http://172.16.0.1:9200/_cluster/health?pretty=true</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">{</span>
  <span class="s2">"</span><span class="s">cluster_name"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">devcluster"</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">status"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">red"</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">timed_out"</span> <span class="pi">:</span> <span class="nv">false</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">number_of_nodes"</span> <span class="pi">:</span> <span class="nv">3</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">number_of_data_nodes"</span> <span class="pi">:</span> <span class="nv">3</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">active_primary_shards"</span> <span class="pi">:</span> <span class="nv">24</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">active_shards"</span> <span class="pi">:</span> <span class="nv">24</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">relocating_shards"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">initializing_shards"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">unassigned_shards"</span> <span class="pi">:</span> <span class="nv">26</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">delayed_unassigned_shards"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">number_of_pending_tasks"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">number_of_in_flight_fetch"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">task_max_waiting_in_queue_millis"</span> <span class="pi">:</span> <span class="nv">0</span><span class="pi">,</span>
  <span class="s2">"</span><span class="s">active_shards_percent_as_number"</span> <span class="pi">:</span> <span class="nv">48.0</span>
<span class="pi">}</span>
</code></pre></div></div>

<p><b>Check the status of unassigned shards:</b><br />
First, you need to identify which shards are unassigned. You can do this by using the <span style="color:#e83e8c">_cat/shards</span> API. This will give you a list of all shards in your cluster along with their status.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XGET -u elastic:password http://172.16.0.1:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">.monitoring-kibana-7-2024.06.27                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.27                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.ds-.logs-deprecation.elasticsearch-default-2024.05.29-000001 0 p STARTED</span>
<span class="s">.ds-.logs-deprecation.elasticsearch-default-2024.05.29-000001 0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.apm-agent-configuration                                      0 p STARTED</span>
<span class="s">.apm-agent-configuration                                      0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.kibana_7.17.13_001                                           0 p STARTED</span>
<span class="s">.kibana_7.17.13_001                                           0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.apm-custom-link                                              0 p STARTED</span>
<span class="s">.apm-custom-link                                              0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.25                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.25                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-kibana-7-2024.06.24                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.24                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.ds-ilm-history-5-2024.05.29-000001                           0 p STARTED</span>
<span class="s">.ds-ilm-history-5-2024.05.29-000001                           0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.kibana_task_manager_7.17.13_001                              0 p STARTED</span>
<span class="s">.kibana_task_manager_7.17.13_001                              0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.tasks                                                        0 p STARTED</span>
<span class="s">.tasks                                                        0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-kibana-7-2024.06.22                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.22                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-kibana-7-2024.06.26                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.26                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.kibana_security_session_1                                    0 p UNASSIGNED INDEX_CREATED</span>
<span class="s">.kibana_security_session_1                                    0 r UNASSIGNED REPLICA_ADDED</span>
<span class="s">.monitoring-kibana-7-2024.06.25                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.25                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.27                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.27                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-kibana-7-2024.06.21                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.21                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.26                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.26                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.geoip_databases                                              0 p STARTED</span>
<span class="s">.geoip_databases                                              0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-kibana-7-2024.06.23                               0 p STARTED</span>
<span class="s">.monitoring-kibana-7-2024.06.23                               0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.23                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.23                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.kibana-event-log-7.17.13-000001                              0 p STARTED</span>
<span class="s">.kibana-event-log-7.17.13-000001                              0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.22                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.22                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.21                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.21                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.monitoring-es-7-2024.06.24                                   0 p STARTED</span>
<span class="s">.monitoring-es-7-2024.06.24                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>
<span class="s">.security-7                                                   0 p STARTED</span>
<span class="s">.security-7                                                   0 r UNASSIGNED CLUSTER_RECOVERED</span>

</code></pre></div></div>

<p><b>Check cluster allocation explanation:</b><br />
To understand why shards are unassigned, you can use the <span style="color:#e83e8c">_cluster/allocation/explain</span> API. This will provide a detailed explanation of the allocation decisions.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XGET -u elastic:password http://172.16.0.1:9200/_cluster/allocation/explain?pretty"</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
    <span class="pi">{</span>
      <span class="s2">"</span><span class="s">node_id"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">vO3fSByBTxOCFzNsql-95g"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">node_name"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">dev01"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">transport_address"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">172.16.0.2:9300"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">node_attributes"</span> <span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">ml.machine_memory"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">6067675136"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">ml.max_open_jobs"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">512"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">xpack.installed"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">ml.max_jvm_size"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">2147483648"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">transform.node"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
      <span class="pi">},</span>
      <span class="s2">"</span><span class="s">node_decision"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">no"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">weight_ranking"</span> <span class="pi">:</span> <span class="nv">2</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">deciders"</span> <span class="pi">:</span> <span class="pi">[</span>
        <span class="pi">{</span>
          <span class="s2">"</span><span class="s">decider"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">enable"</span><span class="pi">,</span>
          <span class="s2">"</span><span class="s">decision"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">NO"</span><span class="pi">,</span>
          <span class="s2">"</span><span class="s">explanation"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">no</span><span class="nv"> </span><span class="s">allocations</span><span class="nv"> </span><span class="s">are</span><span class="nv"> </span><span class="s">allowed</span><span class="nv"> </span><span class="s">due</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">cluster</span><span class="nv"> </span><span class="s">setting</span><span class="nv"> </span><span class="s">[cluster.routing.allocation.enable=none]"</span>
        <span class="pi">}</span>
      <span class="pi">]</span>
    <span class="pi">}</span><span class="err">,</span>
    <span class="pi">{</span>
      <span class="s2">"</span><span class="s">node_id"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">kXPPZQDzTBu4ClLeWO95qQ"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">node_name"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">dev02"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">transport_address"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">172.16.0.3:9300"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">node_attributes"</span> <span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">ml.machine_memory"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">6067666944"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">ml.max_open_jobs"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">512"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">xpack.installed"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">ml.max_jvm_size"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">2147483648"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">transform.node"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
      <span class="pi">},</span>
      <span class="s2">"</span><span class="s">node_decision"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">no"</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">weight_ranking"</span> <span class="pi">:</span> <span class="nv">3</span><span class="pi">,</span>
      <span class="s2">"</span><span class="s">deciders"</span> <span class="pi">:</span> <span class="pi">[</span>
        <span class="pi">{</span>
          <span class="s2">"</span><span class="s">decider"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">enable"</span><span class="pi">,</span>
          <span class="s2">"</span><span class="s">decision"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">NO"</span><span class="pi">,</span>
          <span class="s2">"</span><span class="s">explanation"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">no</span><span class="nv"> </span><span class="s">allocations</span><span class="nv"> </span><span class="s">are</span><span class="nv"> </span><span class="s">allowed</span><span class="nv"> </span><span class="s">due</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">cluster</span><span class="nv"> </span><span class="s">setting</span><span class="nv"> </span><span class="s">[cluster.routing.allocation.enable=none]"</span>
        <span class="pi">}</span>
</code></pre></div></div>
<p><br />
<b>Automatic reallocation:</b><br />
Sometimes, cluster settings might prevent the allocation of shards. You can check your current cluster settings with:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XGET -u elastic:password http://172.16.0.1:9200/_cluster/settings?pretty"</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">{</span>
    <span class="s2">"</span><span class="s">peristent"</span> <span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">cluster"</span> <span class="pi">:</span> <span class="pi">{</span>
            <span class="s2">"</span><span class="s">routing"</span> <span class="pi">:</span> <span class="pi">{</span>
                <span class="s2">"</span><span class="s">allocation"</span> <span class="pi">:</span> <span class="pi">{</span>
                    <span class="s2">"</span><span class="s">enable"</span> <span class="pi">:</span> <span class="s2">"</span><span class="s">none"</span>
                <span class="pi">}</span>
            <span class="pi">}</span>
        <span class="pi">}</span>
    <span class="pi">}</span>
<span class="pi">}</span>
</code></pre></div></div>
<p>If shard allocation is disabled just like above, you can enable it by updating the cluster settings:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XPUT -u elastic:password http://172.16.0.1:9200/_cluster/settings" -H 'Content-Type</span><span class="err">:</span> <span class="s">application/json' -d '{</span>
  <span class="s">"persistent"</span><span class="err">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">cluster.routing.allocation.enable"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">all"</span>
  <span class="pi">}</span>
<span class="err">}</span><span class="s1">'</span>
</code></pre></div></div>
<p><br /></p>

<p><b>Force reallocation of shards:</b><br />
If you want to manually allocate an unassigned shard to a specific node, you can use the <span style="color:#e83e8c">_cluster/reroute</span> API. Here’s how to force the allocation of a stale primary shard:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XPOST -u elastic:password http://172.16.0.1:9200/_cluster/reroute" -H 'Content-Type</span><span class="err">:</span> <span class="s">application/json' -d '{</span>
  <span class="s">"commands"</span><span class="err">:</span> <span class="pi">[</span>
    <span class="pi">{</span>
      <span class="s2">"</span><span class="s">allocate_stale_primary"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">index"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">index_name"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">shard"</span><span class="pi">:</span> <span class="nv">shard_number</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">node"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">node_name"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">accept_data_loss"</span><span class="pi">:</span> <span class="nv">true</span>
      <span class="pi">}</span>
    <span class="pi">}</span>
  <span class="pi">]</span>
<span class="err">}</span><span class="s1">'</span>
</code></pre></div></div>
<p>Replace <span style="color:#e83e8c">index_name</span>, <span style="color:#e83e8c">shard_number</span>, and <span style="color:#e83e8c">node_name</span> with the appropriate values.<br />
<span style="color:#e83e8c">index:</span> The name of the index to which the shard belongs.<br />
<span style="color:#e83e8c">shard:</span> The shard number.<br />
<span style="color:#e83e8c">node:</span> The name of the node to which you want to assign the shard.<br />
<span style="color:#e83e8c">accept_data_loss:</span> Set to true if you want to allocate a stale primary shard, acknowledging that data loss might occur.<br />
<br />
To allocate an unassigned replica shard, you can use:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">curl -XPOST -u elastic:password http://172.16.0.1:9200/_cluster/reroute" -H 'Content-Type</span><span class="err">:</span> <span class="s">application/json' -d '{</span>
  <span class="s">"commands"</span><span class="err">:</span> <span class="pi">[</span>
    <span class="pi">{</span>
      <span class="s2">"</span><span class="s">allocate_replica"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">index"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">index_name"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">shard"</span><span class="pi">:</span> <span class="nv">shard_number</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">node"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">node_name"</span>
      <span class="pi">}</span>
    <span class="pi">}</span>
  <span class="pi">]</span>
<span class="err">}</span><span class="s1">'</span>
</code></pre></div></div>
<p>To get the shard number and index name of unassigned shards in Elasticsearch, you can use the <span style="color:#e83e8c">_cat/shards</span> API. This API provides detailed information about all the shards in your cluster, including their index names, shard numbers, and current states.<br />
<br /></p>

<p><b>Additional Steps and Considerations:</b><br />
<span style="color:#e83e8c">Check Node Availability:</span> Ensure all nodes are up and running. Unassigned shards might be due to nodes being down or unreachable.</p>

<p><span style="color:#e83e8c">Disk Space:</span> Make sure there is enough disk space on the nodes. Elasticsearch won’t allocate shards to nodes running low on disk space.<br />
<br />
By following these steps, you should be able to diagnose and resolve issues related to unassigned shards in your Elasticsearch cluster.</p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<p><br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script></p>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="ElasticSearch" /><summary type="html"><![CDATA[There are multiple reasons why shards might get unassigned, ranging from misconfigured allocation settings to lack of disk space.]]></summary></entry><entry><title type="html">Percona XtraDB Multi-Master Replication cluster setup between 3 nodes</title><link href="https://www.bidhankhatri.com.np/system/Percona-XTraDB-Multi-Master-Replication-cluster-setup-between-3-nodes/" rel="alternate" type="text/html" title="Percona XtraDB Multi-Master Replication cluster setup between 3 nodes" /><published>2024-04-22T02:16:41+00:00</published><updated>2024-04-22T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/Percona-XTraDB-Multi-Master-Replication-cluster-setup-between-3-nodes</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/Percona-XTraDB-Multi-Master-Replication-cluster-setup-between-3-nodes/"><![CDATA[<p>This guide describes the steps to establish a Percona XtraDB Cluster v8.0 among three Ubuntu 22.04 nodes.</p>

<p>Install Percona XtraDB Cluster on all hosts that you are planning to use as cluster nodes and ensure you have root access to the MySQL server on each node. In this setup, <span style="color:#e83e8c">Multi-Master replication</span> is implemented.</p>

<p>In Multi-Master replication, there are multiple nodes acting as master nodes. Data is replicated between nodes, allowing updates and insertions on a group of master nodes, resulting in multiple copies of the data.<br />
<br /></p>

<p align="center">
    <img src="https://www.bidhankhatri.com.np/assets/images/percona.png" />

</p>

<table>
  <thead>
    <tr>
      <th>Node</th>
      <th>IP</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>node1</td>
      <td>172.16.0.12</td>
    </tr>
    <tr>
      <td>node2</td>
      <td>172.16.0.13</td>
    </tr>
    <tr>
      <td>node3</td>
      <td>172.16.0.14</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<p>Execute installation commands in all 3 nodes.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">apt update</span>
<span class="s">apt install -y wget gnupg2 lsb-release curl</span>
<span class="s">wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb</span>
<span class="s">dpkg -i percona-release_latest.generic_all.deb</span>
<span class="s">apt update</span>
<span class="s">percona-release setup pxc80</span>
<span class="s">apt install percona-xtradb-cluster</span>
</code></pre></div></div>

<h3 id="encrypt-pxc-traffic">Encrypt PXC Traffic</h3>
<p>There are two kinds of traffic in Percona XtraDB Cluster: <span style="color:#e83e8c">client-server traffic</span> (the one between client applications and cluster nodes), and <span style="color:#e83e8c">replication traffic</span>, which includes SST, IST, write-set replication, and various service messages.</p>

<p>Percona XtraDB Cluster supports encryption for all types of traffic. Replication traffic encryption can be configured either automatically or manually. In this guide, we are configuring automatic version.</p>

<p><span style="color:#e83e8c">SST (State Snapshot Transfer)</span> is the full copy of data from one node to another. It’s used when a new node joins the cluster and needs to transfer data from an existing node.</p>

<p><span style="color:#e83e8c">IST (Incremental State Transfer)</span> is a functionality which, instead of transferring the whole state snapshot, catches up with the group by receiving the missing writesets, but only if the writeset is still in the donor’s writeset cache.</p>

<h3 id="encrypt-relication-traffic">Encrypt Relication Traffic</h3>
<p>Replication traffic refers to the inter-node traffic, which includes SST, IST, and regular replication traffic.<br />
Percona XtraDB Cluster supports a single configuration option to secure the entire replication traffic, often referred to as SSL automatic configuration. Alternatively, you can configure the security of each channel by specifying independent parameters.<br />
The automatic SSL encryption configuration requires key and certificate files. MySQL generates default key and certificate files and places them in the data directory.<br />
Percona XtraDB Cluster includes the <span style="color:#e83e8c">pxc-encrypt-cluster-traffic</span> variable, enabling automatic SSL encryption for SST, IST, and replication traffic.<br />
By default, pxc-encrypt-cluster-traffic is enabled, ensuring a secured channel for replication. This variable is not dynamic and cannot be changed at runtime.<br />
If you wish to disable encryption for replication traffic, you must stop the cluster and update the <span style="color:#e83e8c">[mysqld]</span> section of the configuration file on each node with <span style="color:#e83e8c">pxc-encrypt-cluster-traffic=OFF</span>. Then, restart the cluster.</p>

<p>But in our case we are not disabling the encryption traffic between the nodes so follow the below steps. Update/Add these variables in <span style="color:#e83e8c">/etc/mysql/mysql.conf.d/mysqld.cnf</span> file.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">wsrep_node_name=node1</span>
<span class="s">wsrep_node_address=172.16.0.12</span>
<span class="s">pxc_strict_mode=ENFORCING</span>
<span class="s">wsrep_provider=/usr/lib/galera4/libgalera_smm.so</span>
<span class="s">wsrep_cluster_name=pxc-cluster</span>
<span class="s">wsrep_cluster_address=gcomm://172.16.0.12,172.16.0.13,172.16.0.14</span>
</code></pre></div></div>
<p>Similary add/update above variables in other 2 nodes too.
<span style="color:#e83e8c">wsrep_node_name</span> and <span style="color:#e83e8c">wsrep_node_address</span> variables only need to be updated as per node. Other parameters remain same.</p>

<p>Now Bootstrap the first node.
After you configure all PXC nodes, initialize the cluster by bootstrapping the first node. The initial node must contain all the data that you want to be replicated to other nodes.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">systemctl start mysql@bootstrap.service</span>
</code></pre></div></div>
<p>When you start the node using the previous command, it runs in bootstrap mode with <span style="color:#e83e8c">wsrep_cluster_address=gcomm://</span>. This tells the node to initialize the cluster with <span style="color:#e83e8c">wsrep_cluster_conf_id</span> variable set to <span style="color:#e83e8c">1</span>. After you add other nodes to the cluster, you can then restart this node as normal, and it will use standard configuration again.</p>

<p>To make sure that the cluster has been initialized, run the following:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">show status like 'wsrep%';</span>
</code></pre></div></div>
<p>The output shows that the cluster size is 1 node, it is the primary component, the node is in the Synced state, it is fully connected and ready for write-set replication.</p>

<p>Now copy the certs from node1 to other 2 remaining nodes. Certs will be in path <span style="color:#e83e8c">/var/lib/mysql.</span><br />
It is important that your cluster use the same SSL certificates and keys on all nodes.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">bdn@node02:~$ ls /var/lib/mysql/*pem</span>
<span class="s">/var/lib/mysql/ca-key.pem       /var/lib/mysql/client-key.pem   /var/lib/mysql/server-cert.pem</span>
<span class="s">/var/lib/mysql/ca.pem           /var/lib/mysql/private_key.pem  /var/lib/mysql/server-key.pem</span>
<span class="s">/var/lib/mysql/client-cert.pem  /var/lib/mysql/public_key.pem</span>
</code></pre></div></div>

<p>To verify that the server and client certificates are correctly signed by the same CA certificate, run the following command:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">bdn@node02:/var/lib/mysql$ openssl verify -CAfile ca.pem server-cert.pem client-cert.pem</span>
<span class="na">server-cert.pem</span><span class="pi">:</span> <span class="s">OK</span>
<span class="na">client-cert.pem</span><span class="pi">:</span> <span class="s">OK</span>
</code></pre></div></div>
<p>By default, it generates certificates valid for 10 years.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">bdn@node02:/var/lib/mysql$ openssl x509 -enddate -noout -in server-cert.pem</span>
<span class="s">notAfter=Apr 17 13:36:50 2034 GMT</span>
</code></pre></div></div>
<p>Now start mysql in second node.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">systemctl start mysql</span>
</code></pre></div></div>
<p>Similary, start mysql service in third node too.<br />
Now you can stop the <span style="color:#e83e8c">mysql@bootstrap.service</span> service in node1 and start <span style="color:#e83e8c">mysql service</span><br />
Now all 3 nodes are connected to the cluster.</p>

<p>You will see similar logs in <span style="color:#e83e8c"><code class="language-plaintext highlighter-rouge">/var/log/mysql/error.log</code></span> file.
<br /></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">2024-04-21T17:21:17.442573Z 1 [Note] [MY-000000] [Galera] ===========================================</span>
<span class="s">=====</span>
<span class="na">View</span><span class="pi">:</span>
  <span class="na">id</span><span class="pi">:</span> <span class="s">0c233cf9-fe50-11ee-a3e9-dfbfab2fa1fe:31</span>
  <span class="na">status</span><span class="pi">:</span> <span class="s">primary</span>
  <span class="na">protocol_version</span><span class="pi">:</span> <span class="m">4</span>
  <span class="na">capabilities</span><span class="pi">:</span> <span class="s">MULTI-MASTER, CERTIFICATION, PARALLEL_APPLYING, REPLAY, ISOLATION, PAUSE, CAUSAL_READ</span>
<span class="err">,</span> <span class="s">INCREMENTAL_WS, UNORDERED, PREORDERED, STREAMING, NBO</span>
  <span class="s">final</span><span class="err">:</span> <span class="s">no</span>
  <span class="s">own_index</span><span class="err">:</span> <span class="m">0</span>
  <span class="na">members(3)</span><span class="pi">:</span>
        <span class="na">0</span><span class="pi">:</span> <span class="s">60c1bc1f-0003-11ef-bae7-7347836516bcb, node01</span>
        <span class="na">1</span><span class="pi">:</span> <span class="s">7b9841ae-0003-11ef-8586-4b8504592b099, node02</span>
        <span class="na">2</span><span class="pi">:</span> <span class="s">8f92b85a-0003-11ef-b411-5a2ad6404a18f, node03</span>
</code></pre></div></div>

<p>Now, any changes made to any MySQL server will automatically be reflected on the other nodes.</p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<p><br />
<br />
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script></p>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="mysql" /><summary type="html"><![CDATA[This guide describes the steps to establish a Percona XtraDB Cluster v8.0 among three Ubuntu 22.04 nodes.]]></summary></entry><entry><title type="html">Site-to-Site VPN between Mikrotik router and Ubuntu 22.04 through strongSwan using IPsec IKEv2</title><link href="https://www.bidhankhatri.com.np/vpn/Site-to-Site-VPN-between-Mikrotik-router-and-Ubuntu-22.04-through-strongSwan-using-IPsec-IKEv2/" rel="alternate" type="text/html" title="Site-to-Site VPN between Mikrotik router and Ubuntu 22.04 through strongSwan using IPsec IKEv2" /><published>2024-04-13T02:16:41+00:00</published><updated>2024-04-13T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/vpn/Site-to-Site-VPN-between-Mikrotik-router-and-Ubuntu-22.04-through-strongSwan-using-IPsec-IKEv2</id><content type="html" xml:base="https://www.bidhankhatri.com.np/vpn/Site-to-Site-VPN-between-Mikrotik-router-and-Ubuntu-22.04-through-strongSwan-using-IPsec-IKEv2/"><![CDATA[<p>We will configure a site-to-site IPsec IKEv2 tunnel between the Mikrotik Router and the StrongSwan server. This will enable secure communication between devices connected behind the Mikrotik router and the StrongSwan server.</p>

<div> 
<script src="/assets/js/mermaid.min.js"></script>
<div class="mermaid">
flowchart LR;
    subgraph Mikrotik Site
        srv01(srv01\n172.16.1.14/24) &lt;--172.16.1.0/24 --&gt; mikrotik((Router))
    end

    subgraph INTERNET
    VPN((IKEv2))

    end

    subgraph strongSwan Site
        strongSwan(strongSwan\n2.2.2.2/32 - WAN IP - eth0\n17.16.2.10/24 - LAN IP - eth1\n) -- 172.16.2.0/24 &lt;--&gt; srv02(srv02\n 172.16.2.14/24)
    end

    mikrotik(Mikrotik Router\n 1.1.1.1/32 - WAN IP\n172.16.1.1/24 - LAN IP) == IPSec Tunnel &lt;==&gt; INTERNET ==IPSec Tunnel &lt;==&gt; strongSwan
    style mikrotik fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
    style strongSwan fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5

</div></div>

<h3 id="install-strongswan-in-ubuntu">Install strongSwan in ubuntu</h3>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">apt-get install strongswan libcharon-extra-plugins strongswan-pki</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/ipsec.conf</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">config setup</span>
        <span class="s">charondebug="all"</span>
        <span class="s">uniqueids=yes</span>
        <span class="s">strictcrlpolicy=no</span>
<span class="s">conn B1-TO-HO</span>
        <span class="s">authby=secret</span>
        <span class="s">left=%defaultroute</span>
        <span class="s">leftid=2.2.2.2</span>
        <span class="s">leftsubnet=172.16.2.0/24</span>
        <span class="s">right=1.1.1.1</span>
        <span class="s">rightsubnet=172.16.1.0/24</span>  
        <span class="s">ike=aes256-sha2_256-modp1024!</span>
        <span class="s">esp=aes256-sha256-modp1024!</span>
        <span class="s">keyingtries=0</span>
        <span class="s">ikelifetime=1h</span>
        <span class="s">lifetime=8h</span>
        <span class="s">dpddelay=30</span>

</code></pre></div></div>
<p><span style="color:#e83e8c">config setup:</span> This section contains global configuration options for StrongSwan.<br />
<span style="color:#e83e8c">charondebug=”all”:</span> This option sets the debugging level for the IKE daemon (charon) to “all”, which means it will log all debugging messages. This can be useful for troubleshooting.<br />
<span style="color:#e83e8c">uniqueids=yes:</span> This option ensures that each IKE_SA (Internet Key Exchange Security Association) has a unique ID. This helps avoid conflicts in case multiple connections are established.<br />
<span style="color:#e83e8c">strictcrlpolicy=no:</span> This option specifies whether strict certificate revocation list (CRL) checking is enforced. Setting it to “no” means that CRL checking will not be strictly enforced.<br />
<span style="color:#e83e8c">conn B1-TO-HO:</span> This section defines a specific connection between two peers.<br />
<span style="color:#e83e8c">authby=secret:</span> This option specifies that authentication will be performed using a shared secret key.<br />
<span style="color:#e83e8c">left=%defaultroute:</span> This option specifies that the local endpoint (left side) of the connection will be determined based on the default route.<br />
<span style="color:#e83e8c">leftid=2.2.2.2:</span> This option specifies the identity (ID) of the local endpoint. In this case, it’s set to the IP address 2.2.2.2.<br />
<span style="color:#e83e8c">leftsubnet=172.16.2.0/24:</span> This option specifies the local subnet that will be accessible through the VPN tunnel.<br />
<span style="color:#e83e8c">right=1.1.1.1:</span> This option specifies the IP address of the remote endpoint (right side) of the connection.<br />
<span style="color:#e83e8c">rightsubnet=172.16.1.0/24:</span> This option specifies the remote subnet that will be accessible through the VPN tunnel.<br />
<span style="color:#e83e8c">ike=aes256-sha2_256-modp1024!:</span> This option specifies the IKE (Internet Key Exchange) encryption algorithm, integrity algorithm, and Diffie-Hellman group to be used for negotiating phase 1 of the IPsec tunnel. In this case, AES 256-bit encryption, SHA-256 hashing, and MODP 1024-bit Diffie-Hellman group are used.<br />
<span style="color:#e83e8c">esp=aes256-sha256-modp1024!:</span> This option specifies the ESP (Encapsulating Security Payload) encryption algorithm, integrity algorithm, and Diffie-Hellman group to be used for negotiating phase 2 of the IPsec tunnel. In this case, AES 256-bit encryption, SHA-256 hashing, and MODP 1024-bit Diffie-Hellman group are used.<br />
<span style="color:#e83e8c">keyingtries=0:</span> This option specifies the number of attempts for establishing the IKE_SA. Setting it to 0 means that no retries will be attempted.<br />
<span style="color:#e83e8c">ikelifetime=1h:</span> This option specifies the lifetime of the IKE_SA (Phase 1) in hours.<br />
<span style="color:#e83e8c">lifetime=8h:</span> This option specifies the lifetime of the CHILD_SA (Phase 2) in hours.<br />
<span style="color:#e83e8c">dpddelay=30:</span> This option specifies the delay (in seconds) before Dead Peer Detection (DPD) is initiated. DPD is used to detect if the peer is still reachable. In this case, DPD will be initiated after 30 seconds of inactivity.</p>

<p>set preshared key:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/ipsec.secrets</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># This file holds shared secrets or RSA private keys for authentication.</span>

<span class="c1"># RSA private key for this host, authenticating it to any other host</span>
<span class="c1"># which knows the public part.</span>

<span class="na">2.2.2.2 1.1.1.1 </span><span class="pi">:</span> <span class="s">PSK $I8WC#53D@$#%#</span>
</code></pre></div></div>
<p><span style="color:#e83e8c">2.2.2.2:</span> This is the local endpoint’s IP address.<br />
<span style="color:#e83e8c">1.1.1.1:</span> This is the remote endpoint’s IP address.<br />
<span style="color:#e83e8c">PSK:</span> This indicates that a pre-shared key is used for authentication.<br />
<span style="color:#e83e8c">$I8WC#53D@$#%#:</span> This is the actual pre-shared key used for authentication.</p>

<p>So, in this case, the pre-shared key <code class="language-plaintext highlighter-rouge">$I8WC#53D@$#%#</code> is used for authentication between the local endpoint with IP address <code class="language-plaintext highlighter-rouge">2.2.2.2</code> and the remote endpoint with IP address <code class="language-plaintext highlighter-rouge">1.1.1.1</code></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">systemctl restart strongswan-starter</span>
</code></pre></div></div>
<p>Now we need to enable IP forwarding. The configuration line <code class="language-plaintext highlighter-rouge">"net.ipv4.ip_forward = 1"</code> enables IP forwarding on a Linux system. IP forwarding allows the system to forward packets from one network interface to another, essentially acting as a router or gateway.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/sysctl.conf</span>
<span class="s">net.ipv4.ip_forward = </span><span class="m">1</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">root@int-vpn-01:~# sysctl -p</span>
<span class="s">net.ipv4.ip_forward = </span><span class="m">1</span>
</code></pre></div></div>
<p>Allow traffic through ufw firewall</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">ufw allow from 1.1.1.1/32 proto udp to any port 500,4500 comment 'Mikrotik'</span> 
<span class="s">ufw route allow in on any out on any comment 'for VPN Traffic'</span>
</code></pre></div></div>
<h3 id="mikrotik-configuration">Mikrotik Configuration</h3>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="s">/ip ipsec profile</span>
<span class="s">add name="DH_HO_Profile1" hash-algorithm=sha256 enc-algorithm=aes-256 dh-group=modp1024 lifetime=1d proposal-check=obey nat-traversal=yes dpd-interval=2m dpd-maximum-failures=5</span>

<span class="s">/ip ipsec peer</span>
<span class="s">add name="DH_HO_Peer1" address=2.2.2.2/32 profile=DH_HO_Profile1 exchange-mode=ike2 send-initial-contact=yes</span> 

<span class="s">/ip ipsec identity</span>
<span class="s">add peer=DH_HO_Peer1 auth-method=pre-shared-key secret="$I8WC#53D@$#%#" generate-policy=no</span>

<span class="s">/ip ipsec proposal</span>
<span class="s">add name="DH_HO_Proposal1" auth-algorithms=sha256 enc-algorithms=aes-256-cbc lifetime=30m pfs-group=modp1024</span>

<span class="s">/ip ipsec policy</span>
<span class="s">add dst-address=172.16.2.0/24 peer="DH_HO_Peer1" proposal="DH_HO_Proposal1" sa-dst-address=2.2.2.2 sa-src-address=1.1.1.1 src-address=172.16.1.0/24 tunnel=yes</span>
</code></pre></div></div>

<p>Further NAT rule:</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">/ip firewall nat</span>
<span class="s">add chain=srcnat action=accept src-address=172.16.1.0/24 dst-address=172.16.2.0/24 log=yes log-prefix=""</span>
</code></pre></div></div>

<h4 id="allow-port-5004500udp-in-mikrotik">Allow port 500/4500/UDP in Mikrotik</h4>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">/ip firewall filter</span>
<span class="s">add chain=input action=accept protocol=udp src-port="" dst-port=500,4500 log=n&gt; log-prefix=""</span> 
</code></pre></div></div>
<p>check phase 1 status:<br />
<img src="https://www.bidhankhatri.com.np/assets/images/mktk_ipsec_phase1.png" alt="image-center" /></p>

<p>check phase 2 status:<br />
<img src="https://www.bidhankhatri.com.np/assets/images/mktk_ipsec_phase2.png" alt="image-center" /></p>

<p>open UDP port 500,4500 for remote server:<br />
<img src="https://www.bidhankhatri.com.np/assets/images/mktk_fw.png" alt="image-center" /></p>

<p>check connectivity:<br />
<img src="https://www.bidhankhatri.com.np/assets/images/mktk_ping.png" alt="image-center" /></p>

<p>check ipsec status in strongSwan VPN server:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">root@int-vpn-01:~# ipsec status</span>
<span class="s">Security Associations (1 up, 0 connecting)</span><span class="err">:</span>
 <span class="s">B1-TO-HO[13]</span><span class="err">:</span> <span class="s">ESTABLISHED 5 minutes ago, 2.2.2.2[2.2.2.2]...1.1.1.1[1.1.1.1]</span>
 <span class="s">B1-TO-HO{26}</span><span class="err">:</span>  <span class="s">INSTALLED, TUNNEL, reqid 1, ESP in UDP SPIs</span><span class="err">:</span> <span class="s">c006274a_i 01310acd_o</span>
 <span class="s">B1-TO-HO{26}</span><span class="err">:</span>   <span class="s">172.16.2.0/24 === 172.16.1.0/24</span>
<span class="s">root@int-vpn-01:~#</span>
</code></pre></div></div>

<p>Now our Mikrotik site can communicate with strongSwan site.</p>

<p>Additional,
You have to configure the static route on every node behind the VPN gateway server. Otherwise, the <code class="language-plaintext highlighter-rouge">172.16.1.0/24</code> network will not be able to reach other <code class="language-plaintext highlighter-rouge">172.16.2.x</code> IPs.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">ip route add 172.16.1.0/24 via 172.16.2.10</span>
</code></pre></div></div>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="VPN" /><category term="ubuntu" /><category term="mikrotik" /><summary type="html"><![CDATA[We will configure a site-to-site IPsec IKEv2 tunnel between the Mikrotik Router and the StrongSwan server. This will enable secure communication between devices connected behind the Mikrotik router and the StrongSwan server.]]></summary></entry><entry><title type="html">Monitor HA Cluster running Pacemaker and Corosync using Prometheus and Grafana using Docker</title><link href="https://www.bidhankhatri.com.np/monitoring/Monitor-HA-Cluster-running-pacemakr-and-corosync/" rel="alternate" type="text/html" title="Monitor HA Cluster running Pacemaker and Corosync using Prometheus and Grafana using Docker" /><published>2023-08-15T02:16:41+00:00</published><updated>2023-08-15T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/monitoring/Monitor-HA-Cluster-running-pacemakr-and-corosync</id><content type="html" xml:base="https://www.bidhankhatri.com.np/monitoring/Monitor-HA-Cluster-running-pacemakr-and-corosync/"><![CDATA[<p>We will use Grafana with prometheus in container to monitor High availability cluster running by Pacemaker and Corosync.</p>

<p>Grafana dashboard which we will be using shows the details of a HA cluster running Pacemaker/Corosync. It is built on top of <span style="color:#e83e8c"><code class="language-plaintext highlighter-rouge">ha_cluster_exporter</code></span> but it also requires Prometheus <span style="color:#e83e8c"><code class="language-plaintext highlighter-rouge">node_exporter</code></span> to be configured on the target nodes, and it also assumes that the target nodes in each cluster are grouped via the job label.</p>

<h4 id="install-ha_cluster_exporter">INSTALL HA_CLUSTER_EXPORTER</h4>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">mkdir /usr/local/ha_cluster_exporter</span>
<span class="s">cd /usr/local/ha_cluster_exporter</span>
<span class="s">wget https://github.com/ClusterLabs/ha_cluster_exporter/releases/download/1.3.3/ha_cluster_exporter-amd64.gz</span> 
<span class="s">gunzip ha_cluster_exporter-amd64.gz</span>
<span class="s">mv ha_cluster_exporter-amd64 ha_cluster_exporter</span>
<span class="s">chmmod +x ha_cluster_exporter</span>
</code></pre></div></div>

<p>Create systemd file.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/systemd/system/ha_cluster_exporter.service</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">Unit</span><span class="pi">]</span>
<span class="s">Description=HA Cluster Exporter</span>

<span class="pi">[</span><span class="nv">Service</span><span class="pi">]</span>
<span class="s">User=root</span>
<span class="s">WorkingDirectory=/usr/local/ha_cluster_exporter</span>
<span class="s">ExecStart=/usr/local/ha_cluster_exporter/ha_cluster_exporter</span>
<span class="s">Restart=always</span>

<span class="pi">[</span><span class="nv">Install</span><span class="pi">]</span>
<span class="s">WantedBy=multi-user.target</span>
</code></pre></div></div>
<p>Start and enable service.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">systemctl enable --now ha_cluster_expoter</span>
</code></pre></div></div>

<h4 id="install-node_exporter">INSTALL NODE_EXPORTER</h4>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz</span> 
<span class="s">tar xvf node_exporter-1.6.1.linux-amd64.tar.gz</span> 
<span class="s">mv node_exporter-1.6.1.linux-amd64.tar.gz node_exporter</span>
<span class="s">mv node_exporter /usr/local/</span>
<span class="s">cd /usr/local</span>
<span class="s">mv node_exporter-1.6.1.linux-amd64 node_exporter</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/systemd/system/node_exporter.service</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">Unit</span><span class="pi">]</span>
<span class="s">Description=Prometheus Node Exporter</span>

<span class="pi">[</span><span class="nv">Service</span><span class="pi">]</span>
<span class="s">User=root</span>
<span class="s">WorkingDirectory=/usr/local/node_exporter</span>
<span class="s">ExecStart=/usr/local/node_exporter/node_exporter --collector.systemd --collector.systemd.unit-include="pcsd|pacemaker|corosync)".service</span>
<span class="s">Restart=always</span>

<span class="pi">[</span><span class="nv">Install</span><span class="pi">]</span>
<span class="s">WantedBy=multi-user.target</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">systemctl enable --now node_exporter</span>
</code></pre></div></div>

<p><b>Now Build Prometheus and Grafana Container</b><br />
For the persistent container storage, execute below commands.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">mkdir /var/Docker_home</span>
<span class="s">cd /var/Docker_home</span>
<span class="s">mkdir Prometheus Grafana</span>
<span class="s">mkdir /Prometheus/PromDB</span>
<span class="s">mkdir /Grafana/data</span>
<span class="s">chmod 777 /prometheus/PromDB</span>
<span class="s">chmod 777 /Grafana/data</span>
</code></pre></div></div>

<p>Create Docker Compose file.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim docker-compose.yml</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">prometheus</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">prom/prometheus</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">prometheus</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">9090:9090</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">./Prometheus/prometheus.yml:/etc/prometheus/prometheus.yml</span>
      <span class="pi">-</span> <span class="s">./Prometheus/PromDB:/prometheus</span>

  <span class="na">grafana</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">grafana/grafana-oss</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">grafana</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">3000:3000</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">./Grafana/data:/var/lib/grafana</span>

</code></pre></div></div>
<p>The code defines Docker services: Prometheus for monitoring (port 9090, config and data persistence), and Grafana for visualization (port 3000, data persistence). Docker volumes ensure host-container data sharing, allowing configuration and data to be stored persistently across restarts in respective directories. <br />
<br /></p>

<p>Create prometheus config file.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /var/Docker_home/Prometheus/prometheus.yml</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">global</span><span class="pi">:</span>
  <span class="na">scrape_interval</span><span class="pi">:</span>     <span class="s">10s</span>
  <span class="na">evaluation_interval</span><span class="pi">:</span> <span class="s">10s</span>

<span class="na">scrape_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">job_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ha-cluster"</span>
    <span class="na">static_configs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">targets</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">ram-01.bidhankhatri.com.np:9664'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">ram-02.bidhankhatri.com.np:9664'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">ram-01.bidhankhatri.com.np:9100'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">ram-02.bidhankhatri.com.np:9100'</span><span class="pi">]</span>

</code></pre></div></div>
<blockquote>
  <p>Port <span style="color:#e83e8c"><b>9664</b></span> for <b>ha_cluster_exporter</b> and Port <span style="color:#e83e8c"><b>9100</b></span> for <b>node_exporter</b></p>
</blockquote>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">docker-compose up -d</span>
</code></pre></div></div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">Pulling prometheus (prom/prometheus:)...</span>
<span class="na">latest</span><span class="pi">:</span> <span class="s">Pulling from prom/prometheus</span>
<span class="na">d5c4df21b127</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">2f5f7d8898a1</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">300c29bb5b04</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">be6ad5a51a35</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">ea6cf9f81dfe</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">b5ac85a4be54</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">d32980b63d51</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">502ed6d3bdc8</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">7bed70210741</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">3b19398e1689</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">d358eb0a0392</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">d6eaeaf54563</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">Digest</span><span class="pi">:</span> <span class="s">sha256:d6ead9daf2355b9923479e24d7e93f246253ee6a5eb18a61b0f607219f341a80</span>
<span class="na">Status</span><span class="pi">:</span> <span class="s">Downloaded newer image for prom/prometheus:latest</span>
<span class="s">Pulling grafana (grafana/grafana-oss:)...</span>
<span class="na">latest</span><span class="pi">:</span> <span class="s">Pulling from grafana/grafana-oss</span>
<span class="na">4db1b89c0bd1</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">312681f4cad0</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">8b7b65888846</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">dd9c3d04d541</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">959325519a8e</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">16cb2df7bffd</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">94d1f5f5bfea</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">e3281a1a7e8f</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">5c0c2b741753</span><span class="pi">:</span> <span class="s">Pull complete</span>
<span class="na">Digest</span><span class="pi">:</span> <span class="s">sha256:423040d62678074111e4e72d7dcef23480a94eb4f21b9173204d1a5ee972ec59</span>
<span class="na">Status</span><span class="pi">:</span> <span class="s">Downloaded newer image for grafana/grafana-oss:latest</span>
<span class="s">Creating prometheus ... done</span>
<span class="s">Creating grafana    ... done</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">docker</span> <span class="nx">ps</span>   
</code></pre></div></div>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">CONTAINER</span> <span class="nx">ID</span>   <span class="nx">IMAGE</span>                 <span class="nx">COMMAND</span>                  <span class="nx">CREATED</span>         <span class="nx">STATUS</span>              <span class="nx">PORTS</span>                                       <span class="nx">NAMES</span>
<span class="mi">1</span><span class="nx">fb48738a930</span>   <span class="nx">prom</span><span class="o">/</span><span class="nx">prometheus</span>       <span class="dl">"</span><span class="s2">/bin/prometheus --c…</span><span class="dl">"</span>   <span class="mi">2</span> <span class="nx">minutes</span> <span class="nx">ago</span>   <span class="nx">Up</span> <span class="nx">About</span> <span class="nx">a</span> <span class="nx">minute</span>   <span class="mf">0.0</span><span class="p">.</span><span class="mf">0.0</span><span class="p">:</span><span class="mi">9090</span><span class="o">-&gt;</span><span class="mi">9090</span><span class="o">/</span><span class="nx">tcp</span><span class="p">,</span> <span class="p">:::</span><span class="mi">9090</span><span class="o">-&gt;</span><span class="mi">9090</span><span class="o">/</span><span class="nx">tcp</span>   <span class="nx">prometheus</span>
<span class="nx">f5362d262246</span>   <span class="nx">grafana</span><span class="o">/</span><span class="nx">grafana</span><span class="o">-</span><span class="nx">oss</span>   <span class="dl">"</span><span class="s2">/run.sh</span><span class="dl">"</span>                <span class="mi">2</span> <span class="nx">minutes</span> <span class="nx">ago</span>   <span class="nx">Up</span> <span class="nx">About</span> <span class="nx">a</span> <span class="nx">minute</span>   <span class="mf">0.0</span><span class="p">.</span><span class="mf">0.0</span><span class="p">:</span><span class="mi">3000</span><span class="o">-&gt;</span><span class="mi">3000</span><span class="o">/</span><span class="nx">tcp</span><span class="p">,</span> <span class="p">:::</span><span class="mi">3000</span><span class="o">-&gt;</span><span class="mi">3000</span><span class="o">/</span><span class="nx">tcp</span>   <span class="nx">grafana</span>
</code></pre></div></div>

<p>Both Grafana and Prometheus are up now.</p>

<h4 id="configure-grafana">CONFIGURE GRAFANA</h4>
<p>Go to <span style="color:#4a6ee0">http://YOURIPADDRESS:<span style="color:#e83e8c">3000</span></span> to access grafana UI. Default username/password is <span style="color:#e83e8c">admin/admin</span>. change it after login.</p>

<ol>
  <li>
    <p>1st connect Grafana to Prometheus: <br />
Home » Connections » Add new connection » Search for Prometheus » Create a Prometheus data source » 
update prometheus server URL: <span style="color:#4a6ee0">http://<b>YOURIPADDRESS</b>:<span style="color:#e83e8c">9090</span></span><br />
Click on Save &amp; Test</p>
  </li>
  <li>
    <p>Second, Import Grafana Dashboard for HA cluster. Ref: <span style="color:#4a6ee0">https://grafana.com/grafana/dashboards/12229-ha-cluster-details/</span><br />
Home » Dashboard » New [ drop down] » Import » Put ID 12229 and click on Load » Choose prometheus which we configured earlier<br />
Click on Import</p>
  </li>
</ol>

<p>Similar graph you will see.
<img src="https://www.bidhankhatri.com.np/assets/images/HA_cluster_details.png" alt="image-center" /></p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="Monitoring" /><category term="Pacemaker" /><category term="Corosync" /><category term="HighAvailability" /><summary type="html"><![CDATA[We will use Grafana with prometheus in container to monitor High availability cluster running by Pacemaker and Corosync.]]></summary></entry><entry><title type="html">GFS2 Filesystem setup in RHEL8 with Pacemaker and Corosync</title><link href="https://www.bidhankhatri.com.np/system/GFS2-Filesystem-setup-in-RHEL8-with-Pacemaker-and-Corosync/" rel="alternate" type="text/html" title="GFS2 Filesystem setup in RHEL8 with Pacemaker and Corosync" /><published>2023-07-14T02:16:41+00:00</published><updated>2023-07-14T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/system/GFS2-Filesystem-setup-in-RHEL8-with-Pacemaker-and-Corosync</id><content type="html" xml:base="https://www.bidhankhatri.com.np/system/GFS2-Filesystem-setup-in-RHEL8-with-Pacemaker-and-Corosync/"><![CDATA[<p>We will configure Pacemaker/Corosync to enable the sharing of a disk between two nodes through the GFS2 clustered filesystem.</p>
<div> 
<script src="/assets/js/mermaid.min.js"></script>


<div class="mermaid">
flowchart TB
    subgraph Vmware
    ram-01.bidhankhatri.com.np\n10.12.6.10--&gt;Shared-Disk-/dev/sdb
    ram-02.bidhankhatri.com.np\n10.12.6.11--&gt;Shared-Disk-/dev/sdb
    end

</div></div>

<h2 id="gfs2-file-system">GFS2 File system</h2>

<p>GFS2 (Global File System 2) is a cluster file system designed for use in Linux-based environments. It is an enhanced version of the original GFS (Global File System), which was developed by Red Hat. GFS2 allows multiple servers to have concurrent read and write access to a shared file system, providing high availability and scalability for data storage.</p>

<p>GFS2 is commonly used in scenarios where multiple servers need simultaneous access to a shared file system, such as in high-performance computing (HPC) clusters, database clusters, or virtualization environments. It provides a reliable and scalable solution for managing data across a cluster of Linux servers.</p>

<p>Now lets start the setup.</p>
<ol>
  <li>Cluster setup with Pacemaker/Corosync</li>
  <li>GFS2 Setup</li>
</ol>

<p>Various components of the cluster stack (corosync, pacemaker, etc.) has to configured.</p>
<h3 id="pacemaker">Pacemaker:</h3>
<p>Pacemaker is a high-availability cluster resource manager — software that runs on a set of hosts (a cluster
of nodes) in order to preserve integrity and minimize downtime of desired services (resources).</p>
<h3 id="corosync">Corosync:</h3>
<p>Corosync is an cluster messaging and membership service that is often used in conjunction with the Pacemaker software to build high availability (HA) clusters. It provides a reliable and scalable communication infrastructure for coordinating and synchronizing the activities of nodes in a cluster. Corosync operates as a cluster messaging layer, enabling the exchange of information and cluster-related events among the nodes.</p>

<h3 id="cluster-setup-with-pacemakercorosync"><b>Cluster setup with Pacemaker/Corosync</b></h3>
<p>We will setup 2 node cluster and share the disk between them. <br />
Nodes:</p>
<ul>
  <li><span style="color:#e83e8c">ram-01.bidhankhatri.com.np</span></li>
  <li><span style="color:#e83e8c">ram-02.bidhankhatri.com.np</span></li>
</ul>

<p><span style="color:#4a6ee0">Prerequisites:</span></p>
<ul>
  <li>Update both node with its IP in each other <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file. Its always recommended to update host details in <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file when you are trying to setup any cluster instead depending upon the external DNS.</li>
</ul>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vim /etc/hosts</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">10.12.6.10 ram-01.bidhankhatri.com.np</span>
<span class="s">10.12.6.11 ram-02.bidhankhatri.com.np</span>
</code></pre></div></div>
<ul>
  <li>Ensure NTP is set up and synchronize the time between nodes to avoid significant time differences, which could adversely affect cluster performance. Verify this configuration on both nodes.</li>
</ul>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">ntpstat</span>
</code></pre></div></div>

<p>Now first enable the HighAvailability repo on both nodes and install packages. After that start and enable <code class="language-plaintext highlighter-rouge">pcsd</code>, <code class="language-plaintext highlighter-rouge">pacemaker</code> and <code class="language-plaintext highlighter-rouge">corosync</code> service.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">yum install pcs pacemaker fence-agents-all</span>
<span class="s">systemctl enable --now pcsd</span>
<span class="s">systemctl enable --now pacemaker</span>
<span class="s">systemctl enable --now corosync</span>
</code></pre></div></div>

<p>Enable ports required for HighAvailability</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">firewall-cmd --permanent --add-service=high-availability</span>
<span class="s">firewall-cmd --reload</span>
</code></pre></div></div>
<p>The installed packages will create a <span style="color:#e83e8c"><code class="language-plaintext highlighter-rouge">hacluster</code></span> user with a disabled password. While this is fine for running pcs commands locally, the account needs a login password in order to perform such tasks as syncing the corosync configuration, or starting and stopping the cluster on other nodes.</p>

<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># passwd hacluster</span>
<span class="pi">[</span><span class="nv">root@ram-02 ~</span><span class="pi">]</span><span class="c1"># passwd hacluster</span>
</code></pre></div></div>

<h4 id="configure-corosync">CONFIGURE COROSYNC</h4>
<p>On either node, use <code class="language-plaintext highlighter-rouge">pcs cluster auth</code> to authenticate as the <span style="color:#e83e8c"><code class="language-plaintext highlighter-rouge">hacluster</code></span> user:</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs host auth ram-01.bidhankhatri.com.np ram-02.bidhankhatri.com.np</span>
<span class="na">Username</span><span class="pi">:</span> <span class="s">hacluster</span>
<span class="na">Password</span><span class="pi">:</span> <span class="err">******</span>
<span class="na">ram-01.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Authorized</span>
<span class="na">ram-02.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Authorized</span>
<span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1">#</span>
</code></pre></div></div>
<blockquote>
  <p>NOTE: If you are using RHEL7/Centos7 or Fedora then command to authenticate hosts is: <br />
<code class="language-plaintext highlighter-rouge">pcs cluster auth ram-01.bidhankhatri.com.np ram-02.bidhankhatri.com.np</code></p>
</blockquote>

<p>Next, use below command on the same node to generate and synchronize the corosync between the cluster nodes. We will be setting <span style="color:#e83e8c">“gfs-cluster”</span> as our cluster name.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs cluster setup gfs-cluster ram-01.bidhankhatri.com.np ram-02.bidhankhatri.com.np</span>
</code></pre></div></div>
<blockquote>
  <p>NOTE: In Centos7, command is:<br />
<code class="language-plaintext highlighter-rouge">pcs cluster setup --name gfs-cluster ram-01.bidhankhatri.com.np ram-02.bidhankhatri.com.np</code></p>
</blockquote>

<p>Check cluster status:</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs cluster status</span>
<span class="na">Cluster Status</span><span class="pi">:</span>
 <span class="na">Status of pacemakerd</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Pacemaker</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">running'</span> <span class="s">(last updated 2023-07-07 15:03:12 -04:00)</span>
 <span class="na">Cluster Summary</span><span class="pi">:</span>
  <span class="na">* Stack</span><span class="pi">:</span> <span class="s">corosync</span>
  <span class="na">* Current DC</span><span class="pi">:</span> <span class="s">ram-01.bidhankhatri.com.np (version 2.0.0-10.el8-b67d8d0de9) - partition with quorum</span>
  <span class="na">* Last updated</span><span class="pi">:</span> <span class="s">Fri Jul 7 16:11:18 </span><span class="m">2023</span>
  <span class="na">* Last change</span><span class="pi">:</span> <span class="s">Fri Jul 7 16:11:00 2023 by hacluster via crmd on ram-01.bidhankhatri.com.np</span>
  <span class="err">*</span> <span class="s">2 nodes configured</span>
  <span class="err">*</span> <span class="s">0 resources instances configured</span>
<span class="na">Node List</span><span class="pi">:</span>
  <span class="na">* Online</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">ram-01.bidhankhatri.com.np ram-02.bidhankhatri.com.np</span> <span class="pi">]</span>

<span class="na">PCSD Status</span><span class="pi">:</span>
  <span class="na">ram-01.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>
  <span class="na">ram-02.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>
</code></pre></div></div>

<p>A Red Hat High Availability cluster requires that you configure fencing for the cluster. So before doing the GFS2 setup, we have to configure fencing first.</p>

<h4 id="stonith-setup">STONITH SETUP</h4>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs stonith create vmfence fence_vmware_rest pcmk_host_map="ram-01.bidhankhatri.com.np:ram-01-vm;ram-02.bidhankhatri.com.np:ram-02-vm" ip=http://192.168.2.1 ssl_insecure=1 username="adminuser@bidhankhatri.com.np" password="****" delay=10 pcmk_monitor_timeout=120s</span>
</code></pre></div></div>
<p>The above provided command is used to create a fencing resource in the Pacemaker cluster manager using the VMware REST API. Fencing, also known as STONITH (Shoot The Other Node In The Head)</p>

<p><span style="color:#e83e8c">pcs stonith create vmfence:</span> This is the command to create a new fencing resource named “vmfence” in Pacemaker.<br />
<span style="color:#e83e8c">fence_vmware_rest:</span> This specifies the fence agent to be used, which in this case is the VMware REST API fence agent. This agent allows Pacemaker to communicate with VMware vSphere to fence a misbehaving node.<br />
<span style="color:#e83e8c">pcmk_host_map=”ram-01.bidhankhatri.com.np:ram-01-vm;ram-02.bidhankhatri.com.np:ram-02-vm”:</span> This option specifies the mapping between the hostnames of the nodes in the Pacemaker cluster and the corresponding virtual machine (VM) names in the VMware environment. It indicates which VM corresponds to which cluster node.<br />
<span style="color:#e83e8c">ip=http://192.168.2.1</span> This option specifies the IP address or hostname of the VMware vCenter server.<br />
<span style="color:#e83e8c">ssl_insecure=1:</span> This option tells the fence agent to ignore SSL certificate validation errors. Use this option only if you have a self-signed or untrusted SSL certificate on your vCenter server. <br />
<span style="color:#e83e8c">username=”adminuser@bidhankhatri.com.np”:</span> This option specifies the username to authenticate with the VMware vCenter server.</p>

<h4 id="gfs2-setup">GFS2 SETUP</h4>
<p>Enable repo: rhel-8-for-x86_64-resilientstorage-rpms</p>

<p>On both nodes of the cluster, install the <code class="language-plaintext highlighter-rouge">lvm2-lockd</code>, <code class="language-plaintext highlighter-rouge">gfs2-utils</code>, and <code class="language-plaintext highlighter-rouge">dlm</code> packages.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">yum install lvm2-lockd gfs2-utils dlm</span>
</code></pre></div></div>
<p>On both nodes of the cluster, set the <code class="language-plaintext highlighter-rouge">use_lvmlockd</code> configuration option in the <code class="language-plaintext highlighter-rouge">/etc/lvm/lvm.conf</code> file to <code class="language-plaintext highlighter-rouge">use_lvmlockd=1</code>.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">use_lvmlockd=1</span>
</code></pre></div></div>

<p>Set the global Pacemaker parameter <code class="language-plaintext highlighter-rouge">no-quorum-policy</code> to <code class="language-plaintext highlighter-rouge">freeze</code>.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs property set no-quorum-policy=freeze</span>
</code></pre></div></div>
<blockquote>
  <p>By default, when quorum is lost, all resources on the remaining partition are immediately stopped. This is the safest and most optimal option, but GFS2 requires quorum to function. If quorum is lost, both GFS2 applications and the GFS2 mount cannot be stopped correctly. To solve this, set no-quorum-policy to freeze when using GFS2. This means the remaining partition will remain inactive until quorum is regained.</p>
</blockquote>

<p>To configure a GFS2 file system in a cluster, it is necessary to establish a <code class="language-plaintext highlighter-rouge">dlm</code> resource, which is a mandatory dependency. In this case, the provided example creates the dlm resource within a resource group called <code class="language-plaintext highlighter-rouge">"locking."</code></p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs resource create dlm --group locking ocf:pacemaker:controld op monitor interval=30s on-fail=fence</span>
</code></pre></div></div>

<p>Clone the <code class="language-plaintext highlighter-rouge">locking</code> resource group so that the resource group can be active on both nodes of the cluster.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs resource clone locking interleave=true</span>
</code></pre></div></div>

<p>Set up an <code class="language-plaintext highlighter-rouge">lvmlockd</code> resource as part of the locking resource group.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs resource create lvmlockd --group locking ocf:heartbeat:lvmlockd op monitor interval=30s on-fail=fence</span>
</code></pre></div></div>

<p>Check the status of the cluster to ensure that the locking resource group has started on both nodes of the cluster.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># pcs status --full</span>

<span class="na">Cluster name</span><span class="pi">:</span> <span class="s">gfs-cluster</span>
<span class="na">Status of pacemakerd</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Pacemaker</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">running'</span> <span class="s">(last updated 2023-07-09 08:03:40 -04:00)</span>
<span class="na">Cluster Summary</span><span class="pi">:</span>
  <span class="na">* Stack</span><span class="pi">:</span> <span class="s">corosync</span>
  <span class="na">* Current DC</span><span class="pi">:</span> <span class="s">ram-01.bidhankhatri.com.np (version 2.1.4-5.e18_7.2-dc6eb4362e) - partition with quorum</span>
  <span class="na">* Last updated</span><span class="pi">:</span> <span class="s">Sun Jul 09 08:03:41 </span><span class="m">2023</span>
  <span class="na">* Last change</span><span class="pi">:</span> <span class="s">Sun Jul 9 08:03:37 2023 by root via cibadmin on ram-01.bidhankhatri.com.np</span>
  <span class="err">*</span> <span class="s">2 ndoes configured</span>
  <span class="err">*</span> <span class="s">5 resource instances configured</span>

<span class="na">Node List</span><span class="pi">:</span>
  <span class="na">* Online</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">ram-01.bidhankhatri.com.np (1) ram-02.bidhankhatri.com.np (2)</span> <span class="pi">]</span>

<span class="na">Full List of Resources</span><span class="pi">:</span>
  <span class="na">* Clone Set</span><span class="pi">:</span> <span class="s">locking-clone [locking]</span><span class="err">:</span>
    <span class="na">* Resource Group: locking:0</span><span class="pi">:</span>
      <span class="na">* dlm    (ocf::pacemaker:controld)</span><span class="pi">:</span>       <span class="s">Started ram-01.bidhankhatri.com.np</span>
      <span class="na">* lvmlockd    (ocf:pacemaker:lvmlockd)</span><span class="pi">:</span>         <span class="s">Started ram-01.bidhankhatri.com.np</span>
    <span class="na">* Resource Group: locking:1</span><span class="pi">:</span>
      <span class="na">* dlm    (ocf::pacemaker:controld)</span><span class="pi">:</span>       <span class="s">Started ram-02.bidhankhatri.com.np</span>
      <span class="na">* lvmlockd    (ocf:pacemaker:lvmlockd)</span><span class="pi">:</span>         <span class="s">Started ram-02.bidhankhatri.com.np</span>
  <span class="na">* vmfence     (stonith:fence_vmware_rest)</span><span class="pi">:</span>    <span class="s">started ram-01.bidhankhatri.com.np</span>

<span class="na">Migrration Summary</span><span class="pi">:</span>

<span class="na">Tickets</span><span class="pi">:</span>

<span class="na">PCSD Status</span><span class="pi">:</span>
  <span class="na">ram-01.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>
  <span class="na">ram-02.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>

<span class="na">Daemon Status</span><span class="pi">:</span>
  <span class="na">corosync</span><span class="pi">:</span> <span class="s">active/enabled</span>
  <span class="na">pacemaker</span><span class="pi">:</span> <span class="s">active/enabled</span>
  <span class="na">pcsd</span><span class="pi">:</span> <span class="s">active/enabled</span>
</code></pre></div></div>

<p>On one node, create a shared volume group. Create volume group <code class="language-plaintext highlighter-rouge">"shared_vg"</code></p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vgcreate --shared shared_vg /dev/sdb</span>
  <span class="s">Physical volume "/dev/vdb" successfully created.</span>
  <span class="s">Volume group "shared_vg" successfully created</span>
  <span class="s">VG shared_vg starting dlm lockspace</span>
  <span class="s">Starting locking.  Waiting until locks are ready...</span>

</code></pre></div></div>

<p>In 2nd node.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">vgchange --lockstart shared_vg</span>
  <span class="s">VG shared_vg starting dlm lockspace</span>
  <span class="s">Starting locking.  Waiting until locks are ready...</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">lvcreate --activate sy -l 100%FREE -n shared_lv shared_vg</span>
  <span class="s">Logical volume "shared_lv" created.</span>
</code></pre></div></div>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># mkfs.gfs2 -j2 -p lock_dlm -t gfs-cluster:gfs2 /dev/shared_vg/shared_lv</span>
<span class="s">/dev/shared-vg is a symbolic link to /dev/dm-7</span>
<span class="s">this will destory any data on /dev/dm-7</span>
<span class="s">Are you sure you want to proceed? [y/n] y</span>
<span class="na">Discarding device contents (may take a while on large devices)</span><span class="pi">:</span> <span class="s">Done</span>
<span class="na">Adding journals</span><span class="pi">:</span> <span class="s">Done</span>
<span class="na">Building resource groups</span><span class="pi">:</span> <span class="s">Done</span>
<span class="na">Creating quota file</span><span class="pi">:</span> <span class="s">Done</span>
<span class="na">Writing superblock and syncing</span><span class="pi">:</span> <span class="s">Done</span>
<span class="na">Device</span><span class="pi">:</span>              <span class="s">/dev/shared_vg/shared_lv</span>
<span class="na">Block size</span><span class="pi">:</span>          <span class="m">4096</span>
<span class="na">Device size</span><span class="pi">:</span>         <span class="s">8.00 GB (2096128 blocks)</span>
<span class="na">Filesystem size</span><span class="pi">:</span>     <span class="s">8.00 GB (2096128 blocks)</span>
<span class="na">Journals</span><span class="pi">:</span>            <span class="m">2</span>
<span class="na">Journal size</span><span class="pi">:</span>        <span class="s">32MB</span>
<span class="na">Resource groups</span><span class="pi">:</span>     <span class="m">34</span>
<span class="na">Locking protocol</span><span class="pi">:</span>    <span class="s2">"</span><span class="s">lock_dlm"</span>
<span class="na">Lock table</span><span class="pi">:</span>          <span class="s2">"</span><span class="s">gfs-cluster:gfs2"</span>
<span class="na">UUID</span><span class="pi">:</span>                <span class="s">6c5a011c-188a-48d1-adc8-774b256b2850</span>
</code></pre></div></div>
<p><span style="color:#e83e8c">• -p lock_dlm</span> specifies that we want to use the kernel’s DLM.<br />
<span style="color:#e83e8c">• -j 2</span> indicates that the filesystem should reserve enough space for two journals (one for each node that will access the filesystem).<br />
<span style="color:#e83e8c">• -t gfs-cluster:gfs2</span> specifies the lock table name. The format for this field is
<span style="color:#4a6ee0">clustername:fsname.</span><br />
For clustername, we need to use the same value we specified originally with <code class="language-plaintext highlighter-rouge">pcs cluster setup</code> (which is also the value of cluster_name in <span style="color:#4a6ee0">/etc/corosync/corosync.conf</span>). If you are unsure what your cluster name is, you can look in /etc/corosync/corosync.conf or execute the command.<br />
 <code class="language-plaintext highlighter-rouge">pcs cluster corosync ram-01.bidhankhatri.com.np | grep -i "Cluster name"</code>.</p>

<p>Create an <code class="language-plaintext highlighter-rouge">LVM-activate</code> resource for logical volume to automatically activate that logical volume on all nodes.<br />
Create an <code class="language-plaintext highlighter-rouge">LVM-activate</code> resource named <code class="language-plaintext highlighter-rouge">sharedlv</code> for the logical volume <code class="language-plaintext highlighter-rouge">shared_lv</code> in volume group <code class="language-plaintext highlighter-rouge">shared_vg</code>.<br />
Below command creates a Pacemaker cluster resource called <span style="color:#e83e8c">“sharedlv”</span> that manages a logical volume named <span style="color:#e83e8c">“shared_lv”</span> belonging to the volume group <span style="color:#e83e8c">“shared_vg.”</span> The resource agent used is <span style="color:#e83e8c">“ocf:heartbeat:LVM-activate,”</span> and the LV is configured to be activated in shared mode, allowing multiple nodes to access it concurrently. The VG access mode is set to <span style="color:#e83e8c">“lvmlockd”</span> to enable distributed locking for concurrent VG access.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs resource create sharedlv --group shared_vg ocf:heartbeat:LVM-activate lvname=shared_lv vgname=shared_vg activation_mode=shared vg_access_mode=lvmlockd</span>
</code></pre></div></div>
<p><span style="color:#e83e8c">pcs:</span> It stands for “Pacemaker Configuration System” and refers to the command-line tool used for managing Pacemaker clusters.<br />
<span style="color:#e83e8c">resource create:</span> This command is used to create a new resource in the Pacemaker cluster.<br />
<span style="color:#e83e8c">sharedlv:</span> It is the name given to the resource being created.<br />
<span style="color:#e83e8c">–group shared_vg:</span> It specifies that the resource should be added to the resource group named <span style="color:#4a6ee0">“shared_vg.”</span> A resource group is a logical grouping of resources that are managed together.<br />
<span style="color:#e83e8c">ocf:heartbeat:LVM-activate:</span> It specifies the resource agent to be used for managing the shared logical volume. In this case, the Heartbeat OCF resource agent is used. The LVM-activate resource agent is responsible for activating an LVM logical volume.<br />
<span style="color:#e83e8c">lvname=shared_lv:</span> It specifies the name of the logical volume to be managed by the resource. In this case, the LV is named <span style="color:#4a6ee0">“shared_lv.”</span><br />
<span style="color:#e83e8c">vgname=shared_vg:</span> It specifies the name of the volume group (VG) to which the logical volume belongs. In this case, the VG is named <span style="color:#4a6ee0">“shared_vg.”</span><br />
<span style="color:#e83e8c">activation_mode=shared:</span> It specifies the activation mode for the LV. <span style="color:#4a6ee0">“Shared”</span> mode indicates that the LV can be activated by multiple nodes simultaneously.<br />
<span style="color:#e83e8c">vg_access_mode=lvmlockd:</span> It specifies the access mode for the volume group. <code class="language-plaintext highlighter-rouge">"lvmlockd"</code> is a locking daemon that provides a distributed lock manager for LVM, allowing multiple nodes to access the VG concurrently.</p>

<p>Clone the new resource group.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs resource clone shared_vg interleave=true</span>
</code></pre></div></div>
<p>Configure ordering constraints to ensure that the <code class="language-plaintext highlighter-rouge">locking</code> resource group that includes the <code class="language-plaintext highlighter-rouge">dlm</code> and <code class="language-plaintext highlighter-rouge">lvmlockd</code> resources starts first.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs constraint order start locking-clone then shared_vg-clone</span>
</code></pre></div></div>
<p>Configure colocation constraints to ensure that the vg resource group start on the same node as the <code class="language-plaintext highlighter-rouge">locking</code> resource group.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># pcs constraint colocation add shared_vg-clone with locking-clone</span>
</code></pre></div></div>
<p>On both nodes in the cluster, verify that the logical volumes are active. There may be a delay of a few seconds.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">[</span><span class="nv">root@ram-01 ~</span><span class="pi">]</span><span class="c1"># lvs</span>
  <span class="s">LV         VG          Attr       LSize</span>
  <span class="s">shared_lv shared_vg  -wi-ao-----  &lt;8.00g</span>

<span class="pi">[</span><span class="nv">root@ram-02 ~</span><span class="pi">]</span><span class="c1"># lvs</span>
  <span class="s">LV         VG          Attr       LSize</span>
  <span class="s">shared_lv shared_vg  -wi-ao-----  &lt;8.00g</span>
</code></pre></div></div>
<p>Create a file system resource to automatically mount each GFS2 file system on all nodes. You should not add the file system to the <code class="language-plaintext highlighter-rouge">/etc/fstab</code> file because it will be managed as a Pacemaker cluster resource.</p>

<p>Create a file system resource to automatically mount each GFS2 file system on all nodes.
You should not add the file system to the /etc/fstab file because it will be managed as a Pacemaker cluster resource.</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">pcs resource create sharedfs --group shared_vg ocf:heartbeat:Filesystem device="/dev/shared_vg/shared_lv" directory="/app" fstype="gfs2" options=noatime op monitor interval=10s on-fail=fence</span>
</code></pre></div></div>

<p><span style="color:#e83e8c">resource create:</span> This command is used to create a new resource in the Pacemaker cluster.<br />
<span style="color:#e83e8c">sharedfs:</span> It is the name given to the resource being created.<br />
<span style="color:#e83e8c">–group shared_vg:</span> It specifies that the resource should be added to the resource group named <span style="color:#4a6ee0">“shared_vg.”</span><br />
<span style="color:#e83e8c">ocf:heartbeat:Filesystem:</span> It specifies the resource agent to be used for managing the filesystem resource. In this case, the Heartbeat OCF resource agent for Filesystem is used.<br />
<span style="color:#e83e8c">device=”/dev/shared_vg/shared_lv”:</span> It specifies the device associated with the filesystem resource. In this case, the filesystem is located on the logical volume <span style="color:#4a6ee0">/dev/shared_vg/shared_lv.</span><br />
<span style="color:#e83e8c">directory=”/app”:</span> It specifies the directory where the filesystem should be mounted. In this case, the filesystem will be mounted on the directory <span style="color:#4a6ee0">/app.</span>  <br />
<span style="color:#e83e8c">fstype=”gfs2”:</span> It specifies the filesystem type. In this case, the filesystem type is <span style="color:#4a6ee0">“gfs2,”</span> which stands for Global File System 2.<br />
<span style="color:#4a6ee0">options=acl,noatime:</span> Specifies the mount options for the filesystem. The <span style="color:#4a6ee0">acl</span> option enables Access Control Lists (ACLs), which provide more granular access control on files and directories. The <span style="color:#4a6ee0">noatime</span> option disables updating the access time for files when they are accessed, potentially improving performance.<br />
<span style="color:#e83e8c">op monitor interval=10s on-fail=fence:</span> It defines the resource’s monitoring behavior. The monitor operation will be performed on the resource every 10 seconds (<span style="color:#4a6ee0">interval=10s</span>). If the monitor operation fails, the node where the resource is running will be fenced (<span style="color:#4a6ee0">on-fail=fence</span>), indicating a failure and allowing the resource to be recovered on another node.</p>

<p>Verification steps:</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">mount | grep gfs2</span>
<span class="s">/dev/mapper/shared_vg-shared_lv on /app type gfs2 (rw,noatime,acl)</span>

<span class="s">mount | grep gfs2</span>
<span class="s">/dev/mapper/shared_vg-shared_lv on /app type gfs2 (rw,noatime,acl)</span>

<span class="s">df -h /mnt/gfs/</span>
<span class="s">Filesystem                       Size    Used   Avail   Use%  Mounted on</span>
<span class="s">/dev/mapper/shared_vg-shared_lv  8.0G    67M    8.0G    1%    /app</span>
</code></pre></div></div>
<p>check cluster status:</p>
<div class="code-header">
    <button class="copy-code-button">
        Copy Code
    </button>
  </div>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># pcs status --full</span>

<span class="na">Cluster name</span><span class="pi">:</span> <span class="s">gfs-cluster</span>
<span class="na">Status of pacemakerd</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Pacemaker</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">running'</span> <span class="s">(last updated 2023-07-09 08:03:40 -04:00)</span>
<span class="na">Cluster Summary</span><span class="pi">:</span>
  <span class="na">* Stack</span><span class="pi">:</span> <span class="s">corosync</span>
  <span class="na">* Current DC</span><span class="pi">:</span> <span class="s">ram-01.bidhankhatri.com.np (version 2.1.4-5.e18_7.2-dc6eb4362e) - partition with quorum</span>
  <span class="na">* Last updated</span><span class="pi">:</span> <span class="s">Sun Jul 09 08:03:41 </span><span class="m">2023</span>
  <span class="na">* Last change</span><span class="pi">:</span> <span class="s">Sun Jul 9 08:03:37 2023 by root via cibadmin on ram-01.bidhankhatri.com.np</span>
  <span class="err">*</span> <span class="s">2 ndoes configured</span>
  <span class="err">*</span> <span class="s">9 resource instances configured</span>

<span class="na">Node List</span><span class="pi">:</span>
  <span class="na">* Online</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">ram-01.bidhankhatri.com.np (1) ram-02.bidhankhatri.com.np (2)</span> <span class="pi">]</span>

<span class="na">Full List of Resources</span><span class="pi">:</span>
  <span class="na">* Clone Set</span><span class="pi">:</span> <span class="s">locking-clone [locking]</span><span class="err">:</span>
    <span class="na">* Resource Group: locking:0</span><span class="pi">:</span>
      <span class="na">* dlm    (ocf::pacemaker:controld)</span><span class="pi">:</span>       <span class="s">Started ram-01.bidhankhatri.com.np</span>
      <span class="na">* lvmlockd    (ocf:pacemaker:lvmlockd)</span><span class="pi">:</span>         <span class="s">Started ram-01.bidhankhatri.com.np</span>
    <span class="na">* Resource Group: locking:1</span><span class="pi">:</span>
      <span class="na">* dlm    (ocf::pacemaker:controld)</span><span class="pi">:</span>       <span class="s">Started ram-02.bidhankhatri.com.np</span>
      <span class="na">* lvmlockd    (ocf:pacemaker:lvmlockd)</span><span class="pi">:</span>         <span class="s">Started ram-02.bidhankhatri.com.np</span>
  <span class="na">* vmfence     (stonith:fence_vmware_rest)</span><span class="pi">:</span>    <span class="s">started ram-01.bidhankhatri.com.np</span>
  <span class="na">* Clone Set</span><span class="pi">:</span> <span class="s">shared_vg-clone [shared_vg]</span><span class="err">:</span>
    <span class="na">* Resource Group: shared_vg:0</span><span class="pi">:</span>
      <span class="na">* sharedlv    (ocf::heartbeat:LVM-activate)</span><span class="pi">:</span>       <span class="s">Started ram-01.bidhankhatri.com.np</span>
      <span class="na">* sharedfs    (ocf:heartbeat:Filesystem)</span><span class="pi">:</span>         <span class="s">Started ram-01.bidhankhatri.com.np</span>
    <span class="na">* Resource Group: shared_vg:1</span><span class="pi">:</span>
      <span class="na">* sharedlv    (ocf::heartbeat:LVM-activate)</span><span class="pi">:</span>       <span class="s">Started ram-02.bidhankhatri.com.np</span>
      <span class="na">* sharedfs    (ocf:heartbeat:Filesystem)</span><span class="pi">:</span>         <span class="s">Started ram-02.bidhankhatri.com.np</span>

<span class="na">Migrration Summary</span><span class="pi">:</span>

<span class="na">Tickets</span><span class="pi">:</span>

<span class="na">PCSD Status</span><span class="pi">:</span>
  <span class="na">ram-01.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>
  <span class="na">ram-02.bidhankhatri.com.np</span><span class="pi">:</span> <span class="s">Online</span>

<span class="na">Daemon Status</span><span class="pi">:</span>
  <span class="na">corosync</span><span class="pi">:</span> <span class="s">active/enabled</span>
  <span class="na">pacemaker</span><span class="pi">:</span> <span class="s">active/enabled</span>
  <span class="na">pcsd</span><span class="pi">:</span> <span class="s">active/enabled</span>
</code></pre></div></div>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="System" /><category term="GFS2" /><category term="RHEL8" /><category term="HighAvailability" /><category term="Pacemaker" /><summary type="html"><![CDATA[We will configure Pacemaker/Corosync to enable the sharing of a disk between two nodes through the GFS2 clustered filesystem.]]></summary></entry><entry><title type="html">SAML Authentication for AWS OpenSearch with Okta and Role mapping</title><link href="https://www.bidhankhatri.com.np/cloud/SAML-Authentication-for-AWS-Opensearch-with-Okta-and-role-mapping/" rel="alternate" type="text/html" title="SAML Authentication for AWS OpenSearch with Okta and Role mapping" /><published>2023-05-21T02:16:41+00:00</published><updated>2023-05-21T02:16:41+00:00</updated><id>https://www.bidhankhatri.com.np/cloud/SAML-Authentication-for-AWS-Opensearch-with-Okta-and-role-mapping</id><content type="html" xml:base="https://www.bidhankhatri.com.np/cloud/SAML-Authentication-for-AWS-Opensearch-with-Okta-and-role-mapping/"><![CDATA[<p>We are going to set up <b>IdP-initiated</b> (Okta) SAML authentication for AWS OpenSearch. We will create two Okta groups: <span style="color:#4a6ee0">“opensearch-admin”</span> and <span style="color:#4a6ee0">“opensearch-user,”</span> and define different roles for OpenSearch.</p>

<h4 id="introduction">Introduction</h4>
<p>SAML authentication for OpenSearch Dashboards allows you to utilize your existing identity provider for offering single sign-on (SSO) on Amazon OpenSearch Service domains running OpenSearch or Elasticsearch 6.7 or later. To enable SAML authentication, fine-grained access control must be enabled.</p>

<p>Instead of authenticating through Amazon Cognito or the internal user database, SAML authentication for OpenSearch Dashboards allows you to utilize third-party identity providers to log in, manage fine-grained access control, search your data, and create visualizations. OpenSearch Service supports providers that adhere to the SAML 2.0 standard, including Okta, Keycloak, Active Directory Federation Services (ADFS), Auth0, and AWS IAM Identity Center (the successor to AWS Single Sign-On).</p>

<p>SAML authentication for Dashboards is specifically designed for accessing OpenSearch Dashboards through a web browser. It’s important to note that your SAML credentials cannot be used to directly make HTTP requests to the OpenSearch or Dashboards APIs.</p>

<h4 id="saml-configuration-overview">SAML configuration overview</h4>
<p>This documentation assumes that you have an existing identity provider and some familiarity with it.</p>

<p>The OpenSearch Dashboards login flow can take one of two forms:</p>
<ol>
  <li><b>Service provider (SP) initiated:</b> You navigate to Dashboards (for example, https://my-domain.us-east-1.es.amazonaws.com/_dashboards), which redirects you to the login screen. After you log in, the identity provider redirects you to Dashboards.</li>
  <li><b>Identity provider (IdP) initiated:</b> You navigate to your identity provider ( E.g Okta), log in, and choose OpenSearch Dashboards from an application directory.</li>
</ol>

<p>OpenSearch Service provides two single sign-on URLs, SP-initiated and IdP-initiated, but you only need the one that matches your desired OpenSearch Dashboards login flow. Here In our case, We are going to setup <span style="color:#e83e8c"><b>IdP initiated</b></span> setup.</p>

<p>Regardless of the authentication type you choose, the objective is to log in through your identity provider and obtain a SAML assertion that includes your username (required) and any backend roles (optional, but recommended). This information enables fine-grained access control to assign permissions to SAML users. In external identity providers, backend roles are commonly referred to as ‘roles’ or ‘groups’.</p>

<p><span style="color:#e83e8c">STEPS:</span></p>
<ol>
  <li>Identity Provider (Okta) setup</li>
  <li>Prepare OpenSearch Service for SAML configuration</li>
  <li>SAML configuration in OpenSearch Service Domain and Okta</li>
  <li>Validate Okta users access and role</li>
</ol>

<h4 id="identity-provider-okta-setup">Identity Provider (Okta) setup</h4>
<p>( Create 2 groups <span style="color:#4a6ee0">“opensearch-admin”</span> and <span style="color:#4a6ee0">“opensearch-user”</span> and assign users to it. )</p>
<ol>
  <li>Signup for Okta Account</li>
  <li>Create Users in Okta</li>
  <li>Create Groups in Okta</li>
  <li>Assign Users to Groups</li>
</ol>

<p>Login to your Okta Admin page. If you don’t have Okta then you can singup for Okta free trial account which will be valid for 30 days. To get trail access, go to this page and fill the form. https://www.okta.com/free-trial/</p>

<p><span style="color:#e83e8c">Create Users in Okta:</span> <b>Directory</b> » <b>People</b> » <b>Add Person</b><br />
Create multiple users according to your need. Here we created 2 users <span style="color:#ee964b">“bidhan.khatri”</span> and <span style="color:#ee964b">“ram.thapa”</span></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/okta_user.png" alt="image-center" /></p>

<p><span style="color:#e83e8c">Create Groups in Okta:</span> <b>Directory</b> » <b>Groups</b> » <b>Add Group</b> <br />
We will create 2 groups: <span style="color:#4a6ee0">“opensearch-admin”</span> and <span style="color:#4a6ee0">“opensearch-user.”</span><br />
Assign user <span style="color:#ee964b">“bidhan.khatri”</span> to <span style="color:#4a6ee0">“opensearch-admin”</span> and user <span style="color:#ee964b">“ram.thapa”</span> to <span style="color:#4a6ee0">“opensearch-user”.</span><br />
<span style="color:#4a6ee0">“opensearch-admin”</span> is a Okta group for admin access in OpenSearch Dashboard and <span style="color:#4a6ee0">“opensearch-user”</span> for read access which we will configure later.</p>

<p><span style="color:#e83e8c">Assign Users to Groups:</span><br />
<b>Directory</b> » <b>Groups</b> » <b>opensearch-user</b> » <b> Assign people</b> ( Click + sign of the user <span style="color:#ee964b">“ram.thapa”</span> )<br />
Similary, do for another group.<br />
<b>Directory</b> » <b>Groups</b> » <b>opensearch-admin</b> » <b> Assign people</b> ( select user <span style="color:#ee964b">“bidhan.khatri”</span>)</p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/okta_group_user_assign.png" alt="image-center" /></p>

<h4 id="create-opensearch-in-aws">Create OpenSearch in AWS</h4>
<p>If you haven’t created OpenSearch then follow the below steps, otherwise go to next section <b>“SAML configuration in OpenSearch Service Domain and Okta”</b></p>

<p>Open <b>AWS OpenSearch Service</b> and Click on <b>Create domain.</b><br />
<span style="color:#e83e8c">Domain Name:</span> <b>bdn</b><br />
<span style="color:#e83e8c">Domain Created Method:</span> <b>Standard create</b>  <br />
<span style="color:#e83e8c">Templates:</span>  <b>Dev/test</b>  <br />
<span style="color:#e83e8c">Deployment Options(s):</span>  <b>Domain without standby</b> <br />
<span style="color:#e83e8c">Availability Zones(s):</span>  <b>1-AZ</b>  <br />
<span style="color:#e83e8c">Engine options:</span>  <b>2.5(latest)</b> <br />
<span style="color:#e83e8c">Data nodes:</span>  <b>t3.small.search</b>  <br />
<span style="color:#e83e8c">Number of Nodes:</span>  <b>1</b>  <br />
<span style="color:#e83e8c">Storage Type:</span>  <b>EBS</b> <br />
<span style="color:#e83e8c">EBS storage size per node:</span>  <b>10G</b>   <br />
<span style="color:#e83e8c">Network:</span>  <b>Public Access</b>   <br />
<span style="color:#e83e8c">Fine-grained access control:</span> <b>Create master user</b>      // This credential will be used for logging opensearch dashboard. <br />
<span style="color:#e83e8c">Access Policy:</span> Domain access Policy &gt; <b>Only use fine-grained access control<br />
Other things as default</b>  <br />
Now look at the summary and confirm for new domain and click <b>“create”</b></p>

<h4 id="saml-configuration-in-opensearch-service-domain-and-okta">SAML configuration in OpenSearch Service Domain and Okta</h4>
<p>Go to Amazon OpenSearch Service and in your domain. Go to <b>“bdn Domain”</b> <br />
Click on <b>Actions</b> » <b>Edit security configuration</b></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/aws_bdn_domain.png" alt="image-center" /></p>

<p>Tick on <b>“Enable SAML Authentication”</b></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/saml_auth_os_dashboard.png" alt="image-center" /></p>

<p>We will be using the <span style="color:#e83e8c"><b>Service Provider entity ID</b></span> and <span style="color:#e83e8c"><b>IdP-initiated SSO URL</b></span> for <b>Okta SAML configuration.</b></p>

<p>The OpenSearch Dashboards login flows below step:<br />
You navigate to your <b>identity provider (Okta)</b>, log in, and choose OpenSearch Dashboards from an application directory.</p>

<blockquote>
  <p><u>We will complete rest of the above OpenSearch Service SAML configuration after the <b>Okta SAML configuration.</b></u><br />
  Above SAML information is required for Okta SAML setup.</p>
</blockquote>

<h4 id="okta-saml-configuration">Okta SAML Configuration</h4>
<p>Go back to your <b>OKTA ADMIN PAGE</b> and choose <b>Applications</b><br />
Click on <b>Create App Integration</b> Choose <b>SAML 2.0</b> and click <b>NEXT</b></p>

<p><span style="color:#e83e8c"><b>App name:</b></span> <b>OpenSearch</b><br />
<u><i>SAML Settings</i></u> <br />
<span style="color:#e83e8c"><b>Single sign-on URL:</b></span> <a href="https://search-bdn-gu3ogldk6nakq776cama6dmjq4.us-east-2.es.amazonaws.com/_dashboards/_opendistro/_security/saml/acs/idpinitiated  " style="color:green">https://search-bdn-gu3ogldk6nakq776cama6dmjq4.us-east-2.es.amazonaws.com/_dashboards/_opendistro/_security/saml/acs/idpinitiated </a> <b>[ IdP - initiated SSO URL from bdn domain]</b> <br />
Put tick on <i>Use this for Recipient URL and Destination URL</i>  <br />
<span style="color:#e83e8c"><b>Audience URI (SP Entity ID):</b></span> <a href="https://search-bdn-gu3ogldk6nakq776cama6dmjq4.us-east-2.es.amazonaws.com " style="color:green">https://search-bdn-gu3ogldk6nakq776cama6dmjq4.us-east-2.es.amazonaws.com</a>  <b>[ Service provider entity ID from bdn domain]</b><br />
<i>( Both URL’s are copied from SAML configuration in OpenSearch Service Domain )</i></p>

<p><span style="color:#e83e8c"><b>Default RelayState:</b></span> <i>leave it blank</i> <br />
<span style="color:#e83e8c"><b>Name ID format:</b></span> Select <b>EmailAdress</b> from drop down<br />
<span style="color:#e83e8c"><b>Application Username:</b></span> <b>Okta username</b>  <br />
<span style="color:#e83e8c"><b>Update application username on:</b></span> <b>Create and update</b></p>

<p>Attribute Statements (optional)<br />
<span style="color:#e83e8c">Name:</span> <a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress " style="color:green">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress</a><br />
<span style="color:#e83e8c"><b>Name:</b></span> Select <b>URI Reference</b> from dropdown<br />
<span style="color:#e83e8c"><b>value:</b></span><b> user.email</b></p>

<p>Group Attribute Statements (optional)<br />
<span style="color:#e83e8c"><b>Name:</b></span><a href="http://schemas.xmlsoap.org/claims/Group" style="color:green">http://schemas.xmlsoap.org/claims/Group</a><br />
<span style="color:#e83e8c"><b>Normal Format:</b></span> <b>Unspecified</b> <br />
<span style="color:#e83e8c"><b>Filter:</b></span> Starts with <b>“opensearch”</b></p>

<p>Now click on <b>Next</b></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/okta_saml_integration.png" alt="image-center" /><br />
<img src="https://www.bidhankhatri.com.np/assets/images/okta_saml_intregation_02.png" alt="image-center" /></p>

<blockquote>
  <p>As our both Okta Groups <b>“opensearch-admin”</b> and <b>“opensearch-user”</b> naming starts with string <b>opensearch.</b> Here while configuring group section, we defined <span style="color:#e83e8c">opensearch</span> in Group attribute and used filter <span style="color:#e83e8c">Starts with</span> to catch both groups.</p>
</blockquote>

<p>Are you a customer or partner? Tick on <i>“I’m a software vendor. I’d like to integrate my app with Okta”</i> » <b>Finish</b></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/opensearch_application.png" alt="image-center" /><br />
Choose <b>Sign on</b> menu. Under SAML Setup: Click on <span style="color:#e83e8c"><b>“View SAML setup instructions”</b></span><br />
Go down and select and copy all the content of IDP metadata section. This is an Okta Identity provider metadata for SAML configutation. We have to use this XML content in OpenSearch service. Copy below XML information from the box to somewhere for now.
<img src="https://www.bidhankhatri.com.np/assets/images/idp_metadata.png" alt="image-center" /></p>

<p><span style="color:#e83e8c">Assign Groups to Application “OpenSearch”:</span><br />
Go to option <b>Assignments</b> menu and click on <b>assign</b> » <b>Assign to Groups</b> 
Choose both groups <span style="color:#4a6ee0">“opensearch-admin”</span> and <span style="color:#4a6ee0">“opensearch-user”</span>. Click on <b>Assign</b></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/application_okta_assignment.png" alt="image-center" /></p>

<p>As you can see now both groups are assigned to application <b>OpenSearch.</b></p>

<h4 id="back-to-aws-opensearch-domain-saml-authentication-options"><b>Back to AWS OpenSearch domain SAML authentication options</b></h4>
<p>Under the <b>Import IdP metadata section:</b><br />
<span style="color:#e83e8c"><b>Metadata fromm IdP:</b></span> Copy the <b>Okta Identity Provider metadata</b> which we copied earlier here in the IdP box.<br />
<span style="color:#e83e8c"><b>IdP entity ID:</b></span> It will auto populate once you provide IdP metadata.<br />
<span style="color:#e83e8c"><b>SAML master username - optional:</b></span> Leave it blank.<br />
<span style="color:#e83e8c"><b>SAML master backend role - optional:</b></span> <b>opensearch-admin</b> (Okta group which need full permission in OpenSearch Dashboard.)</p>

<p>Click on Additional settings:<br />
<span style="color:#e83e8c"><b>Subject key - optional:</b></span> Leave it blank.<br />
<span style="color:#e83e8c"><b>Roles Key - optional:</b></span> <a href="http://schemas.xmlsoap.org/claims/Group" style="color:green">http://schemas.xmlsoap.org/claims/Group</a><br />
<span style="color:#e83e8c"><b>Session time to live:</b></span> <b>60</b> minutes<br />
and save the changes.</p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/os_saml_config.png" alt="image-center" /><br />
<img src="https://www.bidhankhatri.com.np/assets/images/os_saml_config_02.png" alt="image-center" /></p>

<h4 id="validate-okta-users-access-and-rbac">Validate Okta users access and RBAC</h4>
<p>Now login the Okta dashboard from your user <span style="color:#ee964b">“bidhan.khatri”.</span><br />
Click on <b>“My Apps”</b> » under <b>“work”</b> » click on <b>“OpenSearch”</b> app to open the AWS OpenSearch Dashboard. User will get full access as user <span style="color:#ee964b">“bidhan.khatri”</span> belongs to okta group <span style="color:#e83e8c">“opensearch-admin”</span></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/okta_app_dashboard.png" alt="image-center" /></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/opensearch-dashboard.png" alt="image-center" /></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/opensearch-dashboard-role.png" alt="image-center" /></p>

<p>All the users belongs to okta group <span style="color:#e83e8c">“opensearch-admin”</span> will receive full permission in OpenSearch Dashboard. We have defined to get full access for <span style="color:#e83e8c">“opensearch-admin”</span> while configuring SAML for AWS OpenSearch domain. <br />
Similary, We can setup <b>Role-based access control (RBAC)</b> using Okta Group based on the OpenSearch Role. For okta group <span style="color:#e83e8c">“opensearch-user”</span> to give Read Only access in all index in OpenSearch dashboard, we have to login OpenSearch Dashboard with full permission user first and create a role in OpenSearch for <span style="color:#e83e8c">“opensearch-user”</span> group and map to it.</p>

<h3 id="give-read-access-to-group-opensearch-user"><b>Give READ Access to Group “opensearch-user”</b></h3>
<p>First, login OpenSearch from the user belonging to Okta group <span style="color:#e83e8c">“opensearch-admin”.</span> We need full permission user access to setup a new role. We will be giving only read permission to all the index to group user <span style="color:#e83e8c">“opensearch-user”</span></p>

<p><b>Security » Roles » Create role » Role-Read » Index permissisions</b><br />
<span style="color:#e83e8c"><b>Index:</b></span> <b>*</b><br />
<span style="color:#e83e8c"><b>Index permmissions:</b></span> <b>read get </b><br />
<span style="color:#e83e8c"><b>Tenant permissions:</b></span> <b>global_tenant</b></p>

<p>click <b>Update</b></p>

<p>Go to <b>Mapped users » Map users » In Backend roles » Add “opensearch-user” » Map</b></p>

<blockquote>
  <p><b>Backend Role</b> is basically a Group in external authentication system like AD,LDAP with whom OpenSearch role is mapped so that instead of giving access to the 100 different users, we can simply provide access to a single AD group.</p>
</blockquote>

<p><img src="https://www.bidhankhatri.com.np/assets/images/role-read-index.png" alt="image-center" /><br />
<img src="https://www.bidhankhatri.com.np/assets/images/role-index-perm.png" alt="image-center" /><br />
<img src="https://www.bidhankhatri.com.np/assets/images/role-read-tenant.png" alt="image-center" /><br />
<img src="https://www.bidhankhatri.com.np/assets/images/role-read-user-map.png" alt="image-center" /></p>

<p><img src="https://www.bidhankhatri.com.np/assets/images/role-read-final.png" alt="image-center" /></p>

<p>Now once user <span style="color:#ee964b">ram.thapa</span> login OpenSearch Dashboard, he will see only read access given to all the indices.<br />
To check your permission, Click on your user icon » click <b> View roles and identities.”</b><br />
He will get below roles defined.
<img src="https://www.bidhankhatri.com.np/assets/images/ram-dashboard.png" alt="image-center" /></p>

<!---
For Ads
-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3654805645342032" crossorigin="anonymous"></script>

<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-TD3E7GW3B7"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-TD3E7GW3B7');
</script>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
    </tr>
  </tbody>
</table>

<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="bidhan.khatri" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>]]></content><author><name>Bidhan Khatri</name><email>bdn@bidhankhatri.com.np</email></author><category term="Cloud" /><category term="AWS" /><category term="SAML" /><category term="Okta" /><category term="OpenSearch" /><summary type="html"><![CDATA[We are going to set up IdP-initiated (Okta) SAML authentication for AWS OpenSearch. We will create two Okta groups: “opensearch-admin” and “opensearch-user,” and define different roles for OpenSearch.]]></summary></entry></feed>