Blog about things

blogging experiment about hacking

Fun With puPHPet

puPHPet is a nifty tool to help configure a virtual PHP development evironment. It uses vagrant to manage virtual machines with puppet to configure the machine. It’s a great start but we need some additional configurations. This is the story of extending puPHPet to our needs.

puppet hiera

Recent puPHPpet uses puppet’s hiera facility to provide configuration information to puppet at runtime. I would like to utilize hiera for our additional configurations but puPHPet only seems to utilize hiera sources for a small subset of parameters. puPHPpet help mentions a common.yaml hiera file but the code uses config.yaml instead. That’s confusing but ../puphpet/puppet/hiera.yaml spells it out for us.

1
2
3
4
5
6
7
--
:backends: yaml
:yaml:
    :datadir: '/vagrant/puphpet'
:hierarchy:
    - config
:logger: console
read hiera from yaml

Puppet provides a command-line tool to test the hiera lookup. Here is a simple example running on the virtual machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ hiera postgresql --yaml /vagrant/puphpet/config.yaml --config /vagrant/puphpet/puppet/hiera.yaml
{"install"=>"1",
 "settings"=>
  {"root_password"=>"bigsecret",
   "user_group"=>"postgres",
   "encoding"=>"UTF8",
   "version"=>"9.3",
   "listen_addresses"=>"*"},
 "databases"=>
  {"YqjrqQdPkxGG"=>
    {"grant"=>"ALL",
     "name"=>"db_name",
     "user"=>"db_user",
     "password"=>"biggersecret",
     "sql_file"=>"/var/www/database/dumps/smalldump.sql"}},
 "adminer"=>0
set listen_addresses in postgresql.conf to listen on external interface

I added some new parameters to the auto-generated config.yaml set to confirm puppet is reading them as expected. I added ‘settings’=>‘listen_addresses’. The value is read as expected, as confirmed in the listing above.

I want to set listen_addresses=‘*’ in postgresql.conf so the postgresql server will listen for connections on an external interface(s).

I added the listen_addresses parameter, sourced from hiera, to the invocation of class postgresql::server in puppet/nodes/postqresql.pp. The values in hash $postgresql_values are already read from the postgresql hiera key at the beginning of this manifest.

1
2
3
4
5
6
7
8
9
...
    }->
    class { 'postgresql::server':
      postgres_password => $postgresql_values['settings']['root_password'],
      version           => $postgresql_values['settings']['version'],
      listen_addresses  => $postgresql_values['settings']['listen_addresses'],
      require           => Group[$postgresql_values['settings']['user_group']],
    }
...

Output from the vagrant provision command shows the localhost value being changed in postgresql server config. yay.

1
2
3
==> default: Notice: /Stage[main]/Postgresql::Server::Config/Postgresql::Server::Config_entry[listen_addresses]/Postgresql_conf[listen_addresses]/value: value changed 'localhost' to '*'

 ==>/Stage[main]/Main/Iptables_port[kfgtd4YQS5bp]/Firewall[100 tcp/5432]/ensure: created

pg_hba.conf

Now that we are listening for connections on an external interface we must allow our heroic administrator to connect to that virtual host port via the host only network.

After going down the wrong path trying to directly instantiate postgresql::server:pg_hba_rule classes I found postgresql::server accepts an arrray of strings to specifiy access control rules. I only had to make an additional modification in node/postgresql.pp to pass an array from config.yaml.

1
2
3
4
5
6
7
8
9
10
...
    }->
    class { 'postgresql::server':
      postgres_password => $postgresql_values['settings']['root_password'],
      version           => $postgresql_values['settings']['version'],
      listen_addresses  => $postgresql_values['settings']['listen_addresses'],
      ipv4acls          => $postgresql_values['settings']['ipv4acls'],
      require           => Group[$postgresql_values['settings']['user_group']],
    }
...

Adding ipv4acls parameter above to nodes/postgresql.pp change enables use of ipv4acl entries during server creation.

A couple of sample access control specifications for pg_hba.conf.

1
2
3
   ipv4acls: 
      - 'host referall referall_admin 0.00.0/0 md5'
      - 'hostssl referall jamie 0.0.0.0/0 cert'

firewall

Lastly, puphpet installs firewall rules on the virtual host to allow access to TCP ports 22, 80, and 443. That means we cannot reach postgresql server port (5432) from outside the virtual machine, i.e. the host. Lucky for us, by default, the class postgresql::server manages the firewall settings writing a rule to allow access to port TCP/5432.

If postgresql::params::manage_firewall == false we would have to manage our own firewall rules, which is exposed via the firewalls section of config.yaml. This is what a firewall entry might look like:

1
2
3
4
5
6
7
8
firewall:
    install: '1'
    rules:
        voud9p0Db4Qz:
            port: '5432'
            priority: '100'
            proto: tcp
            action: accept

one more thing - sudoers

puPHPet has an option to populate a database by running a SQL dump file through psql. It uses this command to accomplish the task.

1
command     => "sudo -u postgres psql ${dbname} < ${sql_file}",

Vagrant creates user ‘vagrant’ to enable remote login and management of the virtual machine. The user has permission, via sudo, to run any command, without specifying a password, as the user root. The psql commmand needs to be run as user postgres to use unix socket for connection to the database.

To enable that capability I added a file, /etc/sudoers.d/10_vagrant, to define a sudoers entry allowing user vagrant to run commands as any user, with no password. There is already a file /etc/sudoers.d/vagrant that I should probably overwrite with this new entry.

nodes/sudoers.pp
1
2
3
4
5
6
7
8
9
10
# nodes/sudoers.pp
# allow user vagrant to run any command as any user

file { '/etc/sudoers.d/10_vagrant':
  ensure => file,
  owner => 'root',
  group => 'root',
  mode => '0440',
  content => 'vagrant ALL=(ALL) NOPASSWD:ALL'
}

virtualbox fail

There was something wrong with a virtualbox version (4.3.10 or 4.3.11 I think) we were using. Virtualbox did not properly create interfaces or routes for a hostonly network causing connection failure from the host machine. Version 4.3.16 and 4.3.18 worked fine.