Back to blog
AzureBest PracticesCloud SecurityCompute & ContainersIdentity & Access

Azure Linux VM Password Authentication Enabled: Risks and Fix

Azure Linux VMs with SSH password auth are brute-force magnets. Learn why it matters and how to enforce key-based authentication with CLI, Terraform, and policy.

TL;DR

This check flags Azure Linux VMs that accept SSH password logins instead of requiring SSH keys. Passwords are guessable and constantly brute-forced on internet-facing VMs. Disable password auth and enforce key-based access with disablePasswordAuthentication: true.

If you run Linux virtual machines in Azure, the way users authenticate over SSH is one of the highest-impact security decisions you make. The VM Linux Password Authentication Enabled check looks for VMs that still allow logging in with a username and password rather than an SSH key pair. It is an easy setting to leave on by accident, and it is one of the first things attackers probe for.

This post walks through what the check catches, why password auth is a real liability on Azure Linux VMs, and exactly how to remediate and prevent it across the CLI, the portal, and infrastructure as code.


What this check detects

The check inspects the OS profile of each Azure Linux VM and looks at the linuxConfiguration.disablePasswordAuthentication property. When that value is false (or unset, which defaults to allowing passwords if no SSH key is provided), the VM accepts password-based SSH authentication.

In practice, Lensix flags any Linux VM where the configuration resolves to:

{
  "osProfile": {
    "linuxConfiguration": {
      "disablePasswordAuthentication": false
    }
  }
}

Note: This setting controls what Azure provisions into the VM's sshd_config at deployment time. A VM created with disablePasswordAuthentication: true has PasswordAuthentication no baked in. That said, someone with root access can still re-enable it later by editing sshd_config directly, so the Azure property is the starting point, not the whole story.


Why it matters

Passwords are the weakest practical form of SSH authentication. They are short enough to be guessed, frequently reused across systems, and routinely leaked in breaches. An SSH key pair, by contrast, relies on a private key that effectively cannot be brute-forced.

The risk is not theoretical. Any VM with a public IP and port 22 open starts receiving automated login attempts within minutes of coming online. Botnets continuously sweep Azure address ranges, trying common usernames like admin, azureuser, ubuntu, and root against lists of leaked and default passwords.

A few ways this turns into an incident:

  • Credential stuffing: A password reused from another service that was breached gets tried against your VM and works.
  • Brute force: A weak or short password falls to a dictionary attack over hours or days of sustained guessing.
  • Lateral movement: One compromised VM is used as a foothold to reach internal systems that trust traffic from the same subnet.
  • Cryptomining and ransomware: Once inside, attackers commonly drop miners that spike your compute bill or encrypt data for extortion.

Warning: Even VMs without a public IP are at risk. If an attacker compromises one resource in your virtual network, password-enabled VMs become easy next hops. Internal does not mean safe.

From a compliance angle, CIS Azure Foundations Benchmark explicitly recommends SSH key authentication over passwords for Linux VMs, and many internal security baselines require it. Leaving password auth on is the kind of finding that shows up in audits and slows down approvals.


How to fix it

The right fix depends on whether you are creating a new VM or hardening an existing one.

Option 1: Disable password auth on an existing VM

For a running VM, you have two layers to address. First, update the Azure OS profile so the VM is recorded as key-only. Note that this property is immutable after creation on most VM models, so the dependable approach is to fix the running configuration directly and add a key.

Add your public key to the VM:

az vm user update \
  --resource-group my-rg \
  --name my-linux-vm \
  --username azureuser \
  --ssh-key-value "$(cat ~/.ssh/id_ed25519.pub)"

Then disable password authentication inside the VM. The cleanest way is to run a command on the host itself:

Danger: Make absolutely sure your SSH key works before disabling password auth. If your key is wrong and password login is off, you can lock yourself out of the VM entirely. Open a second SSH session with your key and confirm it works first.

az vm run-command invoke \
  --resource-group my-rg \
  --name my-linux-vm \
  --command-id RunShellScript \
  --scripts "sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && systemctl restart sshd"

On newer distributions, SSH settings may live in drop-in files under /etc/ssh/sshd_config.d/. Check there if the change does not take effect, since a file like 50-cloud-init.conf can override the main config.

Option 2: Create new VMs key-only from the start

This is the simplest path. Pass --generate-ssh-keys or your own key and never set a password:

az vm create \
  --resource-group my-rg \
  --name my-linux-vm \
  --image Ubuntu2204 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --public-ip-sku Standard

When you supply an SSH key and no password, Azure sets disablePasswordAuthentication: true automatically.

Fixing it in the Azure portal

  1. The OS authentication type cannot be switched on an existing VM through the portal, so use it for new VMs.
  2. During VM creation, under the Basics tab, set Authentication type to SSH public key.
  3. Paste an existing public key or let Azure generate a new key pair, then download the private key.
  4. For existing VMs, use the CLI approach above or redeploy from a corrected template.

Fixing it in infrastructure as code

If you provision with Terraform, set the right block on the azurerm_linux_virtual_machine resource:

resource "azurerm_linux_virtual_machine" "app" {
  name                = "my-linux-vm"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  size                = "Standard_B2s"
  admin_username      = "azureuser"

  # This is the key line: never set admin_password
  disable_password_authentication = true

  admin_ssh_key {
    username   = "azureuser"
    public_key = file("~/.ssh/id_ed25519.pub")
  }

  network_interface_ids = [azurerm_network_interface.app.id]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }
}

Tip: In Terraform, disable_password_authentication defaults to true. The usual cause of a flagged VM is someone setting admin_password and flipping it to false to allow that. Drop the password field entirely and the safe default takes over.


How to prevent it from happening again

Manual fixes do not scale, and a setting you cleaned up today gets reintroduced next sprint when someone copies an old template. Push the guardrail upstream.

Azure Policy

Azure ships a built-in policy to audit and block password-authenticated Linux VMs. Assign it at the subscription or management group level so it applies everywhere:

az policy assignment create \
  --name "deny-linux-vm-password-auth" \
  --display-name "Deny Linux VM password authentication" \
  --policy "0017a08b-37e2-4708-a0c5-49b2b2eb0c79" \
  --scope "/subscriptions/<subscription-id>"

Use the Audit effect first to see what would break, then switch to Deny once your estate is clean. Deny mode stops non-compliant VMs from being created at all.

Warning: Flip a Deny policy on without auditing first and you can block a legitimate deployment pipeline mid-release. Run in Audit for a sprint, fix the stragglers, then enforce.

CI/CD policy-as-code

Catch the misconfiguration before it ever reaches Azure by scanning IaC in your pipeline. Tools like Checkov and tfsec flag password-enabled Linux VMs out of the box. A minimal pipeline step:

checkov -d ./infra --framework terraform \
  --check CKV_AZURE_178

Fail the build on any finding so a pull request that reintroduces password auth never merges. Pair this with a Terraform plan review so reviewers see the change before approving.

Continuous monitoring with Lensix

Policies and pipeline checks cover what you provision through approved paths. Drift still happens: someone edits sshd_config by hand, or a VM gets created out of band. Lensix runs the vm_passwordauth check continuously across your subscriptions so a VM that slips through gets surfaced fast rather than discovered during an incident.


Best practices

  • Use SSH keys everywhere, no exceptions. Prefer Ed25519 keys (ssh-keygen -t ed25519) over older RSA keys.
  • Do not bake passwords into VM templates. If a template has an adminPassword parameter for Linux, that is a code smell worth removing.
  • Centralize access with Azure Bastion or AAD login. Azure AD login for Linux VMs lets you authenticate with managed identities and enforce conditional access and MFA, removing standing SSH credentials entirely.
  • Restrict port 22. Lock SSH down to known IP ranges with a network security group, or avoid exposing it at all by going through Bastion.
  • Rotate and revoke keys. When someone leaves the team, remove their public key from authorized_keys across hosts. Automate this rather than tracking it in a spreadsheet.
  • Audit drift, not just config. The Azure property and the live sshd_config can disagree. Verify the running state, not only the deployment intent.

Tip: For teams that need occasional password fallback for break-glass access, do not re-enable SSH passwords. Use Azure Bastion with just-in-time access or a separately governed emergency account instead. It keeps your everyday VMs key-only while still giving you a recovery path.

Disabling password authentication is one of the cheapest, highest-leverage hardening steps available for Linux VMs. It removes an entire class of brute-force and credential-stuffing attacks for the cost of one configuration property. Set it as the default, enforce it with policy, and verify it continuously.