Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalize pwfilesupport #25

Merged
merged 34 commits into from
Mar 9, 2017
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8e055a2
now with reverse verification
stromp Jan 12, 2016
db72942
new attrs
stromp Jan 12, 2016
58b7d61
cleaning
stromp Jan 21, 2016
2ca135c
cleaning
stromp Jan 21, 2016
8795a17
adding template
stromp Jan 22, 2016
a94728b
cleaning up
stromp Jan 22, 2016
1e4676f
beautify
stromp Jan 22, 2016
85bdc3e
updated metadata and readme
stromp Jan 22, 2016
be63926
swap user and group
stromp Jan 22, 2016
c2b010d
now with reverse verification
stromp Jan 12, 2016
c872cac
new attrs
stromp Jan 12, 2016
4b3b680
cleaning
stromp Jan 21, 2016
c8c4450
cleaning
stromp Jan 21, 2016
c350714
adding template
stromp Jan 22, 2016
8ca7da1
cleaning up
stromp Jan 22, 2016
2c2795a
beautify
stromp Jan 22, 2016
01babb1
updated metadata and readme
stromp Jan 22, 2016
f4674bc
swap user and group
stromp Jan 22, 2016
f116ec3
Merge branch 'adding_pwfilesupport' of github.com:schubergphilis/dove…
stromp Jan 27, 2016
914efcf
now with reverse verification
stromp Jan 12, 2016
3fc2d10
Merge pull request #1 from schubergphilis/adding_pwfilesupport
Feb 11, 2016
fcdbb10
Version 2.4.1001
Feb 11, 2016
74e1e86
now with reverse verification
stromp Jan 12, 2016
a4e8fb7
Merge branch 'adding_pwfilesupport' of github.com:schubergphilis/dove…
stromp Feb 29, 2016
9e8e9ba
Merge pull request #2 from schubergphilis/adding_pwfilesupport
stromp Mar 1, 2016
8370366
Merge remote-tracking branch 'upstream/master'
varet80 Feb 24, 2017
01d9e1b
New Feature: Create a Passwd file using Databags
varet80 Feb 27, 2017
5f8f4bd
Merge remote-tracking branch 'upstream/master' into finalize_pwfilesu…
varet80 Feb 28, 2017
e5bc35d
Unit testing
varet80 Mar 1, 2017
3581e56
Feature: Userdb file support (like passwd)
varet80 Mar 3, 2017
b15165b
Small fixes after Last commit
varet80 Mar 7, 2017
c53fc05
Kitchen and Travis modification:
varet80 Mar 7, 2017
469a453
Fix attribute conditional to follow Rubocop rule Style/ConditionalAss…
varet80 Mar 7, 2017
39d13f8
Align with Mantainer's requests
varet80 Mar 8, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ driver:
provisioner:
name: chef_zero
require_chef_omnibus: true
data_bags_path: ./test/data_bags
# client_rb:
# treat_deprecation_warnings_as_errors: true # WiP on some depends

Expand Down Expand Up @@ -49,7 +50,7 @@ suites:
- centos-6
- centos-6.6
- centos-7
- centos-7.0
- centos-7.2
- fedora-20
- fedora-21
- opensuse-12.3
Expand All @@ -61,3 +62,20 @@ suites:
- name: attributes
run_list:
- recipe[dovecot_test::attributes]
- name: create_pwfile
run_list:
- recipe[dovecot_test::create_pwfile]
excludes:
# tests are not working on older versions of dovecot
- centos-6.7
- debian-8.2
- ubuntu-12.04
- ubuntu-15.10
- ubuntu-16.04
- fedora-20
- fedora-21
- opensuse-12.3
- opensuse-13.2
- oraclelinux-6
- scientific-6.6

2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ AllCops:
- vendor/**/*
Metrics/ModuleLength:
Max: 121
Metrics/LineLength:
Max: 121
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ env:
- TESTS="integration[attributes-ubuntu-1510,verify]"
- TESTS="integration[attributes-ubuntu-1604,verify]"
- TESTS="integration[attributes-scientific-66,verify]"
- TESTS="integration[create-pwfile-centos-72,verify]"
- TESTS="integration[create-pwfile-debian-79,verify]"
- TESTS="integration[create-pwfile-ubuntu-14.04,verify]"

before_install:
- chef --version &> /dev/null || curl -L https://www.getchef.com/chef/install.sh | sudo bash -s -- -P chefdk -v 1.2.22
Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Table of Contents
* [dovecot::ohai_plugin](#dovecotohai_plugin)
* [dovecot::from_package](#dovecotfrom_package)
* [dovecot::service](#dovecotservice)
* [dovecot::create_pwfile](#dovecotcreate_pwfile)
* [Ohai Plugin](#ohai-plugin)
* [Usage Examples](#usage-examples)
* [Including in a Cookbook Recipe](#including-in-a-cookbook-recipe)
Expand All @@ -60,6 +61,7 @@ Table of Contents
* [Quota-status Service Example](#quota-status-service-example)
* [Quota-warning Service Example](#quota-warning-service-example)
* [LDAP Example](#ldap-example)
* [Password File Example](#password-file-example)
* [A Complete Example](#a-complete-example)
* [Testing](#testing)
* [Contributing](#contributing)
Expand Down Expand Up @@ -126,6 +128,9 @@ To see a more complete description of the attributes, go to the [Dovecot wiki2 c
| `node['dovecot']['services']` | `{}` | Dovecot Services configuration as a hash of hashes ([see the examples below](#service-examples)). Supported services: anvil, director, imap-login, pop3-login, lmtp, imap, pop3, auth, auth-worker, dict, tcpwrap, managesieve-login managesieve, quota-status, quota-warning and doveadm.
| `node['dovecot']['conf']['mail_plugins']` | `[]` | Dovecot default enabled mail_plugins.
| `node['dovecot']['ohai_plugin']['build-options']` | `true` | Whether to enable reading build options inside ohai plugin. Can be disabled to be lighter.
| `node['dovecot']['databag_name']` | `dovecot` | The databag to use.
| `node['dovecot']['databag_users_item']` | `users` | The databag item to use for User's database (Passwords).
| `node['dovecot']['conf']['password_file']` | `#{node['dovecot']['conf_path']}/password` | The Password file location

## Main Configuration Attributes

Expand Down Expand Up @@ -465,6 +470,14 @@ Installs the required packages. Used by the default recipe if `node['dovecot']['

Configures the Dovecot service. Used by the default recipe.

## dovecot::create_pwfile

Creates and configures a password file from local mailboxes based on a data bag.


* `node['dovecot']['databag_name']`: The Databag on which items are stored.
* `node['dovecot']['databag_users_item']`: The databag item to use (under the databag set)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add databag_name attribute here?

Also, adding a example content of the data bag would make its usage easier. The same you used for the tests will be enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it better to have it:
node['dovecot']['databag_name']['users_item'] ?


Ohai Plugin
===========

Expand Down Expand Up @@ -895,6 +908,46 @@ node.default['dovecot']['conf']['ldap']['base'] = node['openldap']['basedn']
include_recipe 'dovecot'
```

## Password File Example

This is an example how to use userdb password file.

```ruby
# Define databag and item inside Databag (default.conf)
node.default['dovecot']'databag_name'] = 'dovecot'
node.default['dovecot']['databag_users_item'] = 'users'

# Attributes for userdb to function
node.default['dovecot']['auth']['passwdfile'] = {
'passdb' => {
'driver' => 'passwd-file',
'args' => node['dovecot']['conf']['password_file']
},
'userdb' => {
'driver' => 'passwd-file',
'args' => "username_format=%u #{node['dovecot']['conf']['password_file']}",
'default_fields' => 'home=/var/dovecot/vmail/%d/%n'
}
}


#include this recipe on your
include_recipe 'dovecot::pwfile-file'
```

Databag example.
Two ways of defining an user example included

```json
{
"users": {
"dilan": "password1234",
"vassilis": [
"vassilis1234",null,null,null,null,null,null
]
}
}
```
## A Complete Example

This is a complete recipe example for installing and configuring Dovecot 2 to work with PostfixAdmin MySQL tables, including IMAP service:
Expand Down
9 changes: 4 additions & 5 deletions attributes/conf_10_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@

# conf.d/10-ssl.conf

if node['platform_family'] == 'suse' && node['platform_version'].to_i < 13
default['dovecot']['conf']['ssl'] = false
else
default['dovecot']['conf']['ssl'] = nil
end
default['dovecot']['conf']['ssl'] = \
if node['platform_family'] == 'suse' && node['platform_version'].to_i < 13
false
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


case node['platform_family']
when 'rhel', 'fedora'
Expand Down
2 changes: 2 additions & 0 deletions attributes/conf_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
default['dovecot']['conf_files_user'] = 'root'
default['dovecot']['conf_files_group'] = node['dovecot']['group']
default['dovecot']['conf_files_mode'] = '00644'
default['dovecot']['conf']['password_file'] = \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The \ at the en of this line is not needed.

"#{node['dovecot']['conf_path']}/password"

default['dovecot']['sensitive_files'] = %w(
*.conf.ext
Expand Down
2 changes: 2 additions & 0 deletions attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@
default['dovecot']['user'] = 'dovecot'
default['dovecot']['group'] = node['dovecot']['user']
default['dovecot']['user_homedir'] = node['dovecot']['lib_path']
default['dovecot']['databag_name'] = 'dovecot'
default['dovecot']['databag_users_item'] = 'users'
103 changes: 103 additions & 0 deletions libraries/pwfile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# encoding: UTF-8
#
# Cookbook Name:: dovecot
# Library:: Pwfile
# Author:: Xabier de Zuazo (<xabier@zuazo.org>)
# Copyright:: Copyright (c) 2013-2014 Onddo Labs, SL.
# License:: Apache License, Version 2.0
#
# 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 DovecotCookbook
# Helper module to check password file and import it
module Pwfile
extend Chef::Mixin::ShellOut

def self.exists?(localdata)
true if ::File.exist?(localdata)
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK you don't need an if there:

def self.exists?(localdata)
  ::File.exist?(localdata)
end


def self.file_to_hash(inputfile)
output_entries = {}
passwordfile = File.open(inputfile, File::RDONLY | File::CREAT, 640)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To use octal numbers, you must add a 0 before the number. So, we need to use 0640 here.

passwordfile.readlines.each do |line|
user, data = fileline_to_userdb_hash(line)
output_entries[user] = data
end
passwordfile.close
output_entries
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method can be simplified as follows:

def self.file_to_hash(inputfile)
  File.open(inputfile, File::RDONLY | File::CREAT, 0640) do |passwordfile|
    passwordfile.readlines.each_with_object({}) do |line, output_entries|
      user, data = fileline_to_userdb_hash(line)
      output_entries[user] = data
    end
  end
end


def self.passfile_read(input)
# Returns Password file in userdb style hash and if exists
[file_to_hash(input), exists?(input)]
end

def self.fileline_to_userdb_hash(input)
# Returns a hash of details daken from the userdb file line
data = [nil] * 7
if input.strip.split(':').length == 2
user, data[0] = input.strip.split(':')
else
user = input.strip.split(':')[0]
data = input.strip.split(':')[1..7]
end
[user, data]
end

def self.dbentry_to_array(key, value)
# Returns an array with 8 values to use with user copy.
if value.is_a?(Array)
[key] + (value + ([nil] * (7 - value.size)))
else
[key, value] + ([nil] * 6)
end
end

def self.password_valid?(hashed_pw, plaintext_pw)
true unless shell_out(
"/usr/bin/doveadm pw -t '#{hashed_pw}' -p '#{plaintext_pw}'"
).exitstatus != 0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same here, no need for the unless:

def self.password_valid?(hashed_pw, plaintext_pw)
  shell_out("/usr/bin/doveadm pw -t '#{hashed_pw}' -p '#{plaintext_pw}'")
    .exitstatus == 0
end

end

def self.arrays_same?(array1, array2)
true if (array1 - array2).empty? && (array2 - array1).empty?
end

def self.encrypt_password(plaintextpass)
# Return the password generated by dovecot and remove newline
shell_out("/usr/bin/doveadm pw -s MD5 -p \
#{plaintextpass}").stdout.tr("\n", '')
end

def self.generate_userpass(input_creds, plaintextpass, updated, file_exists)
if !input_creds.nil? && file_exists == true
if password_valid?(
input_creds[0], plaintextpass
)
return [input_creds[0], updated]
end
end
[encrypt_password(plaintextpass), true]
end

def self.compile_users(databag_users, current_users, pwfile_exists, updated, credentials)
databag_users.each do |username, user_details|
current_user = dbentry_to_array(username, user_details)
current_user[1], updated = generate_userpass(current_users[username], current_user[1], updated, pwfile_exists)
credentials.push(current_user)
end
updated
end
end
end
24 changes: 23 additions & 1 deletion metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
Installs and configures Dovecot, open source IMAP and POP3 email server.
EOH
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '3.2.0' # WiP
version '3.2.0' # WIP

if respond_to?(:source_url)
source_url "https://github.com/zuazo/#{name}-cookbook"
Expand Down Expand Up @@ -55,6 +55,7 @@
'Provides an Ohai plugin for reading dovecot install information.'
recipe 'dovecot::from_package', 'Installs the required packages.'
recipe 'dovecot::service', 'Configures the Dovecot service.'
recipe 'dovecot::create_pwfile', 'Creates a userdb password file from databag.'

attribute 'dovecot/install_from',
display_name: 'dovecot install method',
Expand All @@ -79,6 +80,20 @@
required: 'optional',
default: '"dovecot"'

attribute 'dovecot/databag_name',
display_name: 'Databag name',
description: 'The name of the databag to use',
type: 'string',
required: 'optional',
default: '"dovecot"'

attribute 'dovecot/databag_users_item',
display_name: 'Databag users item name',
description: 'The name item to put the users in',
type: 'string',
required: 'optional',
default: '"users"'

attribute 'dovecot/user_homedir',
display_name: 'dovecot homedir',
description: 'Dovecot system user home directory.',
Expand Down Expand Up @@ -122,6 +137,13 @@
required: 'optional',
default: '00644'

attribute 'dovecot/conf/password_file',
display_name: 'path of password file',
description: 'The path and filename of the password file',
type: 'string',
required: 'optional',
default: 'node["dovecot"]["conf_path"]/password'

attribute 'dovecot/sensitive_files',
display_name: 'dovecot sensitve files',
description:
Expand Down
59 changes: 59 additions & 0 deletions recipes/create_pwfile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#
# Cookbook Name:: dovecot
# Recipe:: create_pwfile
# Author:: Xabier de Zuazo (<xabier@zuazo.org>)
# Copyright:: Copyright (c) 2013-2014 Onddo Labs, SL.
# License:: Apache License, Version 2.0
#
# 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.
#
# The file credentials should be like:
# user:password:uid:gid:(gecos):home:(shell):extra_fields
# We ignore gecos and shell, all the others are included in the script but
# uid,gid,home,extra_Fields can be nil
# user:pass:<num>:<num>::<string>::<Extras string>

# Predefined Variables
credentials = []
update_credentials = false

ruby_block 'databag_to_dovecot_userdb' do
block do
passwd_file = node['dovecot']['conf']['password_file']
databag_users = data_bag_item(node['dovecot']['databag_name'], node['dovecot']['databag_users_item'])['users']

# Check if passwd file exists
local_creds, pwfile_exists = DovecotCookbook::Pwfile.passfile_read(passwd_file)
# Check if users on both passwd file and databag are the same
# if not, force credentials update
update_credentials = true unless DovecotCookbook::Pwfile.arrays_same?(databag_users.keys, local_creds.keys)
# Check if users has a changed password, if not change it and force update
update_credentials = DovecotCookbook::Pwfile.compile_users(databag_users,
local_creds,
pwfile_exists,
update_credentials,
credentials)
end
action :run
end

template node['dovecot']['conf']['password_file'] do
source 'password.erb'
owner node['dovecot']['user']
group node['dovecot']['group']
mode '0640'
variables(
credentials: credentials
)
only_if { update_credentials }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a sensitive true here 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is added on my next commit ;)

end
3 changes: 3 additions & 0 deletions templates/default/password.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<% @credentials.each do |user, password, uid, gid, gecos, homedir, shell, extra_fields| -%>
<%= user %>:<%= password %>:<%= uid %>:<%= gid %>::<%= homedir %>::<%= extra_fields %>
<% end -%>
Loading