Published on 14 November 2014 by

Permissions on Windows have never been a simple thing to manage. Unix Mode does a reasonable job administering some permissions, but what most Windows admins really want is to work with the actual permissions.

We heard you loud and clear. And yes, we have a module for that. You can work directly with ACLs (Access Control Lists) and to a degree, security descriptors, through the puppetlabs-acl module. The ACL module serves to make that process easy for you while satisfying your needs for very advanced permissions.

The ACL module was released in May 2014, and works with Puppet Enterprise 3.2+ (and open source Puppet 3.4.0+). ACL became a supported module as soon as it was released. ACL adds a type and provider for Windows so you can manage those pesky permissions without a ton of hassle.

Let’s talk about a couple of real-world scenarios:

  • You need to set permissions appropriately for something like IIS/Apache.
  • You have a directory or file that you need to lock down to just admins.

These are very possible with the ACL module and just a few short lines of Puppet code. We’ll show you that in the Examples section below, but first let’s talk about ACLs and ACEs for those who want a refresher.

ACLs and ACEs, oh my!

ACLs (also called Discretionary Access Control Lists) typically contain a list of access control entries (ACEs). An ACE is a defined trustee (identity) with a set of rights, and information about how those rights are passed to (and inherited by) child objects — for example, files and folders. For each ACE, the ACL contains an allowed/denied status, as well as the ACE's propagation strategy. You cannot specify inherited ACEs in a manifest; you can only specify whether to allow upstream inheritance to flow into the managed target location (the location where you are applying the ACL).

ACL features

Here are some features of Puppet’s ACL you should know:

  • Puppet can manage the complete set of ACEs or ensure that some Puppet-specified entries are present, while leaving existing entries alone.
  • You can lock down a path to only the specified permissions.
  • Identities can be users and/or groups, domain users/groups, and/or SIDs.
  • You can point multiple ACL resources at the same target path. Let me say that again in case there was confusion: You can point multiple ACL resources at the same path.
  • Propagation and inheritance of ACEs is set to Windows defaults, but can be specified per ACE.

Here’s what sets Puppet’s ACL module apart from other configuration management tools:

  • ACE order. The order of your ACEs matters. If they are incorrectly ordered, it can cause issues. So we apply ACEs in the order you’ve specified in the manifest. And we insert them in the correct location when merging with unmanaged ACEs.
  • SID (Security ID) support. We support specifying identities as SIDs.
  • Very, very granular permissions. Do you need to apply read attributes (RA)? Yes, we can do that.

Getting Started

Let’s take a look at what a typical ACL resource looks like:

acl { 'c:/temp':
  permissions => [
   { identity => 'Administrator', rights => ['full'] },
   { identity => 'Users', rights => ['read','execute'] }
 ],
}

If you were to run the above on a system that had the module installed, you would be giving the Administrator account full access to the temp folder, and giving the Users group access to read and execute (and list for folders). All the other options are set to Windows defaults, but if we need to get to them, we can. Let’s look at that same ACL resource with all the options specified:

acl { 'c:/temp':
  target      => 'c:/temp',
  target_type => 'file',
  purge       => 'false',
  permissions => [
   { identity => 'Administrator', rights => ['full'], type=> 'allow', child_types => 'all', affects => 'all' },
   { identity => 'Users', rights => ['read','execute'], type=> 'allow', child_types => 'all', affects => 'all' }
  ],
  owner       => 'Administrators',
  group       => 'Users',
  inherit_parent_permissions => 'true',
}

We have just specified the resource with all parameters and properties specified; what you are seeing is how the defaults line up. The only exception here are owner and group, which by default are not managed unless specified. The defaults for these depend on the user that created the target (the folder) and could be different based on who the user has as their default group and owner.

With both of the above examples we have done the following:

  • We’ve given Administrator full access to c:\temp.
  • We’ve given Users read/execute access to c:\temp.
  • We are by default not removing explicit unspecified permissions (purge => ‘false’).
  • We are by default inheriting the permissions from the parent folder (inherit_parent_permissions => ‘true’).
  • For each ACE, we are allowing the permission by default (type => ‘allow’).
  • For each ACE, we apply the permission to all child types by default, both folders and files (child_types => ‘all’).
  • For each ACE, we propagate the permission to self, direct children and all those further down by default (affects => ‘all’). Note that all types below child types are called grandchildren, no matter what the level of depth.

By default, if a user is not granted access through an ACE (whether individually or as a member of a group), then Windows will deny access. So if a user is not the Administrator account and not a member of the Users group, they will not have access to c:\temp. With the ACL module, this also means that access could be granted outside of Puppet (when purge => ‘false’) and/or it is an inherited ACE (when inherit_parent_permissions => ‘true’).

ACL type structure

Now let’s take a look at the ACL type and all it has to offer.

Parameters

The parameters are (bold means the parameter is required):

  • name - The name of the ACL resource, used as target if target is not set explicitly (see the target parameter).
  • purge - Determines whether unmanaged explicit access control entries should be removed. Combine purge => ‘true’, inherit_parent_permissions => ‘false’ to really lock down a folder. Supports true, false and listed_permissions. Defaults to false.
  • target - The location; defaults to the same value as name.
  • target_type - Currently supports only file; defaults to file.

Properties

The properties are (bold means the property is required):

  • owner - User/Group/SID that owns the ACL. If not specified, the provider will not manage this value.
  • group - User/Group/SID that has some level of access. If not specified, the provider will not manage this value. Group is not commonly used on Windows.
  • inherit_parent_permissions - Whether we inherit permissions from parent ACLs or not. Default is true.
  • permissions - The list of ACEs as an array. This should be in the order you want them applied. We cover permissions in more detail below.

Permissions property

The permissions property could be considered the most important part of the ACL resource, because it contains each ACE in the order specified for the ACL. The available elements for each ACE hash are identity, rights, type, child_types, affects, and mask.

The elements are (bold means the key is required):

  • identity - This is the user/group/SID.
  • rights - An array with the following values: full, modify, mask_specific, write, read, and execute. The full, modify, and mask_specific values are mutually exclusive, and when any of these values are used, they must be the only value specified in rights. The full value indicates all rights. The modify value is cumulative, implying write, read, execute and DELETE all in one. If you specify mask_specific, you must also specify the mask element in the permissions hash. The write, read, and execute values can be combined however you want.
  • type - Whether to allow or deny the access. Defaults to allow.
  • child_types - Which types of children are allowed to inherit this permission, whether objects, containers, all or none. Defaults to all.
  • affects - How inheritance is propagated. Valid values are all, self_only, children_only, self_and_direct_children_only, or direct_children_only. Defaults to all.
  • mask - An integer representing access mask, passed as a string. Mask should be used only when paired with rights => [‘mask_specific’]. For more information on mask, see the granular permissions example below.

For more specific details and up-to-date information on the ACL type, see the usage documentation.

Examples

In the following examples we are going to show you how to lock down a folder, set appropriate permissions for a website, and set very granular permissions.

Locking down a folder for sensitive data

Here’s what you need to do when you keep sensitive data in a specific folder, and need to limit access to administrators only.

acl { 'c:/sensitive_data':
  purge                       => true,
  inherit_parent_permissions  => false,
  permissions => [
   { identity => 'Administrators', rights => ['full'] }
 ],
}

We’ve done the following:

  • Since permissions are inherited from parent ACLs by default, we set inherit_parent_permissions => false so no permissions are inherited from the parent ACL.
  • ACLs will also allow unmanaged ACEs to coexist with Puppet-managed permissions, so we need to specify purge => true to ensure that all permissions other than those we have specified are removed.
  • We’ve given the Administrators group full permission to the directory.
  • If an ACE has not granted permission for a user, Windows will by default deny access. So in this case, only users that are part of the Administrators group will be able to access this folder.

That was pretty simple! And very self-documenting as well. It’s worth mentioning that one goal of Puppet is for anyone to be able to read and understand the intent of the code, and the ACL module does not disappoint. Now let’s try something a little more involved.

Website setup with ACLs

Let’s take a look at setting up an IIS site and locking down permissions.

$website_location = 'C:\sites\thestuff'
$website_name = 'the.stuff'
$website_port = '80'

# add windows features
windowsfeature { 'Web-WebServer':
  installmanagementtools => true,
} ->
windowsfeature { 'Web-Asp-Net45':
} ->

# remove default web site
iis::manage_site { 'Default Web Site':
  ensure        => absent,
  site_path     => 'any',
  app_pool      => 'DefaultAppPool',
} ->

# application in iis
iis::manage_app_pool { "${website_name}":
  enable_32_bit           => true,
  managed_runtime_version => 'v4.0',
} ->
iis::manage_site { "${website_name}":
  site_path     => $website_location,
  port          => "${website_port}",
  ip_address    => '*',
  app_pool      => "${website_name}",
} ->

# lock down web directory
acl { "${website_location}":
  purge                       => true,
  inherit_parent_permissions  => false,
  permissions => [
   { identity => 'Administrators', rights => ['full'] },
   { identity => 'IIS_IUSRS', rights => ['read'] },
   { identity => 'IUSR', rights => ['read'] },
   { identity => "IIS APPPOOL\\${website_name}", rights => ['read'] }
 ],
} ->
acl { "${website_location}/App_Data":
  permissions => [
   { identity => "IIS APPPOOL\\${website_name}", rights => ['modify'] },
   { identity => 'IIS_IUSRS', rights => ['modify'] }
 ],
}

The script above does the following:

  1. Ensures IIS is installed and ASP.NET is set up.
  2. Ensures the default web site is removed.
  3. Ensures our site is set up correctly.
  4. Uses ACL to ensure IIS users have read access.
  5. Uses ACL to ensure the processes that run the website can modify the App_Data directory.

All of that, done in just a few lines of code! You can add more for getting the files there in the first place, using Puppet, but I have left that as an exercise for the reader :)

Granular permissions

In our last example, let’s get into very granular permissions.

acl { 'c:/granular_permissions':
  permissions => [
   { identity => 'Administrators', rights => ['full'] },
   { identity => 'Bob', rights => ['mask_specific'], mask => '1507839' } # Modify + WRITE_DAC + FILE_DELETE_CHILD
 ],
}

We’ve done the following:

So you see 1507839 and it looks like a magic number. How does one arrive at this number? I wrote a blog post on this once that shows you how to add up the numbers. Now we have a much simpler way to add those permissions: We have created and made available a worksheet to add up the ACL rights mask! This will allow you to add up the rights and will give you a heads up if you should use named rights from the module (like read or execute or a combination of read, execute).

Tips for using ACLs

Here are some tips to consider when using ACL:

  • If you are specifying an ACL, please ensure you don’t set mode on file resources.
  • Don’t use fully qualified names except when using domain accounts. When identities (users) are local-machine specific, don’t fully qualify the name (e.g., Machine-Name\Administrator), as that doesn’t translate well to multiple machines with different names.
  • Avoid SIDs unless you need them. When ACLs attempt to set dependencies on user resources, the ACL module will go with a fully qualified user name, which you won’t usually want to put into manifests (e.g., MACHINE-NAME\Administrators), unless you are specifying domain accounts.
  • Don’t use Windows 8.3 short file names. I think 1980 just called.
  • Unicode identities don’t work yet. This is due to a bug in Puppet, and will be fixed in future versions of Puppet.
  • ACEs are checked for uniqueness based on identity, type, child_types, and affects. Don’t differ based on rights alone. It will confuse the provider.

ACL module: We think it’s awesome, and we think you will, too!

ACLs on Windows give you a lot of control over permissions. The ACL module harnesses that ability and makes it easy — even fun — to control permissions on Windows. While the ACL module makes it easy to get started, it allows advanced users to manage permissions in granular detail. And did we mention it’s a supported module? That means you get the same support for this module that you get with your Puppet Enterprise license. Give the ACL module a look today — if you manage permissions on Windows, we think you are going to love it!

Rob Reynolds is a software engineer at Puppet Labs.

Learn more

Share via:
Posted in:
Tagged:

In your example for application in iis, the website_name should be app_pool_name, right?

# application in iis
iis::manage_app_pool { "${website_name}":

Also, how do we add an identity to app_pool?

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.