Share via


What is SSH Posture Control for Windows?

SSH Posture Control enables you to audit and configure SSH Server security posture on Windows Server 2025. SSH Posture Control integrates seamlessly with Azure Governance services (Policy, Machine Configuration) so you can:

  • Ensure compliance with sshd standards in your industry or organization
  • Reduce attack surface of sshd-based remote management
  • Ensure consistent sshd setup across your fleet for security and productivity

Screenshot showing list of compliant SSH checks

To help you demonstrate compliance to auditors (and to help you take action where needed) each compliance check includes evidence via the Reasons field indicating how compliance or non-compliance was determined.

You can customize the sshd parameters (port number, allowed groups, etc.) or use the policy default values.

Documentation for getting started

Selecting audit-only vs. audit-and-configure behavior

When assigning an SSH Posture Control policy, you can choose audit-only (aka "Audit") behavior or audit-and-configure (aka "Configure") behavior.

Policy definition Azure Policy effect Notes on what to expect
Audit-only behavior **Audit** SSH Posture Control on Windows machines auditIfNotExists The policy includes more restrictive settings compared to many popular system images. For example, denial of root ssh access. Accordingly, expect to see Not-compliant states reported.
Audit-and-configure behavior **Configure** SSH Posture Control on Windows machines deployIfNotExists As above, you can expect to see Not-compliant states reported initially. Subsequently, the machines will be reconfigured to match the policy, resulting in eventual Compliant states.

For existing machines, admins typically start with audit-only behavior to determine existing state and to discover dependencies such as accounts allowed for systems automation. After comparing the existing fleet against SSH Posture Control defaults, you would decide which SSH Posture Control parameters to customize. After this analysis and planning, you would then transition to audit-and-configure behavior (with safe deployment practices such as rings).

For greenfield scenarios or disposable test machines, you might choose to skip that analysis and move directly to audit-and-configure behavior, starting fresh with strong SSH Posture Control defaults.

Caution

Before configuring any machines, take great care to validate your new configuration. You could accidentally lose access to your machines.

Examples of accidentally locking yourself out include:

  • The net authorization settings applied (combination of allowUsers,denyGroups, etc.) do not allow the logins you need
  • The port you configure for sshd is blocked by other controls in your environment (Host firewall rules, network firewall rules, etc.)
    • To avoid overstepping enterprise team boundaries, SSH Posture Control only configured sshd. It does not currently attempt to modify on-machine firewall rules, etc. to accommodate the configured sshd port. If you would like to discuss these scenarios with us, please contact us (see Additional resources below).

SSH Posture Control scope: rules, defaults, and customization

The following table lists the items that can be audited or configured with SSH Posture Control. Each of these is known as a rule.

Most rules can be given custom values, via policy assignment parameters to audit against or configure and audit. For example, if the standard in your organization is to use port 1111 (rather than 22) for sshd, you would set the corresponding parameter in the policy assignment. These parameters have identifiers which are included in the table below. Typically, the short parameter name is used programmatically (for example with az policy assignment create --params ...), while the longer parameter display name is used in Azure portal workflows.

When customizing values, take care to provide values which are compatible with sshd. For example, allowGroups takes a space delimited list of group name patterns. For reference, see the sshd_config man page. The sshd_config reference is also useful for understanding other sshd behaviors such as how allow and deny lists intersect.

Note

To preserve table layout, some cell values have been moved to footnotes below the table.

Rule name Default value Parameter name Parameter display name
Ensure that the allowed groups for SSH are configured <footnote 1> AllowGroups Allowed groups
Ensure that the allowed users for SSH access are configured "" AllowUsers Allowed users
Ensure that the authentication method for SSH is configured any AuthenticationMethods Authentication methods
Ensure that the denied groups for SSH are configured "" DenyGroups Denied groups
Ensure that the denied users for SSH are configured "" DenyUsers Denied users
Ensure that the facility code used when logging messages for SSH is configured LOCAL0 SyslogFacility System logging facility
Ensure that the SSH MaxAuthTries is configured 6 MaxAuthTries Maximum authentication attempts
Ensure that the SSH HostKey is configured <footnote 2> HostKey Host key
Ensure that the authorized key file for SSH is configured <footnote 3> AuthorizedKeysFile Authorized key file
Ensure that GSSApiAuthentication for SSH is configured false GSSAPIAuthentication GSSAPI authentication
Ensure that the SSH warning banner is configured <footnote 4> Banner Banner
Ensure that appropriate ciphers are used for SSH aes128-ctr,aes192-ctr,aes256-ctr Ciphers Allowed ciphers
Ensure that only approved MAC algorithms are used hmac-sha2-256 MACs MAC algorithms
Ensure that the SSH ClientAliveCountMax is configured 0 ClientAliveCountMax Number of client alive messages
Ensure that the SSH port is configured 22 Port Port
Ensure that the SSH LoginGraceTime is configured 60 LoginGraceTime Login grace time
Ensure that the SSH ClientAliveInterval is configured 3600 ClientAliveInterval Client alive interval
Ensure that the SSH PermitEmptyPasswords is configured false PermitEmptyPasswords Empty password permission

Table footnotes:

  1. administrators "openssh users"

  2. __PROGRAMDATA__/ssh/ssh_host_ecdsa_key

  3. %programdata%/ssh/administrators_authorized_keys

  4. #######################################################################/r/n/r/nAuthorized access only!/r/n/r/nIf you are not authorized to access or use this system, disconnect now!/r/n/r/n#######################################################################/r/n

    1. Note: this displays to end users as:
    #######################################################################
    
    Authorized access only!
    
    If you are not authorized to access or use this system, disconnect now!
    
    #######################################################################
    

Additional (non-sshd) policy parameters

These additional policy parameters are available during policy assignment. These influence Azure Policy assignment behavior, as opposed to sshd settings on machines.

Name Description Default
Include Arc connected servers By selecting this option, you agree to be charged monthly per Arc connected machine. FALSE
Effect Enable or disable the execution of this policy <Depends on Selecting audit-only vs. audit-and-configure behavior>

Policy definitions? Policy assignments? Guest assignments? Machine Configuration? How does this all fit together?

To get started with SSH Posture Control, your core action is to create a policy assignment. Your policy assignment links a policy definition (e.g., "Audit SSH Posture Control for Windows machines") to a scope (e.g., "my_factory_3_resource_group").

As you use the system, you will encounter additional resource types and terminology, as summarized in the following.

diagram showing how a policy assignment links machines to the Machine Configuration service via guest assignments

Description
Policy definition Within the Policy service, the abstract data which describes a cluster of available audit and/or configuration behaviors. For example, "Audit SSH Posture Control on Windows machines".
Policy assignment Links an abstract policy definition to a concrete scope, such as a resource group. The policy assignment can include Parameters and other properties which are specific to that assignment.
Machine Configuration The Azure service and agentry which handle auditing and setting configuration at the OS level.
Guest assignment Resource that acts as a three-way link between the policy assignment, the machine, and the Machine Configuration service. Policy creates and monitors guest assignment resources as needed.
For more information on "guest" vs. "machine" terminology, see Why do I see the terms "Guest Configuration" and "Automanage" in places?
Machine An Arc-enabled machine or an Azure VM.

About compatibility (SSH Server implementations, etc.)

SSH Posture Control is designed for the mainstream general-purpose Windows Server scenario of a single long-running SSH Server instance:

  • whose lifecycle is managed by the init system such as Service Control Manager
  • whose behavior is governed by sshd_config file, consistent with OpenSSH sshd behavior
  • whose effective configuration/state is revealed by sshd -T output, consistent with OpenSSH sshd behavior

For all supported OSes (see below), this is the default SSH Server use case.

In principle, a machine could have any number of SSH server instances running with varying lifetimes, based on any number of codebases, and taking their configuration from any number of places (config files, command line arguments, compile time parameters, etc.). Such cases are out of scope for SSH Posture Control at this time. If you are interested in such cases for the future, please contact us to discuss.

SSH Posture Control is intended for use on Windows Server 2025 machines supported by Azure Policy and Machine Configuration -

Compatibility with any specific machine at run time cannot be guaranteed as sysadmins and image builders are free to remove components from the OS, make filesystems read-only, etc.

Compatibility with sshd_config Include directives

SSH Posture Control attempts to accommodate and make use of Include directives in sshd_config, as follows:

  • For audit/read actions: Rely on sshd -T to reflect the net configuration from sshd's perspective-- taking into account any Includes.
  • For configure/write actions:
    • Link a new SSH Posture Control specific file to sshd_config (as an Include). Subsequently, place all writes into the linked SSH Posture Control file. This enhances system hygiene and traceability of system changes.

Compatibility with sshd_config Match directives

SSH Posture Control is designed to audit and configure core sshd behavior. It does not attempt to interact with conditional Match blocks (if any) which can apply different sshd configurations to specific populations.

Compatibility with multiple sshd_config values

SSH Posture Control does not support the use of multiple values for the rule 'port' i.e, setting the rule 'port' to 22 and 33. This rule should be configured with a single value to ensure proper functionality and compliance for auditing and configuring scenarios. Other rules such as allow/deny users and ciphers can have multiple values, as long as they are added on a single line.

Example:

  • An existing sshd_config file includes a line "port:22" and another line "port:33".
  • The "Audit SSH" Policy is used to audit for an expected port value of 33.
  • Outcome: The audit may pass or fail unpredictably.
  • Recommendation: Do not use this feature with scenarios such as these.

How can I query the results programmatically?

Using Azure Resource Graph (ARG) queries you can integrate assignment and status data into your own workflows. These examples use Search-AzGraph in PowerShell to execute the ARG query, but PowerShell is not required. You can use ARG from many entry points including the Azure Portal, Azure CLI, REST calls, etc.

  1. At the highest altitude of summarization, you can get machine counts per compliance status bucket. For example:

    $machineCountsQuery = @'
    // SSH machine counts by compliance status
    guestconfigurationresources
    | where name contains "SecureShell"
    | extend complianceStatus = tostring(properties.complianceStatus)
    | summarize machineCount = count() by complianceStatus
    '@
    
    Search-AzGraph -Query $machineCountsQuery
    
    <#
    Sample output from an environment with two machines:
    
    complianceStatus machineCount
    ---------------- ------------
    Pending                     1
    Compliant                   1
    #>
    
  2. To drill in such that you see overall compliance status by machine, you can use the following:

    $machinePerRowQuery = @'
    // SSH machine level compliance
    guestconfigurationresources
    | where name contains "SecureShell"
    | project 
     machine = split(properties.targetResourceId,'/')[-1],
     complianceStatus = properties.complianceStatus,
     lastComplianceStatusChecked = properties.lastComplianceStatusChecked
    '@
    
    Search-AzGraph -Query $machinePerRowQuery
    
    <#
    Sample output:
    
    machine     complianceStatus lastComplianceStatusChecked
    -------     ---------------- ---------------------------
    sshdemovm01 Compliant        2/15/2024 11:07:21 PM
    sshdemovm02 Pending          1/1/0001 12:00:00 AM
    #>
    
  3. To drill down to setting-by-setting details, you can use the following:

    $settingPerRowQuery = @'
    // SSH rule level detail
    GuestConfigurationResources
    | where name contains "SecureShell"
    | project report = properties.latestAssignmentReport,
     machine = split(properties.targetResourceId,'/')[-1],
     lastComplianceStatusChecked=properties.lastComplianceStatusChecked
    | mv-expand report.resources
    | project machine,
     rule = report_resources.resourceId,
     ruleComplianceStatus = report_resources.complianceStatus,
     ruleComplianceReason = report_resources.reasons[0].phrase,
     lastComplianceStatusChecked
    '@
    
    Search-AzGraph $settingPerRowQuery
    
    <#
    Sample output:
    
    machine     rule                                                  ruleComplianceStatus     ruleComplianceReason
    -------     ---------------                                                  ------               ------
    sshdemovm01 Ensure that the allowed groups for SSH are configured            true            ["administrators","openssh users"] contains the expected values: ["administrators","openssh users"]
    sshdemovm01 Ensure that appropriate ciphers are used for SSH                 true            ["aes128-ctr","aes192-ctr","aes256-ctr"] contains the expected values: ["aes128-ctr","aes192-ctr","aes256-ctr"]
    sshdemovm01 Ensure that the authorized key file for SSH is configured        true            "%programdata%/ssh/administrators_authorized_keys" is equal to "%programdata%/ssh/administrators_authorized_keys"
    sshdemovm01 Ensure that the SSH ClientAliveInterval is configured            true            3600 is equal to 3600
    sshdemovm01 Ensure that the SSH PermitEmptyPasswords is configured           true            false is equal to false
    sshdemovm01 Ensure that the SSH port is configured                           true            22 is equal to 22
    sshdemovm01 Ensure that the SSH MaxAuthTries is configured                   true            6 is equal to 6
    sshdemovm01 Ensure that only approved MAC algorithms are used                true            ["hmac-sha2-256"] contains the expected values: ["hmac-sha2-256"]
    sshdemovm01 Ensure that the SSH HostKey is configured                        true            "__PROGRAMDATA__/ssh/ssh_host_ecdsa_key" is equal to "__PROGRAMDATA__/ssh/ssh_host_ecdsa_key"
    sshdemovm01 Ensure that the SSH LoginGraceTime is configured                 true            60 is equal to 60
    #>
    

Why do I see the terms "Guest Configuration" and "Automanage" in places?

The Machine Configuration service has also been known as Guest Configuration and as Automanage Machine Configuration. You may encounter these names as you interact with services and documentation. For example:

  • In the Azure Resource Graph query examples in this article, the data table is called guestconfigurationresources.
  • In the Azure portal, a useful view for observing results is called "Guest Assignments".
  • In the Azure portal, when applying the relevant VM extension to enable Machine Configuration, the extension title is "Automanage Machine Configuration".

For the purposes of SSH Posture Control, there is no meaningful distinction between "guest" and "machine". Arc-enabled machines and Azure VMs are eligible.

What are the identifiers for the built-in policy definitions?

In some cases, such as creating policy assignments with Azure CLI, it may be useful or necessary to refer to a policy definition by id rather than display name.

displayName id

| Audit SSH Posture Control on Windows machines | ... | | Configure SSH Posture Control on Windows machines | ... |