From b3f2ca415ea0c35fc55fc56465df575e8b02097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Wed, 6 Dec 2023 12:12:00 +0100 Subject: [PATCH] Set up SpamAssassin Scan incoming and outgoing email for spam. Use a local Unbound for DNS, so we don't run into blocks for RBL queries. --- Berksfile | 1 + Berksfile.lock | 2 + cookbooks/unbound/CHANGELOG.md | 64 ++++++ cookbooks/unbound/LICENSE | 201 ++++++++++++++++++ cookbooks/unbound/README.md | 78 +++++++ cookbooks/unbound/chefignore | 115 ++++++++++ cookbooks/unbound/kitchen.dokken.yml | 56 +++++ cookbooks/unbound/libraries/helpers.rb | 59 +++++ cookbooks/unbound/libraries/template.rb | 26 +++ cookbooks/unbound/metadata.json | 42 ++++ cookbooks/unbound/metadata.rb | 13 ++ cookbooks/unbound/recipes/default.rb | 25 +++ cookbooks/unbound/renovate.json | 6 + .../resources/config_authority_zone.rb | 93 ++++++++ cookbooks/unbound/resources/config_cachedb.rb | 67 ++++++ cookbooks/unbound/resources/config_dns64.rb | 58 +++++ .../unbound/resources/config_dnscrypt.rb | 80 +++++++ cookbooks/unbound/resources/config_dnstap.rb | 116 ++++++++++ .../resources/config_dynamic_library.rb | 48 +++++ .../unbound/resources/config_forward_zone.rb | 80 +++++++ .../unbound/resources/config_python_script.rb | 53 +++++ .../resources/config_remote_control.rb | 77 +++++++ .../unbound/resources/config_rpz_zone.rb | 98 +++++++++ cookbooks/unbound/resources/config_server.rb | 58 +++++ .../unbound/resources/config_stub_zone.rb | 84 ++++++++ cookbooks/unbound/resources/config_view.rb | 68 ++++++ cookbooks/unbound/resources/package.rb | 36 ++++ .../resources/partials/_config_file.rb | 122 +++++++++++ cookbooks/unbound/resources/service.rb | 69 ++++++ .../default/partials/_generic_config.erb | 22 ++ .../templates/default/unbound.conf.erb | 5 + nodes/mail.kosmos.org.json | 2 + .../kosmos_email/attributes/default.rb | 3 +- site-cookbooks/kosmos_email/metadata.rb | 1 + .../kosmos_email/recipes/default.rb | 2 + .../kosmos_email/recipes/postfix.rb | 5 +- .../kosmos_email/recipes/spamassassin.rb | 34 +++ .../kosmos_email/templates/spamass-milter.erb | 28 +++ .../templates/spamassassin_default.erb | 33 +++ .../templates/spamassassin_local.cf.erb | 119 +++++++++++ 40 files changed, 2145 insertions(+), 4 deletions(-) create mode 100644 cookbooks/unbound/CHANGELOG.md create mode 100644 cookbooks/unbound/LICENSE create mode 100644 cookbooks/unbound/README.md create mode 100644 cookbooks/unbound/chefignore create mode 100644 cookbooks/unbound/kitchen.dokken.yml create mode 100644 cookbooks/unbound/libraries/helpers.rb create mode 100644 cookbooks/unbound/libraries/template.rb create mode 100644 cookbooks/unbound/metadata.json create mode 100644 cookbooks/unbound/metadata.rb create mode 100644 cookbooks/unbound/recipes/default.rb create mode 100644 cookbooks/unbound/renovate.json create mode 100644 cookbooks/unbound/resources/config_authority_zone.rb create mode 100644 cookbooks/unbound/resources/config_cachedb.rb create mode 100644 cookbooks/unbound/resources/config_dns64.rb create mode 100644 cookbooks/unbound/resources/config_dnscrypt.rb create mode 100644 cookbooks/unbound/resources/config_dnstap.rb create mode 100644 cookbooks/unbound/resources/config_dynamic_library.rb create mode 100644 cookbooks/unbound/resources/config_forward_zone.rb create mode 100644 cookbooks/unbound/resources/config_python_script.rb create mode 100644 cookbooks/unbound/resources/config_remote_control.rb create mode 100644 cookbooks/unbound/resources/config_rpz_zone.rb create mode 100644 cookbooks/unbound/resources/config_server.rb create mode 100644 cookbooks/unbound/resources/config_stub_zone.rb create mode 100644 cookbooks/unbound/resources/config_view.rb create mode 100644 cookbooks/unbound/resources/package.rb create mode 100644 cookbooks/unbound/resources/partials/_config_file.rb create mode 100644 cookbooks/unbound/resources/service.rb create mode 100644 cookbooks/unbound/templates/default/partials/_generic_config.erb create mode 100644 cookbooks/unbound/templates/default/unbound.conf.erb create mode 100644 site-cookbooks/kosmos_email/recipes/spamassassin.rb create mode 100644 site-cookbooks/kosmos_email/templates/spamass-milter.erb create mode 100644 site-cookbooks/kosmos_email/templates/spamassassin_default.erb create mode 100644 site-cookbooks/kosmos_email/templates/spamassassin_local.cf.erb diff --git a/Berksfile b/Berksfile index ec09e0e..e82e5ed 100644 --- a/Berksfile +++ b/Berksfile @@ -37,6 +37,7 @@ cookbook 'timezone_iii', '= 1.0.4' cookbook 'ulimit', '~> 1.0.0' cookbook 'users', '~> 5.3.1' cookbook 'zerotier', '~> 1.0.7' +cookbook 'unbound', '~> 3.0.2' # openresty dependency cookbook 'jemalloc', '~> 0.1.7' diff --git a/Berksfile.lock b/Berksfile.lock index cf6ade6..c3bab6f 100644 --- a/Berksfile.lock +++ b/Berksfile.lock @@ -33,6 +33,7 @@ DEPENDENCIES ruby_build (~> 2.5.0) timezone_iii (= 1.0.4) ulimit (~> 1.0.0) + unbound (~> 3.0.2) users (~> 5.3.1) yum zerotier (~> 1.0.7) @@ -99,6 +100,7 @@ GRAPH seven_zip (4.2.2) timezone_iii (1.0.4) ulimit (1.0.0) + unbound (3.0.2) users (5.3.1) windows (7.0.2) yum (7.4.13) diff --git a/cookbooks/unbound/CHANGELOG.md b/cookbooks/unbound/CHANGELOG.md new file mode 100644 index 0000000..ce0a267 --- /dev/null +++ b/cookbooks/unbound/CHANGELOG.md @@ -0,0 +1,64 @@ +# CHANGELOG + +This file is used to list changes made in each version of the unbound cookbook. + +## 3.0.2 - *2023-10-02* + +- Update Ci files and remove CircleCI config + +## 3.0.1 - *2022-09-30* + +- Add missing `fallback-enable` setting to `config_authority_zone` + +## 3.0.0 - *2022-04-04* + +- Add separate configuration resources +- Default recipe now only runs installation +- Refactor configuration template to be Hash driven + +## 2.0.3 - *2022-03-04* + +- resolved cookstyle error: .delivery/project.toml:2:8 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:4:10 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:5:13 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:6:10 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:7:9 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:8:14 convention: `Style/StringLiterals` +- resolved cookstyle error: .delivery/project.toml:9:11 convention: `Style/StringLiterals` + +## 2.0.2 - *2021-08-31* + +- Standardise files with files in sous-chefs/repo-management + +## 2.0.1 - *2021-06-01* + +- Updated tests folder to match other cookbooks +- Updated spec platform to supported version + +## 2.0.0 - 2020-05-05 + +- Upgraded to circleci for testing +- Minimum Chef Infra Client version is now **13.0** +- Removed unused long_description metadata.rb field +- Simplify overly complex platform logic +- Migrate to actions for testing + +## [1.0.1] + +- Simplify logic with root_group +- Fix `root_group` not using new_resource +- Use strings for file modes +- Resolve foodcritic warnings in the `rr` resource +- Fix platform_family logic on the service Update platforms. +- Use dokken images for travis testing. +- Don't test on debian-8/9 and centos-6 as these services don't currently start. +- Account for a list of forward-addrs / effectively disable remote control (#27) + +## [1.0.0] + +- Add new custom resources `unbound_install` & `unbound_configure` + +## [0.1.1] + +- Adding support and kitchen testing for forward_zone generation +- Updating to use Sous Chefs guidelines diff --git a/cookbooks/unbound/LICENSE b/cookbooks/unbound/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/cookbooks/unbound/LICENSE @@ -0,0 +1,201 @@ + 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. diff --git a/cookbooks/unbound/README.md b/cookbooks/unbound/README.md new file mode 100644 index 0000000..700cf8a --- /dev/null +++ b/cookbooks/unbound/README.md @@ -0,0 +1,78 @@ +# Unbound Cookbook + +[![Cookbook Version](https://img.shields.io/cookbook/v/unbound.svg)](https://supermarket.chef.io/cookbooks/unbound) +[![Build Status](https://img.shields.io/circleci/project/github/sous-chefs/unbound/master.svg)](https://circleci.com/gh/sous-chefs/unbound) +[![OpenCollective](https://opencollective.com/sous-chefs/backers/badge.svg)](#backers) +[![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors) +[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) + +Installs and manages the unbound DNS server. + +- [http://unbound.net](http://unbound.net) + +## Maintainers + +This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you’d like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF). + +## Requirements + +### Platform + +A platform with unbound available as a native package. The following platforms have unbound packaged, but note that the filesystem locations are not consistent and at this time only Linux + FHS is supported. + +- Ubuntu/Debian +- Red Hat/CentOS/Fedora (requires EPEL) +- FreeBSD + +### Chef + +- Chef 16 + +## Resources + +- [unbound_config_authority_zone](documentation/unbound_config_authority_zone.md) +- [unbound_config_cachedb](documentation/unbound_config_cachedb.md) +- [unbound_config_dns64](documentation/unbound_config_dns64.md) +- [unbound_config_dnscrypt](documentation/unbound_config_dnscrypt.md) +- [unbound_config_dnstap](documentation/unbound_config_dnstap.md) +- [unbound_config_dynamic_library](documentation/unbound_config_dynamic_library.md) +- [unbound_config_forward_zone](documentation/unbound_config_forward_zone.md) +- [unbound_config_python_script](documentation/unbound_config_python_script.md) +- [unbound_config_remote_control](documentation/unbound_config_remote_control.md) +- [unbound_config_rpz_zone](documentation/unbound_config_rpz_zone.md) +- [unbound_config_server](documentation/unbound_config_server.md) +- [unbound_config_stub_zone](documentation/unbound_config_stub_zone.md) +- [unbound_config_view](documentation/unbound_config_view.md) +- [unbound_package](documentation/unbound_package.md) +- [unbound_service](documentation/unbound_service.md) + +## Recipes + +### default + +Installs unbound using defaults. + +## Contributors + +This project exists thanks to all the people who [contribute.](https://opencollective.com/sous-chefs/contributors.svg?width=890&button=false) + +### Backers + +Thank you to all our backers! + +![https://opencollective.com/sous-chefs#backers](https://opencollective.com/sous-chefs/backers.svg?width=600&avatarHeight=40) + +### Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. + +![https://opencollective.com/sous-chefs/sponsor/0/website](https://opencollective.com/sous-chefs/sponsor/0/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/1/website](https://opencollective.com/sous-chefs/sponsor/1/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/2/website](https://opencollective.com/sous-chefs/sponsor/2/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/3/website](https://opencollective.com/sous-chefs/sponsor/3/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/4/website](https://opencollective.com/sous-chefs/sponsor/4/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/5/website](https://opencollective.com/sous-chefs/sponsor/5/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/6/website](https://opencollective.com/sous-chefs/sponsor/6/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/7/website](https://opencollective.com/sous-chefs/sponsor/7/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/8/website](https://opencollective.com/sous-chefs/sponsor/8/avatar.svg?avatarHeight=100) +![https://opencollective.com/sous-chefs/sponsor/9/website](https://opencollective.com/sous-chefs/sponsor/9/avatar.svg?avatarHeight=100) diff --git a/cookbooks/unbound/chefignore b/cookbooks/unbound/chefignore new file mode 100644 index 0000000..cc170ea --- /dev/null +++ b/cookbooks/unbound/chefignore @@ -0,0 +1,115 @@ +# Put files/directories that should be ignored in this file when uploading +# to a Chef Infra Server or Supermarket. +# Lines that start with '# ' are comments. + +# OS generated files # +###################### +.DS_Store +ehthumbs.db +Icon? +nohup.out +Thumbs.db +.envrc + +# EDITORS # +########### +.#* +.project +.settings +*_flymake +*_flymake.* +*.bak +*.sw[a-z] +*.tmproj +*~ +\#* +REVISION +TAGS* +tmtags +.vscode +.editorconfig + +## COMPILED ## +############## +*.class +*.com +*.dll +*.exe +*.o +*.pyc +*.so +*/rdoc/ +a.out +mkmf.log + +# Testing # +########### +.circleci/* +.codeclimate.yml +.delivery/* +.foodcritic +.kitchen* +.mdlrc +.overcommit.yml +.rspec +.rubocop.yml +.travis.yml +.watchr +.yamllint +azure-pipelines.yml +Dangerfile +examples/* +features/* +Guardfile +kitchen.yml* +mlc_config.json +Procfile +Rakefile +spec/* +test/* + +# SCM # +####### +.git +.gitattributes +.gitconfig +.github/* +.gitignore +.gitkeep +.gitmodules +.svn +*/.bzr/* +*/.git +*/.hg/* +*/.svn/* + +# Berkshelf # +############# +Berksfile +Berksfile.lock +cookbooks/* +tmp + +# Bundler # +########### +vendor/* +Gemfile +Gemfile.lock + +# Policyfile # +############## +Policyfile.rb +Policyfile.lock.json + +# Documentation # +############# +CODE_OF_CONDUCT* +CONTRIBUTING* +documentation/* +TESTING* +UPGRADING* + +# Vagrant # +########### +.vagrant +Vagrantfile diff --git a/cookbooks/unbound/kitchen.dokken.yml b/cookbooks/unbound/kitchen.dokken.yml new file mode 100644 index 0000000..46cae69 --- /dev/null +++ b/cookbooks/unbound/kitchen.dokken.yml @@ -0,0 +1,56 @@ +--- +driver: + name: dokken + privileged: true + chef_version: <%= ENV['CHEF_VERSION'] || 'current' %> + +transport: + name: dokken + +provisioner: + name: dokken + +platforms: + - name: centos-7 + driver: + image: dokken/centos-7 + pid_one_command: /usr/lib/systemd/systemd + + - name: centos-stream-8 + driver: + image: dokken/centos-stream-8 + pid_one_command: /usr/lib/systemd/systemd + + - name: fedora-latest + driver: + image: dokken/fedora-latest + pid_one_command: /usr/lib/systemd/systemd + + - name: ubuntu-18.04 + driver: + image: dokken/ubuntu-18.04 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update + + - name: ubuntu-20.04 + driver: + image: dokken/ubuntu-20.04 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update + + - name: debian-10 + driver: + image: dokken/debian-10 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update + + - name: debian-11 + driver: + image: dokken/debian-11 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update +... diff --git a/cookbooks/unbound/libraries/helpers.rb b/cookbooks/unbound/libraries/helpers.rb new file mode 100644 index 0000000..bb46228 --- /dev/null +++ b/cookbooks/unbound/libraries/helpers.rb @@ -0,0 +1,59 @@ +# +# Cookbook:: unbound +# Library:: helpers +# +# 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. +# + +module Unbound + module Cookbook + module Helpers + def default_config_dir + return '/etc/unbound' if %i(unbound_config unbound_configure unbound_config_server).include?(declared_type) + + return '/etc/unbound/unbound.conf.d' if platform?('debian', 'ubuntu') + + case declared_type + when :unbound_config_local + '/etc/unbound/local.d' + when :unbound_config_key + '/etc/unbound/keys.d' + else + '/etc/unbound/conf.d' + end + end + + def default_includes_dir + case node['platform_family'] + when 'rhel', 'fedora' + %w(/etc/unbound/conf.d/*.conf /etc/unbound/local.d/*.conf) + when 'debian' + %w(/etc/unbound/unbound.conf.d/*.conf) + else + raise "Unsupported platform family #{node['platform_family']}" + end + end + + def unbound_yes_no?(value) + case value + when true + 'yes' + when false + 'no' + when 'yes', 'YES', 'no', 'NO' + value.downcase + end + end + end + end +end diff --git a/cookbooks/unbound/libraries/template.rb b/cookbooks/unbound/libraries/template.rb new file mode 100644 index 0000000..acc3c62 --- /dev/null +++ b/cookbooks/unbound/libraries/template.rb @@ -0,0 +1,26 @@ +# +# Cookbook:: unbound +# Library:: template +# +# 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. +# + +module Unbound + module Cookbook + module TemplateHelpers + def template_partial_indent(output, level, spaces = 2) + output.split("\n").each { |l| l.prepend(' ' * (level * spaces)) }.join("\n") + end + end + end +end diff --git a/cookbooks/unbound/metadata.json b/cookbooks/unbound/metadata.json new file mode 100644 index 0000000..560d7e2 --- /dev/null +++ b/cookbooks/unbound/metadata.json @@ -0,0 +1,42 @@ +{ + "name": "unbound", + "description": "Manages unbound DNS resolver", + "long_description": "", + "maintainer": "Sous Chefs", + "maintainer_email": "help@sous-chefs.org", + "license": "Apache-2.0", + "platforms": { + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "centos": ">= 0.0.0", + "redhat": ">= 0.0.0", + "scientific": ">= 0.0.0", + "oracle": ">= 0.0.0", + "amazon": ">= 0.0.0" + }, + "dependencies": { + + }, + "providing": { + + }, + "recipes": { + + }, + "version": "3.0.2", + "source_url": "https://github.com/sous-chefs/unbound", + "issues_url": "https://github.com/sous-chefs/unbound/issues", + "privacy": false, + "chef_versions": [ + [ + ">= 16" + ] + ], + "ohai_versions": [ + + ], + "gems": [ + + ], + "eager_load_libraries": true +} diff --git a/cookbooks/unbound/metadata.rb b/cookbooks/unbound/metadata.rb new file mode 100644 index 0000000..7e52ea9 --- /dev/null +++ b/cookbooks/unbound/metadata.rb @@ -0,0 +1,13 @@ +name 'unbound' +maintainer 'Sous Chefs' +maintainer_email 'help@sous-chefs.org' +license 'Apache-2.0' +description 'Manages unbound DNS resolver' +version '3.0.2' +issues_url 'https://github.com/sous-chefs/unbound/issues' +source_url 'https://github.com/sous-chefs/unbound' +chef_version '>= 16' + +%w(debian ubuntu centos redhat scientific oracle amazon).each do |os| + supports os +end diff --git a/cookbooks/unbound/recipes/default.rb b/cookbooks/unbound/recipes/default.rb new file mode 100644 index 0000000..28e20ef --- /dev/null +++ b/cookbooks/unbound/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Cookbook:: unbound +# Recipe:: default +# +# Copyright:: 2011, Joshua Timberman +# +# 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. +# + +log 'v3_warning' do + message 'Version 3.0.0 of this cookbook removed all configuration actions from the default recipe' + level :warn +end + +unbound_package 'unbound' diff --git a/cookbooks/unbound/renovate.json b/cookbooks/unbound/renovate.json new file mode 100644 index 0000000..39a2b6e --- /dev/null +++ b/cookbooks/unbound/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/cookbooks/unbound/resources/config_authority_zone.rb b/cookbooks/unbound/resources/config_authority_zone.rb new file mode 100644 index 0000000..6ea9626 --- /dev/null +++ b/cookbooks/unbound/resources/config_authority_zone.rb @@ -0,0 +1,93 @@ +# +# Cookbook:: unbound +# Resource:: config_authority_zone +# +# 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. +# + +unified_mode true + +provides :unbound_config_auth_zone + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/authority-zone-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :zone_name, String, + default: lazy { name } + +property :primary, [String, Array], + coerce: proc { |p| Array(p) } + +property :master, [String, Array], + coerce: proc { |p| Array(p) } + +property :url, [String, Array], + coerce: proc { |p| Array(p) } + +property :allow_notify, [String, Array], + coerce: proc { |p| Array(p) } + +property :fallback_enabled, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :for_downstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :for_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :zonemd_check, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :zonemd_reject_absence, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :zonefile, String + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'name' => new_resource.zone_name, + 'primary' => new_resource.primary.dup, + 'master' => new_resource.master.dup, + 'url' => new_resource.url.dup, + 'allow-notify' => new_resource.allow_notify.dup, + 'fallback-enabled' => new_resource.fallback_enabled, + 'for-downstream' => new_resource.for_downstream, + 'for-upstream' => new_resource.for_upstream, + 'zonemd-check' => new_resource.zonemd_check, + 'zonemd-reject-absence' => new_resource.zonemd_reject_absence, + 'zonefile' => new_resource.zonefile, + }.compact + + config = { + 'auth-zone' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_cachedb.rb b/cookbooks/unbound/resources/config_cachedb.rb new file mode 100644 index 0000000..1c415b7 --- /dev/null +++ b/cookbooks/unbound/resources/config_cachedb.rb @@ -0,0 +1,67 @@ +# +# Cookbook:: unbound +# Resource:: config_cachedb +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/cachedb.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :backend, String + +property :secret_seed, String + +property :redis_server_host, String + +property :redis_server_port, Integer + +property :redis_timeout, Integer + +property :redis_expire_records, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + cachedb_config = { + 'backend' => new_resource.backend, + 'secret-seed' => new_resource.secret_seed, + 'redis-server-host' => new_resource.redis_server_host, + 'redis-server-port' => new_resource.redis_server_port, + 'redis-timeout' => new_resource.redis_timeout, + 'redis-expire-records' => new_resource.redis_expire_records, + }.compact + + config = { + 'cachedb' => cachedb_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_dns64.rb b/cookbooks/unbound/resources/config_dns64.rb new file mode 100644 index 0000000..4c4a9e5 --- /dev/null +++ b/cookbooks/unbound/resources/config_dns64.rb @@ -0,0 +1,58 @@ +# +# Cookbook:: unbound +# Resource:: config_dns64 +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/dns64.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :dns64_prefix, String + +property :dns64_synthall, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dns64_ignore_aaaa, String + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + dns64_config = { + 'dns64-prefix' => new_resource.dns64_prefix, + 'dns64-synthall' => new_resource.dns64_synthall, + 'dns64-ignore-aaaa' => new_resource.dns64_ignore_aaaa, + }.compact + + config = { + 'server' => dns64_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_dnscrypt.rb b/cookbooks/unbound/resources/config_dnscrypt.rb new file mode 100644 index 0000000..f6ebff8 --- /dev/null +++ b/cookbooks/unbound/resources/config_dnscrypt.rb @@ -0,0 +1,80 @@ +# +# Cookbook:: unbound +# Resource:: config_dnscrypt +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/dnscrypt.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :dnscrypt_enable, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnscrypt_port, Integer + +property :dnscrypt_provider, [String, Array], + coerce: proc { |p| Array(p) } + +property :dnscrypt_secret_key, String + +property :dnscrypt_provider_cert, String + +property :dnscrypt_provider_cert_rotated, String + +property :dnscrypt_shared_secret_cache_size, String + +property :dnscrypt_shared_secret_cache_slabs, Integer + +property :dnscrypt_nonce_cache_size, String + +property :dnscrypt_nonce_cache_slabs, Integer + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + dnscrypt_config = { + 'dnscrypt-enable' => new_resource.dnscrypt_enable, + 'dnscrypt-port' => new_resource.dnscrypt_port, + 'dnscrypt-provider' => new_resource.dnscrypt_provider.dup, + 'dnscrypt-secret-key' => new_resource.dnscrypt_secret_key, + 'dnscrypt-provider-cert' => new_resource.dnscrypt_provider_cert, + 'dnscrypt-provider-cert-rotated' => new_resource.dnscrypt_provider_cert_rotated, + 'dnscrypt-shared-secret-cache-size' => new_resource.dnscrypt_shared_secret_cache_size, + 'dnscrypt-shared-secret-cache-slabs' => new_resource.dnscrypt_shared_secret_cache_slabs, + 'dnscrypt-nonce-cache-size' => new_resource.dnscrypt_nonce_cache_size, + 'dnscrypt-nonce-cache-slabs' => new_resource.dnscrypt_nonce_cache_slabs, + }.compact + + config = { + 'dnscrypt' => dnscrypt_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_dnstap.rb b/cookbooks/unbound/resources/config_dnstap.rb new file mode 100644 index 0000000..60c2d0f --- /dev/null +++ b/cookbooks/unbound/resources/config_dnstap.rb @@ -0,0 +1,116 @@ +# +# Cookbook:: unbound +# Resource:: config_dnstap +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/dnstap.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :dnstap_enable, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_bidirectional, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_socket_path, String + +property :dnstap_ip, String + +property :dnstap_tls, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_tls_server_name, String + +property :dnstap_tls_cert_bundle, String + +property :dnstap_tls_client_key_file, String + +property :dnstap_tls_client_cert_file, String + +property :dnstap_send_identity, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_send_version, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_identity, String + +property :dnstap_version, String + +property :dnstap_log_resolver_query_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_log_resolver_response_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_log_client_query_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_log_client_response_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_log_forwarder_query_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :dnstap_log_forwarder_response_messages, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'dnstap-enable' => new_resource.dnstap_enable, + 'dnstap-bidirectional' => new_resource.dnstap_bidirectional, + 'dnstap-socket-path' => new_resource.dnstap_socket_path, + 'dnstap-ip' => new_resource.dnstap_ip, + 'dnstap-tls' => new_resource.dnstap_tls, + 'dnstap-tls-server-name' => new_resource.dnstap_tls_server_name, + 'dnstap-tls-cert-bundle' => new_resource.dnstap_tls_cert_bundle, + 'dnstap-tls-client-key-file' => new_resource.dnstap_tls_client_key_file, + 'dnstap-tls-client-cert-file' => new_resource.dnstap_tls_client_cert_file, + 'dnstap-send-identity' => new_resource.dnstap_send_identity, + 'dnstap-send-version' => new_resource.dnstap_send_version, + 'dnstap-identity' => new_resource.dnstap_identity, + 'dnstap-version' => new_resource.dnstap_version, + 'dnstap-log-resolver-query-messages' => new_resource.dnstap_log_resolver_query_messages, + 'dnstap-log-resolver-response-messages' => new_resource.dnstap_log_resolver_response_messages, + 'dnstap-log-client-query-messages' => new_resource.dnstap_log_client_query_messages, + 'dnstap-log-client-response-messages' => new_resource.dnstap_log_client_response_messages, + 'dnstap-log-forwarder-query-messages' => new_resource.dnstap_log_forwarder_query_messages, + 'dnstap-log-forwarder-response-messages' => new_resource.dnstap_log_forwarder_response_messages, + }.compact + + config = { + 'dnstap' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_dynamic_library.rb b/cookbooks/unbound/resources/config_dynamic_library.rb new file mode 100644 index 0000000..8500454 --- /dev/null +++ b/cookbooks/unbound/resources/config_dynamic_library.rb @@ -0,0 +1,48 @@ +# +# Cookbook:: unbound +# Resource:: config_dynamic_library +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/dyn-lib-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :dynlib_file, [String, Array], + coerce: proc { |p| Array(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + config = { + 'dynlib-file' => new_resource.dynlib_file.dup, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_forward_zone.rb b/cookbooks/unbound/resources/config_forward_zone.rb new file mode 100644 index 0000000..05d9eb7 --- /dev/null +++ b/cookbooks/unbound/resources/config_forward_zone.rb @@ -0,0 +1,80 @@ +# +# Cookbook:: unbound +# Resource:: config_forward_zone +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/forward-zone-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :zone_name, String, + default: lazy { name } + +property :forward_host, [String, Array], + coerce: proc { |p| Array(p) } + +property :forward_addr, [String, Array], + coerce: proc { |p| Array(p) } + +property :forward_first, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :forward_tls_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :forward_ssl_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :forward_tcp_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :forward_no_cache, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'name' => new_resource.zone_name, + 'forward-host' => new_resource.forward_host.dup, + 'forward-addr' => new_resource.forward_addr.dup, + 'forward-first' => new_resource.forward_first, + 'forward-tls-upstream' => new_resource.forward_tls_upstream, + 'forward-ssl-upstream' => new_resource.forward_ssl_upstream, + 'forward-tcp-upstream' => new_resource.forward_tcp_upstream, + 'forward-no-cache' => new_resource.forward_no_cache, + }.compact + + config = { + 'forward-zone' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_python_script.rb b/cookbooks/unbound/resources/config_python_script.rb new file mode 100644 index 0000000..d9a5923 --- /dev/null +++ b/cookbooks/unbound/resources/config_python_script.rb @@ -0,0 +1,53 @@ +# +# Cookbook:: unbound +# Resource:: config_python_script +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/python-script-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :python_script, [String, Array], + coerce: proc { |p| Array(p) }, + required: true + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + declare_resource(:package, 'python3-unbound') + + config = { + 'python' => { + 'python-script' => new_resource.python_script.dup, + }, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_remote_control.rb b/cookbooks/unbound/resources/config_remote_control.rb new file mode 100644 index 0000000..6042ef6 --- /dev/null +++ b/cookbooks/unbound/resources/config_remote_control.rb @@ -0,0 +1,77 @@ +# +# Cookbook:: unbound +# Resource:: config_remote_control +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/remote-control.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :control_enable, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :control_interface, [String, Array], + coerce: proc { |p| Array(p) } + +property :control_port, Integer + +property :control_use_cert, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :control_key_file, String + +property :control_cert_file, String + +property :server, String + +property :server_key_file, String + +property :server_cert_file, String + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + remote_control = { + 'control-enable' => new_resource.control_enable, + 'control-interface' => new_resource.control_interface.dup, + 'control-port' => new_resource.control_port, + 'control-use-cert' => new_resource.control_use_cert, + 'control-key-file' => new_resource.control_key_file, + 'control-cert-file' => new_resource.control_cert_file, + 'server-key-file' => new_resource.server_key_file, + 'server-cert-file' => new_resource.server_cert_file, + }.compact + + config = { + 'remote-control' => remote_control, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_rpz_zone.rb b/cookbooks/unbound/resources/config_rpz_zone.rb new file mode 100644 index 0000000..2e8292a --- /dev/null +++ b/cookbooks/unbound/resources/config_rpz_zone.rb @@ -0,0 +1,98 @@ +# +# Cookbook:: unbound +# Resource:: config_rpz_zone +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/rpz-zone-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :zone_name, String, + default: lazy { name } + +property :primary, [String, Array], + coerce: proc { |p| Array(p) } + +property :master, [String, Array], + coerce: proc { |p| Array(p) } + +property :url, [String, Array], + coerce: proc { |p| Array(p) } + +property :allow_notify, [String, Array], + coerce: proc { |p| Array(p) } + +property :zonefile, String + +property :rpz_action_override, [String, Symbol], + equal_to: %w(nxdomain nodata passthru drop disabled cname), + coerce: proc { |p| p.to_s } + +property :rpz_cname_override, String + +property :rpz_log, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :rpz_log_name, String + +property :rpz_signal_nxdomain_ra, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :for_downstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :tags, [String, Array], + coerce: proc { |p| "\"#{p.to_a.join(' ')} \"" } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'name' => new_resource.zone_name, + 'primary' => new_resource.primary.dup, + 'master' => new_resource.master.dup, + 'url' => new_resource.url.dup, + 'allow-notify' => new_resource.allow_notify.dup, + 'zonefile' => new_resource.zonefile, + 'rpz-action-override' => new_resource.rpz_action_override, + 'rpz-cname-override' => new_resource.rpz_cname_override, + 'rpz-log' => new_resource.rpz_log, + 'rpz-log-name' => new_resource.rpz_log_name, + 'rpz-signal-nxfomain-ra' => new_resource.rpz_signal_nxdomain_ra, + 'for-downstream' => new_resource.for_downstream, + 'tags' => new_resource.tags.dup, + }.compact + + config = { + 'rpz' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_server.rb b/cookbooks/unbound/resources/config_server.rb new file mode 100644 index 0000000..0e58d7e --- /dev/null +++ b/cookbooks/unbound/resources/config_server.rb @@ -0,0 +1,58 @@ +# +# Cookbook:: unbound +# Resource:: config_server +# +# 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. +# + +unified_mode true + +provides :unbound_config_server +provides :unbound_configure +provides :unbound_config + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/unbound.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :include, [String, Array], + default: lazy { default_includes_dir }, + coerce: proc { |p| Array(p) } + +property :server, Hash, + default: {}, + description: 'Server configuration as a Hash' + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + config = { + 'include' => new_resource.include.dup, + 'server' => new_resource.server.dup, + }.compact + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_stub_zone.rb b/cookbooks/unbound/resources/config_stub_zone.rb new file mode 100644 index 0000000..2a74fa5 --- /dev/null +++ b/cookbooks/unbound/resources/config_stub_zone.rb @@ -0,0 +1,84 @@ +# +# Cookbook:: unbound +# Resource:: config_stub_zone +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/stub-zone-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :zone_name, String, + default: lazy { name } + +property :stub_host, [String, Array], + coerce: proc { |p| Array(p) } + +property :stub_addr, [String, Array], + coerce: proc { |p| Array(p) } + +property :stub_prime, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :stub_first, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :stub_tls_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :stub_ssl_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :stub_tcp_upstream, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +property :stub_no_cache, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'name' => new_resource.zone_name, + 'stub-host' => new_resource.stub_host.dup, + 'stub-addr' => new_resource.stub_addr.dup, + 'stub-prime' => new_resource.stub_prime, + 'stub-first' => new_resource.stub_first, + 'stub-tls-upstream' => new_resource.stub_tls_upstream, + 'stub-ssl-upstream' => new_resource.stub_ssl_upstream, + 'stub-tcp-upstream' => new_resource.stub_tcp_upstream, + 'stub-no-cache' => new_resource.stub_no_cache, + }.compact + + config = { + 'stub-zone' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/config_view.rb b/cookbooks/unbound/resources/config_view.rb new file mode 100644 index 0000000..67f602b --- /dev/null +++ b/cookbooks/unbound/resources/config_view.rb @@ -0,0 +1,68 @@ +# +# Cookbook:: unbound +# Resource:: config_view +# +# 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. +# + +unified_mode true + +use 'partials/_config_file' + +property :config_file, String, + default: lazy { "#{config_dir}/view-#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :zone_name, String, + default: lazy { name } + +property :local_zone, [String, Array], + coerce: proc { |p| Array(p) } + +property :local_data, [String, Array], + coerce: proc { |p| Array(p) } + +property :local_data_ptr, [String, Array], + coerce: proc { |p| Array(p) } + +property :view_first, [String, true, false], + coerce: proc { |p| unbound_yes_no?(p) } + +load_current_value do |new_resource| + current_value_does_not_exist! unless ::File.exist?(new_resource.config_file) + + if ::File.exist?(new_resource.config_file) + owner ::Etc.getpwuid(::File.stat(new_resource.config_file).uid).name + group ::Etc.getgrgid(::File.stat(new_resource.config_file).gid).name + mode ::File.stat(new_resource.config_file).mode.to_s(8)[-4..-1] + end +end + +action_class do + def do_template_action + zone_config = { + 'name' => new_resource.zone_name, + 'local-zone' => new_resource.local_zone.dup, + 'local-data' => new_resource.local_data.dup, + 'local-data-ptr' => new_resource.local_data_ptr.dup, + 'view-first' => new_resource.view_first, + }.compact + + config = { + 'view' => zone_config, + } + + perform_config_action(config) + end +end diff --git a/cookbooks/unbound/resources/package.rb b/cookbooks/unbound/resources/package.rb new file mode 100644 index 0000000..faff561 --- /dev/null +++ b/cookbooks/unbound/resources/package.rb @@ -0,0 +1,36 @@ +# +# Cookbook:: unbound +# Resource:: package +# +# 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. +# + +unified_mode true + +provides :unbound_install + +property :packages, [String, Array], + coerce: proc { |p| p.is_a?(Array) ? p : [ p ] }, + default: %w(unbound), + description: 'Unbound packages to install.' + +action_class do + def do_package_action(action) + package 'unbound' do + package_name new_resource.packages + action action + end + end +end + +%i(install upgrade remove).each { |pkg_action| action(pkg_action) { do_package_action(action) } } diff --git a/cookbooks/unbound/resources/partials/_config_file.rb b/cookbooks/unbound/resources/partials/_config_file.rb new file mode 100644 index 0000000..eae83b2 --- /dev/null +++ b/cookbooks/unbound/resources/partials/_config_file.rb @@ -0,0 +1,122 @@ +# +# Cookbook:: unbound +# Resource:: _config_file +# +# 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. +# + +unified_mode true + +include Unbound::Cookbook::Helpers + +property :owner, String, + default: 'root', + description: 'Set to override config file owner. Defaults to root.' + +property :group, String, + default: 'unbound', + description: 'Set to override config file group. Defaults to unbound.' + +property :mode, String, + default: '0640', + description: 'Set to override config file mode. Defaults to 0640.' + +property :directory_mode, String, + default: '0750', + description: 'Set to override config directory mode. Defaults to 0750.' + +property :config_dir, String, + default: lazy { default_config_dir }, + desired_state: false, + description: 'Set to override unbound configuration directory.' + +property :config_file, String, + default: lazy { "#{config_dir}/#{name}.conf" }, + desired_state: false, + description: 'Set to override unbound configuration file.' + +property :cookbook, String, + default: 'unbound', + desired_state: false, + description: 'Template source cookbook for the unbound configuration file.' + +property :template, String, + default: 'unbound.conf.erb', + desired_state: false, + description: 'Template source file for the unbound configuration file.' + +property :sensitive, [true, false], + desired_state: false, + description: 'Ensure that sensitive resource data is not output by Chef Infra Client.' + +property :sort, [true, false], + default: true + +property :template_properties, Hash, + default: {} + +property :extra_options, Hash, + default: {} + +action_class do + def deepsort? + return if defined?(DeepSort) + + begin + Gem::Specification.find_by_name('deepsort') + rescue Gem::MissingSpecError + declare_resource(:chef_gem, 'deepsort') + end + + require 'deepsort' + + true + end + + def perform_config_action(config) + directory new_resource.config_dir do + owner new_resource.owner + group new_resource.group + mode new_resource.directory_mode + + recursive true + + action new_resource.action.eql?(:delete) ? :delete : :create + end + + config.merge!(new_resource.extra_options.dup) unless new_resource.extra_options.empty? + + if new_resource.sort + deepsort? + config.deep_sort! + end + + template new_resource.config_file do + cookbook new_resource.cookbook + source new_resource.template + + owner new_resource.owner + group new_resource.group + mode new_resource.mode + sensitive new_resource.sensitive + + helpers(Unbound::Cookbook::TemplateHelpers) + + variables(content: config) + + action new_resource.action + end + end +end + +%i(create create_if_missing delete).each { |action_type| action(action_type) { do_template_action } } diff --git a/cookbooks/unbound/resources/service.rb b/cookbooks/unbound/resources/service.rb new file mode 100644 index 0000000..3797275 --- /dev/null +++ b/cookbooks/unbound/resources/service.rb @@ -0,0 +1,69 @@ +# +# Cookbook:: unbound +# Resource:: service +# +# 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. +# + +unified_mode true + +property :service_name, String, + default: 'unbound', + description: 'The service name to perform actions upon' + +property :config_test, [true, false], + default: true, + description: 'Perform configuration file test before performing service action' + +property :config_test_fail_action, Symbol, + equal_to: %i(raise log), + default: :raise, + description: 'Action to perform upon configuration test failure.' + +action_class do + def perform_config_test + cmd = shell_out('/usr/sbin/unbound-checkconf') + cmd.error! + rescue Mixlib::ShellOut::ShellCommandFailed + if new_resource.config_test_fail_action.eql?(:log) + Chef::Log.error("Configuration test failed, #{new_resource.service_name} #{action} action aborted!\n\n"\ + "Error\n-----\n#{cmd.stderr}") + else + raise "Configuration test failed, #{new_resource.service_name} #{action} action aborted!\n\n"\ + "Error\n-----\nAction: #{action}\n#{cmd.stderr}" + end + end + + def do_service_action(service_action) + with_run_context(:root) do + if %i(start restart reload).include?(service_action) + if new_resource.config_test + perform_config_test + Chef::Log.info("Configuration test passed, creating #{new_resource.service_name} #{new_resource.declared_type} resource with action #{service_action}") + else + Chef::Log.info("Configuration test disabled, creating #{new_resource.service_name} #{new_resource.declared_type} resource with action #{service_action}") + end + + declare_resource(:service, new_resource.service_name) { delayed_action(service_action) } + else + declare_resource(:service, new_resource.service_name) { action(service_action) } + end + end + end +end + +%i(start stop restart reload enable disable).each { |action_type| action(action_type) { do_service_action(action_type) } } + +action :test do + converge_by('Performing configuration test') { perform_config_test } +end diff --git a/cookbooks/unbound/templates/default/partials/_generic_config.erb b/cookbooks/unbound/templates/default/partials/_generic_config.erb new file mode 100644 index 0000000..9cb367f --- /dev/null +++ b/cookbooks/unbound/templates/default/partials/_generic_config.erb @@ -0,0 +1,22 @@ +<% unless @content.nil? -%> +<% @content.each do |key, value| %> +<% case value %> +<% when nil %> +<%= key %> +<% when String, Numeric %> +<%= key %><% if @separator %><%= @separator %><% end %> <%= value %> +<% when Array %> +<% value.each do |val| %> +<% if val.is_a?(Hash) %> +<%= key %><% if @separator %><%= @separator %><% end %> +<%= template_partial_indent(render('partials/_generic_config.erb', cookbook: 'unbound', variables: { content: val, separator: ':' }), 1, 2) %> +<% else %> +<%= key %><% if @separator %><%= @separator %><% end %> <%= val %> +<% end %> +<% end %> +<% when Hash %> +<%= key %><% if @separator %><%= @separator %><% end %> +<%= template_partial_indent(render('partials/_generic_config.erb', cookbook: 'unbound', variables: { content: value, separator: ':' }), 1, 2) %> +<% end %> +<% end %> +<% end %> diff --git a/cookbooks/unbound/templates/default/unbound.conf.erb b/cookbooks/unbound/templates/default/unbound.conf.erb new file mode 100644 index 0000000..763be29 --- /dev/null +++ b/cookbooks/unbound/templates/default/unbound.conf.erb @@ -0,0 +1,5 @@ +# +# Generated by Chef Infra for <%= node['fqdn'] %> +# Do NOT modify this file by hand, any changes will be overwritten. + +<%= render('partials/_generic_config.erb', cookbook: 'unbound', variables: { content: @content, separator: ':' }) %> diff --git a/nodes/mail.kosmos.org.json b/nodes/mail.kosmos.org.json index 0e7f7b0..2e384ef 100644 --- a/nodes/mail.kosmos.org.json +++ b/nodes/mail.kosmos.org.json @@ -36,7 +36,9 @@ "kosmos-postfix::default", "hostname::default", "kosmos-base::letsencrypt", + "unbound::default", "kosmos_email::opendkim", + "kosmos_email::spamassassin", "kosmos_email::postfix", "postfix::server", "postfix::default", diff --git a/site-cookbooks/kosmos_email/attributes/default.rb b/site-cookbooks/kosmos_email/attributes/default.rb index dd60bc8..0276203 100644 --- a/site-cookbooks/kosmos_email/attributes/default.rb +++ b/site-cookbooks/kosmos_email/attributes/default.rb @@ -1,4 +1,3 @@ node.default["email"]["domain"] = "example.com" node.default["email"]["hostname"] = "mail.example.com" -# node.default["email"]["user"] = "ray" -# node.default["email"]["group"] = "email" +node.default["email"]["report_contact"] = "abuse@example.com" diff --git a/site-cookbooks/kosmos_email/metadata.rb b/site-cookbooks/kosmos_email/metadata.rb index cc689c3..71cf8ef 100644 --- a/site-cookbooks/kosmos_email/metadata.rb +++ b/site-cookbooks/kosmos_email/metadata.rb @@ -7,4 +7,5 @@ version '0.1.0' chef_version '>= 18.0' depends "hostname" +depends "unbound" depends "postfix" diff --git a/site-cookbooks/kosmos_email/recipes/default.rb b/site-cookbooks/kosmos_email/recipes/default.rb index d775dfa..96eced0 100644 --- a/site-cookbooks/kosmos_email/recipes/default.rb +++ b/site-cookbooks/kosmos_email/recipes/default.rb @@ -21,6 +21,8 @@ firewall_rule "private network access" do source "10.1.1.0/24" end +include_recipe 'unbound' include_recipe 'kosmos_email::opendkim' +include_recipe 'kosmos_email::spamassassin' include_recipe 'kosmos_email::postfix' include_recipe 'kosmos_email::dovecot' diff --git a/site-cookbooks/kosmos_email/recipes/postfix.rb b/site-cookbooks/kosmos_email/recipes/postfix.rb index 1e9475c..69b7b57 100644 --- a/site-cookbooks/kosmos_email/recipes/postfix.rb +++ b/site-cookbooks/kosmos_email/recipes/postfix.rb @@ -36,9 +36,10 @@ node.normal['postfix']['main']['virtual_transport'] = "lmtp:unix:private/dovecot node.normal['postfix']['main']['smtputf8_enable'] = "no" node.normal['postfix']['main']['recipient_delimiter'] = "+" node.normal['postfix']['main']['alias_maps'] = "hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf" -node.normal['postfix']['main']['milter_protocol'] = "2" +node.normal['postfix']['main']['smtpd_sender_login_maps'] = "ldap:/etc/postfix/ldap-username-aliases.cf" +node.normal['postfix']['main']['milter_protocol'] = "6" node.normal['postfix']['main']['milter_default_action'] = "accept" -node.normal['postfix']['main']['smtpd_milters'] = "inet:localhost:12301" +node.normal['postfix']['main']['smtpd_milters'] = "inet:localhost:12301 local:spamass/spamass.sock" node.normal['postfix']['main']['non_smtpd_milters'] = "inet:localhost:12301" node.normal['postfix']['master'] = { diff --git a/site-cookbooks/kosmos_email/recipes/spamassassin.rb b/site-cookbooks/kosmos_email/recipes/spamassassin.rb new file mode 100644 index 0000000..3971c62 --- /dev/null +++ b/site-cookbooks/kosmos_email/recipes/spamassassin.rb @@ -0,0 +1,34 @@ +# +# Cookbook:: kosmos_email +# Recipe:: spamassassin +# + +%w[ + spamassassin + spamc + spamass-milter +].each do |pkg| + apt_package pkg +end + +domain = node["email"]["domain"] +report_contact = node["email"]["report_contact"] + +template "/etc/default/spamassassin" do + source "spamassassin_default.erb" + mode 0644 + variables options: "-u debian-spamd --nouser-config --max-children 10" + notifies :restart, "service[spamassassin]", :delayed +end + +template "/etc/spamassassin/local.cf" do + source "spamassassin_local.cf.erb" + mode 0644 + variables whitelist_auth: "*@#{domain}", + report_contact: report_contact + notifies :restart, "service[spamassassin]", :delayed +end + +service "spamassassin" do + action [:enable, :start] +end diff --git a/site-cookbooks/kosmos_email/templates/spamass-milter.erb b/site-cookbooks/kosmos_email/templates/spamass-milter.erb new file mode 100644 index 0000000..8922fb5 --- /dev/null +++ b/site-cookbooks/kosmos_email/templates/spamass-milter.erb @@ -0,0 +1,28 @@ +# spamass-milt startup defaults + +# OPTIONS are passed directly to spamass-milter. +# man spamass-milter for details + +# Non-standard configuration notes: +# See README.Debian if you use the -x option with sendmail +# You should not pass the -d option in OPTIONS; use SOCKET for that. + +# Default, use the spamass-milter user as the default user, ignore +# messages from localhost +OPTIONS="-u spamass-milter -i 127.0.0.1" + +# Reject emails with spamassassin scores > 15. +#OPTIONS="${OPTIONS} -r 15" + +# Do not modify Subject:, Content-Type: or body. +#OPTIONS="${OPTIONS} -m" + +###################################### +# If /usr/sbin/postfix is executable, the following are set by +# default. You can override them by uncommenting and changing them +# here. +###################################### +# SOCKET="/var/spool/postfix/spamass/spamass.sock" +# SOCKETOWNER="postfix:postfix" +# SOCKETMODE="0660" +###################################### diff --git a/site-cookbooks/kosmos_email/templates/spamassassin_default.erb b/site-cookbooks/kosmos_email/templates/spamassassin_default.erb new file mode 100644 index 0000000..624688a --- /dev/null +++ b/site-cookbooks/kosmos_email/templates/spamassassin_default.erb @@ -0,0 +1,33 @@ +# /etc/default/spamassassin +# Duncan Findlay + +# WARNING: please read README.spamd before using. +# There may be security risks. + +# Prior to version 3.4.2-1, spamd could be enabled by setting +# ENABLED=1 in this file. This is no longer supported. Instead, please +# use the update-rc.d command, invoked for example as "update-rc.d +# spamassassin enable", to enable the spamd service. + +# Options +# See man spamd for possible options. The -d option is automatically added. + +# SpamAssassin uses a preforking model, so be careful! You need to +# make sure --max-children is not set to anything higher than 5, +# unless you know what you're doing. + +OPTIONS="<%= @options %>" + +# Pid file +# Where should spamd write its PID to file? If you use the -u or +# --username option above, this needs to be writable by that user. +# Otherwise, the init script will not be able to shut spamd down. +PIDFILE="/var/run/spamd.pid" + +# Set nice level of spamd +#NICE="--nicelevel 15" + +# Cronjob +# Set to anything but 0 to enable the cron job to automatically update +# spamassassin's rules on a nightly basis +CRON=0 diff --git a/site-cookbooks/kosmos_email/templates/spamassassin_local.cf.erb b/site-cookbooks/kosmos_email/templates/spamassassin_local.cf.erb new file mode 100644 index 0000000..c196054 --- /dev/null +++ b/site-cookbooks/kosmos_email/templates/spamassassin_local.cf.erb @@ -0,0 +1,119 @@ +# This is the right place to customize your installation of SpamAssassin. +# +# See 'perldoc Mail::SpamAssassin::Conf' for details of what can be +# tweaked. +# +# Only a small subset of options are listed below +# +########################################################################### + +dns_available yes +dns_server 127.0.0.1 + +whitelist_auth <%= @whitelist_auth %> + +# A 'contact address' users should contact for more info. (replaces +# _CONTACTADDRESS_ in the report template) +report_contact <%= @report_contact %> + + +# Add *****SPAM***** to the Subject header of spam e-mails +# +# rewrite_header Subject *****SPAM***** + + +# Save spam messages as a message/rfc822 MIME attachment instead of +# modifying the original message (0: off, 2: use text/plain instead) +# +# report_safe 1 + + +# Set which networks or hosts are considered 'trusted' by your mail +# server (i.e. not spammers) +# +# trusted_networks 212.17.35. + + +# Set file-locking method (flock is not safe over NFS, but is faster) +# +# lock_method flock + + +# Set the threshold at which a message is considered spam (default: 5.0) +# +# required_score 5.0 + + +# Use Bayesian classifier (default: 1) +# +# use_bayes 1 + + +# Bayesian classifier auto-learning (default: 1) +# +# bayes_auto_learn 1 + + +# Set headers which may provide inappropriate cues to the Bayesian +# classifier +# +# bayes_ignore_header X-Bogosity +# bayes_ignore_header X-Spam-Flag +# bayes_ignore_header X-Spam-Status + + +# Whether to decode non- UTF-8 and non-ASCII textual parts and recode +# them to UTF-8 before the text is given over to rules processing. +# +# normalize_charset 1 + +# Textual body scan limit (default: 50000) +# +# Amount of data per email text/* mimepart, that will be run through body +# rules. This enables safer and faster scanning of large messages, +# perhaps having very large textual attachments. There should be no need +# to change this well tested default. +# +# body_part_scan_size 50000 + +# Textual rawbody data scan limit (default: 500000) +# +# Amount of data per email text/* mimepart, that will be run through +# rawbody rules. +# +# rawbody_part_scan_size 500000 + +# Some shortcircuiting, if the plugin is enabled +# +ifplugin Mail::SpamAssassin::Plugin::Shortcircuit +# +# default: strongly-whitelisted mails are *really* whitelisted now, if the +# shortcircuiting plugin is active, causing early exit to save CPU load. +# Uncomment to turn this on +# +# SpamAssassin tries hard not to launch DNS queries before priority -100. +# If you want to shortcircuit without launching unneeded queries, make +# sure such rule priority is below -100. These examples are already: +# +# shortcircuit USER_IN_WHITELIST on +# shortcircuit USER_IN_DEF_WHITELIST on +# shortcircuit USER_IN_ALL_SPAM_TO on +# shortcircuit SUBJECT_IN_WHITELIST on + +# the opposite; blacklisted mails can also save CPU +# +# shortcircuit USER_IN_BLACKLIST on +# shortcircuit USER_IN_BLACKLIST_TO on +# shortcircuit SUBJECT_IN_BLACKLIST on + +# if you have taken the time to correctly specify your "trusted_networks", +# this is another good way to save CPU +# +# shortcircuit ALL_TRUSTED on + +# and a well-trained bayes DB can save running rules, too +# +# shortcircuit BAYES_99 spam +# shortcircuit BAYES_00 ham + +endif # Mail::SpamAssassin::Plugin::Shortcircuit