Skip to main content
emnode

Hardening open-source databases: the basics

What does an exposed Azure Database for MySQL or PostgreSQL server actually look like?

A managed MySQL or PostgreSQL server holds your application's live data, and exposure shows up in a handful of distinct shapes rather than one setting. Public network access can be left on so the server is reachable from any address instead of only a private endpoint. SSL enforcement, or require_secure_transport on flexible servers, can be off so clients still connect in cleartext. Sign-in can rely on a local database password with no Microsoft Entra administrator provisioned. Audit logging through pgaudit can be unconfigured so nobody can reconstruct who did what. Geo-redundant backup can be missing so a regional outage loses the database. Each is its own Defender for Cloud recommendation, but the underlying risk is the same: a store of live data that more of the internet can reach, more of it travels in the clear, and less of it can be traced or recovered than you meant.

Microsoft Defender for Cloud turns each of these into its own recommendation, which is why one database-heavy subscription can fail a dozen of these checks at once. 'Public network access should be disabled' and 'Private endpoint should be enabled' close the widest doors; 'Enforce SSL connection should be enabled' and require_secure_transport encrypt the wire; the Microsoft Entra administrator and Entra-only checks move identity off shared passwords; the pgaudit family records behaviour; and geo-redundant backup protects recovery. They read as separate problems on the report, but they are one job: stop an open-source database being reachable, readable, anonymous or unrecoverable by accident.

Most of this exposure is drift, not intent. A server created with public access for a quick test that nobody locked down, a module that never set SSL enforcement, a flexible server where require_secure_transport was flipped off during a migration, an Entra administrator nobody provisioned, pgaudit left at defaults. Two of these services also matter to the timeline: Azure Database for MySQL and PostgreSQL Single Server are being retired in favour of Flexible Server, so several of these recommendations apply to a deployment model you may be migrating off. The work is to find every server that is reachable, readable, anonymous or unaudited more widely than intended, decide the handful that are exceptions, and lock the rest down.

In this lesson you will learn how Azure Database for MySQL and PostgreSQL express public reachability, in-transit protection, identity, audit and recoverability, how to find every over-exposed or under-protected server in a subscription, and how to lock them down without breaking live applications. The Controls this lesson covers section lists every Defender for Cloud recommendation in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.

Fun fact

The database that answered the whole internet

Exposed managed databases are a staple of automated internet scanning: services such as Shodan continuously index every reachable MySQL and PostgreSQL endpoint, so a server left open to public network access is found within hours, not months, often before anyone on the team notices. When that server also accepts cleartext connections or relies on a weak shared password, an opportunistic scan turns into a breach. Azure now defaults flexible servers to private access and TLS enforcement, but every server created with public access, every Single Server carried over from the older deployment model, and every flexible server where require_secure_transport was flipped off still needs to be checked and locked explicitly.

Finding database exposure across a subscription

Priya is the security lead at a scale-up preparing for its first ISO 27001 audit. Defender for Cloud shows database findings spread across MySQL and PostgreSQL servers in two subscriptions, a mix of older Single Servers and newer flexible servers that pre-date the team's current guardrails.

Rather than work the findings one by one, Priya starts by listing which flexible servers still allow public network access, so the genuinely internet-facing servers can be separated from drift before anything changes.

Start by listing the PostgreSQL flexible servers that still allow public network access. These are the widest-open doors.

$ az postgres flexible-server list --query "[].{name:name, public:network.publicNetworkAccess}" -o table
Name Public
---------------- --------
ordersprod-pg Enabled
reporting-stg-pg Enabled
internal-pg Disabled
# ordersprod-pg holds live customer orders AND is public. Close it first.

A production server that holds live data and is reachable from the public internet is the highest-value target in this group. Confirm SSL enforcement on the same servers, then fix these first.

How Defender for Cloud decides a database is exposeddeep dive

Most of these recommendations resolve to one of a few properties on the server. Network reachability is the network.publicNetworkAccess property on a flexible server, or the public-network-access and firewall configuration on a Single Server: when public access is enabled, the server answers from any address rather than only a private endpoint, and the related checks flag a missing private endpoint and the 'Allow access to Azure services' firewall rule that opens the server to all of Azure. In-transit protection is SSL enforcement on a Single Server and the require_secure_transport server parameter on a flexible server: when it is off, the server accepts cleartext connections, so credentials and query data travel unencrypted.

Identity, audit and recovery are the rest. The Microsoft Entra administrator checks look for a provisioned Entra admin, and the PostgreSQL flexible-server check goes further by requiring Entra-only authentication, meaning local password sign-in is disabled. The pgaudit family inspects server parameters: pgaudit.log should include role, ddl and misc, pgaudit.log_statement and log_statement_once should be on, and pgaudit.log_level should be log, with log file retention set above the default. Note that pgaudit.log_level only takes effect when pgaudit.log_client is on, which is a deliberate trade-off because log_client also streams audit events to connecting clients. Recoverability is geo-redundant backup, a property fixed at server creation that replicates backups to the paired region.

Defender for Cloud evaluates these against the Microsoft Cloud Security Benchmark on a periodic cycle, so a fix does not flip the recommendation to Healthy instantly. The control-plane change itself is immediate, but the posture catches up on the next assessment, so a server can be encrypted, private and audited yet still read as failing until the next evaluation. This matters when you are gathering audit evidence against a deadline. The strongest position combines the server setting with an Azure Policy backstop: a deny policy on public network access means no individual server can be created in the open state again. Turning that policy on is the difference between checking that nothing is exposed today and guaranteeing that nothing can be exposed tomorrow.

What is the impact of leaving a database exposed?

The direct impact is data exposure and loss. A server reachable from the public internet is found quickly by automated scanners, and if it also accepts cleartext connections or relies on a weak shared password, an opportunistic scan becomes a breach of live, structured data. A server with no Microsoft Entra administrator and no audit logging cannot tell you who connected or what they ran, so an incident becomes unprovable. A server without geo-redundant backup loses its data entirely if its region has a sustained outage. The overwhelming majority of database incidents were accidents: a setting that could be left open was left open by a module or a migration nobody intended.

The second-order impact is blast radius and trust. Every internet-facing database is attack surface that has to be defended continuously, and a database that cannot be audited cannot be cleared during an investigation, so even a near-miss costs weeks of forensic effort. Moving servers onto private endpoints, enforcing SSL, centralising identity in Microsoft Entra and turning on pgaudit shrinks that surface to the connections you actually operate and makes every one of them traceable.

On the compliance side, every modern framework, ISO 27001, SOC 2, HIPAA, PCI DSS and the UK and EU data-protection regimes, expects evidence that databases are not publicly reachable, that connections are encrypted, that privileged access is identity-governed and audited, and that data is recoverable. A passing set of database recommendations across every subscription is the cheapest and most defensible artefact you can hand an auditor.

How do you lock open-source databases down safely?

Work the capability as one loop rather than chasing individual findings. The order matters: confirm which servers are genuinely production and how clients connect before you start closing things, so you do not take a live application offline.

1. Inventory every server by exposure and deployment model

List the MySQL and PostgreSQL servers that allow public network access, that do not enforce SSL or require_secure_transport, that have no Microsoft Entra administrator, that have pgaudit unconfigured, and that lack geo-redundant backup. Separate Single Servers from flexible servers, because the older Single Server is being retired and several findings point to a deployment model you may be migrating off rather than hardening in place.

2. Close the network and the wire, highest impact first

Disable public network access and put the production servers behind a private endpoint, remove the 'Allow access to Azure services' rule, and enforce SSL on Single Servers or set require_secure_transport to ON on flexible servers, with ssl_min_protocol_version at TLSv1.2 or above. Confirm clients use the right SSL mode before you flip require_secure_transport, because a cleartext client will stop connecting the moment it is enforced.

3. Move identity to Entra, then turn on audit

Provision a Microsoft Entra administrator on every server, and on PostgreSQL flexible servers enable Entra-only authentication so local password sign-in is disabled. Then configure pgaudit: set pgaudit.log to include role, ddl and misc, set pgaudit.log_statement and pgaudit.log_statement_once to ON, set pgaudit.log_level to log, and raise log file retention above three days so the audit trail survives long enough to be useful. Turn on connection_throttle to slow brute-force attempts.

4. Protect recovery, then ratchet it shut with Azure Policy

Enable geo-redundant backup on the servers that hold data that matters, noting it is set at creation and may require a restore to a new server to adopt, and add customer-managed keys for the regulated workloads that need them. Then assign a deny policy so no new server can be created with public network access enabled, and audit policies for SSL, Entra and private endpoints, so the safe configuration is enforced rather than merely encouraged.

# 1. Lock the wire and the network on a production PostgreSQL flexible server.
RG=db-prod-rg
SRV=ordersprod-pg

# Enforce encrypted connections (dynamic, no restart needed).
az postgres flexible-server parameter set \
  --resource-group "$RG" --server-name "$SRV" \
  --source user-override \
  --name require_secure_transport --value ON

# Pin the minimum TLS version.
az postgres flexible-server parameter set \
  --resource-group "$RG" --server-name "$SRV" \
  --source user-override \
  --name ssl_min_protocol_version --value TLSv1.2

# Disable public network access.
az postgres flexible-server update \
  --resource-group "$RG" --name "$SRV" \
  --public-access Disabled

# 2. Turn on the pgaudit trail and connection throttling.
for p in "pgaudit.log=role,ddl,misc" \
         "pgaudit.log_client=ON" \
         "pgaudit.log_level=log" \
         "pgaudit.log_statement=on" \
         "pgaudit.log_statement_once=on" \
         "connection_throttle.enable=on"; do
  az postgres flexible-server parameter set \
    --resource-group "$RG" --server-name "$SRV" \
    --source user-override \
    --name "${p%%=*}" --value "${p#*=}"
done

# 3. Provision a Microsoft Entra administrator and disable password sign-in.
az postgres flexible-server ad-admin create \
  --resource-group "$RG" --server-name "$SRV" \
  --display-name "dba-team" \
  --object-id "$(az ad group show --group dba-team --query id -o tsv)" \
  --type Group
az postgres flexible-server update \
  --resource-group "$RG" --name "$SRV" \
  --microsoft-entra-auth Enabled --password-auth Disabled

# 4. Ratchet it shut: deny any future PostgreSQL flexible server with public access.
# Look the built-in policy up by its EXACT display name, never paste a guessed GUID.
PID=$(az policy definition list \
  --query "[?displayName=='Public network access should be disabled for PostgreSQL flexible servers'].name" \
  -o tsv)
az policy assignment create \
  --name deny-public-pg-flex \
  --policy "$PID" \
  --params '{"effect":{"value":"Deny"}}' \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000"

Quick quiz

Question 1 of 5

Defender for Cloud shows database findings across public access, SSL enforcement, Microsoft Entra administrators and pgaudit on several MySQL and PostgreSQL servers. What is the most efficient way to think about them?

You can now treat Azure Database for MySQL and PostgreSQL as one capability rather than a scatter of findings: inventory what is reachable, readable, anonymous or unrecoverable too widely, close the network and the wire first, move identity to Microsoft Entra, turn on pgaudit, protect recovery with geo-redundant backup, and ratchet the estate shut with Azure Policy. Treat the Single Server findings as a planned migration to Flexible Server. The Controls this lesson covers section below links every recommendation in this group to its deep page and fix.

Back to the library

Controls this lesson covers

One capability, many Microsoft Defender for Cloud recommendations. This lesson is the shared playbook; each control below keeps its own deep page with the exact check, severity and a copy-and-paste fix.

Azure Database for PostgreSQL flexible server