top of page

Deploy MISP in an Azure Container Instances (ACI) and Integrate with Microsoft Sentinel

  • 1 day ago
  • 6 min read

If you're running a SOC on Microsoft Sentinel, you've probably hit that point where you need better threat intelligence sharing. Sure, you can plug in a few commercial feeds, but if you want to collaborate with peer organizations or tailor your intel to your environment, you need something more flexible. That's where MISP (Malware Information Sharing Platform) comes in.


The catch? MISP wasn't exactly built with Azure in mind. It's typically deployed on VMs with all the usual LAMP stack baggage. But here's the thing—you can actually run it pretty cleanly in Azure Container Instances (ACI), which gives you faster deployment, easier maintenance, and frankly, less headache than managing another VM. Then you wire it into Sentinel using the built-in data connector, and suddenly your analysts are working with contextualized, shareable threat intel instead of just CSV dumps.


In this walkthrough, I'll show you how to spin up MISP in ACI and get it feeding into Sentinel. We'll cover the Docker deployment, networking quirks, and the actual integration steps. By the end, you'll have a working threat intel platform that's actually useful.


Prerequisites

Before we get started, make sure you have:

  • An active Azure subscription with permissions to create container instances and networking resources

  • Azure CLI installed and authenticated (az login)

  • A Microsoft Sentinel workspace already deployed

  • Contributor or Owner role on the resource group you'll be deploying to

  • Basic familiarity with Docker concepts (though you don't need Docker locally)

  • A domain name or willingness to work with Azure's FQDN (for HTTPS setup)

  • At least 30 minutes—MISP's first boot takes a while to initialize the database


Step 1 – Create a Resource Group and Storage Account

First things first—let's set up the foundation. MISP needs persistent storage because you definitely don't want to lose your threat intel database every time the container restarts.

az group create \
  --name rg-misp-production \
  --location eastus

az storage account create \
  --name stmispdata001 \
  --resource-group rg-misp-production \
  --location eastus \
  --sku Standard_LRS

az storage share create \
  --name misp-data \
  --account-name stmispdata001 \
  --quota 100

That storage share is where MISP will keep its MySQL database, uploaded files, and configuration. The 100GB quota is honestly overkill for most environments, but storage is cheap and running out mid-incident is not.


Step 2 – Deploy MISP Container to Azure Container Instances

Now we'll deploy the actual MISP container. I'm using the coolacid/misp-docker image because it's well-maintained and packages everything you need. You could build your own, but unless you have specific requirements, don't reinvent the wheel.

STORAGE_KEY=$(az storage account keys list \
  --account-name stmispdata001 \
  --resource-group rg-misp-production \
  --query "[0].value" -o tsv)

az container create \
  --resource-group rg-misp-production \
  --name misp-instance \
  --image coolacid/misp-docker:latest \
  --dns-name-label misp-yourorg \
  --ports 80 443 \
  --cpu 2 \
  --memory 4 \
  --environment-variables \
    MYSQL_HOST=localhost \
    MYSQL_DATABASE=misp \
    MYSQL_USER=misp \
    MYSQL_PASSWORD='ChangeThisPassword!' \
    MISP_ADMIN_EMAIL=admin@yourorg.com \
    MISP_ADMIN_PASSPHRASE='AnotherStrongPassword!' \
    MISP_BASEURL=https://misp-yourorg.eastus.azurecontainer.io \
  --azure-file-volume-account-name stmispdata001 \
  --azure-file-volume-account-key $STORAGE_KEY \
  --azure-file-volume-share-name misp-data \
  --azure-file-volume-mount-path /var/lib/mysql

A couple of things to note here. First, the --dns-name-label needs to be globally unique across Azure—so pick something tied to your org. Second, yes, we're passing passwords as environment variables. In production, you'd want to use Azure Key Vault references, but for a lab or pilot, this gets you moving.

The container will take 5-10 minutes to fully initialize on first boot. MISP has to set up the database schema, populate default taxonomies, and configure Apache. Grab a coffee.


Step 3 – Verify MISP is Running

Once the deployment finishes, check that everything came up cleanly:

az container show \
  --resource-group rg-misp-production \
  --name misp-instance \
  --query "{FQDN:ipAddress.fqdn,ProvisioningState:provisioningState}" \
  --output table

You should see the FQDN and a Succeeded provisioning state. Open that FQDN in your browser (it'll be something like https://misp-yourorg.eastus.azurecontainer.io). You'll probably get a certificate warning because we're using a self-signed cert—that's expected. Click through and you should see the MISP login page.

Log in with the email and password you set in the environment variables. If you can't log in, check the container logs:

az container logs \
  --resource-group rg-misp-production \
  --name misp-instance \
  --tail 50

Look for any MySQL connection errors or initialization failures.


Step 4 – Configure MISP Authentication and API Access

Before we hook this into Sentinel, we need to set up an API key. In the MISP web interface:

  1. Navigate to Administration → List Users

  2. Click on your admin user

  3. Scroll down to Auth Keys and click Add authentication key

  4. Give it a comment like "Sentinel Integration"

  5. Copy the generated key—you'll need it in a minute

While you're in the admin panel, also verify your MISP.baseurl setting under Administration → Server Settings & Maintenance. It should match the FQDN we set earlier. If it doesn't, MISP will generate broken links in exports.


Step 5 – Enable the MISP Connector in Microsoft Sentinel

Now we move over to Sentinel. Open your Sentinel workspace in the Azure portal:

  1. Go to Content hub and search for "MISP"

  2. Install the Threat Intelligence - MISP solution if you haven't already

  3. Navigate to Configuration → Data connectors

  4. Find Threat Intelligence - MISP and click Open connector page

  5. Click Connect

You'll be prompted for a few pieces of info:

  • MISP instance URL: Your container's FQDN (e.g., https://misp-yourorg.eastus.azurecontainer.io)

  • MISP API key: The auth key you just generated

  • Indicator types: Select what you want imported (usually domains, IPs, URLs, file hashes)

  • Threat types: Choose relevance (malicious-activity, attribution, etc.)

  • Import frequency: I usually set this to 5 minutes for active environments

Click Apply and Sentinel will start polling MISP.


Step 6 – Verify Indicators Are Flowing

This is where you make sure everything actually works. In MISP, create a test event:

  1. Click Event Actions → Add Event

  2. Fill in basic info (Distribution: Your organization only, Threat Level: High)

  3. Add an attribute—let's do a malicious IP: 192.0.2.50 (this is a documentation IP, won't hurt anything)

  4. Tag it appropriately and Publish the event

Wait about 5 minutes, then head back to Sentinel. Go to Threat Management → Threat Intelligence and search for that IP. If you see it listed with a source of "MISP," you're in business.

If nothing shows up after 10 minutes, check the data connector status for errors. Common issues are certificate validation failures (if you're using self-signed certs) or firewall rules blocking Sentinel's outbound connection.


Step 7 – Create an Analytics Rule to Act on MISP Indicators

Having indicators in Sentinel is nice, but useless if they don't trigger alerts. Let's create a simple rule that fires when a MISP indicator matches your logs.

Go to Configuration → Analytics → Create → Scheduled query rule:

  • Name: "MISP Indicator Match - Network Connections"

  • Tactics: Select relevant MITRE ATT&CK tactics

  • Severity: Medium

Use this KQL query:

let MISPIndicators = ThreatIntelligenceIndicator
| where SourceSystem == "MISP"
| where isnotempty(NetworkIP) or isnotempty(NetworkDestinationIP)
| extend IOC = coalesce(NetworkIP, NetworkDestinationIP);
CommonSecurityLog
| where TimeGenerated > ago(1h)
| extend DestIP = DestinationIP
| join kind=inner MISPIndicators on $left.DestIP == $right.IOC
| project TimeGenerated, DeviceVendor, DeviceProduct, DestIP, ThreatType, Description, SourceSystem

Set the query to run every 5 minutes and look back 1 hour. Adjust based on your log volume and tolerance for alert fatigue.


Troubleshooting

Container keeps restarting: Check memory allocation. MISP is database-heavy—if you skimped on RAM, bump it to 4GB minimum. Also verify your storage account key is correct.


Certificate errors blocking Sentinel connection: If you're using a self-signed cert, you have two options. One, get a proper cert from Let's Encrypt (you'll need to configure this in the MISP container). Two, adjust Sentinel's connector settings—though Microsoft generally expects valid certs in production.


Indicators not showing up in Sentinel: Verify the MISP event is actually published (not just saved as a draft). Also check the distribution level—Sentinel won't pull events marked as internal-only unless your connector is configured for it.


Slow performance in ACI: Two CPU cores is the minimum. If you're ingesting thousands of indicators or have multiple analysts working in MISP simultaneously, consider bumping to 4 cores. You can update the container without redeploying.


MySQL data disappearing after restart: Your volume mount probably failed. Double-check the storage account name and key. Run az container show and look at the volumes section to confirm it's attached.


Hardening Considerations

Let's be honest—exposing MISP directly to the internet with a self-signed cert isn't great. Here's how to tighten things up:


Use Azure Application Gateway: Put an App Gateway with a proper SSL cert in front of the container instance. This gives you WAF protection and solves the certificate issue. Point your DNS at the App Gateway and configure backend health probes.


Lock down network access: By default, ACI is publicly accessible. If your Sentinel workspace and MISP users are in Azure, put the container instance in a VNet and use Private Endpoints.

Only allow inbound from your jump box or VPN.


Rotate API keys regularly: Set a reminder to regenerate the Sentinel API key every 90 days. It takes 30 seconds and limits exposure if the key leaks.


Enable MISP audit logging: Under Administration → Server Settings, enable the audit log. Forward these to Sentinel so you have visibility into who's adding and modifying indicators.


Use Key Vault for secrets: Store your MySQL password and MISP admin password in Azure Key Vault and reference them in your container deployment. This keeps them out of deployment scripts and ARM templates.


Implement RBAC in MISP: Don't give everyone admin access. Create read-only analyst accounts and limit event publishing to senior analysts or automation accounts.

Set up backup for the storage account: Configure Azure Backup for your storage account. If someone accidentally nukes an important event or the database corrupts, you want a restore option.


Comments


Subscribe

Thanks for submitting!

bottom of page