Best approach to creating wrapper classes

154 views
Skip to first unread message

Scott Jaffa

unread,
Apr 2, 2015, 5:02:30 PM4/2/15
to puppet...@googlegroups.com
Hi,

I'm working in an environment where certain parameters need to be enforced per security requirements..  

The ways we've identified to do this are:

1)  Put the specific settings in the profile:
Advantages:  Utilize stock roles and profiles pattern, plenty of documentation and guides online.
Disadvantage:  The settings are part of the profile and thus two groups need to share ownership of the same module.  Reduces flexibility or speed due to additional enforcement needed by shared ownership.

2)  Modify the modules themselves.
Advantages:  Configuration is part of the module.
Disadvantages:  We are now maintaining all custom modules.  

3)  Extend roles and profiles to add an additional layer between existing profiles and the modules.
The workflow would be:
Role (business layer) > Profile (technology layer) > Security (security layer) > Module.  
Advantages:  Engineering configuration and security configuration are seperated, with security configuration enforced.
Disadvantages:  Need a way to present most options up to the profiles layer for parameterization, while enforcing a few options.


We'd prefer to go with option 3.  Does this make sense?

If so, some tips on how to go about this would be appreciated.  Does it make sense for the security module to inherit the base module in this case?  It would look something like this (but actually work :) )
class sec_profile::ssh inherits ::ssh {  
$server_options = { 'Protocol' => '2', 'Ciphers' => 'aes128-ctr,aes192-ctr,aes256-ctr', 'PermitRootLogin' => 'no', 'ClientAliveInterval' => '900', 'PermitEmptyPasswords' => 'no', 'PasswordAuthentication' => 'no', 'Port' => [22], } }

If not, can you suggest a good approach to present the base module options to the profile?  We'd like to to allow parameterization / hiera lookups at the profile layer, preferrably without having to reimplement each option in the security layer.

Thanks!

Scott

Christopher Wood

unread,
Apr 2, 2015, 7:37:31 PM4/2/15
to puppet...@googlegroups.com
You might be interested in this thread:

https://groups.google.com/forum/#!topic/puppet-users/nmVQQA6G-f8
> --
> You received this message because you are subscribed to the Google Groups
> "Puppet Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [1]puppet-users...@googlegroups.com.
> To view this discussion on the web visit
> [2]https://groups.google.com/d/msgid/puppet-users/a0e99a07-261c-4327-8d0e-a8379f3f23e9%40googlegroups.com.
> For more options, visit [3]https://groups.google.com/d/optout.
>
> References
>
> Visible links
> 1. mailto:puppet-users...@googlegroups.com
> 2. https://groups.google.com/d/msgid/puppet-users/a0e99a07-261c-4327-8d0e-a8379f3f23e9%40googlegroups.com?utm_medium=email&utm_source=footer
> 3. https://groups.google.com/d/optout

jcbollinger

unread,
Apr 3, 2015, 9:15:00 AM4/3/15
to puppet...@googlegroups.com


On Thursday, April 2, 2015 at 4:02:30 PM UTC-5, Scott Jaffa wrote:
Hi,

I'm working in an environment where certain parameters need to be enforced per security requirements..  

The ways we've identified to do this are:

1)  Put the specific settings in the profile:
Advantages:  Utilize stock roles and profiles pattern, plenty of documentation and guides online.
Disadvantage:  The settings are part of the profile and thus two groups need to share ownership of the same module.  Reduces flexibility or speed due to additional enforcement needed by shared ownership.

2)  Modify the modules themselves.
Advantages:  Configuration is part of the module.
Disadvantages:  We are now maintaining all custom modules.  

3)  Extend roles and profiles to add an additional layer between existing profiles and the modules.
The workflow would be:
Role (business layer) > Profile (technology layer) > Security (security layer) > Module.  
Advantages:  Engineering configuration and security configuration are seperated, with security configuration enforced.
Disadvantages:  Need a way to present most options up to the profiles layer for parameterization, while enforcing a few options.


We'd prefer to go with option 3.  Does this make sense?


I'm having trouble understanding how you propose to factor out security considerations from the technology to which they apply.  Is this just about ownership of data, or do there need to be bona fide security-specific resources?  If the former, then what do you need that you cannot achieve via a security-specific level in your Hiera hierarchy?  If the latter, then how would making the security classes responsible for declaring component-level classes (per option 3) achieve the separation of concerns you claim as an advantage?

 

If so, some tips on how to go about this would be appreciated.  Does it make sense for the security module to inherit the base module in this case?  It would look something like this (but actually work :) )
class sec_profile::ssh inherits ::ssh {  
$server_options = { 'Protocol' => '2', 'Ciphers' => 'aes128-ctr,aes192-ctr,aes256-ctr', 'PermitRootLogin' => 'no', 'ClientAliveInterval' => '900', 'PermitEmptyPasswords' => 'no', 'PasswordAuthentication' => 'no', 'Port' => [22], } }



If you are contemplating class inheritance for the purpose of greater freedom in applying resource property overrides, then maybe they would be useful to you.  If you have an idea that they would do anything else for you, then put it out of your mind -- class inheritance doesn't work that way (whatever way that happens to be).  Note, however, that often you can perform resource overrides without class inheritance, that often it is better to modify the external data from which modules draw property values than to override property values after the fact, and that class inheritance creates a very tight coupling that is probably better avoided if it crosses module boundaries.

 
If not, can you suggest a good approach to present the base module options to the profile?  We'd like to to allow parameterization / hiera lookups at the profile layer, preferrably without having to reimplement each option in the security layer.



It would help if you presented a representative example of what you're trying to configure, and explained the challenge you face with respect to that.  What you've presented so far is too abstract for me to offer any specific advice.


John

Scott Jaffa

unread,
Apr 7, 2015, 4:30:30 PM4/7/15
to puppet...@googlegroups.com


On Thursday, April 2, 2015 at 7:37:31 PM UTC-4, Christopher Wood wrote:
You might be interested in this thread: 

https://groups.google.com/forum/#!topic/puppet-users/nmVQQA6G-f8 

 
Thanks!
 

On Friday, April 3, 2015 at 9:15:00 AM UTC-4, jcbollinger wrote:


On Thursday, April 2, 2015 at 4:02:30 PM UTC-5, Scott Jaffa wrote:
Hi,

I'm working in an environment where certain parameters need to be enforced per security requirements..  

The ways we've identified to do this are:

1)  Put the specific settings in the profile:
Advantages:  Utilize stock roles and profiles pattern, plenty of documentation and guides online.
Disadvantage:  The settings are part of the profile and thus two groups need to share ownership of the same module.  Reduces flexibility or speed due to additional enforcement needed by shared ownership.

2)  Modify the modules themselves.
Advantages:  Configuration is part of the module.
Disadvantages:  We are now maintaining all custom modules.  

3)  Extend roles and profiles to add an additional layer between existing profiles and the modules.
The workflow would be:
Role (business layer) > Profile (technology layer) > Security (security layer) > Module.  
Advantages:  Engineering configuration and security configuration are seperated, with security configuration enforced.
Disadvantages:  Need a way to present most options up to the profiles layer for parameterization, while enforcing a few options.


We'd prefer to go with option 3.  Does this make sense?


I'm having trouble understanding how you propose to factor out security considerations from the technology to which they apply.  Is this just about ownership of data, or do there need to be bona fide security-specific resources?  If the former, then what do you need that you cannot achieve via a security-specific level in your Hiera hierarchy?  If the latter, then how would making the security classes responsible for declaring component-level classes (per option 3) achieve the separation of concerns you claim as an advantage?

 

If so, some tips on how to go about this would be appreciated.  Does it make sense for the security module to inherit the base module in this case?  It would look something like this (but actually work :) )
class sec_profile::ssh inherits ::ssh {  
$server_options = { 'Protocol' => '2', 'Ciphers' => 'aes128-ctr,aes192-ctr,aes256-ctr', 'PermitRootLogin' => 'no', 'ClientAliveInterval' => '900', 'PermitEmptyPasswords' => 'no', 'PasswordAuthentication' => 'no', 'Port' => [22], } }



If you are contemplating class inheritance for the purpose of greater freedom in applying resource property overrides, then maybe they would be useful to you.  If you have an idea that they would do anything else for you, then put it out of your mind -- class inheritance doesn't work that way (whatever way that happens to be).  Note, however, that often you can perform resource overrides without class inheritance, that often it is better to modify the external data from which modules draw property values than to override property values after the fact, and that class inheritance creates a very tight coupling that is probably better avoided if it crosses module boundaries.

Yes, the goal is strictly to provide flexibility in parameters.  I think this is a case where inheritance can make sense, but, particularly as an end goal is the public release of these modules, I'd like to make sure they are designed correctly, or at least today's definition of correctly.
 
If not, can you suggest a good approach to present the base module options to the profile?  We'd like to to allow parameterization / hiera lookups at the profile layer, preferrably without having to reimplement each option in the security layer.



It would help if you presented a representative example of what you're trying to configure, and explained the challenge you face with respect to that.  What you've presented so far is too abstract for me to offer any specific advice.


John

Certainly!

The goal here is to build security hardening into the Puppet configuration stack while still allowing flexibility for environment configuration, as, for example, it is reasonable to turn off one or more hardening settings.  Ideally, any module released would allow one to select their hardening standard, whether CIS, STIG, or other.

Conceptually this would extend the roles and profiles pattern.  In particular, profiles exist to define technology stacks.  This likely will result in multiple profiles calling the same module.   The idea is to inject another layer above the modules, which have a 1:1 correlation with the modules.  This wrapper module would provide an expose the specific configuration options required for security hardening, while allowing the calling profile to pass through environment parameters, as is done today.
  
To continue with the SSH example (pardon the mix of pseudocode and bad puppet code, my puppet code is very rusty):

Today, the profile declares SSH as a needed class:
class profile::base {

class { '::ssh': }  
}
This uses the ssh class (saz-ssh in my instance) with default options. However, the security configuration requires some options be set.

What we'd do is call:
class { 'sec_profile::ssh':
security_hardening => 'true' (or via hiera lookup)
This class would, via inheritance or another method, expose all of the existing parameters of the saz-ssh module which we downloaded from the forge. However, within this class, it would specifically set the needed security options. Logic would be added to identify if security hardening is enabled, and if so, set the appropriate parameters as required. This would not modify any of the inherited module logic. As above, sec_profile would then inherit saz-ssh, adding the needed parameters, but allow a profile to set any other parameters. In this way, the only code which needs writing is the specific security options, and the rest is exposed as is. This would obviously link the wrapper module to a specific base module, but that's ok:
class { 'sec_profile::ssh':
security_hardening => 'true',
non_security_parameter => 'value'
...
The obvious question is: why not put the security options into the profiles themselves and be done with it? 1) As mentioned above, we could have multiple profiles calling the same module, requiring maintainance of settings in multiple places.
2) Engineering and security would have to share the profile classes, which breaks some of the role separation and makes auditing (slightly) more complicated.
3) The goal is to release the security modules publicly, and wrapping them into the site specific profiles makes that impossible.

Regards,

Scott

jcbollinger

unread,
Apr 8, 2015, 10:10:24 AM4/8/15
to puppet...@googlegroups.com


This is where your separation of concerns falls down.  Any change to the underlying profile requires at least a review of the security-layer wrapper, and possibly a change there, too.

 
 This wrapper module would provide an expose the specific configuration options required for security hardening, while allowing the calling profile to pass through environment parameters, as is done today.


Huh?  What are "environment parameters"?

 
  
To continue with the SSH example (pardon the mix of pseudocode and bad puppet code, my puppet code is very rusty):

Today, the profile declares SSH as a needed class:
class profile::base {

class { '::ssh': }  
}
This uses the ssh class (saz-ssh in my instance) with default options. However, the security configuration requires some options be set.

What we'd do is call:
class { 'sec_profile::ssh':
security_hardening => 'true' (or via hiera lookup) }
This class would, via inheritance or another method, expose all of the existing parameters of the saz-ssh module which we downloaded from the forge.


Not via class inheritance, that's for sure.  Puppet does not support inheriting from parameterized classes.  If you try to do it anyway then you will find that the parent class gets instantiated with all default parameters.  And no, what you're describing is not "resource property overrides" (the only reason ever to consider class inheritance in Puppet).  You have fallen into the classic trap of thinking that Puppet "classes" are similar to Java or C++ "classes".  They are not.  At best, they are distant cousins.  In fairness, though, Puppet terminology borrows a lot from the OO world, so that's an easy mistake to make,

 
However, within this class, it would specifically set the needed security options. Logic would be added to identify if security hardening is enabled, and if so, set the appropriate parameters as required. This would not modify any of the inherited module logic. As above, sec_profile would then inherit saz-ssh, adding the needed parameters, but allow a profile to set any other parameters. In this way, the only code which needs writing is the specific security options, and the rest is exposed as is. This would obviously link the wrapper module to a specific base module, but that's ok:
class { 'sec_profile::ssh':
security_hardening => 'true',
non_security_parameter => 'value'


Coupling the wrapper classes to the underlying profiles is poor form.  Such tight coupling should generally occur only among classes of the same module, not across module boundaries.  It is doable, but it is certainly not "ok" if separation of concerns is a genuine goal of this exercise.

Moreover, in most contexts and for most purposes it is ill-advised to use resource-style class declarations at all.  We can have that discussion if you like.  I cannot think of a single scenario where using them across module boundaries would be a good idea.  If all you want is to modulate class parameters, and especially if you're ok with the hardening parameters being overridden at need, then you're proposing to go to a whole lot of effort to produce bad code to do something that Hiera can already do for you.

 
...
The obvious question is: why not put the security options into the profiles themselves and be done with it? 1) As mentioned above, we could have multiple profiles calling the same module, requiring maintainance of settings in multiple places.


No, you don't understand.  Putting the option data inside your modules (whether for security options or general options) is an inherent problem, especially if multiple profiles use the same module.  This is another aspect of the situation that Hiera already solves, in conjunction with class declarations of appropriate form.

 
2) Engineering and security would have to share the profile classes, which breaks some of the role separation and makes auditing (slightly) more complicated.


Separating the security options out into separate classes in a separate module doesn't help much there because even though the two groups may have responsibility for different files, you have very tight coupling between those belonging to one group and those belonging to the other.  You don't end up with very good role separation that way, either, because changes at the profile level require review and possible changes at the security level, which may in turn require review and changes back in Engineering at the role level.

 
3) The goal is to release the security modules publicly, and wrapping them into the site specific profiles makes that impossible.



Your community spirit is appreciated, but no knowledgable Puppeteer would use modules built along the lines you describe.

Nothing you said gives me any reason to think that there is a better approach to your problem than via Hiera (which is not even among the options you proposed).  In particular, your concept of an approach based on class inheritance is a non-starter.  You could possibly do the job via wrapper classes without class inheritance, but I see no reason to think that the result would justify all the extra effort.

Instead, take all the site-specific and option data out of your modules altogether, and manage it via Hiera and automatic data binding instead.  Manage the security-related data at a high priority level in your data hierarchy, but support overriding hardening options by establishing at least one level with higher priority.


John

Scott Jaffa

unread,
Apr 8, 2015, 5:33:41 PM4/8/15
to puppet...@googlegroups.com
John,

Thanks for the detailed reply.  While we aren't in agreement on some of the finer points, it is moot as you've made it quite clear that the listed approaches won't work at a technical level.
Stepping back, can you suggest a good method by which one could separate out cross organizational (in this case security hardening) parameters in a way that they could be shared across organizations? 
Assuming the answer, shared or not, for the security layer is hiera, I need to put more thought into the structure.

Thanks,

Scott

jcbollinger

unread,
Apr 9, 2015, 9:51:36 AM4/9/15
to puppet...@googlegroups.com


On Wednesday, April 8, 2015 at 4:33:41 PM UTC-5, Scott Jaffa wrote:
John,

Thanks for the detailed reply.  While we aren't in agreement on some of the finer points, it is moot as you've made it quite clear that the listed approaches won't work at a technical level.
Stepping back, can you suggest a good method by which one could separate out cross organizational (in this case security hardening) parameters in a way that they could be shared across organizations? 
Assuming the answer, shared or not, for the security layer is hiera, I need to put more thought into the structure.


You want to harden machine configurations managed via Puppet.  To a large extent, that means setting class parameter values that tend to improve security; certainly everything you have discussed so far boils down to that.  The central work to be performed, then, is to identify which parameters of which classes need attention, and to determine what values you prefer (for security) for those parameters.  You were already planning to do this, at least for Puppet modules used within your organization, and it constitutes the vast majority of the work that will be needed.

The remaining question, then, is how to package the hard (parameter, value) pairs in such a way that Puppet can apply them, and, preferrably, in a way that can usefully be shared among organizations.  If it is not at this point obvious to you that this problem is right in the center of Hiera's wheelhouse, then I urge you to freshen up your Hiera knowledge and to read up on Puppet automatic data binding before you devote any other effort to planning this endeavor.  The main thing you need to do is put your hardened parameters in a (one) YAML file.  Such a file could easily be shared, and those who want to use it -- yourself included -- need only to configure it as the data source for a high-priority level of their Hiera hierarchy.  Class parameter values that are assigned via automatic data binding will draw on these data.  Class parameter values that are set by any other means were out of reach of any general-purpose scheme to begin with.


John

Reply all
Reply to author
Forward
0 new messages