Version information
This version is compatible with:
- Puppet Enterprise 2023.8.x, 2023.7.x, 2023.6.x, 2023.5.x, 2023.4.x, 2023.3.x, 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
- Puppet >= 7.0.0 < 9.0.0
- ,
Start using this module
Add this module to your Puppetfile:
mod 'simp-tpm', '3.5.1'
Learn more about managing modules with a PuppetfileDocumentation
Table of Contents
- Description
- Setup - The basics of getting started with tpm
- Usage - Configuration options and additional functionality
- Reference - An under-the-hood peek at what the module is doing and how
- Limitations - OS compatibility, etc.
- Development - Guide for contributing to the module
Description
This module manages TPM, including taking ownership. You must take ownership of a TPM to load and unload certs, use it as a PKCS #11 interface, or to use SecureBoot.
The TPM ecosystem has been designed to be difficult to automate. The difficulty
has shown many downsides of using a tool like this module to manage your
TPM device. For example, simply reading the TPM's public key after taking
ownership of the device requires the owner password to be typed in at the
command line. This is an intentional feature to encourage admins to be
physically present at the machine with the device. To get around this, the
provider included in this module and the advanced facts use Ruby's expect
library to interact with the command line. This module also drops the owner
password in the Puppet $vardir
to make interacting with trousers in facts
possible.
This is a SIMP module
This module is a component of the System Integrity Management Platform, a compliance-management framework built on Puppet.
If you find any issues, they may be submitted to our bug tracker.
This module is optimally designed for use within a larger SIMP ecosystem, but it can be used independently:
- When included within the SIMP ecosystem, security compliance settings will be managed from the Puppet server.
- If used independently, all SIMP-managed security subsystems are disabled by default and must be explicitly opted into by administrators. Please review the
$client_nets
,$enable_*
and$use_*
parameters inmanifests/init.pp
for details.
Setup
What tpm affects
WARNING
This module can take ownership of your TPM. This could be a destructive process and is not easily reversed. For that reason, the provider does not support clearing a TPM.
This module will:
- Install
tpm-tools
andtrousers
- Enable the
tcsd
service - (OPTIONAL) Take ownership of the TPM
- The password will be in a flat file in
$vardir/simp
- The password will be in a flat file in
- (OPTIONAL) Install
tboot
, create policy, and add grub entry
Setup Requirements
In order to use this module or a TPM in general, you must do the following:
- Enable the TPM in BIOS
- Set a user/admin BIOS password
- Be able to type in the user/admin password at boot time, every boot
Beginning with the TPM module
NOTE
Using the 'well-known' SRK password is not recommended for actual use, but it is required for both Intel TXT (Trusted Boot) and the PKCS#11 interface. If you aren't using either of those technologies, please use a real password.
Include the TPM class and set the passwords in hiera. If either of the passwords
are the string 'well-known', then the well known option will be added to the
tpm_takeownership
command used to take ownership of the TPM:
classes:
- tpm
tpm::take_ownership: true
tpm::ownership::advanced_facts: true
tpm::ownership::owner_pass: 'twentycharacters0000'
tpm::ownership::srk_pass: 'well-known'
To enable the PKCS#11 interface, add the tpm::pkcs11
class to your node and set the PINs in hiera:
classes:
- tpm::pkcs11
tpm::pkcs11::so_pin: '12345678'
tpm::pkcs11::user_pin: '87654321'
To start with Trusted Boot follow the directions below carefully.
Usage
Ownership
The type and provider for tpm ownership provided in this module can be used as follows:
tpm_ownership { 'tpm0':
ensure => present,
owner_pass => 'well-known',
srk_pass => 'well-known',
advanced_facts => true
}
PKCS#11
The PKCS#11 slot type and provider can be enabled as follows:
tpmtoken { 'TPM PKCS#11 token':
ensure => present,
so_pin => '12345678',
user_pin => '87654321'
}
Trusted Boot
This module supports versions of tboot 1.9.6 and later. This module only supports grub2.
Known Errors
There are known errors in tboot v1.9.6 and the creation of the LCP and VLP fail with memory errors. This was fixed in tboot v1.9.7.
By default policy creation is disabled because as of Sept 06, 2018 tboot v1.9.6 is the version delivered with RedHat 7.5. If you want to compile tboot yourself the source can be obtained from the sourceforge: https://sourceforge.net/projects/tboot/
In order to check if tboot version is > 1.9.6 and policy is not true it needs to do two passes because the fact for the version is executed before the module installs tboot.
To avoid this the tboot version can be set in hiera:
---
tpm::tboot::tboot_version: "1.9.6"
Setting up trusted boot
To set up trusted boot on a system do the following:
- Make sure the TPM owner password is 20 characters long and the SRK password
is 'well-known', equivalent to
tpm_takeownership -z
- Download the appropriate SINIT for your platform from the Intel website
- Extract the zip and put it on a webserver somewhere or in a profile module.
- Set the following data in hiera:
---
tpm::tboot::sinit_name: 2nd_gen_i5_i7_SINIT_51.BIN # the appropriate BIN
tpm::tboot::sinit_source: 'puppet:///profiles/2nd_gen_i5_i7_SINIT_51.BIN' # where ever you choose to stash this
tpm::tboot::owner_password: "%{alias('tpm::ownership::owner_pass')}"
tpm::ownership::owner_pass: "whatever your password is"
# If you are using version 1.9.7 or later and want the LCP and VLP updated:
tpm::tboot::create_policy: true
# To avoid puppet having to do 2 passes to determine what version of tboot is installed
# you can set the version of tboot.
tpm::tboot::tboot_version: "1.9.6"
- Include the
tpm::tboot
class:
---
classes:
- tpm
- tpm::tboot
- Run puppet (run it twice if you have not set the tboot version). Reboot and select the tboot option from the menu.
- Check the
tboot
fact for a measured launch:puppet facts | grep measured_launch
or just runtxt-stat
Removing other options from the boot menu
If only the tboot menu option should be available to users then set the following in hiera:
---
tpm::tboot::purge_boot_entries: true
This removes the execute permissions from the /etc/grub.d/10_linux file. If you decide to remove tboot later, these permissions will need to be set back to executable and the grub2-mkconfig run again.
Locking the kernel
The tpm::tboot
class will use the yum::versionlock
define from the
voxpupuli/yum
module to make sure the version of the kernel that the tboot
policy was created with doesn't get upgraded without the user knowing. To
disable this, set the tpm::tboot::lock_kernel_packages
parameter to false
.
This module does provide a script to upgrade the policy, though it shouldn't be run from Puppet. To update your verified launch policy, do the following steps:
yum update kernel
grub2-mkconfig -o /etc/grub2.cfg
sh /root/txt/txt/update_tboot_policy.sh <owner password>
And reboot!
Reference
See REFERENCE.md for API details.
Limitations
SIMP Puppet modules are generally intended for use on Red Hat Enterprise Linux
and compatible distributions, such as CentOS. Please see the
metadata.json
file for the most up-to-date list of
supported operating systems, Puppet versions, and module dependencies.
This module does not support clearing a previously owned TPM.
Development
Please read our Contribution Guide
Acceptance tests
TODO: There are currently no acceptance tests. We would need to use a virtual TPM to ensure test system stability, and it requires quite a few patches to libvirt, associated emulation software, Beaker, and Vagrant before acceptance tests for this module become feasible. Read our progress so far on the issue.
Reference
Table of Contents
Classes
tpm
: Provides utilities for interacting with a TPMtpm::ownership
: Take ownership of the TPM in the system, using an auto-generated password created with simplib's passgen.tpm::pkcs11
: Manage the tpm-enabled PKCS #11 interfacetpm::tboot
: Create a launch policy, modify grub, and enable tboot.tpm::tboot::grub
: Configure grubtpm::tboot::grub::grub1
: Manage grub configuration for tboottpm::tboot::grub::grub2
: Manage grub2 configurationtpm::tboot::lock_kernel
: Lock the kernel to avoid automatically invalidating the launch policytpm::tboot::policy
: Generate and install policytpm::tboot::sinit
: Install the sinit for your platform
Resource types
tpm_ownership
: A type to manage ownership of a TPM.owner_pass
is required, whilesrk_pass
is only necessary if you aren't using Trusted Boot or the PKCtpmtoken
: This type will manage the PKCS #11 interface provided by opencryptoki, and backed by the TPM. Example: include 'tpm' tpmtoken { 'tpmtok
Classes
tpm
Provides utilities for interacting with a TPM
Parameters
The following parameters are available in the tpm
class:
ima
Data type: Boolean
Toggles IMA on or off. NOTE: This parameter is deprecated and throws a warning if specified. IMA may remain on if the ima module is enabled elsewhere.
Default value: false
take_ownership
Data type: Boolean
Enable to allow Puppet to take ownership of the TPM.
Default value: false
tpm::ownership
The password must be generated with passgen in order for most of the facts to be functional post-ownership, as the tpm commands from tpm-tools require the owner password.
Parameters
The following parameters are available in the tpm::ownership
class:
owned
Data type: Boolean
Whether or not the module should take ownership
Default value: true
owner_pass
Data type: Variant[Enum['well-known'],String[20]]
The TPM owner password
Default value: simplib::passgen( "${facts['fqdn']}_tpm0_owner_pass", { 'length' => 20 } )
srk_pass
Data type: Optional[String]
The TPM SRK password
- Defaults to an empty String because according to the trousers documentation it needs to be null to be useful.
Default value: undef
advanced_facts
Data type: Boolean
Enable facts that require the owner password to function. The password will
be on the client filesystem (in $vardir/simp
) if enabled.
Default value: false
tpm::pkcs11
If the SO_PIN_LOCKED
flag gets thrown, you will have to reset your interface
by deleting the /var/lib/opencryptoki/tpm/root/NVTOK.DAT file.
Parameters
The following parameters are available in the tpm::pkcs11
class:
so_pin
Data type: String
4-8 character password used for the Security Officer pin.
Default value: simplib::passgen( "${facts['fqdn']}_pkcs_so_pin", { 'length' => 8 } )
user_pin
Data type: String
4-8 character password used for the user pin.
Default value: simplib::passgen( "${facts['fqdn']}_pkcs_user_pin", { 'length' => 8 } )
tpm::tboot
This version of tpm::tboot will work only with tboot versions 1.9.6 or later. To use an earlier version on tboot use pupmod-simp-tpm version 1.1.0.
Parameters
The following parameters are available in the tpm::tboot
class:
purge_boot_entries
lock_kernel_packages
create_policy
sinit_name
sinit_source
tboot_version
kernel_packages_to_lock
rsync_source
rsync_server
rsync_timeout
owner_password
tboot_boot_options
additional_boot_options
policy_script
policy_script_source
update_script
update_script_source
package_ensure
purge_boot_entries
Data type: Boolean
Remove other, nontrusted boot entries from Grub
Default value: false
lock_kernel_packages
Data type: Boolean
Lock kernel related packages in YUM, to avoid accidentally invalidating the launch policy
Default value: true
create_policy
Data type: Boolean
The verified launch policy and launch control policies will be updated using the scripts identified by parameter policy_script.
Default value: false
sinit_name
Data type: Optional[String]
Name of the SINIT policy file, usually ending in *.BIN
Default value: undef
sinit_source
Data type: Optional[String]
Puppet file
resouce source attribute for the SINIT binary
Default value: simplib::lookup('simp_options::rsync', { 'default_value' => undef })
tboot_version
Data type: Optional[String]
The verson of tboot installed on the remote system
Default value: $facts['tboot_version']
kernel_packages_to_lock
Data type: Array[String]
List of kernel related packages to lock
@example
The binary was manually copied over to /root/BIN
, so this entry was set
to file:///root/BIN
Default value:
[ 'kernel','kernel-bigmem','kernel-enterprise',
'kernel-smp','kernel-debug','kernel-unsupported',
'kernel-source','kernel-devel','kernel-PAE',
'kernel-PAE-debug','kernel-modules', 'kernel-headers' ]
rsync_source
Data type: String
Rsync location for the SINIT binary
Default value: "tboot_${::environment}/"
rsync_server
Data type: Optional[String]
Rsync server to use for pulling the sinit images
Default value: simplib::lookup('simp_options::rsync::server', { 'default_value' => '127.0.0.1' })
rsync_timeout
Data type: Integer
Rsync timeout
Default value: simplib::lookup('simp_options::rsync::timeout', { 'default_value' => 1 })
owner_password
Data type: String
The TPM owner password
Default value: simplib::passgen( "${facts['fqdn']}_tpm0_owner_pass", { 'length' => 20 } )
tboot_boot_options
Data type: Array[String]
Kernel parameters for the tboot kernel min_ram=0x2000000
is required on
systems with more than 4GB of memory
@see the tboot documentation in /usr/share/simp/tboot-*/README
Default value: ['logging=serial,memory,vga','min_ram=0x2000000']
additional_boot_options
Data type: Array[String]
Regular Linux kernel parameters, specific to tboot sessions intel_iommu=on
is the default here to force the kernel to load VT-d
Default value: ['intel_iommu=on']
policy_script
Data type: Stdlib::AbsolutePath
The script to generate the tboot policy. This should not be changed
Default value: '/root/txt/create_lcp_boot_policy.sh'
policy_script_source
Data type: String
Where to find the script. This should also not be changed
Default value: 'puppet:///modules/tpm/create_lcp_tboot_policy.sh'
update_script
Data type: Stdlib::AbsolutePath
The script to use for updating the tboot policy. This should not be changed.
Default value: '/root/txt/update_tboot_policy.sh'
update_script_source
Data type: String
Where to find the update script. This should not be changed.
Default value: 'puppet:///modules/tpm/update_tboot_policy.sh'
package_ensure
Data type: String
How to ensure the tboot
package will be managed
Default value: simplib::lookup('simp_options::package_ensure', { 'default_value' => 'installed' })
tpm::tboot::grub
This class is controlled by tpm::tboot
tpm::tboot::grub::grub1
This class is controlled by tpm::tboot
tpm::tboot::grub::grub2
This class is controlled by tpm::tboot
tpm::tboot::lock_kernel
This class is controlled by tpm::tboot
tpm::tboot::policy
This class is controlled by tpm::tboot
tpm::tboot::sinit
This class is controlled by tpm::tboot
Resource types
tpm_ownership
A type to manage ownership of a TPM. owner_pass
is required, while
srk_pass
is only necessary if you aren't using Trusted Boot or the PKCS#11
interface. The SRK (Storage Root Key) password must be to be null in order to
use those features.
If you need to use a 'well-known' password, make the password equal to the
string 'well-known'. The provider will then use the -z
or -y
option when
taking ownership of the TPM with tpm_takeownership
.
Example:
include 'tpm'
tpm_ownership { 'tpm0': owned => true, owner_pass => 'badpass', }
Properties
The following properties are available in the tpm_ownership
type.
owned
Valid values: true
, false
Ownership status of the TPM
Parameters
The following parameters are available in the tpm_ownership
type.
advanced_facts
Valid values: true
, false
, yes
, no
Enabling the advanced facts will write your owner password to a file on the system, only readable by the root user. It will be used to query the TPM using trousers.
Default value: false
name
namevar
The name of the resource - usually tpm0, the default device.
Default value: tpm0
owner_pass
The owner password of the TPM
provider
The specific backend to use for this tpm_ownership
resource. You will seldom need to specify this --- Puppet will
usually discover the appropriate provider for your platform.
srk_pass
The Storage Root Key(SRK) password of the TPM
Default value: well-known
tpmtoken
This type will manage the PKCS #11 interface provided by opencryptoki, and backed by the TPM.
Example: include 'tpm'
tpmtoken { 'tpmtok': ensure => present, so_pin => '87654321', user_pin => '87654321' }
Properties
The following properties are available in the tpmtoken
type.
ensure
Valid values: present
, absent
The basic property that the resource should be in.
Default value: present
Parameters
The following parameters are available in the tpmtoken
type.
label
The tag of the slot, to be used during initialization
provider
The specific backend to use for this tpmtoken
resource. You will seldom need to specify this --- Puppet will usually
discover the appropriate provider for your platform.
so_pin
Security Officer (SO) PIN for the interface
user_pin
User PIN for the interface
- Wed Oct 18 2023 Steven Pritchard steve@sicura.us - 3.5.1
- Replace calls to
File.exists?
withFile.exist?
for compatibility with Ruby 3
- Wed Oct 11 2023 Steven Pritchard steve@sicura.us - 3.5.0
- [puppetsync] Updates for Puppet 8
- These updates may include the following:
- Update Gemfile
- Add support for Puppet 8
- Drop support for Puppet 6
- Update module dependencies
- These updates may include the following:
- Mon Jul 24 2023 Chris Tessmer chris.tessmer@onyxpoint.com - 3.4.0
- Add RockyLinux 8 support
- Sat Oct 15 2022 Trevor Vaughan trevor@sicura.us - 3.3.1
- Update puppet/yum dependency version
- Thu Jun 17 2021 Chris Tessmer chris.tessmer@onyxpoint.com - 3.3.0
- Removed support for Puppet 5
- Ensured support for Puppet 7 in requirements and stdlib
- Sat Dec 19 2020 Chris Tessmer chris.tessmer@onyxpoint.com - 3.2.1
- Removed EL6 support
- Thu Dec 12 2019 Trevor Vaughan tvaughan@onyxpoint.com - 3.2.0-0
- Documentation update
- Added REFERENCE.md
- Fri Aug 02 2019 Robert Vincent pillarsdotnet@gmail.com - 3.2.0-0
- Drop Puppet 4 support
- Add Puppet 6 support
- Add puppetlabs-stdlib 6 support
- Add puppet-yum 4 support
- Tue Feb 12 2019 Liz Nemsick lnemsick.simp@gmail.com - 3.1.1-0
- Use simplib::passgen() in lieu of passgen(), a deprecated simplib Puppet 3 function.
- Expanded the upper limits of the stdlib and yum Puppet module versions
- Thu Sep 13 2018 Jeanne Greulich jeanne.greulich@onyxpoint.com - 3.1.0-0
- Added support for tboot V1.9.6 and removed support for tboot.1.9.4. Use pupmod-simp-tpm 1.1.0 if tboot v1.9.4 is required.
- Made creating the VLP and LCP optional because they don't work in tboot v1.9.6.
- Tboot V1.9.7 was released on Sept 06, 2018 but not packaged for Redhat. Tested with a locally compiled version and the creation of the policy was fixed in that version.
- Added check for 20 character owner passwords because they are required.
- Thu Aug 09 2018 Michael Morrone michael.morrone@onypoint.com - 3.1.0-0
- Removed direct module support for IMA, as that functionality has been ported to the ima module. Instead, for backward compatibility, this module now uses the ima module, when tpm::ima is set to true.
- Sun Jul 15 2018 Chris Tessmer chris.tessmer@onypoint.com - 3.0.0-0
- Removed support for TPM 2.0 (into its own module)
- Mon Jul 09 2018 Trevor Vaughan tvaughan@onyxpoint.com - 1.2.1-0
- Fix CHANGELOG ordering
- Wed Jan 03 2018 Nick Miller nick.miller@onyxpoint.com - 1.2.0-0
- tpm::ima::policy was not previously callable from tpm::ima
- tpm::ima::policy will now disable many default IMA checks by default
- Mon Dec 04 2017 Nick Miller nick.miller@onyxpoint.com - 1.1.1-0
- Updated to support Puppet 5
- IMA policy service
- Moved the import_ima_rules systemd unit file from /usr/systemd to /etc/systemd on systemd based systems
- Service is now stopped, but enabled, so will only take affect at reboot, not during puppet run
- Thu Aug 17 2017 Nick Miller nick.miller@onyxpoint.com - 1.1.0-0
- Improvments to the facts:
- Add tests for following facts:
- tpm
- ima_log_size
- has_tpm
- Confine tpm fact on the existance of the
tpm-tools
package - Migrate to the built in facter timeout
- Add tests for following facts:
- Improvements to the
tpm_ownership
provider- Added instances feature
- run
puppet resource tpm_ownership
and see the resource
- run
- New properties to the type to reflect system state
- Changed default owner_pass to 'well-known'
- Removed 'ensure' parameter in favor of the 'owned' param
- Improved documentation
- Added instances feature
- Improvements to the
tpm::ownership
class- Added 'owned' parameter to pass to the tpm_ownership type
- New
tpm::tboot
class to enable Trusted Boot- See
tpm::tboot
for details - Automatically lock the kernel package and other kernel related packages to avoid automatically invalidating launch policy
- See
- Depend on augeasproviders_grub instead of generic and deprecated augeasproviders
- Thu Jul 06 2017 Liz Nemsick lnemsick.simp@gmail.com - 1.0.1-0
- Update puppet dependency and remove OBE pe dependency in metadata.json
- Wed Jan 04 2017 Nick Miller nick.miller@onyxpoint.com - 1.0.0-0
- Strongly type module
- Mon Nov 21 2016 SIMP-Team https://groups.google.com/forum/#!forum/simp - 1.0.0-0
- Updated module for compliance markup API v1.0.0 compatibility.
- Tue Nov 17 2016 Nick Miller nick.miller@onyxpoint.com - 0.2.1-0
- Added a check to the ima_log_size fact to make sure that the file needed exists before executing
- Wed Oct 5 2016 Nick Miller nick.miller@onyxpoint.com - 0.2.0-0
- Added ability to use
tpm_takeownership
well-known password options
- Fri Sep 30 2016 Nick Miller nick.miller@onyxpoint.com - 0.2.0-0
- Added a feature to manage the PKCS#11 slot provided by the TPM
- Also added a class that takes advantage of it
- Tue Sep 27 2016 Nick Miller nick.miller@onyxpoint.com - 0.2.0-0
- Added functionality to take ownership of the TPM
- Tue Mar 01 2016 Ralph Wright ralph.wright@onyxpoint.com - 0.0.1-10
- Added compliance function support
- Mon Nov 09 2015 Chris Tessmer chris.tessmer@onypoint.com - 0.0.1-9
- migration to simplib and simpcat (lib/ only)
- Mon Jul 27 2015 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-8
- Disable IMA by default.
- Thu Jul 09 2015 Nick Markowski nmarkowski@kewcorp.com - 0.0.1-7
- Cast ima_audit to string when passed to kernel_parameter.
- Thu Feb 19 2015 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-6
- Migrated to the new 'simp' environment.
- Fri Jan 16 2015 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-5
- Changed puppet-server requirement to puppet
- Sat Aug 23 2014 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-4
- Replaced the reboot calls with the new reboot_notify type.
- Sat Aug 02 2014 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-3
- Upadted the has_tpm fact to use /sys
- Fixed the ima_enabled fact to use /proc/cmdline
- Thu Jul 31 2014 Adam Yohrling adam.yohrling@onyxpoint.com - 0.0.1-2
- Added has_tpm fact
- Added installation of tpm-tools and trousers (by dependency)
- Added tcsd service
- Updated spec_helper to include rubygems (didn't run without)
- Updated spec tests
- Changed existing logic to use str2bool in tpm::ima class for fact check
- Thu Jul 10 2014 Trevor Vaughan tvaughan@onyxpoint.com - 0.0.1-2
- Updated the 'tpm::ima' class to use the new 'common::reboot' functionality as well as the kernel_parameter augeasproviders mods
- Mon Apr 28 2014 Nick Markowski nmarkowski@keywcorp.com - 0.0.1-1
- Updated ima_enabled fact to properly return IMA status
- Typo fix in the template
- Thu Mar 27 2014 Nick Markowski nmarkowski@keywcorp.com - 0.0.1-0
- Initial Commit.
- Provided basic IMA functionality to set kernel boot flags, and mount securityfs at /sys/kernel/security if present.
Dependencies
- simp/ima (>= 0.1.0 < 1.0.0)
- simp/simplib (>= 4.9.0 < 5.0.0)
- puppetlabs/stdlib (>= 8.0.0 < 10.0.0)
- puppet/yum (>= 2.0.0 < 8.0.0)
pupmod-simp-tpm - A Puppet Module for managing the TPM -- Per Section 105 of the Copyright Act of 1976, these works are not entitled to domestic copyright protection under US Federal law. The US Government retains the right to pursue copyright protections outside of the United States. The United States Government has unlimited rights in this software and all derivatives thereof, pursuant to the contracts under which it was developed and the License under which it falls. --- 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.