Securing PWM

In last week’s post we set up PWM insecurely. In this post, we are going to secure it down and install mysql to store the reset questions. This guide assumes you have this CentOS 7 server publicly accessible with ports 80 and 443 available to the entire world. First, we will need to install mysql, set up a database, and add a user to that database. To do that, we need to edit our manifest.pp and append the following:

 class { '::mysql::server':
     root_password           => 'My4cc0unt$$password!',
     remove_default_accounts => true,
     package_name            => 'mariadb-server',
     package_ensure          => 'installed',
     service_name            => 'mariadb',
 }

 mysql::db { 'pwm':
     user     => 'pwm',
     password => 'pwm_passworD2!', # Can't do a password hash here :(
 }

 class { 'mysql::bindings':
     java_enable => true,
 }

file { '/opt/tomcat8/pwm/lib/mysql-connector-java.jar':
     ensure  => link,
     target  => '/usr/share/java/mysql-connector-java.jar',
     require => Class['mysql::bindings']
}

We will also need to install additional modules:

puppet module install puppetlabs-firewall
puppet module install jfryman-nginx
puppet module install jfryman-selinux
puppet module install danzilio-letsencrypt

Now we can set up our web server and directories by appending the following:

file {"/var/www/${::hostname}":
     ensure => 'directory',
     owner  => 'nginx',
}

file {"/var/www/${::hostname}/index.html":
     ensure  => 'file',
     owner   => 'nginx',
     content => '<html><head><meta http-equiv="refresh" content="0;URL=/pwm" /></head></html>',
     require => File["/var/www/${::hostname}"],
}

firewall { '100 allow http, https access':
     dport  => [80, 443],
     proto  => tcp,
     action => accept,
}

selinux::fcontext {'set-httpd-context':
     context  => "httpd_sys_content_t",
     pathname => "/var/www(/.*)?",
}

selinux::boolean {'httpd_can_network_connect': }

class { 'nginx': }

nginx::resource::upstream { 'pwm':
     members => [ 'localhost:8080' ],
}

nginx::resource::vhost { "${::hostname}.${::domain}":
     ensure           => present,
     www_root         => "/var/www/${::hostname}/",
     #ssl              => true,
     index_files      => [],
     try_files        => ['$uri','$uri/index.html','@pwm'],
     #rewrite_to_https => true,
     #ssl_cert         => "/etc/letsencrypt/live/${::fqdn}/fullchain.pem",
     #ssl_key          => "/etc/letsencrypt/live/${::fqdn}/privkey.pem",
}

nginx::resource::location{'@pwm':
     proxy => 'http://pwm',
     vhost => "${::hostname}.${::domain}",
     ssl   => true,
}

Leave the ssl options commented out for now. We need the web server set up before running letsencrypt. After we save the file and run another puppet apply manifest.pp we can add the letsencrypt information and uncomment the ssl information.

class { ::letsencrypt:
     email   => 'andrew.wippler@example.com',
     require => Class['nginx'],
}

letsencrypt::certonly { 'pwm cert':
     domains       => ["${::fqdn}"],
     plugin        => 'webroot',
     webroot_paths => ["/var/www/${::hostname}"],
     manage_cron   => true,
 }

Apply the manifest one more time and enjoy your new instance of PWM. As a reference, my entire manifest can be seen below:

include git
include java

tomcat::install { '/opt/tomcat8':
  source_url => 'https://www.apache.org/dist/tomcat/tomcat-8/v8.5.3/bin/apache-tomcat-8.5.3.tar.gz'
}

tomcat::instance { 'tomcat8-pwm':
  catalina_home => '/opt/tomcat8',
  catalina_base => '/opt/tomcat8/pwm',
}

tomcat::war { 'pwm.war':
  catalina_base => '/opt/tomcat8/pwm',
  war_source    => '/path/to/pwm.war', # or http://domain.tld/pwm.war
  before        => File['/opt/tomcat8/pwm/lib/mysql-connector-java.jar'],
}

augeas { 'web.xml':
  incl => '/opt/tomcat8/pwm/webapps/pwm/WEB-INF/web.xml',
  context => '/files/opt/tomcat8/pwm/webapps/pwm/WEB-INF/web.xml/web-app',
  lens    => 'Xml.lns',
  changes => 'set context-param[1]/param-value/#text /opt/tomcat8/pwm/webapps/pwm/WEB-INF',
}

class { '::mysql::server':
  root_password           => 'My4cc0unt$$password!',
  remove_default_accounts => true,
  package_name            => 'mariadb-server',
  package_ensure   	  => 'installed',
  service_name            => 'mariadb',
}

mysql::db { 'pwm':
  user     => 'pwm',
  password => 'pwm_passworD2!', # Can't do a password hash here :(
}

class { 'mysql::bindings':
  java_enable => true,
}

file { '/opt/tomcat8/pwm/lib/mysql-connector-java.jar':
  ensure  => link,
  target  => '/usr/share/java/mysql-connector-java.jar',
  require => Class['mysql::bindings']
}

file {"/var/www/${::hostname}":
  ensure => 'directory',
  owner  => 'nginx',
}

file {"/var/www/${::hostname}/index.html":
  ensure  => 'file',
  owner   => 'nginx',
  content => '<html><head><meta http-equiv="refresh" content="0;URL=/pwm" /></head></html>',
  require => File["/var/www/${::hostname}"],
}

firewall { '100 allow http, https access':
  dport   => [80, 443],
  proto  => tcp,
  action => accept,
  }

selinux::fcontext {'set-httpd-context':
  context  => "httpd_sys_content_t",
  pathname => "/var/www(/.*)?",
}

selinux::boolean {'httpd_can_network_connect': }

class { 'nginx': }

nginx::resource::upstream { 'pwm':
  members => [ 'localhost:8080' ],
}
nginx::resource::vhost { "${::hostname}.${::domain}":
     ensure           => present,
     index_files      => [],
     www_root         => "/var/www/${::hostname}/",
     ssl              => true,
     try_files        => ['$uri', '$uri/index.html', '@pwm'],
     rewrite_to_https => true,
     ssl_cert         => "/etc/letsencrypt/live/${::fqdn}/fullchain.pem",
     ssl_key          => "/etc/letsencrypt/live/${::fqdn}/privkey.pem",
}

nginx::resource::location{'@pwm':
     proxy => 'http://pwm',
     vhost => "${::hostname}.${::domain}",
     ssl   => true,
}
class { ::letsencrypt: 
    email   => 'andrew.wippler@example.com',
    require => Class['nginx'],
} 

letsencrypt::certonly { 'pwm cert':
    domains       => ["${::fqdn}"],
    plugin        => 'webroot',
    webroot_paths => ["/var/www/${::hostname}"],
    manage_cron   => true, 
}