Search This Blog

Wednesday, July 1, 2026

Give Your LLM Hands: Turn Browser Automation Into MCP Tools

LLMs are brilliant at deciding what to do and useless at actually doing it. Automation tools are the opposite — they execute flawlessly but can't reason about anything. The obvious move is to bolt the two together: let the model plan, and let deterministic automation carry out each step. The question is how you connect them cleanly.

The answer that's become a standard is MCP — the Model Context Protocol. It's a common language for exposing "tools" to a model. Wrap your automation actions as MCP tools, point a reasoning model at them, and the model can drive real software. This is the exact pattern under MeBot's agentic RPA. Let's build a small version of it.

Step 1 — Install the pieces

pip install "mcp[cli]" playwright
playwright install chromium

Step 2 — Stand up an MCP server

FastMCP (bundled with the MCP SDK) turns plain Python functions into tools. Any function you decorate becomes something a model can call — the docstring and type hints are the tool's description, so the model knows when to use it.

from mcp.server.fastmcp import FastMCP
from playwright.sync_api import sync_playwright

mcp = FastMCP("mebot-browser")

_pw = sync_playwright().start()
browser = _pw.chromium.launch(headless=False)
page = browser.new_page()

Step 3 — Expose automation actions as tools

Here's the key design decision: the tools themselves stay strictly deterministic. Each one does exactly one concrete thing — no cleverness, no improvising. You never want an LLM "creatively interpreting" a click on a banking screen. The intelligence lives in the model's choice of which tool to call; the execution is boringly reliable.

@mcp.tool()
def open_url(url: str) -> str:
    """Navigate the browser to a URL."""
    page.goto(url)
    return f"Opened {url}"

@mcp.tool()
def click(text: str) -> str:
    """Click the visible element matching the given text."""
    page.get_by_text(text, exact=False).first.click()
    return f"Clicked '{text}'"

@mcp.tool()
def type_text(label: str, value: str) -> str:
    """Type text into the field with the given label."""
    page.get_by_label(label).fill(value)
    return f"Typed into '{label}'"

@mcp.tool()
def read_screen() -> str:
    """Return the visible text on the current page."""
    return page.inner_text("body")

if __name__ == "__main__":
    mcp.run()

Step 4 — Let a reasoning model drive

Now connect a model as an MCP client. It requests the tool list, then works toward a goal by calling tools and reading results — a loop of observe → decide → act. Give it an objective like:

Goal: "Log into the portal and download this month's invoice."

The model plans and calls tools in sequence:
  open_url("https://portal.example.com")
  read_screen()                      -> sees a login form
  type_text("Username", "...")
  type_text("Password", "...")
  click("Sign in")
  read_screen()                      -> sees the dashboard
  click("Invoices")
  ...

Crucially, the model reads the screen between steps. When a button moves or a field is renamed, it adapts — because it's reasoning about what's actually there, not replaying recorded coordinates. That's the difference between a bot that breaks on the first UI change and one that copes.

Step 5 — Guardrails

Autonomy without brakes is a liability, so real deployments add two things: an approvals gate for irreversible or high-risk actions (payments, deletions, submissions) that pauses for a human, and vision — OCR and UI detection — for legacy screens where there's no clean text or label to target. Together they make the agent safe to point at production systems, including the old Windows apps that classic RPA struggles with most.

Deterministic execution, reasoning on top

That's the whole philosophy in one line: keep the doing precise, put the thinking in a layer above it. Wrapping actions as MCP tools is what lets a model orchestrate real work without you hard-coding every branch — and it's exactly how MeBot turns brittle scripts into automation that adapts.

If you'd rather deploy this than assemble it — with the vision layer, approvals inbox, and legacy-app support already built — that's what we ship → takemebot.com

Embed Analytics Into Your Product in an Afternoon (Without Building a BI Team)

Sooner or later, every SaaS product hears the same request from customers: "Can we get reporting on our own data, inside the app?" And every engineering team gives the same sigh, because building analytics from scratch — a query layer, a charting engine, per-tenant security, a dashboard builder — is months of work that has nothing to do with your actual product.

That's the whole reason Datalytics ships as an embeddable SDK. Instead of building BI, you drop a component into your app and pass it a secure token. Here's the full integration, end to end.

Step 1 — Drop in the component

Datalytics dashboards are web components (built on Angular Elements), which means they run in any frontend — React, Vue, Angular, or plain HTML. Add the script and place the element:

<script src="https://cdn.getdatalytics.com/embed.js"></script>

<datalytics-dashboard
    dashboard-id="sales-overview"
    theme="light">
</datalytics-dashboard>

At this point the component exists but shows nothing — because it doesn't yet know who's asking or what they're allowed to see. That's the important part.

Step 2 — Mint a signed token on your backend

Embedded analytics lives or dies on security: customer A must never see customer B's data. The clean way to enforce this is a two-token handshake. Token one is an identity assertion your own backend signs — you already know who the logged-in user is and which tenant they belong to, so you vouch for them.

Your server signs a short-lived RS256 JWT with your private key. Datalytics verifies it with your public key — so the browser never holds any long-lived secret:

// Node / NestJS backend
import jwt from 'jsonwebtoken';

function mintDatalyticsToken(user) {
  return jwt.sign(
    {
      sub: user.id,
      tenant: user.tenantId,          // scopes all queries to this tenant
      scope: ['dashboard:sales-overview'],
    },
    PRIVATE_KEY,                       // your RSA private key
    {
      algorithm: 'RS256',
      expiresIn: '10m',                // short-lived on purpose
      audience: 'datalytics',
      issuer: 'your-app',
    },
  );
}

Step 3 — The two-token exchange

Your frontend fetches that identity token and hands it to the component. Datalytics verifies the signature, reads the tenant and scope, and exchanges it for its own short-lived session token that governs the live dashboard connection. Two tokens, two jobs: yours proves identity, theirs runs the session.

const el = document.querySelector('datalytics-dashboard');

// fetch the signed identity token from YOUR backend
const res = await fetch('/api/analytics-token');
const { token } = await res.json();

// hand it over — the component does the exchange internally
el.token = token;

The dashboard renders, and every query it runs is automatically filtered to that tenant. Row-level security is enforced by the signed tenant claim — not by anything editable in the browser.

Step 4 — Make it look like your app

An embedded dashboard should feel native, not bolted-on. Theme it with CSS custom properties so it inherits your product's look:

datalytics-dashboard {
  --dl-accent: #0e9384;
  --dl-font: 'Inter', sans-serif;
  --dl-radius: 10px;
}

What you just avoided

No query engine, no charting library, no dashboard builder, no per-tenant security model — and no BI team to maintain any of it. Your users get live, self-serve analytics inside your product; you shipped it in an afternoon and moved on with your roadmap.

If you've been putting off "in-app reporting" because building it is a project of its own, this is the shortcut → getdatalytics.com

How We Run Our Own LLMs on Our Own Hardware (and Why We Don't Just Call the Big APIs)

When you need an LLM, the default move in 2026 is to grab an API key from one of the big providers and start calling. For a lot of what we build at Megam, that's the wrong default. Our automation reads client back-office data. Our telehealth platform touches patient records. Our conferencing tool handles private calls. None of that can be shipped off to someone else's cloud — and honestly, even where it could, the economics and the loss of control often don't make sense.

So we run our own inference. Here's how we stand it up, and where the line actually falls between self-hosting and calling an API.

Why self-host at all

  • Data residency & privacy. The data never leaves infrastructure we control. For regulated clients — healthcare, finance, GCC data-residency rules — this isn't a preference, it's the requirement.
  • Cost at volume. Per-token pricing is cheap for a demo and brutal at scale. A fixed box you've already paid for doesn't meter you by the request.
  • Control. The model doesn't change under you overnight, doesn't deprecate, doesn't rate-limit you mid-batch. You pin the version and it stays put.
  • Offline capability. Air-gapped and on-prem deployments simply can't reach a public API. Local is the only option.

Step 1 — Pick the box, size the model to the VRAM

The single constraint that matters is GPU memory. Rough working guide, using 4-bit quantized models:

~7-8B  model  ->  ~6 GB VRAM   (fits almost anything)
~14B   model  ->  ~10-12 GB
~27-32B model  ->  ~20-24 GB    (a 32 GB card handles this comfortably)
~70B   model  ->  ~40 GB+       (big card or multi-GPU)

A modern 24-32 GB card covers the vast majority of real workloads. You don't need frontier-scale hardware to get genuinely useful results.

Step 2 — Install Ollama and pull your models

Ollama is the simplest way to serve open models. One server can host several — chat, vision, and embeddings — and load them on demand.

# Linux install
curl -fsSL https://ollama.com/install.sh | sh

# Pull a few models for different jobs
ollama pull qwen2.5          # general chat / reasoning
ollama pull qwen2.5vl        # vision (images, screenshots, documents)
ollama pull nomic-embed-text # embeddings for RAG / search

Step 3 — Serve it on the network with an OpenAI-compatible API

By default Ollama listens only on localhost. To let your apps reach it, bind it to all interfaces:

# Expose Ollama on the LAN
export OLLAMA_HOST=0.0.0.0:11434
ollama serve

Important: Ollama has no built-in authentication, so never expose port 11434 to the open internet directly. Put Nginx in front of it for TLS and an API key, so only your apps get through:

server {
    listen 443 ssl http2;
    server_name ai.yourdomain.com;

    # ssl_certificate ... ssl_certificate_key ...;

    location /v1/ {
        # simple shared-secret gate
        if ($http_authorization != "Bearer YOUR_SECRET_KEY") {
            return 401;
        }
        proxy_pass http://127.0.0.1:11434;
        proxy_set_header Host $host;
        proxy_read_timeout 300s;   # long generations
    }
}

Step 4 — Call it from any app (it's just the OpenAI SDK)

Because Ollama speaks the OpenAI API format, your application code doesn't care that it's talking to your own box. You change the base_url and nothing else. Migrating from a public API — or back — is a one-line change.

from openai import OpenAI

client = OpenAI(
    base_url="https://ai.yourdomain.com/v1",
    api_key="YOUR_SECRET_KEY",
)

resp = client.chat.completions.create(
    model="qwen2.5",
    messages=[{"role": "user", "content": "Summarize this ticket in one line: ..."}],
)
print(resp.choices[0].message.content)

Step 5 — One server, many jobs

The same endpoint routes different tasks to different models — send chat to qwen2.5, screenshots and documents to qwen2.5vl, and use nomic-embed-text to build the vectors behind a RAG search. Ollama swaps models in and out of VRAM as calls come in, so a single machine covers a surprising amount of ground.

Where this powers our products

This isn't a lab experiment — it's the shared engine under everything we ship. The reasoning layer in MeBot (our agentic RPA) calls it to decide next actions. Datalytics uses it for natural-language analytics. Olivasal runs meeting summaries on it. Saffron, our telehealth platform, runs clinical models on it. One on-prem inference tier, many products — and no client data ever leaves the building.

The honest tradeoffs

Self-hosting isn't free. You own the ops — uptime, drivers, model updates. Open models are excellent now but a very hardest reasoning task may still favour a frontier API. And you'll think about concurrency and batching that a managed API hides from you. Our rule of thumb: self-host anything touching sensitive data or running at volume; reach for a frontier API only for the rare task that genuinely needs it. For most real work, your own hardware wins on cost, privacy, and control at the same time.

If you need AI running inside your own walls — because your data can't leave, or the API bill doesn't scale — that's precisely what we build for clients → megamtech.com

Build a Self-Hosted Meeting-Summary Pipeline (No Cloud, No Data Leaving Your Box)

Every AI notetaker on the market has the same catch: to summarize your meeting, it uploads your audio to its cloud. For a regulated team — healthcare, finance, anyone under data-residency rules — that's a non-starter. Your private calls quietly become someone else's data.

This is exactly why we built the summary engine inside Olivasal to run entirely on infrastructure you control. In this post I'll show you the core of it: take a recorded meeting, produce a clean transcript, and generate a structured summary with action items — all on your own box, nothing leaving the network.

What you'll build

A three-stage pipeline: recording → transcript → AI summary. Stage 1 uses your meeting recording (from LiveKit egress, or any file). Stage 2 transcribes with faster-whisper. Stage 3 summarizes with a local Qwen model served by Ollama. No external API is ever called.

Prerequisites

# System: ffmpeg for audio, plus Python 3.10+
# A GPU makes transcription much faster, but CPU works too

pip install faster-whisper openai

# Ollama for the local LLM (https://ollama.com)
ollama pull qwen2.5

Step 1 — Get the audio

If you're running a conference stack, LiveKit's egress writes a recording when the call ends. Composite egress gives you one mixed file; per-track egress gives you one file per participant (we'll use that later for speaker labels). Either way, extract a clean 16 kHz mono WAV — the format Whisper likes best:

ffmpeg -i meeting.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio.wav

Step 2 — Transcribe locally with faster-whisper

faster-whisper is a reimplementation of Whisper that's several times quicker and lighter on memory. The vad_filter flag trims silence so you don't waste compute on dead air.

from faster_whisper import WhisperModel

# use device="cpu", compute_type="int8" if you have no GPU
model = WhisperModel("large-v3", device="cuda", compute_type="float16")

segments, info = model.transcribe(
    "audio.wav",
    beam_size=5,
    vad_filter=True,
)

transcript = " ".join(seg.text.strip() for seg in segments)
print(f"Detected language: {info.language}")
print(transcript)

That's your full transcript, produced without a single byte leaving the machine.

Step 3 — Summarize with a local LLM

Ollama exposes an OpenAI-compatible endpoint, so you can use the standard openai client and just point it at localhost. We ask for structured JSON so the output is easy to store and render.

from openai import OpenAI
import json

# api_key is required by the client but ignored by Ollama
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

prompt = f"""You are a meeting assistant. From the transcript below, return JSON with:
- "summary": a 3-sentence overview
- "decisions": a list of key decisions made
- "action_items": a list of objects with "task" and "owner"

Transcript:
{transcript}"""

resp = client.chat.completions.create(
    model="qwen2.5",
    messages=[{"role": "user", "content": prompt}],
    response_format={"type": "json_object"},
    temperature=0.2,
)

notes = json.loads(resp.choices[0].message.content)
print(json.dumps(notes, indent=2))

You now have clean, structured meeting notes — summary, decisions, and owner-tagged action items — generated entirely on your own hardware.

Step 4 — Add speaker labels (optional)

This is where per-track egress earns its keep. Instead of one mixed file, you get a separate audio track per participant. Transcribe each track on its own, tag every segment with that participant's identity, then merge all segments back together in timestamp order. The result is a speaker-attributed transcript — "Priya: … / Arun: …" — which makes the LLM's action-item ownership far more accurate, because it can see who actually committed to what.

Why self-hosted matters here

The convenience of AI summaries usually comes at the cost of handing your conversations to a vendor. This pipeline gives you the convenience and keeps every stage — media, transcript, and model — inside your own walls. For regulated teams, that's not a nice-to-have; it's the whole requirement.

This is the engine behind Olivasal, our self-hosted video conferencing with built-in AI summaries — and the same core powers the clinical scribe in our telehealth platform, Saffron. If you'd rather have this running out of the box than wire it up yourself, that's exactly what we built → olivasal.com

Catching Insurance Claim Denials Before You Submit

In revenue-cycle management, the denial is the most expensive message you'll ever receive. Every denied claim is money you already earned — care delivered, work done — now frozen behind rework, appeals, and a filing clock that's ticking down. Some of it comes back after weeks of effort. A painful share never comes back at all, and quietly gets written off.

What makes it worse is when most teams deal with denials: after they happen. The claim goes out, the payer sends it back, and only then does someone open a worklist and start the reactive scramble. It's the most expensive possible point to intervene — you're now paying staff to recover revenue you'd already booked.

Here's the thing we kept noticing across RCM work: denials aren't random. They cluster into a handful of predictable patterns — a missing or expired prior authorization, an eligibility lapse, a diagnosis-to-service code mismatch, a modifier issue, a timely-filing miss, a duplicate. And if a pattern is predictable, it's catchable — before the claim leaves the building, when the fix costs a few minutes instead of a multi-week appeal.

That single shift — from working denials to preventing them — is where the money is. So we've been building it.

Why a generic scrubber doesn't cut it

The hard part is that "what causes a denial" depends entirely on where you're billing.

  • In the US, claims run on X12, and the reasons come back as CARC/RARC codes buried in the remittance.
  • In KSA, it's NPHIES — a FHIR R4 world with its own adjudication outcomes and error structures.
  • In the UAE, it's eClaimLink / DHPO — an XML format with DHA and DOH code sets of their own.

A rule that catches a denial in one market is meaningless in another. And on top of the market differences, every individual payer has its own quirks — the undocumented reasons this insurer rejects that service. A one-size-fits-all checker misses most of what actually matters.

What we're building

The approach we're rolling into our RCM stack has two layers, on purpose:

  1. An explainable rule engine. Market-aware, payer-aware rules that check a claim before submission — eligibility, authorization, coding logic, completeness, timely-filing windows. Rules are the right first layer because they're editable and they tell you exactly why a claim is risky. When a client says "this payer always denies X," that becomes a rule in minutes.
  2. A denial-probability model on top. Trained on the client's own historical claims and remittances, it scores each new claim and surfaces the likely denial reason plus a suggested fix — learning the payer quirks the rules haven't caught yet.

Everything lands where the client can see it: a live dashboard (built on Datalytics) showing first-pass denial rate, the top denial reasons by payer and service, and — the number that actually matters — how that rate drops over time.

The whole philosophy fits on a sticky note: the cheapest denial is the one that never happens. Prevention beats appeal every time, because prevention costs minutes and appeals cost weeks.

We're putting this to work in RCM across the UAE, KSA, and US markets. If denials are eating into your margin and your team is stuck working them after the fact, that's exactly the problem we're solving — let's talk → megamtech.com

Tuesday, December 11, 2018

Jenkins JSON API usage

<?php



class Jenkins
{

private $crumb_field;
private $crumb_value;

/**
* Undocumented check the jenkins url is active
*
* @return boolean
*/
function isActive()
{
$response = \Httpful\Request::get(JENKINS_URL . 'api/json/')
->expectsJson()
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();

return isset($response->body->assignedLabels[0]->name);

}
function getVersion()
{

$response = \Httpful\Request::get(JENKINS_URL . 'api/json/')

->expectsJson()

->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
}
/**
*
* getLastBuild information for the $jobName sent
* @param mixed $jobName - Valid job name
*/

function getLastBuildInfo($jobName)
{

$response = \Httpful\Request::get(JENKINS_URL . '/job/' . $jobName . '/lastBuild/api/json')
->expectsJson()
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
return $response->body;
}
/**
* get all available jobs from the Jenkins with basic information
*
* @return Object
*/
function getJobs()
{
$response = \Httpful\Request::get(JENKINS_URL . '/api/json?tree=jobs[name,color,url,description,buildable,inQueue,healthReport,lastStatbleBuild,lastUnstableBuild,lastSuccessfulBuild,lastFailedBuild]')
->expectsJson()
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
return $response->body;
}
/**
* get config.xml for the jobname passed from Jenkins URL
*
* @param [string] $jobName
* @return Object
*/
function getJobConfig($jobName)
{

$response = \Httpful\Request::get(JENKINS_URL . 'job/' . $jobName . '/config.xml')
->expectsXml()
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
return $response;
}
/**
* trigger a job
*
* @param [string] $jobName
* @return Object
*/
function buildJob($jobName)
{
if ($this->crumb_field == '' || $this->crumb_value == '') {
$this->getCrumb();
}
$response = \Httpful\Request::post(JENKINS_URL .'job/'.$jobName .'/build')
->addHeader($this->crumb_field, $this->crumb_value)
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
return $response;
}
/**
* Get crumb information
*
* @return void
*/
function getCrumb()
{
$response = \Httpful\Request::get(JENKINS_URL . 'crumbIssuer/api/json')
->authenticateWith(JENKINS_USER, JENKINS_PASS)
->expectsJson()
->send();
$this->crumb_field = $response->body->crumbRequestField;
$this->crumb_value = $response->body->crumb;
}
/**
* Copy job to a new name
*
* @param [type] $from - Jobname
* @param [type] $to - Jobname
* @return Object
*/
function createJob($from, $to)
{

if ($this->crumb_field == '' || $this->crumb_value == '') {
$this->getCrumb();
}
$response = \Httpful\Request::post(JENKINS_URL . 'createItem?name=' . $to . '&from=' . $from . '&mode=copy')
->addHeader($this->crumb_field, $this->crumb_value)

->authenticateWith(JENKINS_USER, JENKINS_PASS)
->send();
return $response;
}

}

Wednesday, August 10, 2016

Cloudflare 522 ERROR - Nginx - Fail2Ban



In my case I was using Ubuntu 16.04 with Fail2ban installed as if fail2ban takes the ips from nginx access log, Which has repeated cloudflare ips listed on the file (As if Cloudflare used as reverse proxy). Fail2Ban started block Cloudflare ips 

The solution would be logging the original ips of the request on the access file instead of Cloudflare ip

First check your Nginx has "ngx_http_realip_module"
nginx -V

If Enabled add the ips below to a file under conf.d 

set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 199.27.128.0/21;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;

# use any of the following two
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;


CloudFlare ip might change so please check this link

Wednesday, August 3, 2016

nginx error - ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY

If you get the following error 

This site can’t be reached
The webpage at https://yourdomain.com/ might be temporarily down or it may have moved permanently to a new web address.
ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY

Change your nginx conf as follows

server
{
    listen 443 ssl http2;
    server_name yourdomain.com;
    ...
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:AES256+EDH';
    ...
}

Reference : CloudFlare 

Sunday, June 12, 2016

Remove Skype ads



  1. Exit Skype
  2. Open Internet Explorer
  3. Open Internet Options
  4. Go to Security and select Restricted sites
  5. Click Sites
  6. Add https://apps.skype.com/, click OK
  7. Go to General, click Delete, and click Delete again
  8. Open Skype, the blank space should be gone and there will be no ads

I don't know what the implications are and whether there any issues with other skype apps, but so far this is the only possible solution that I can think of."

Saturday, June 4, 2016

Find malicious or hacked file in linux

First find the outgoing connections with the following command

netstat -nputwN

Check the connections and find the connection which is trying to attack the other systems. For example PID 11009 in this scenario.

Use the following command to identify the list of files involved in the process execution

lsof -p 11009


Tuesday, April 5, 2016

why mcrypt_create_iv is slow



If you don't specify argument for mcrypt_create_iv(), it will use /dev/random(on Linux) by default as a random number generator. The problem of /dev/random is that it's random pool depends on the system interrupts, when there is not enough system interrupts, it cannot generate enough random numbers, then the process tries to get the random numbers will wait and hang.

So instead of 
mcrypt_create_iv($size)
use 
mcrypt_create_iv($size, MCRYPT_DEV_URANDOM); 

See the difference then

Thursday, March 17, 2016

Block traffic to your server from a particular Country



Create a file where we can declare some rules to use:


sudo nano /etc/iptables.firewall.rules


Inside there you'll want to paste the following:


*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0

-A INPUT -i lo -j ACCEPT

-A INPUT -d 127.0.0.0/8 -j REJECT

# Accept all established inbound connections

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow all outbound traffic - you can modify this to only allow certain traffic

-A OUTPUT -j ACCEPT

# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).

-A INPUT -p tcp --dport 80 -j ACCEPT

-A INPUT -p tcp --dport 443 -j ACCEPT

# Allow SSH connections

#

# The -dport number should be the same port number you set in sshd_config


-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT

# Allow ping

-A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# Log iptables denied calls

-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Drop all other inbound - default deny unless explicitly allowed policy

-A INPUT -j DROP

-A FORWARD -j DROP

COMMIT

Save that. Next, we need to apply those rules – this is just a text file, and we need to instruct iptables to actually use it.


sudo iptables-restore < /etc/iptables.firewall.rules


That should have loaded the rules and applied them; you can check by


iptables -L


The output of that command ought to look like


Chain INPUT (policy ACCEPT)


target prot opt source destination


ACCEPT all -- anywhere anywhere


REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable


ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED


ACCEPT tcp -- anywhere anywhere tcp dpt:http


ACCEPT tcp -- anywhere anywhere tcp dpt:https


ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh


ACCEPT icmp -- anywhere anywhere


LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables denied: "


DROP all -- anywhere anywhere






Chain FORWARD (policy ACCEPT)


target prot opt source destination


DROP all -- anywhere anywhere






Chain OUTPUT (policy ACCEPT)


target prot opt source destination


ACCEPT all -- anywhere anywhere


Great, it's working! But if you reboot the server it won't be. So lets fix that by creating a file which will run at boot.


sudo nano /etc/network/if-pre-up.d/firewall


Inside that file paste:


#!/bin/sh


/sbin/iptables-restore < /etc/iptables.firewall.rules


Save it. Now we must make sure it's allowed to execute:


sudo chmod +x /etc/network/if-pre-up.d/firewall


Done. The firewall is now running with those rules applied and those rules will be re-applied every time the server reboots. But it's not blocking China yet; it's only blocking anything not on port 80 or 443 (http and https).
Using ipset to block China


You can't manually add a few thousand IP addresses to your iptables, and even doing it automatically is a bad idea because it can cause a lot of CPU load (or so I've read). Instead we can use ipset which is designed for this sort of thing. ipset handles big lists of ip addresses; you just create a list and then tell iptables to use that list in a rule.


Note; I assume that the entirety of the following is done as root. Adjust accordingly if your system is based on sudo.


apt-get install ipset


Next, I wrote a small Bash script to do all the work, which you should be able to understand from the comments in it. Create a file:


nano /etc/block-china.sh


Here's what you want to paste into it:


# Create the ipset list


ipset -N china hash:net






# remove any old list that might exist from previous runs of this script


rm cn.zone






# Pull the latest IP set for China


wget -P . http://www.ipdeny.com/ipblocks/data/countries/cn.zone






# Add each IP address from the downloaded list into the ipset 'china'


for i in $(cat /etc/cn.zone ); do ipset -A china $i; done






# Restore iptables


/sbin/iptables-restore < /etc/iptables.firewall.rules


Save the file. Make it executable:


chmod +x /etc/block-china.sh


This hasn't done anything yet, but it will in a minute when we run the script. First, we need to add a rule into iptables that refers to this new ipset list the script above defines:


nano /etc/iptables.firewall.rules


Add the following line:


-A INPUT -p tcp -m set --match-set china src -j DROP


Save the file. To be clear, my full iptables.firewall.rules now looks like this:


*filter






# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0


-A INPUT -i lo -j ACCEPT


-A INPUT -d 127.0.0.0/8 -j REJECT






# Accept all established inbound connections


-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT






# Block anything from China


# These rules are pulled from ipset's china list


# The source file is at /etc/cn.zone (which in turn is generated by a shell script at /etc/block-china.sh )


-A INPUT -p tcp -m set --match-set china src -j DROP






# Allow all outbound traffic - you can modify this to only allow certain traffic


-A OUTPUT -j ACCEPT






# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).


-A INPUT -p tcp --dport 80 -j ACCEPT


-A INPUT -p tcp --dport 443 -j ACCEPT






# Allow SSH connections


#


# The -dport number should be the same port number you set in sshd_config


#


-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT






# Allow ping


-A INPUT -p icmp -j ACCEPT






# Log iptables denied calls


-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7






# Drop all other inbound - default deny unless explicitly allowed policy


-A INPUT -j DROP


-A FORWARD -j DROP






COMMIT


Right now, nothing has changed with the server because no new rules have been applied; to do so, run the block-china.sh script:


/etc/block-china.sh


This should show some output as it pulls a fresh list of Chinese based IPs and then, after a few seconds or so, it will complete and drop you back to a command prompt.


To test if it worked, run:


iptables -L


You should now see a new rule blocking China – the output ought to look like this:


Chain INPUT (policy ACCEPT)


target prot opt source destination


ACCEPT all -- anywhere anywhere


REJECT all -- anywhere loopback/8 reject-with icmp-port-unreachable


ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED


DROP tcp -- anywhere anywhere match-set china src


ACCEPT tcp -- anywhere anywhere tcp dpt:http


ACCEPT tcp -- anywhere anywhere tcp dpt:https


ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh


ACCEPT icmp -- anywhere anywhere


LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables denied: "


DROP all -- anywhere anywhere






Chain FORWARD (policy ACCEPT)


target prot opt source destination


DROP all -- anywhere anywhere






Chain OUTPUT (policy ACCEPT)


target prot opt source destination


ACCEPT all -- anywhere anywhere


Almost done! This works, and will continue to work on re-boots. But, IP addresses change and that list will grow stale over time. If you want to pull and apply an updated list of IPs you can just run the block-china.sh script again.






Configure your websever:


We use Ngnix, So steps to block traffic from China do as follow :


Check ip modules are enabled






nginx -V






If you see --with-http_geoip_module in the output, you are ready to use the GeoIP database with nginx:


root@server1:~# nginx -V
nginx version: nginx/1.2.1
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-pcre-jit --with-debug --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-auth-pam --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-echo --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-upstream-fair --add-module=/build/buildd-nginx_1.2.1-2.1-amd64-fMGfEu/nginx-1.2.1/debian/modules/nginx-dav-ext-module
root@server1:~#





Installing The GeoIP Database


On Debian/Ubuntu, the GeoIP database can be installed as follows:


apt-get install geoip-database libgeoip1


This places the GeoIP database in /usr/share/GeoIP/GeoIP.dat.


It is possible that it is a bit outdated. Therefore we can optionally download a fresh copy from the GeoIP web site:


mv /usr/share/GeoIP/GeoIP.dat /usr/share/GeoIP/GeoIP.dat_bak


cd /usr/share/GeoIP/
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
gunzip GeoIP.dat.gz





Configuring nginx


Open /etc/nginx/nginx.conf...


vi /etc/nginx/nginx.conf


... and place this in the http {} block, before any include lines:



[...]
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default yes;
FK no;
FM no;
EH no;
}
[...]




This allows all countries, except the three countries set to no (you can find a list of country codes here). To do it the other way round, i.e. block all countries and allow only a few, you'd do it this way:



[...]
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default no;
FK yes;
FM yes;
EH yes;
}
[...]




Now, this actually doesn't block any country, it just sets the $allowed_country variable. To actually block countries, you must open your vhost configuration and place the following code in the server {} container (this can go inside and also outside any location {} block):



[...]
if ($allowed_country = no) {
return 444;
}
[...]




This returns the 444 error code to any visitor from a blocked country. What this does is it closes the connection without sending any headers. You can also use another error code like 403 ("Forbidden") if you like.


Reload nginx afterwards:


/etc/init.d/nginx reload





4 Links


· nginx: http://nginx.org/


· nginx Wiki: http://wiki.nginx.org/


· HttpGeoipModule: http://wiki.nginx.org/HttpGeoipModule









































Monday, August 17, 2015

Multiple JDK versions in Centos






Downloading Latest Java Archive

Java latest archive is available on its official site. We recommend to download latest version of Java from Oracle official website. After completing download also extract archive with given commands.

For 64 Bit:-

# cd /opt/
# wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.tar.gz"

# tar xzf jdk-7u79-linux-x64.tar.gz

For 32 Bit:-

# cd /opt/
# wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-i586.tar.gz"

# tar xzf jdk-7u79-linux-i586.tar.gz
Note: If Above wget command doesn’t not work for you watch this example video to download java source archive using terminal.
Use archive file as per your system configuration. For this example we are using CentOS 7.0 (64 bit) system.
Install Java with Alternatives
After extracting Java archive file, we just need to set up to use newer version of Java using alternatives. Use the following commands to do it.
# cd /opt/jdk1.7.0_79/
# alternatives --install /usr/bin/java java /opt/jdk1.7.0_79/bin/java 2
# alternatives --config java
There are 3 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*  1           /opt/jdk1.7.0_60/bin/java
 + 2           /opt/jdk1.7.0_72/bin/java
   3           /opt/jdk1.7.0_79/bin/java

Enter to keep the current selection[+], or type selection number: 3 [Press Enter]
Now you may also required to set up javac and jar commands path using alternatives command.
# alternatives --install /usr/bin/jar jar /opt/jdk1.7.0_79/bin/jar 2
# alternatives --install /usr/bin/javac javac /opt/jdk1.7.0_79/bin/javac 2
# alternatives --set jar /opt/jdk1.7.0_79/bin/jar
# alternatives --set javac /opt/jdk1.7.0_79/bin/javac 
Check Installed Java Version
Use following command to check which version of Java is currently being used by system.
# java -version

java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)








Thursday, May 21, 2015

New Features in Mongo DB 3.0



Enhanced Query Language and Tools

Key MongoDB tools mongoimport, mongoexport, mongodump, mongorestore, mongostat, mongotop and mongooplog have been re-written as multi-threaded processes in Go, allowing faster operations and smaller binaries.
mongodump and mongorestore now execute parallelized backup and recovery for small MongoDB instances. Dumps created by earlier releases can be restored to instances running MongoDB 3.0. 

mongoimport can parallelize loads across multiple collections with multi-threaded bulk inserts allowing for significantly faster imports of CSV, TSV and JSON data exported from other databases or applications. Ensuring data quality, mongoimport now also supports input validation of field names during the import process.

Improved DBA Productivity: Enhanced Query Engine Introspection The MongoDB "explain() method is an invaluable tool for DBAs in optimizing performance. Using explain() output, DBAs can review query plans, ensuring common queries are serviced by well-defined indexes, as well as eliminating any unnecessary indexes that can increase write latency and add overhead during query planning and optimization.
In the latest MongoDB 3.0 release explain() has been significantly enhanced:
  • The query plan can now be calculated and returned without first having to run the query. This enables DBAs to review which plan will be used to execute the query, without having to wait for the query to run to completion.
  • DBAs can run explain() to generate detailed statistics on all query plans considered by the optimizer. Execution statistics are available for every evaluated plan, down to the granularity of execution stage. Now, for example, it is possible for the DBA to distinguish the amount of time a query plan spent sorting the result set from the amount of time spent reading index keys.
  • The explain() method exposes query introspection to a wider range of operations, including find, count, update, remove, group, and aggregate, enabling DBAs to optimize for a wider range of query types.
Richer Geospatial Apps: Big Polygon Support for Multi-Hemisphere Queries 
MongoDB’s geospatial indexes and queries are widely used by developers building modern location-aware applications across industries as diverse as high technology, retail, telecommunications and government. MongoDB 3.0 adds big polygon geospatial support with$intersects and $within operators, allowing execution of queries over geographic areas extending across multiple hemispheres and areas that exceed 50% of the earth’s surface. As an example, an airline can now run queries to identify all its aircraft that have traveled across multiple hemispheres in the past 24 hours.

Enhanced Data Type Support: Easier Time-Series Analytics & Reporting 
The MongoDB 3.0 aggregation pipeline offers a new $dateToString operator that simplifies report generation and grouping data by time interval. The operator formats the ISO Date type as a string with a user-supplied format, allowing developers to construct rich queries with less code.

Faster Issue Resolution: Enhanced Logging 
Log analysis is a critical part of identifying issues and determining root cause. Now in MongoDB 3.0 developers, QA and operations staff have much greater control over the granularity of log messages and specific functional areas of the server to more precisely investigate issues.

Deploying Geo-Distributed, Datacenter-Aware Applications 
Delivering a low latency experience to customers wherever they are located is a key design consideration for distributed systems. Using MongoDB’s native replica sets, copies (replicas) of the database can be deployed to sites physically closer to users, thereby reducing the effects of network latency. Reads can be issued with the nearestread preference, ensuring the query is served from the replica closest to the user, based on ping distance.


Pluggable Storage Engines: Extending MongoDB to New Applications


Multiple storage engines can co-exist within a single MongoDB replica set, making it easy to evaluate and migrate engines. Running multiple storage engines within a replica set can also simplify the process of managing the data lifecycle. For example as different storage engines for MongoDB are developed, it would be possible to create a mixed replica set configured in such a way that:
  1. Operational data requiring low latency and high throughput performance is managed by replica set members using the WiredTiger or in-memory storage engine (currently experimental).
  2. Replica set members configured with an HDFS storage engine expose the operational data to analytical processes running in a Hadoop cluster, which is executing interactive or batch operations rather than real time queries.
MongoDB replication automatically migrates data between primary and secondary replica set members, independent of their underlying storage format. This eliminates complex ETL tools that have traditionally been used to manage data movement.

MongoDB 3.0 ships with two supported storage engines:
  1. The default MMAPv1 engine, an improved version of the engine used in prior MongoDB releases, now enhanced with collection level concurrency control.
  2. The new WiredTiger storage engine. For many applications, WiredTiger's more granular concurrency control and native compression will provide significant benefits in the areas of lower storage costs, greater hardware utilization, higher throughput, and more predictable performance.

Higher Performance & Efficiency

Between 7x and 10x Greater Write Performance
MongoDB 3.0 provides more granular document-level concurrency control, delivering between 7x and 10x greater throughput for most write-intensive applications, while maintaining predictable low latency.

Compression: Up to 80% Reduction in Storage Costs
Despite data storage costs declining 30% to 40% per annum, overall storage expenses continue to escalate as data volumes double every 12 to 18 months. To make matters worse, improvements to storage bandwidth and latency are not keeping pace with data growth, making disk I/O a common bottleneck to scaling overall database performance.


Administrators have the flexibility to configure specific compression algorithms for collections, indexes and the journal, choosing between:
  1. Snappy (the default library for documents and the journal), providing a good balance between high compression ratio – typically around 70%, depending on document data types – and low CPU overhead.
  2. zlib, providing higher document and journal compression ratios for storage-intensive applications, at the expense of extra CPU overhead.
  3. Prefix compression for indexes reducing the in-memory footprint of index storage by around 50% (workload dependent), freeing up more of the working set for frequently accessed documents.
Administrators can modify the default compression settings for all collections and indexes. Compression is also configurable on a per-collection and per-index basis during collection and index creation.
By introducing compression, operations teams get higher performance per node and reduced storage costs.

Creating Multi-Temperature Storage
Combining compression with MongoDB’s location-aware sharding, administrators can build highly efficient tiered storage models to support the data lifecycle. Administrators can balance query latency with storage density and cost by assigning data sets to specific storage devices. For example, consider an application where recent data needs to be accessed quickly, while for older data, latency is less of a priority than storage costs:
  1. Recent, frequently accessed data can be assigned to high performance SSDs with Snappy compression enabled.
  2. Older, less frequently accessed data is tagged to higher capacity, lower-throughput hard disk drives where it is compressed with zlib to attain maximum storage density and lower cost-per-bit.
MongoDB will automatically migrate data between storage tiers based on user-defined policies without administrators having to build tools or ETL processes to manage data movement.

Source : Mongo DB

Thursday, January 29, 2015

Wordpress image upload http error on Nginx




This was due to the following param in Nginx.conf under http section, Just change it to maximum size of you want to upload 

example
client_max_body_size 5M;

Thursday, October 9, 2014

Indian Rupee Symbol in OpenCart


Execute these Queries in you database

Alter table oc_currency change column symbol_left symbol_left varchar(50);

Update oc_currency set symbol_left='<i class="fa fa-inr">&nbsp;</i>' where currency_id=4;
Change the stylesheet.css in "catalog/view/theme/default/stylesheet/stylesheet.css" To remove the font size to make it even across the application

Note by default Font Awesome is included in 2.0 Opencart if your theme doesn't have added it to make this solution work