Skip to content

Commit

Permalink
Merge pull request #173 from bdwyertech/extras
Browse files Browse the repository at this point in the history
Add Support for Extra Configuration Options
  • Loading branch information
atomic111 committed Apr 10, 2017
2 parents d4dc236 + 925f81e commit eaf6c11
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 49 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ override['ssh-hardening']['ssh']['server']['listen_to'] = node['ipaddress']
* `['ssh-hardening']['ssh']['client']['remote_hosts']` - `[]` - one or more hosts, to which ssh-client can connect to.
* `['ssh-hardening']['ssh']['client']['password_authentication']` - `false`. Set to `true` if password authentication should be enabled.
* `['ssh-hardening']['ssh']['client']['roaming']` - `false`. Set to `true` if experimental client roaming should be enabled. This is known to cause potential issues with secrets being disclosed to malicious servers and defaults to being disabled.
* `['ssh-hardening']['ssh']['client']['extras']` - `{}`. Add extra configuration options, see [below](#extra-configuration-options) for details
* `['ssh-hardening']['ssh']['server']['host_key_files']` - `nil` to calculate best hostkey configuration based on server version, otherwise specify an array with file paths (e.g. `/etc/ssh/ssh_host_rsa_key`)
* `['ssh-hardening']['ssh']['server']['dh_min_prime_size']` - `2048` - Minimal acceptable prime length in bits in `/etc/ssh/moduli`. Primes below this number will get removed. (See [this](https://entropux.net/article/openssh-moduli/) for more information and background)
* `['ssh-hardening']['ssh']['server']['dh_build_primes']` - `false` - If own primes should be built. This rebuild happens only once and takes a lot of time (~ 1.5 - 2h on the modern hardware for 4096 length).
Expand Down Expand Up @@ -76,6 +77,8 @@ override['ssh-hardening']['ssh']['server']['listen_to'] = node['ipaddress']
* `['ssh-hardening']['ssh']['server']['sftp']['group']` - `sftponly`. Sets the `Match Group` option of SFTP to allow SFTP only for dedicated users
* `['ssh-hardening']['ssh']['server']['sftp']['chroot']` - `/home/%u`. Sets the directory where the SFTP user should be chrooted

* `['ssh-hardening']['ssh']['server']['extras']` - `{}`. Add extra configuration options, see [below](#extra-configuration-options) for details


## Usage

Expand Down Expand Up @@ -124,6 +127,24 @@ Configure attributes:

This will enable the SFTP Server and chroot every user in the `sftpusers` group to the `/home/sftp/%u` directory.

## Extra Configuration Options
Extra configuration options can be appended to the client or server configuration files. This can be used to override statically set values, or add configuration options not otherwise available via attributes.

The syntax is as follows:
```
# => Extra Server Configuration
default['ssh-hardening']['ssh']['server']['extras'].tap do |extra|
extra['#Some Comment'] = 'Heres the Comment'
extra['AuthenticationMethods'] = 'publickey,keyboard-interactive'
end
# => Extra Client Configuration
default['ssh-hardening']['ssh']['client']['extras'].tap do |extra|
extra['PermitLocalCommand'] = 'no'
extra['Tunnel'] = 'no'
end
```

## Local Testing

For local testing you can use vagrant and Virtualbox of VMWare to run tests locally. You will have to install Virtualbox and Vagrant on your system. See [Vagrant Downloads](http://downloads.vagrantup.com/) for a vagrant package suitable for your system. For all our tests we use `test-kitchen`. If you are not familiar with `test-kitchen` please have a look at [their guide](http://kitchen.ci/docs/getting-started). We are writing our test with [InSpec](https://github.com/chef/inspec).
Expand Down
108 changes: 59 additions & 49 deletions attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,55 +52,65 @@
default['ssh-hardening']['ssh']['ports'] = [22]

# ssh client
default['ssh-hardening']['ssh']['client']['mac'] = nil # nil = calculate best combination for client
default['ssh-hardening']['ssh']['client']['kex'] = nil # nil = calculate best combination for client
default['ssh-hardening']['ssh']['client']['cipher'] = nil # nil = calculate best combination for client
default['ssh-hardening']['ssh']['client']['cbc_required'] = false
default['ssh-hardening']['ssh']['client']['weak_hmac'] = false
default['ssh-hardening']['ssh']['client']['weak_kex'] = false
default['ssh-hardening']['ssh']['client']['remote_hosts'] = []
default['ssh-hardening']['ssh']['client']['password_authentication'] = false # ssh
# http://undeadly.org/cgi?action=article&sid=20160114142733
default['ssh-hardening']['ssh']['client']['roaming'] = false
default['ssh-hardening']['ssh']['client']['send_env'] = ['LANG', 'LC_*', 'LANGUAGE']
default['ssh-hardening']['ssh']['client'].tap do |client|
client['mac'] = nil # nil = calculate best combination for client
client['kex'] = nil # nil = calculate best combination for client
client['cipher'] = nil # nil = calculate best combination for client
client['cbc_required'] = false
client['weak_hmac'] = false
client['weak_kex'] = false
client['remote_hosts'] = []
client['password_authentication'] = false # ssh
# http://undeadly.org/cgi?action=article&sid=20160114142733
client['roaming'] = false
client['send_env'] = ['LANG', 'LC_*', 'LANGUAGE']

# extra client configuration options
client['extras'] = {}
end

# sshd
default['ssh-hardening']['ssh']['server']['kex'] = nil # nil = calculate best combination for server version
default['ssh-hardening']['ssh']['server']['cipher'] = nil # nil = calculate best combination for server version
default['ssh-hardening']['ssh']['server']['mac'] = nil # nil = calculate best combination for server version
default['ssh-hardening']['ssh']['server']['cbc_required'] = false
default['ssh-hardening']['ssh']['server']['weak_hmac'] = false
default['ssh-hardening']['ssh']['server']['weak_kex'] = false
default['ssh-hardening']['ssh']['server']['dh_min_prime_size'] = 2048
default['ssh-hardening']['ssh']['server']['dh_build_primes'] = false
default['ssh-hardening']['ssh']['server']['dh_build_primes_size'] = 4096
default['ssh-hardening']['ssh']['server']['host_key_files'] = nil
default['ssh-hardening']['ssh']['server']['client_alive_interval'] = 600 # 10min
default['ssh-hardening']['ssh']['server']['client_alive_count'] = 3 # ~> 3 x interval
default['ssh-hardening']['ssh']['server']['allow_root_with_key'] = false
default['ssh-hardening']['ssh']['server']['allow_tcp_forwarding'] = false
default['ssh-hardening']['ssh']['server']['allow_agent_forwarding'] = false
default['ssh-hardening']['ssh']['server']['allow_x11_forwarding'] = false
default['ssh-hardening']['ssh']['server']['use_pam'] = true
default['ssh-hardening']['ssh']['server']['challenge_response_authentication'] = false
default['ssh-hardening']['ssh']['server']['deny_users'] = []
default['ssh-hardening']['ssh']['server']['allow_users'] = []
default['ssh-hardening']['ssh']['server']['deny_groups'] = []
default['ssh-hardening']['ssh']['server']['allow_groups'] = []
default['ssh-hardening']['ssh']['server']['print_motd'] = false
default['ssh-hardening']['ssh']['server']['print_last_log'] = false
default['ssh-hardening']['ssh']['server']['banner'] = nil # set this to nil to disable banner or provide a path like '/etc/issue.net'
default['ssh-hardening']['ssh']['server']['os_banner'] = false # (Debian OS family)
default['ssh-hardening']['ssh']['server']['use_dns'] = nil # set this to nil to let us use the default OpenSSH in case it's not set by the user
default['ssh-hardening']['ssh']['server']['use_privilege_separation'] = nil # set this to nil to let us detect the attribute based on the node platform
default['ssh-hardening']['ssh']['server']['login_grace_time'] = '30s'
default['ssh-hardening']['ssh']['server']['max_auth_tries'] = 2
default['ssh-hardening']['ssh']['server']['max_sessions'] = 10
default['ssh-hardening']['ssh']['server']['password_authentication'] = false
default['ssh-hardening']['ssh']['server']['log_level'] = 'verbose'
default['ssh-hardening']['ssh']['server']['accept_env'] = ['LANG', 'LC_*', 'LANGUAGE']
default['ssh-hardening']['ssh']['server'].tap do |server| # rubocop: disable BlockLength
server['kex'] = nil # nil = calculate best combination for server version
server['cipher'] = nil # nil = calculate best combination for server version
server['mac'] = nil # nil = calculate best combination for server version
server['cbc_required'] = false
server['weak_hmac'] = false
server['weak_kex'] = false
server['dh_min_prime_size'] = 2048
server['dh_build_primes'] = false
server['dh_build_primes_size'] = 4096
server['host_key_files'] = nil
server['client_alive_interval'] = 600 # 10min
server['client_alive_count'] = 3 # ~> 3 x interval
server['allow_root_with_key'] = false
server['allow_tcp_forwarding'] = false
server['allow_agent_forwarding'] = false
server['allow_x11_forwarding'] = false
server['use_pam'] = true
server['challenge_response_authentication'] = false
server['deny_users'] = []
server['allow_users'] = []
server['deny_groups'] = []
server['allow_groups'] = []
server['print_motd'] = false
server['print_last_log'] = false
server['banner'] = nil # set this to nil to disable banner or provide a path like '/etc/issue.net'
server['os_banner'] = false # (Debian OS family)
server['use_dns'] = nil # set this to nil to let us use the default OpenSSH in case it's not set by the user
server['use_privilege_separation'] = nil # set this to nil to let us detect the attribute based on the node platform
server['login_grace_time'] = '30s'
server['max_auth_tries'] = 2
server['max_sessions'] = 10
server['password_authentication'] = false
server['log_level'] = 'verbose'
server['accept_env'] = ['LANG', 'LC_*', 'LANGUAGE']

# sshd sftp options
default['ssh-hardening']['ssh']['server']['sftp']['enable'] = false
default['ssh-hardening']['ssh']['server']['sftp']['group'] = 'sftponly'
default['ssh-hardening']['ssh']['server']['sftp']['chroot'] = '/home/%u'
# extra server configuration options
server['extras'] = {}

# sshd sftp options
server['sftp']['enable'] = false
server['sftp']['group'] = 'sftponly'
server['sftp']['chroot'] = '/home/%u'
end
27 changes: 27 additions & 0 deletions spec/recipes/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,33 @@
end
end

describe 'extra configuration values' do
context 'without custom extra config value' do
cached(:chef_run) do
ChefSpec::ServerRunner.new.converge(described_recipe)
end

it 'does not have any extra config options' do
expect(chef_run).to render_file('/etc/ssh/ssh_config')
expect(chef_run).not_to render_file('/etc/ssh/ssh_config').
with_content(/^# Extra Configuration Options/)
end
end

context 'with custom extra config value' do
cached(:chef_run) do
ChefSpec::ServerRunner.new do |node|
node.normal['ssh-hardening']['ssh']['client']['extras']['#ExtraConfig'] = 'Value'
end.converge(described_recipe)
end

it 'uses the extra config attributes' do
expect(chef_run).to render_file('/etc/ssh/ssh_config').with_content(/^# Extra Configuration Options/)
expect(chef_run).to render_file('/etc/ssh/ssh_config').with_content(/^#ExtraConfig Value/)
end
end
end

context 'chef-solo' do
cached(:chef_run) do
ChefSpec::SoloRunner.new.converge(described_recipe)
Expand Down
27 changes: 27 additions & 0 deletions spec/recipes/server_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,33 @@
end
end

describe 'extra configuration values' do
context 'without custom extra config value' do
cached(:chef_run) do
ChefSpec::ServerRunner.new.converge(described_recipe)
end

it 'does not have any extra config options' do
expect(chef_run).to render_file('/etc/ssh/sshd_config')
expect(chef_run).not_to render_file('/etc/ssh/sshd_config').
with_content(/^# Extra Configuration Options/)
end
end

context 'with custom extra config value' do
cached(:chef_run) do
ChefSpec::ServerRunner.new do |node|
node.normal['ssh-hardening']['ssh']['server']['extras']['#ExtraConfig'] = 'Value'
end.converge(described_recipe)
end

it 'uses the extra config attributes' do
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(/^# Extra Configuration Options/)
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(/^#ExtraConfig Value/)
end
end
end

it 'disables the challenge response authentication' do
expect(chef_run).to render_file('/etc/ssh/sshd_config').
with_content(/ChallengeResponseAuthentication no/)
Expand Down
7 changes: 7 additions & 0 deletions templates/default/openssh.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ UseRoaming <%= @node['ssh-hardening']['ssh']['client']['roaming'] ? 'yes' : 'no'
# Send locale environment variables
SendEnv <%= @node['ssh-hardening']['ssh']['client']['send_env'].join(' ') %>
<% end %>
<%- unless @node['ssh-hardening']['ssh']['client']['extras'].empty? %>
# Extra Configuration Options
<%- @node['ssh-hardening']['ssh']['client']['extras'].each do |key, value| %>
<%= key %> <%= value %>
<% end -%>
<% end -%>
7 changes: 7 additions & 0 deletions templates/default/opensshd.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,10 @@ X11Forwarding no
#PermitRootLogin no
#X11Forwarding no
<% end %>
<%- unless @node['ssh-hardening']['ssh']['server']['extras'].empty? %>
# Extra Configuration Options
<%- @node['ssh-hardening']['ssh']['server']['extras'].each do |key, value| %>
<%= key %> <%= value %>
<% end -%>
<% end -%>

0 comments on commit eaf6c11

Please sign in to comment.