node_encrypt
Version information
This version is compatible with:
- Puppet Enterprise 2023.2.x, 2023.1.x, 2023.0.x, 2021.7.x, 2021.6.x, 2021.5.x, 2021.4.x, 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x, 2019.7.x, 2019.5.x, 2019.4.x, 2019.3.x, 2019.2.x, 2019.1.x, 2019.0.x
- Puppet >=6.0.0 < 8.0.0
- , , , , , , , , , , ,
This module has been deprecated by its author since Jun 23rd 2023.
The author has suggested puppetlabs-node_encrypt as its replacement.
Start using this module
Documentation
node_encrypt: over the wire encryption.
Overview
Do you wish your Puppet catalogs didn't contain plain text secrets? Are you tired of limiting access to your Puppet reports because of the passwords clearly visible in the change events? This module will encrypt values for each node specifically, using their own certificates. This means that not only do you not have plain text secrets in the catalog file, but each node can decrypt only its own secrets.
What precisely does that mean? A resource that looks like the examples below will
never have your secrets exposed in the catalog, in any reports, or any other
cached state files. Any parameter of any resource type may be encrypted by
simply annotating your secret string with a function call. This relies on
Deferred execution functions in Puppet 6. If you're running Puppet 5 or
below, then pin this module to v0.4.1 or older for backwards compatibility.
user { 'erwin':
ensure => present,
password => '{vT6YcbBhX.LL6s8'.node_encrypt::secret
}
file { '/etc/secretfile.cfg':
ensure => file,
content => 'this string will be encrypted in your catalog'.node_encrypt::secret
}
file { '/etc/another_secretfile.cfg':
ensure => file,
content => template('path/to/template.erb').node_encrypt::secret,
}
$token = lookup('application_token')
exec { 'authenticate service':
command => '/bin/application-register ${token}'.node_encrypt::secret,
}
This also comes with a Puppet Face which can be used to encrypt content for a node
and then decrypt it on that node. If you like, you may also paste the ciphertext
into your manifest or Hiera datafiles and then manually invoke the node_decrypt()
function as needed.
Usage
node_encrypt::secret()- This function encrypts a string on the server, and then decrypts it on the agent during catalog application.
- Example:
'secret string'.node_encrypt::secret
redact()- This Puppet function allows you to remove from the catalog the value of a
parameter that a class was called with.
- The name of the parameter to redact.
- The message to replace the parameter's value with. (optional)
- This Puppet function allows you to remove from the catalog the value of a
parameter that a class was called with.
puppet node encrypt- This is a Puppet Face that generates encrypted ciphertext on the command line.
puppet node encrypt -t testhost.example.com "encrypt some text"
puppet node decrypt- This is a Puppet Face that decrypts ciphertext on the command line. It can be useful in command-line scripts.
node_decrypt()- This is a Puppet function used to decrypt encrypted text on the agent. You'll only need to use this if you save encrypted content in your manifests or Hiera data files.
- Example:
content => Deferred("node_decrypt", [$encrypted_content])
node_encrypt::certificates- This class will synchronize certificates to all compile servers.
- Generally not needed, unless the
clientcert_pemfact fails for some reason.
node_encrypt::file- Legacy type for backwards code compatibility.
- It just invokes the deferred functions for you so that your old code will continue to compile. This means that it now requires Puppet 6.x+. You should migrate to the deferred function as soon as possible, as this type will be removed in the next major version.
- This is a defined type that wraps a standard file resource, but allows you to encrypt the content in the catalog and reports.
The simplest usage is like the example shown in the Overview. This defined type accepts most of the standard file parameters and simply encrypts the file contents in the catalog.
Function usage:
node_decrypt($string)
This function simply decrypts the ciphertext passed to it using the agent's own certificate. It is generally only useful as a Deferred function on Puppet 6+.
$encrypted = lookup('encrypted_foo')
file { '/etc/something/or/other.conf:
ensure => file,
owner => 'root',
group => 'root',
mode => '0600',
content => Deferred("node_decrypt", [$encrypted]),
}
redact($parameter, $replacewith)
This function will modify the catalog during compilation to remove the named
parameter from the class from which it was called. For example, if you wrote a
class named foo and called redact('bar') from within that class, then the
catalog would not record the value of bar that foo was called with.
class foo($bar) {
# this call will display the proper output, but because it's not a resource
# the string won't exist in the catalog.
notice("Class['foo'] was called with param ${bar}")
# but the catalog won't record what the passed in param was.
redact('bar')
}
class { 'foo':
bar => 'this will not appear in the catalog',
}
Warning: If you use that parameter to declare other classes or resources, then you must take further action to remove the parameter from those declarations!
This takes an optional second parameter of the value to replace the original
parameter declaration with. This parameter is required if the class declares
a type that is not String for the parameter you're redacting.
Using the command line decryption tool
This comes with a Puppet Face that can encrypt or decrypt on the command line.
You can use this in your own scripts via several methods. The ciphertext can be
generated on the CA or any compile server using the puppet node encrypt
command.
# puppet node encrypt -t testhost.puppetlabs.vm "encrypt some text"
-----BEGIN PKCS7-----
MIIMqwYJKoZIhvcNAQcDoIIMnDCCDJgCAQAxggJ7MIICdwIBADBfMFoxWDBWBgNV
BAMMT1B1cHBldCBDQSBnZW5lcmF0ZWQgb24gcHVwcGV0ZmFjdG9yeS5wdXBwZXRs
[...]
MbxinAGtO0eF4i8ova9MJykDPe600IY2b9ZY4mIskDqvHS9bVoK4fJGuRWAXiVBY
bFaZ36l90LkyLLrrSfjah/Tdqd8cHrphofsWVFWBmM1uErX1jBuuzngIehm40pN7
ClVbGy9Ow3zado1spWfDwekLoiU5imk77J9POy0X8w==
-----END PKCS7-----
Decrypting on the agent is just as easy, though a bit more flexible. For convenience in these examples, let's assume that we've set a variable like such:
# export SECRET=$(puppet node encrypt -t testhost.puppetlabs.vm "your mother was a hamster")
You can then decrypt this data by:
- Passing data directly using the
--dataoption:puppet node decrypt --data "${SECRET}"- On some platforms, this may exceed command length limits!
- Setting data in an environment variable and passing the name:
puppet node decrypt --env SECRET
- Piping data to STDIN:
echo "${SECRET}" | puppet node decryptcat /file/with/encrypted/blob.txt | puppet node decrypt
Automatically distributing certificates to compile servers
The agent should send its public certificate as a custom clientcert_pem fact,
making this a seamless zero-config process. In the case that doesn't work, you
can distribute certificates to your compile servers using the
node_encrypt::certificates class so that encryption works from all compile
servers. Please be aware that this class will create a fileserver mount on the
CA node making public certificates available for download by all nodes.
Classify all your servers, including the CA or Primary Server, with this class. This will ensure that all server have all agents' public certificates.
Note: If this is applied to all nodes in your infrastructure then all agents will have all public certificates synched. This is not a security risk, as public certificates are designed to be shared widely, but it is something you should be aware of. If you wish to prevent that, just make sure to classify only your servers.
Parameters:
-
[ca_server]
- If the CA autodetection fails, then you can specify the $fqdn of the CA server here.
-
[sort_order]
- If you've customized your HOCON-based
auth.conf, set the appropriate sort order here. The default rule's weight is 500, so this parameter defaults to300to ensure that it overrides the default.
- If you've customized your HOCON-based
Using on serverless infrastructures
For the most part, node_encrypt doesn't have as much value in a serverless
setup. When the agent is compiling its own catalog, there's no cached catalog or
network transfer. Nevertheless, there are use cases for it. For example, if you
have a report server configured, or are submitting catalogs & reports to PuppetDB,
you likely want to keep secrets hidden.
node_encrypt won't work out of the box on a serverless node because it relies
on the existence of the CA certificates. But it's easy to generate these
certificates so that it will work. Keep in mind that without the full CA
infrastructure, no other node will be able to decrypt these secrets.
Note that this functionality was moved to the puppetserver application
in Puppet 6.x, so you'll need Puppet 5.x to generate this certificate.
$ rm -rf $(puppet config print ssldir --section server)/*
$ puppet cert list -a
$ puppet cert --generate ${puppet config print certname} --dns_alt_names "$(puppet config print dns_alt_names)"
Ecosystem
How is this different from the new Sensitive type?
As of Puppet 4.6, the core language supports a Sensitive type.
This type marks data with a flag that prevents the components of the Puppet and
Puppet Enterprise stack from inadvertently displaying the value. For example, a
string that's marked as Sensitive will not display in reports or in the PE
Console.
Unfortunately, it still exists as plain text in the catalog. The node_encrypt
module encrypts data before it goes into the catalog, and it's only decrypted as
it's being written to disk.
What about Hiera eyaml?
Does this project replace that tool?
Not at all. They exist in different problem spaces. Hiera eyaml is intended to protect your secrets on-disk and in your repository. With Hiera eyaml, you can add secrets to your codebase without having to secure the entire codebase. Having access to the code doesn't mean having access to the secrets in that code.
But the secrets are still exposed in the catalog and in reports. This means you
should be protecting them as well. node_encrypt addresses that problem. The two
projects happily coexist. You can (and should) use eyaml to store your secrets
on disk, while you use node_encrypt to protect the rest of the pipeline.
Integration with other tools
This was designed to make it easy to integrate support into other tooling. For
example, this pull request
adds transparent encryption support to _rc's popular datacat module.
Testing with Onceover
If you use Onceover to test your puppet roles, you'll experience compilation failures when using this module as it won't be able to find the private keys it expects.
Evaluation Error: Error while evaluating a Resource Statement, Evaluation Error: Error while evaluating a Function Call, Not a directory @ rb_sysopen - /dev/null/ssl/private_keys/example.com.pem
In your onceover.yaml file, mock the node_encrypt function as follows.
functions:
node_encrypt:
returns: '-----BEGIN PKCS7----- MOCKED_DATA'
(Note, the text of the mock return is important for users of the node_encrypt::file defined type.)
Disclaimer
I take no liability for the use of this module. As this uses standard Ruby and OpenSSL libraries, it should work anywhere Puppet itself does. I have not yet validated on anything other than CentOS, though.
Contact
2.0.0
Warning: this is a major change and now depends on Puppet 6.x+!
- This now completely drops support for Puppet versions 5 and below.
- The weird node_encrypted_file type was totally removed.
- The only supported encryption is now the deferred function.
- The
node_encrypt::filedefined type was retained for backwards code compatibility, so you may not need to make code changes. It's now just a wrapper for the deferred function and will scream at you when you use it.
1.0.0
- Alex Fisher added some basic support for testing under OnceOver #70
- Some minor cleanups required to get the Approved badge on the Forge #77
- Alex Fisher fixed the ability to redact default resource attributes #75
- Language cleanup #76
0.4.1
- Allow node_encrypt::secret to accept Sensitive[String] values (@danielparks)
0.4.0
Warning: this is a major change in functionality!
- Puppet 6 compatibility
- Added new Deferred function, meaning far far simpler and more universal usage
- Changed encryting certificate module to always use the hostcert
- Changed the target certificate model to look for the first of:
- client cert generated by the CA
- client cert synced by
node_encrypt::certificates - client cert sent by agent as custom fact.
- Which means, that we have a new custom fact, and (test this before relying on it)
you probably don't need the
node_encrypt::certificatesclass anymore. For safer upgrades, we'll default to the known good paths for now, but I'll change the order soon and make the fact the default. - Upgrade path:
- The simplest upgrade is to just upgrade the module and do nothing. It should continue to work the same for now.
- If you're on Puppet 6, the better upgrade path would be to look at the new usage examples and start porting your code. It's far simpler and more universal. You can encrypt any parameter of any type, not just files.
- Try removing the
node_encrypt::certificatesclass and validate that nothing breaks in your infrastructure.
0.3.3
- Add CA server override parameter for cases in which the CA isn't autodetected properly
0.3.2
- Support managing binary files without spurious change notifications.
- Simplify public cert syncing to make it more reliable, especially with external CA
- NOTE: The
certificatesclass will now sync to all classified nodes.
- NOTE: The
0.3.1
- Just a hotpatch fix to resolve a compatibility issue with the Puppet Enterprise module.
- Please install this update if you're having trouble with
puppet node deactivateor the like.
0.3.0
- Certificate distribution works on Puppet 5
- Added new ways to pass data to encrypt on the CLI
- Fixed the Ruby load path issue on OSS installs (@olifre)
- Fixed OpenSSL compatiblility with Ruby 2.4 (@olifre)
0.2.7
- Allow the
redact()function to work with defined types. (@ross-w)
0.2.6
- Correctly handle namespace capitalization (@JeremyEinfeld)
- Use $path parameter properly
- Corrected some spec tests
- Warn when signature verification fails
0.2.5
- Added a
redact()function to remove class parameters from the catalog.
0.2.4
- Reimplements
autobeforeon masters that don't support it. This corrects incompatibilities with 3.x masters.
0.2.3
- Adds command-line decrypt tool. This can be used in scripts or in
execresources.
0.2.2
- Synchronize certificates in a more robust way. The CA node will encrypt
using certificates in the CA store so that no setup is required, but compile
masters will encrypt using their own host certificate and synchronized agent
certificates. This requires classification with
node_encrypt::certificates. - Simpler and more robust role detection. Works in standalone and in MoM setups.
- Use the CA verify chain on the agent.
- Stop playing with strings and just use the built-in
Puppet.settings.
0.2.1
- Working around a possibly broken fqdn is probably just a real bad idea.
This simplifies the CA node identification logic and removes the poorly
thought out
ca_nodeparameter that lasted for less than 24 hours.
0.2.0
- Allow this to work on nodes that are not the CA. (Requires some setup.)
0.1.2
- Erm. Actually include the bit that removes the content from the catalog.
- Added an
autobeforeto ensure this always lets a file resource correct out of sync parameters, if one is declared.
0.1.1
- Validate that plaintext is not passed to node_encrypted_file.
- Add ability to pass pre-encrypted ciphertext to node_encrypt::file.
- More docs and better specs.
#0.1.0
- Initial release.
Dependencies
- puppetlabs/inifile (>= 1.4.0 < 6.0.0)
- puppetlabs/puppet_authorization (>= 0.4.0 < 1.0.0)
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.