Google Cloud Limit metadata server access to specific Windows users or processes

When you attach a service account to a virtual machine (VM) in Google Cloud, applications deployed on the VM can access the metadata server to request access tokens or ID Tokens for the attached service account.

By default, access to the metadata server is not limited to any specific process or user on the VM: even processes running as a low-privilege user such as nobody on Linux or LocalService on Windows have full access to the metadata server and can obtain tokens for the service account. Allowing all processes and users to request access tokens can unnecessarily increase your attack surface – particularly if the service account is highly privileged and the VM runs a large number of background processes.

Fortunately, both Linux and Windows Server let you configure user-specific firewall rules. You can use that capability to limit metadata server access to specific users while disallowing access for everybody else.

Let’s see how we can set this up on a Windows Server VM.

Default firewall rules

By default, the Windows Defender Firewall is configured to block all incoming connections while allowing all outgoing connections. We can see these defaults by opening the Windows Defender Firewall with Advanced Security MMC snap-in and opening the properties:

Windows Defender defaults

In addition to these defaults, Windows Defender Firewall comes with a number of pre-configured firewall rules that – depending on the profile – allow inbound connections for incoming requests.

Because outbound connections are allowed by default, there is typically little reason to have additional allow-rules for outbound access. To account for the possibility that a user changes the default setting however, Compute Engine creates a rule that explicitly permits outbound access to the metadata server on 169.254.169.254:

Default metadata rule

This firewall rule is not created during startup or sysprep, but during the creation of the image. It’s therefore safe to change or delete the rule – Compute Engine won’t try to restore it to its default.

Creating a custom firewall rule

Most firewall solutions use priorities to control the sequence in which firewall rules are evaluated. To grant metadata server access to selected users while disabling access for everybody else, you might be tempted to create two rules:

  1. A low-priority rules that disallows access for everybody
  2. A higher-priority rule that allows access to specific users

But that’s not how Windows Defender works: Windows Defender firewall rules do not have a priority, so we must ensure that a single rule covers all scenarios.

First, let’s create a local group for all users who should be allowed to access the metadata server:

  1. Open a PowerShell prompt.
  2. Create a new group called Compute Engine Metadata Server Users.

    $Group = New-LocalGroup `
        -Name "Compute Engine Metadata Server Users" `
        -Description "Users with access to GCE metadata server"
    
    

Now we replace the default firewall rule created by Compute Engine by a new rule that only allows access to the Compute Engine Metadata Server Users group:

  1. Disable the default firewall rule:

    Disable-NetFirewallrule -DisplayName "Allow outgoing to GCE metadata server"
    
  2. Create a firewall rule that blocks outbound connections to the metadata server:

    $Rule = New-NetFirewallRule `
        -Name BlockGceMetadataAccess `
        -DisplayName "Block outgoing to GCE metadata server" `
        -Direction Outbound `
        -RemoteAddress 169.254.169.254 `
        -Protocol TCP `
        -Action Block
    
  3. To add an exception for the Compute Engine Metadata Server Users group, you now need to set a security filter. Security filters let you specify conditions under which a firewall rule should apply.

    By using Set-NetFirewallSecurityFilter -LocalUser, you can express that a rule should apply to specific users only. You might expect that the cmdlet expects you to just list the users in question – but that’s not the case. Instead, the cmdlet lets you specify a full-blown access-control list (DACL) in the form of a SDDL string. If that ACL grants a user access, then the user is subject to the firewall rule. If the ACL denies access, then that user is exempt from the firewall rule.

    Where it gets a little complicated is that the firewall rule created in the previous step is a deny-rule. That’s necessary to override the default – but it means you have to use some double-denies:

    To allow Compute Engine Metadata Server Users to access the metadata server, you have to exempt them from the firewall rule. To exempt the group from the firewall rule, you have to deny the group access to the rule. So the ACL for the security filter needs to look like this:

    • Allow access to everyone.
    • Deny access to Compute Engine Metadata Server Users group

    In SDDL, that translates to:

    O:LSD:(D;;CC;;;<group>)(A;;CC;;;WD)
    

    There is another important detail you need to consider – for the Guest OS agent to work properly, it must be able to access the metadata server. The agent runs as SYSTEM, so it also should be denied access in the ACL.

    With SYSTEM added to the deny-list, the resulting SDDL string is:

    O:LSD:(D;;CC;;;SY)(D;;CC;;;<group>)(A;;CC;;;WD)
    

    Armed with this SDDL string, create a firewall security filter:

    $Rule |
        Get-NetFirewallSecurityFilter |
        Set-NetFirewallSecurityFilter `
            -LocalUser "O:LSD:(D;;CC;;;SY)(D;;CC;;;$($Group.SID.Value))(A;;CC;;;WD)"
    

In the MMC snap-in, the resulting firewall rule and security filter looks like this:

Local Principals

Testing access

With the firewall rule in place, we can now verify whether it works as expected.

If we log in with a user that is a member in the Compute Engine Metadata Server Users group, accessing the metadata server works fine as usual:

Invoke-RestMethod `
  -Headers @{"Metadata-Flavor" = "Google"} `
  -Uri 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'

access_token
------------
ya29.c.KpcB8QfA7_GK2xR7u6RPvmSln1SChxWiMByH7C1iBZQy...

However, running the same command as any other user now results in an error:

Invoke-RestMethod `
  -Headers @{"Metadata-Flavor" = "Google"} `
  -Uri 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'
  
Invoke-RestMethod : Unable to connect to the remote server
At line:1 char:2
+  Invoke-RestMethod -Headers @{"Metadata-Flavor" = "Google"} -Uri 'htt ...
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Of course, the approach to use firewall rules to limit access to the metadata server is only effective for non-administrative users. Any user with administrative access could simply disable or delete the firewall rule.

Thanks to Marco Ferrari for reviewing this blog post.

Any opinions expressed on this blog are Johannes' own. Refer to the respective vendor’s product documentation for authoritative information.
« Back to home