SlideShare a Scribd company logo
blue-bag
ANSIBLE
&
DRUPAL
MEDICINE SHOW
DRUPAL BRIGHTON CAMP JANUARY 2015
GEORGE BOOBYER

BLUE-BAG
blue-bag
BLUE-BAG
GEORGE BOOBYER

DRUPAL: iAUGUR

GEORGE@BLUE-BAG.COM
TWITTER: iBLUEBAG
www.blue-bag.com
Established in 2000

The year before Drupal 1.0
blue-bag
ANSIBLE & DRUPAL
Security issues
Ansible
Server security
Drupal
Take homes
Questions
blue-bag
ANSIBLE & DRUPAL: TOP 10
1. Docker
2. Kubernetes
3. Taiga
4. Apache Mesos
5. OpenStack
6. Ansible
7. ownCloud
8. Apache Hadoop
9. Drupal
10.OpenDaylight
http://opensource.com/business/14/12/
top-10-open-source-projects-2014
Top 10 open source projects in 2014
blue-bag
SECURITY ISSUES: 2014 TRENDS
CVE-2014-0160
CVE-2014-3566	
CVE-2014-8730
CVE-2014-6277	
CVE-2014-6278	
CVE-2014-7169	
CVE-2014-7186	
CVE-2014-7187
Shellshock Poodle Heartbleed
SSLv3Bash OpenSSL
Exploits get branding!
blue-bag
SECURITY ISSUES: SAFE ENOUGH?
Availability of cloud
servers
Ease of setup
Lack of security
practices
Manual exploits
Constellations of
compromised
machines THAT SHOULD DO IT!
blue-bag
BASIC SECURITY
Principle of least
privilege
Limit all ports
access / iptables
WAF / Appliances
Platform / Providers
Secure as soon as
IP is public: First 5
blue-bag
ANSIBLE BASICS
Agentless
Uses SSH
YAML for configuration
Modules
Open source
not just provisioning
“Ansible is a radically simple IT automation system. It handles
configuration-management, application deployment, cloud
provisioning, ad-hoc task-execution, and multinode
orchestration.
blue-bag
ANSIBLE GOALS
Don’t log into servers
no need for bash scripts
Document build and configuration

(in source control)
Idempotent (idem-what’s-that-again?)
Maintain configuration across inventory
Retain knowledge
blue-bag
GET THE BOOK!
33% DISCOUNT FOR DCB!



http://leanpub.com/ansible-for-devops/c/
VbhTIPZqGvWT
!
https://leanpub.com/
ansible-for-devops
blue-bag
ANSIBLE DOCUMENTATION
Github - https://github.com/ansible/ansible

Code and examples

Ansible Documentation - http://docs.ansible.com/
!
Ansible best practices

http://docs.ansible.com/
playbooks_best_practices.html
!
Ansible Galaxy - https://galaxy.ansible.com
blue-bag
ANSIBLE BASICS
Inventory
Commands / 

Modules
Roles
Tasks
Templates
Facts
Vars
Host Vars
Group vars
Handlers
Tests
blue-bag
ANSIBLE BASICS: MODULES
Command
Shell
Lineinfile
File
Copy
Unarchive
Apt / Yum
apache2_module
htpasswd
Git
Cloud
Homebrew
blue-bag
ANSIBLE / VAGRANT
"## demo	
   "## Vagrantfile	
   %## hosts
# -*- mode: ruby -*-	
# vi: set ft=ruby :	
!
VAGRANTFILE_API_VERSION = "2"	
!
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|	
config.ssh.insert_key = false	
config.vm.provider :virtualbox do |vb|	
vb.customize ["modifyvm", :id, "--memory", "256"]	
end	
	
!
# Application server 1.	
config.vm.define "app1" do |app|	
app.vm.hostname = "bbdemo-app1.dev"	
app.vm.box = "chef/debian-7.4"	
app.vm.network :private_network, ip: "192.168.100.120"	
end	
!
# Application server 2.	
config.vm.define "app2" do |app|	
app.vm.hostname = "bbdemo-app2.dev"	
app.vm.box = "chef/debian-7.4"	
app.vm.network :private_network, ip: "192.168.100.121"	
end	
!
# Database server.	
config.vm.define "db" do |db|	
db.vm.hostname = "bbdemo-db.dev"	
db.vm.box = "chef/debian-7.4"	
db.vm.network :private_network, ip: "192.168.100.122"	
end	
end
# Application servers	
[app]	
192.168.100.120	
192.168.100.121	
!
# Database server	
[db] 	
192.168.100.122	
!
# Group 'multi' with all servers	
[multi:children]	
app	
db	
!
# Variables that will be applied to all
servers"	
[multi:vars] 	
ansible_ssh_user=vagrant	
ansible_ssh_private_key_file=~/.vagrant.d
/insecure_private_key
blue-bag
ANSIBLE BASICS
$ ansible multi -i hosts -a "free -m" -u vagrant
192.168.100.121 | success | rc=0 >>	
total used free shared buffers cached	
Mem: 244 74 170 0 6 34	
-/+ buffers/cache: 32 212	
Swap: 767 0 767	
!
192.168.100.120 | success | rc=0 >>	
total used free shared buffers cached	
Mem: 244 74 170 0 6 34	
-/+ buffers/cache: 32 212	
Swap: 767 0 767	
!
192.168.100.122 | success | rc=0 >>	
total used free shared buffers cached	
Mem: 244 74 170 0 6 34	
-/+ buffers/cache: 32 212	
Swap: 767 0 767
blue-bag
ANSIBLE BASICS
"## demo	
   "## Vagrantfile	
   "## ansible.cfg	
   "## hosts	
   %## log	
%## ansible.log
[defaults]	
log_path = log/ansible.log	
hostfile = ./hosts
…	
[multi]	
ansible_ssh_user=vagrant	
…
$ansible multi -i hosts -a "free -m" -u vagrant
blue-bag
ANSIBLE BASICS
$ ansible multi -a “date"	
!
192.168.100.122 | success | rc=0 >>	
Tue Jan 13 10:14:15 UTC 2015	
!
192.168.100.121 | success | rc=0 >>	
Tue Jan 13 10:14:15 UTC 2015	
!
192.168.100.120 | success | rc=0 >>	
Tue Jan 13 10:14:15 UTC 2015
blue-bag
ANSIBLE BASICS
$ansible multi -s -m apt -a "pkg=ntp state=installed_
update_cache=yes"	
192.168.100.121 | success >> {	
"changed": true,	
"stderr": "",	
"stdout": "Reading package lists...nBuilding dependency tree...nReading state
information...nThe following extra packages will be installed:n libopts25nSuggested
packages:n ntp-docnThe following NEW packages will be installed:n libopts25 ntpn0
upgraded, 2 newly installed, 0 to remove and 72 not upgraded.nNeed to get 565 kB/638 kB of
archives.nAfter this operation, 1364 kB of additional disk space will be used.nGet:1
http://mirrors.kernel.org/debian/ wheezy/main ntp amd64 1:4.2.6.p5+dfsg-2+deb7u1 [565 kB]
nFetched 565 kB in 13s (40.8 kB/s)nSelecting previously unselected package libopts25.r
n(Reading database ... r(Reading database ... 5%r(Reading database ... 10%r(Reading
database ... 15%r(Reading database ... 20%r(Reading database ... 25%r(Reading
database ... 30%r(Reading database ... 35%r(Reading database ... 40%r(Reading
database ... 45%r(Reading database ... 50%r(Reading database ... 55%r(Reading
database ... 60%r(Reading database ... 65%r(Reading database ... 70%r(Reading
database ... 75%r(Reading database ... 80%r(Reading database ... 85%r(Reading
database ... 90%r(Reading database ... 95%r(Reading database ... 100%r(Reading
database ... 37414 files and directories currently installed.)rnUnpacking libopts25
(from .../libopts25_1%3a5.12-0.1_amd64.deb) ...rnSelecting previously unselected package
ntp.rnUnpacking ntp (from .../ntp_1%3a4.2.6.p5+dfsg-2+deb7u1_amd64.deb) ...rnProcessing
triggers for man-db ...rnSetting up libopts25 (1:5.12-0.1) ...rnSetting up ntp
(1:4.2.6.p5+dfsg-2+deb7u1) ...rnStarting NTP server: ntpd.rn"	
}
blue-bag
ANSIBLE BASICS: THE PLAYBOOK
---	
- hosts: all	
gather_facts: true	
sudo: true	
	
tasks:	
- name: NTP | Install NTP	
apt: >	
name=ntp 	
state=installed 	
update_cache=yes	
cache_valid_time=3600
"## demo	
   "## Vagrantfile	
   "## ansible.cfg	
   "## hosts	
   %## log	
%## ansible.log	
"## playbook.yml
$ ansible-playbook playbook.yml
http://docs.ansible.com/apt_module.html
blue-bag
ANSIBLE BASICS: ROLES
$ansible-galaxy init 'myrole'
"## myrole	
   "## README.md	
   "## defaults	
   &   %## main.yml	
   "## files	
   "## handlers	
   &   %## main.yml	
   "## meta	
   &   %## main.yml	
   "## tasks	
   &   %## main.yml	
   "## templates	
   %## vars	
   %## main.yml
Use Galaxy Init to create roles
blue-bag
ANSIBLE BASICS: PROJECT STRUCTURE
https://github.com/iAugur/ansible-playbook-template
"## LICENSE	
"## README.md	
"## ansible.cfg	
"## common	
&   "## handlers	
&   &   %## main.yml	
&   %## vars	
&   %## main.yml	
"## group_vars	
&   %## all.yml	
"## host_vars	
&   %## local.yml	
"## hosts	
"## log	
"## playbook.yml	
"## roles	
   %## base	
   "## README.md	
   "## defaults	
   &   %## main.yml	
   "## files	
   "## handlers	
   &   %## main.yml	
   "## meta	
  &   %## main.yml	
  "## tasks	
  &   %## main.yml	
  "## templates	
  %## vars	
   %## main.yml	
Use a template for
Ansible projects.
Common structure
blue-bag
ANSIBLE BASICS: TESTING
Test play books

$ ansible-playbook --syntax-check --list-tasks main.yml	
Check mode

$ansible-playbook main.yml --check

$ansible-playbook main.yml --check --diff	
Test against Virtual machines

$ ansible-playbook main.yml -l local

blue-bag
BASIC SECURITY: FIRST 5
Lock down SSH
Create users and groups
Limit SSH to RSA key: 

(no passwords / no root)

Limit to AllowGroups
Update APT - unattended updates security
Configure hostname
Configure IPTables
Configure Fail2ban
blue-bag
BASIC SECURITY: FIRST 5
Take what we know
Research
Create templates / vars
Consistent across infrastructure
Use tasks to implement
Idempotent
Version controlled
Documented
blue-bag
FIRST5: SSH - EXAMPLE
"## README.md	
"## tasks	
&   "## main.yml	
&   %## ssh_config.yml	
"## templates	
&   %## ssh_config.j2	
%## vars	
%## main.yml
blue-bag
FIRST5: SSH - EXAMPLE
"## README.md	
"## tasks	
&   "## main.yml	
&   %## ssh_config.yml	
"## templates	
&   %## ssh_config.j2	
%## vars	
%## main.yml
# Package generated configuration file	
# See the sshd_config(5) manpage for details	
!
# What ports, IPs and protocols we listen for	
Port 22	
# Use these options to restrict which interfaces/protocols sshd will bind to	
#ListenAddress ::	
#ListenAddress 0.0.0.0	
Protocol 2	
# HostKeys for protocol version 2	
HostKey /etc/ssh/ssh_host_rsa_key	
HostKey /etc/ssh/ssh_host_dsa_key	
HostKey /etc/ssh/ssh_host_ecdsa_key	
#Privilege Separation is turned on for security	
UsePrivilegeSeparation yes	
!
# Lifetime and size of ephemeral version 1 server key	
KeyRegenerationInterval 3600	
ServerKeyBits 768	
# Logging	
SyslogFacility AUTH	
LogLevel INFO	
# Authentication:	
LoginGraceTime 120
blue-bag
FIRST5: SSH - EXAMPLE
"## README.md	
"## tasks	
&   "## main.yml	
&   %## ssh_config.yml	
"## templates	
&   %## ssh_config.j2	
%## vars	
%## main.yml
# {{ ansible_managed }}	
# See the sshd_config(5) manpage for details	
!
# What ports, IPs and protocols we listen for	
Port {{ ssh_port }}	
# Use these options to restrict which interfaces/protocols sshd will bind to	
#ListenAddress ::	
#ListenAddress 0.0.0.0	
Protocol 2	
# HostKeys for protocol version 2	
HostKey /etc/ssh/ssh_host_rsa_key	
HostKey /etc/ssh/ssh_host_dsa_key	
HostKey /etc/ssh/ssh_host_ecdsa_key	
#Privilege Separation is turned on for security	
UsePrivilegeSeparation yes	
!
# Lifetime and size of ephemeral version 1 server key	
KeyRegenerationInterval 3600	
ServerKeyBits {{ ssh_server_key_bits }}	
# Logging	
SyslogFacility AUTH	
LogLevel INFO	
# Authentication:	
LoginGraceTime {{ ssh_login_grace_time }}
blue-bag
FIRST5: SSH
"## README.md	
"## tasks	
&   "## main.yml	
&   %## ssh_config.yml	
"## templates	
&   %## ssh_config.j2	
%## vars	
%## main.yml
# SSH Settings	
# optionally change port	
ssh_port: 22034	
# reduce login grace time from 120 (2 minutes)	
ssh_login_grace_time: 20	
# increase the server key bit encryption default=768	
ssh_server_key_bits: 4096	
# control the parallelism	
ssh_max_startups: 3:50:10	
# control forwarding	
ssh_allow_tcp_forwarding: “no”	
ssh_allow_x11_forwarding: “no”
blue-bag
FIRST5: SSH
"## README.md	
"## tasks	
&   "## main.yml	
&   %## ssh_config.yml	
"## templates	
&   %## ssh_config.j2	
%## vars	
%## main.yml
--- 	
- name: SSH Config | add ssh config file	
template: >	
src=ssh_config.j2 	
dest=/etc/ssh/sshd_config 	
backup=yes	
notify: restart ssh
blue-bag
BASIC SECURITY: FIRST 5---	
- hosts: all	
gather_facts: true	
sudo: true	
!
pre_tasks:	
- name: Pretask | Update apt cache	
apt: update_cache=yes cache_valid_time=3600	
!
- name: Pretask | Run apt-get upgrade	
apt: upgrade=dist	
	
roles:	
- { role: base , tags: ["base"] }	
- { role: ntp , tags: ["ntp"] }	
- { role: user, tags: ["user"] }	
- { role: ssh , tags: ["ssh"] }	
- { role: fail2ban, tags: ["fail2ban"] }	
- { role: logwatch , tags: ["logwatch"] }	
- { role: exim , tags: ["exim"] }	
	
- { role: geerlingguy.firewall, tags: ["iptables"] }	
	
handlers:	
- include: common/handlers/main.yml
Don’t reinvent the wheel

Use Galaxy Roles

where relevant
blue-bag
ANSIBLE BASICS: PROVISION
---	
- hosts: all	
roles:	
- geerlingguy.mysql	
- geerlingguy.apache	
- geerlingguy.php
A LAMP SERVER IN SIX LINES OF YAML
Jeff Geerling - Ansible for Devops
blue-bag
BASIC SECURITY: SSL POODLE
- name: Apache security | SSL Poodle update	
lineinfile: 	
dest=/etc/apache2/mods-available/ssl.conf 	
regexp="{{ item.regexp }}" 	
line="{{ item.line }}"	
backup=yes	
with_items:	
- {	
regexp: "^# enable only secure protocols:",	
line: "# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3"	
}	
- {	
regexp: "^SSLProtocol",	
line: "SSLProtocol all -SSLv2 -SSLv3"	
}	
notify: restart apache	
tags:	
- poodle
blue-bag
BASIC SECURITY: SSL POODLE
- name: Apache security | SSL Poodle update	
template: src=ssl-poodle.conf.j2 dest=/etc/apache2/conf.d/ssl.conf	
notify: restart apache	
tags: 	
- poodle
# {{ ansible_managed }}	
<IfModule mod_ssl.c>	
!
# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3	
SSLProtocol all -SSLv2 -SSLv3	
!
</IfModule>
SSL-POODLE.CONF.J2
blue-bag
BASIC SECURITY: DRUPAL SECURITY
Ansible role quickly address 

security issues such as 

SA-CORE-2014-005
Inventory of Drupal sites
Task to apply patch
Test for vulnerability
Re-use for future patches
blue-bag
BASIC SECURITY: DRUPAL SECURITY
"## common	
&   "## handlers	
&   &   %## main.yml	
&   %## vars	
&   "## debian.yml	
&   %## os_default.yml	
"## group_vars	
&   "## all	
"## host_vars	
&   %## local	
&   %## example.server.net	
"## hosts	
"## log	
&   %## ansible.log	
"## patch.yml	
"## roles	
   %## SA-CORE-2014-005	
   "## README.md	
   "## files	
   &   "## SA-CORE-2014-005-D7.patch	
   &   %## dbtng.patch	
   "## meta	
   &   %## main.yml	
   %## tasks	
   %## main.yml
SA-CORE-2014-005
blue-bag
BASIC SECURITY: DRUPAL SECURITY
%## SA-CORE-2014-005	
"## README.md	
"## files	
&   "## SA-CORE-2014-005-D7.patch	
&   %## dbtng.patch	
"## meta	
&   %## main.yml	
%## tasks	
%## main.yml
SA-CORE-2014-005
blue-bag
BASIC SECURITY: DRUPAL SECURITY---	
!
ansible_ssh_host: 123.123.123.123	
!
webroot: "/var/www"	
!
drupal7_sites:	
- { 	
drupal_docroot: "{{ webroot }}/example.d7.site/live/htdocs”,	
vhost_name: "example.d7.site.live" 	
} 	
- { 	
drupal_docroot: "{{ webroot }}/example.d7.site/dev/htdocs",	
vhost_name: “example.d7.site.dev" 	
}	
- { 	
drupal_docroot: "{{ webroot }}/example.d7.site/staging/htdocs",	
vhost_name: "example.d7.staging" 	
}	
	
drupal6_sites:	
- { 	
drupal_docroot: "{{ webroot }}example.d6.site",	
vhost_name: "example.d6.site" 	
}
host_vars: example.server.net
blue-bag
BASIC SECURITY: DRUPAL SECURITY
---	
# tasks file for SA-CORE-2014-005-D7	
!
- name: Drupal7 patch | get patch | SA-CORE-2014-005-D7.	
get_url:	
url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch	
dest: /tmp/SA-CORE-2014-005-D7.patch	
!
- name: Drupal7 patch | Apply the patch from the drupal docroot.	
shell: "patch -p1 < /tmp/SA-CORE-2014-005-D7.patch chdir={{ item.drupal_docroot }}"	
with_items: 	
- "{{ drupal7_sites }}"	
!
- name: Drupal7 patch | Clear Drupal caches.	
command: "drush cc all chdir={{ item.drupal_docroot }}"	
with_items: 	
- "{{ drupal7_sites }}"
Role: SA-CORE-2014-005-D7 - main.yml
see blog post: http://www.midwesternmac.com/blogs/jeff-geerling/fixing-drupal-fast-using
blue-bag
BASIC SECURITY: DRUPAL SECURITY
!
- name: Drupal6 patch | get patch | SA-CORE-2014-005-D7.	
copy: 	
src=dbtng.patch 	
dest=/tmp/SACORE2014005D6-dbtng.patch owner=root group=root mode=0644	
	
- name: Drupal6 patch | Apply the patch from the drupal docroot.	
shell: "patch -p1 < /tmp/SACORE2014005D6-dbtng.patch chdir={{ item.drupal_docroot }}"	
with_items: 	
- "{{ drupal6_sites }}"	
tags: D6
Role: SA-CORE-2014-005-D7 - main.yml - (cont..)
blue-bag
BASIC SECURITY: DRUPAL SECURITY
Subscribe to the security bulletins
Follow key Drupal people on twitter
Harden your Drupal installations
Use version control
Leave Wednesday nights free
blue-bag
Don’t use .htaccess
Migrate all .htaccess to vhosts
Get a static IP
Use custom robots.txt
BASIC SECURITY: DRUPAL SECURITY
blue-bag
CUSTOM ROBOTS.TXT
Won’t get overwritten by Drupal update
RewriteCond %{REQUEST_URI} ^/robots.txt$ [NC]

RewriteRule robots.txt$ myrobots.txt [L]
blue-bag
DRUPAL & VHOSTS
Disallow all txt other than robots.txt
Disallow all php other than index.php
Don’t use .htaccess
Migrate all .htaccess to vhosts
Get a static IP
Use custom robots.txt
blue-bag
NO PHP FILES OTHER THAN INDEX.PHP
<FilesMatch "([^index].php|[^myrobots|robots].*.txt)$">	
AuthName "Development"	
AuthUserFile /etc/apache2/.htpasswds/passwd	
AuthType basic	
Require valid-user	
Order deny,allow	
Deny from all	
Allow from 123.123.123.123	
Allow from 127.0.0.1	
Satisfy Any	
</FilesMatch>
blue-bag
DRUPAL & VHOSTS
Restrict file permissions (640 / 440)
Don’t use .htaccess
Migrate all .htaccess to vhosts
Get a static IP
Use custom robots.txt
Disallow all txt other than robots.txt
Disallow all php other than index.php
blue-bag
TEST BEFORE RESTART
$ apachectl configtest
Syntax error on line 46 of example.local.conf:	
RewriteRule: bad flag delimiters
$ apachectl configtest

$ apachectl graceful
RewriteRule ^ index.php [L, NC]
RewriteRule ^ index.php [L,NC]
46
# If you are on RHEL/CentOS:	
$ apachectl -t	
$ /etc/init.d/httpd restart
blue-bag
BASIC SECURITY: DRUPAL SECURITY
Review your logs
Subscribe to the security bulletins
Follow key Drupal people on twitter
Harden your Drupal installations
Use version control
Leave Wednesday nights free
blue-bag
KEEP YOUR LOGS SLIM
## flag robots.txt requests - allow these to test robot behaviour

SetEnvIf Request_URI "^/robots.txt$" robots-request=log



## flag favicon requests

SetEnvIf Request_URI "^/favicon.ico$" favicon-request=nolog



## set do_not_log if any of the above flags are set

SetEnvIf robots-request nolog do_not_log

SetEnvIf favicon-request nolog do_not_log



## only log if do_not_log is not set

CustomLog /var/www/log/mysite-access.log vcommon env=!do_not_log
blue-bag
KEEP YOUR LOGS SLIM
## flag robots.txt requests - allow these to test robot behaviour

SetEnvIf Request_URI "^/robots.txt$" robots-request=log



## flag favicon requests

SetEnvIf Request_URI "^/favicon.ico$" favicon-request=nolog



## flag image, Css and JS requests and other static assets

SetEnvIf Request_URI “.(eot|js|css|woff|gif|png|jp(e)g))$” is_static_asset=nolog



## flag cron calls

SetEnvIf Request_URI "^(.*)cron.php$" cron-request=nolog



## flag monitor calls

BrowserMatchNoCase NewRelicPinger monitor-request=nolog



## set do_not_log if any of the above flags are set

SetEnvIf robots-request nolog do_not_log

SetEnvIf favicon-request nolog do_not_log

SetEnvIf image-request nolog do_not_log

SetEnvIf is_static_asset nolog do_not_log

SetEnvIf monitor-request nolog do_not_log



## only log if do_not_log is not set

CustomLog /var/www/log/mysite-access.log vcommon env=!do_not_log
blue-bag
BASIC SECURITY: GENERAL SECURITY
Mod_security

Drupal specific rules and exclusions

manage exclusions with Ansible
Know your application ‘surface’
blue-bag
## {{ ansible_managed }}	
!
SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass,
setvar:ip.slow_dos_counter=+1, expirevar:ip.slow_dos_counter=60, id:'990002'"	
!
SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client
Connection Dropped due to high number of slow DoS alerts', id:'990003'"	
!
SecRule ARGS "ansibletesttoseeifmodsisworking" "phase:1,log,deny,status:
503,msg:'Test Rule',id:'990004'"	
!
# Rules to allow good bots with no request header - Don’t do this!!	
# SecRule REQUEST_HEADERS:User-Agent "Google|Bing|Yandex" "phase:
1,t:none,allow,nolog,ctl:ruleRemoveById=960015"	
# Do this (use Ansible to manage IP list / User-Agents are spoofed)	
SecRule REMOTE_ADDR "^66.249.64.165" phase:
1,nolog,allow,ctl:ruleEngine=off,id:960015	
!
<LocationMatch "/admin/structure/[pages|views|types]">	
SecRuleRemoveById 981173	
SecRuleRemoveById 960024	
SecRuleRemoveById 981231	
</LocationMatch>
BASIC SECURITY: GENERAL SECURITY
./templates/modsecurity_crs_48_local_exceptions.conf.j2
blue-bag
BASIC SECURITY: GENERAL SECURITY
Mod_security

Drupal specific rule and exclusions

manage exclusions with Ansible
Fail2Ban

Drupal specific actions
IPTables - Whitelists
Remove all unused services
Consider log analysis services

e.g. loggly, paper trail, sumo logic
Know your application ‘surface’
blue-bag
ANSIBLE & DRUPAL
Put all sites in maintenance mode

(Note: better to use maintenance site and re-point traffic to that so you can still work
on your site on your ip)
Pull latest changes
Use in harmony with Drush
Clear caches and other Drush actions

see https://github.com/jenitehan/drupal_update_check
Extend Drush / Drupal to output JSON
Automate common tasks
blue-bag
BASIC SECURITY: DRUPAL SECURITY
Consider a platform provider
Subscribe to the security bulletins
Follow key Drupal people on twitter
Harden your Drupal installations
Use version control
Review your logs
Use security best practices
blue-bag
ANSIBLE & DRUPAL: EXAMPLE 2
Use Case: Commerce site
Product Image updates
Replace the image(s)
Clear the Image Cache/ styles
blue-bag
ansible_ssh_host: 123.123.123.123	
ansible_ssh_user: ansible	
drupal_sites:	
- “/var/www/mysite/live/htdocs”	
- “/var/www/mysite/staging/htdocs"	
- "/var/www/mysite/dev/htdocs"
ANSIBLE & DRUPAL: EXAMPLE 2
./host_vars/server.example.com
./main.yml
---	
- hosts: all	
gather_facts: false	
sudo: true	
!
roles:	
- { role: updateimages , tags: ["updateimages"] }	
!
handlers:	
- include: common/handlers/main.yml
./hosts
[example]	
server.example.com
blue-bag
---	
# vars file for base	
files_src_path: “/path/to/local/updated/images”	
files_dest_path: “/sites/default/files”	
files_to_update:	
- “fa9001.jpg"	
- “fa9002.jpg"	
	
image_cache_folders:	
- “styles/thumbnail/public/product_images"	
- "styles/product_gallery/public/product_images"	
- "styles/medium_tall/public/product_images"	
- "styles/product_full/public/product_images"
ANSIBLE & DRUPAL: EXAMPLE 1
./roles/updateimages/vars/main
---	
- name: clear drupal cache	
command: "drush cc all chdir={{ item }}"	
with_items: 	
- "{{ drupal_sites }}"
./handlers/main.yml
blue-bag
- name: Image Update | Products	
copy: 	
src={{ files_src_path }}/{{ item[1] }} 	
dest={{ item[0] }}/{{ files_dest_path }}/product_images/{{ item[1] }}	
owner=www-data 	
group=www-data 	
mode:0644	
with_nested:	
- drupal_sites	
- files_to_update 	
tags: images	
notify: clear drupal cache	
!
- name: Image Update | Update Cache	
file: >	
path={{ item[0] }}/{{ files_dest_path }}/{{ item[1] }}/{{ item[2] }}

state=absent	
with_nested:	
- drupal_sites	
- image_cache_folders	
- files_to_update	
tags: images	
notify: clear drupal cache
ANSIBLE & DRUPAL: EXAMPLE 2
./roles/updateimages/tasks/main.yml
blue-bag
RUN UPDATE
TASK: [updateimages | Image Update | Products] **************** 	
ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) =>
{"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/
var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path":
“/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size":
193018, "state": "file", "uid": 33}	
changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg'])
=> {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/
var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33",
"mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/
ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33}	
changed: [server.example.com] => (item=['/var/www/mysite/dev/htdocs', 'fa9001.jpg']) =>
{"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/var/
www/mysite/dev/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33",
"mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/
ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33}
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************	
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}	
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}	
changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state":
"absent"}	
changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg",
"state": "absent"}	
PLAY RECAP ******************************************************************** 	
server.example.com : ok=2 changed=2 unreachable=0 failed=0
blue-bag
ANSIBLE TOOLS
Automate playbook runs
Collaborate
Log
Permissions
Scheduled runs
Push button deployment
Free for < 10 nodes
blue-bag
SOME USEFUL TOOLS
Unicode Code Converter: 

http://rishida.net/tools/conversion/
PHP Decoder / Hex Decoder: 

http://ddecode.com/phpdecoder/
Permissions Calculator:
http://permissions-calculator.org/
RegEx:

https://regex101.com/#pcre
Network Tools: 

CIDR masks etc

http://pingtool.org/
blue-bag
CONCLUSION / QUESTIONS
For all local
development
use VLAD
http://git.io/DCB-Vlad
For Ansible get the books
https://leanpub.com/
ansible-for-devops
http://www.ansible.com/
ansible-book

More Related Content

Drupal Camp Brighton 2015: Ansible Drupal Medicine show

  • 1. blue-bag ANSIBLE & DRUPAL MEDICINE SHOW DRUPAL BRIGHTON CAMP JANUARY 2015 GEORGE BOOBYER
 BLUE-BAG
  • 2. blue-bag BLUE-BAG GEORGE BOOBYER
 DRUPAL: iAUGUR
 GEORGE@BLUE-BAG.COM TWITTER: iBLUEBAG www.blue-bag.com Established in 2000
 The year before Drupal 1.0
  • 3. blue-bag ANSIBLE & DRUPAL Security issues Ansible Server security Drupal Take homes Questions
  • 4. blue-bag ANSIBLE & DRUPAL: TOP 10 1. Docker 2. Kubernetes 3. Taiga 4. Apache Mesos 5. OpenStack 6. Ansible 7. ownCloud 8. Apache Hadoop 9. Drupal 10.OpenDaylight http://opensource.com/business/14/12/ top-10-open-source-projects-2014 Top 10 open source projects in 2014
  • 5. blue-bag SECURITY ISSUES: 2014 TRENDS CVE-2014-0160 CVE-2014-3566 CVE-2014-8730 CVE-2014-6277 CVE-2014-6278 CVE-2014-7169 CVE-2014-7186 CVE-2014-7187 Shellshock Poodle Heartbleed SSLv3Bash OpenSSL Exploits get branding!
  • 6. blue-bag SECURITY ISSUES: SAFE ENOUGH? Availability of cloud servers Ease of setup Lack of security practices Manual exploits Constellations of compromised machines THAT SHOULD DO IT!
  • 7. blue-bag BASIC SECURITY Principle of least privilege Limit all ports access / iptables WAF / Appliances Platform / Providers Secure as soon as IP is public: First 5
  • 8. blue-bag ANSIBLE BASICS Agentless Uses SSH YAML for configuration Modules Open source not just provisioning “Ansible is a radically simple IT automation system. It handles configuration-management, application deployment, cloud provisioning, ad-hoc task-execution, and multinode orchestration.
  • 9. blue-bag ANSIBLE GOALS Don’t log into servers no need for bash scripts Document build and configuration
 (in source control) Idempotent (idem-what’s-that-again?) Maintain configuration across inventory Retain knowledge
  • 10. blue-bag GET THE BOOK! 33% DISCOUNT FOR DCB!
 
 http://leanpub.com/ansible-for-devops/c/ VbhTIPZqGvWT ! https://leanpub.com/ ansible-for-devops
  • 11. blue-bag ANSIBLE DOCUMENTATION Github - https://github.com/ansible/ansible
 Code and examples
 Ansible Documentation - http://docs.ansible.com/ ! Ansible best practices
 http://docs.ansible.com/ playbooks_best_practices.html ! Ansible Galaxy - https://galaxy.ansible.com
  • 12. blue-bag ANSIBLE BASICS Inventory Commands / 
 Modules Roles Tasks Templates Facts Vars Host Vars Group vars Handlers Tests
  • 14. blue-bag ANSIBLE / VAGRANT "## demo    "## Vagrantfile    %## hosts # -*- mode: ruby -*- # vi: set ft=ruby : ! VAGRANTFILE_API_VERSION = "2" ! Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.ssh.insert_key = false config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "256"] end ! # Application server 1. config.vm.define "app1" do |app| app.vm.hostname = "bbdemo-app1.dev" app.vm.box = "chef/debian-7.4" app.vm.network :private_network, ip: "192.168.100.120" end ! # Application server 2. config.vm.define "app2" do |app| app.vm.hostname = "bbdemo-app2.dev" app.vm.box = "chef/debian-7.4" app.vm.network :private_network, ip: "192.168.100.121" end ! # Database server. config.vm.define "db" do |db| db.vm.hostname = "bbdemo-db.dev" db.vm.box = "chef/debian-7.4" db.vm.network :private_network, ip: "192.168.100.122" end end # Application servers [app] 192.168.100.120 192.168.100.121 ! # Database server [db] 192.168.100.122 ! # Group 'multi' with all servers [multi:children] app db ! # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d /insecure_private_key
  • 15. blue-bag ANSIBLE BASICS $ ansible multi -i hosts -a "free -m" -u vagrant 192.168.100.121 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767 ! 192.168.100.120 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767 ! 192.168.100.122 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767
  • 16. blue-bag ANSIBLE BASICS "## demo    "## Vagrantfile    "## ansible.cfg    "## hosts    %## log %## ansible.log [defaults] log_path = log/ansible.log hostfile = ./hosts … [multi] ansible_ssh_user=vagrant … $ansible multi -i hosts -a "free -m" -u vagrant
  • 17. blue-bag ANSIBLE BASICS $ ansible multi -a “date" ! 192.168.100.122 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015 ! 192.168.100.121 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015 ! 192.168.100.120 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015
  • 18. blue-bag ANSIBLE BASICS $ansible multi -s -m apt -a "pkg=ntp state=installed_ update_cache=yes" 192.168.100.121 | success >> { "changed": true, "stderr": "", "stdout": "Reading package lists...nBuilding dependency tree...nReading state information...nThe following extra packages will be installed:n libopts25nSuggested packages:n ntp-docnThe following NEW packages will be installed:n libopts25 ntpn0 upgraded, 2 newly installed, 0 to remove and 72 not upgraded.nNeed to get 565 kB/638 kB of archives.nAfter this operation, 1364 kB of additional disk space will be used.nGet:1 http://mirrors.kernel.org/debian/ wheezy/main ntp amd64 1:4.2.6.p5+dfsg-2+deb7u1 [565 kB] nFetched 565 kB in 13s (40.8 kB/s)nSelecting previously unselected package libopts25.r n(Reading database ... r(Reading database ... 5%r(Reading database ... 10%r(Reading database ... 15%r(Reading database ... 20%r(Reading database ... 25%r(Reading database ... 30%r(Reading database ... 35%r(Reading database ... 40%r(Reading database ... 45%r(Reading database ... 50%r(Reading database ... 55%r(Reading database ... 60%r(Reading database ... 65%r(Reading database ... 70%r(Reading database ... 75%r(Reading database ... 80%r(Reading database ... 85%r(Reading database ... 90%r(Reading database ... 95%r(Reading database ... 100%r(Reading database ... 37414 files and directories currently installed.)rnUnpacking libopts25 (from .../libopts25_1%3a5.12-0.1_amd64.deb) ...rnSelecting previously unselected package ntp.rnUnpacking ntp (from .../ntp_1%3a4.2.6.p5+dfsg-2+deb7u1_amd64.deb) ...rnProcessing triggers for man-db ...rnSetting up libopts25 (1:5.12-0.1) ...rnSetting up ntp (1:4.2.6.p5+dfsg-2+deb7u1) ...rnStarting NTP server: ntpd.rn" }
  • 19. blue-bag ANSIBLE BASICS: THE PLAYBOOK --- - hosts: all gather_facts: true sudo: true tasks: - name: NTP | Install NTP apt: > name=ntp state=installed update_cache=yes cache_valid_time=3600 "## demo    "## Vagrantfile    "## ansible.cfg    "## hosts    %## log %## ansible.log "## playbook.yml $ ansible-playbook playbook.yml http://docs.ansible.com/apt_module.html
  • 20. blue-bag ANSIBLE BASICS: ROLES $ansible-galaxy init 'myrole' "## myrole    "## README.md    "## defaults    &   %## main.yml    "## files    "## handlers    &   %## main.yml    "## meta    &   %## main.yml    "## tasks    &   %## main.yml    "## templates    %## vars    %## main.yml Use Galaxy Init to create roles
  • 21. blue-bag ANSIBLE BASICS: PROJECT STRUCTURE https://github.com/iAugur/ansible-playbook-template "## LICENSE "## README.md "## ansible.cfg "## common &   "## handlers &   &   %## main.yml &   %## vars &   %## main.yml "## group_vars &   %## all.yml "## host_vars &   %## local.yml "## hosts "## log "## playbook.yml "## roles    %## base    "## README.md    "## defaults    &   %## main.yml    "## files    "## handlers    &   %## main.yml    "## meta   &   %## main.yml   "## tasks   &   %## main.yml   "## templates   %## vars    %## main.yml Use a template for Ansible projects. Common structure
  • 22. blue-bag ANSIBLE BASICS: TESTING Test play books
 $ ansible-playbook --syntax-check --list-tasks main.yml Check mode
 $ansible-playbook main.yml --check
 $ansible-playbook main.yml --check --diff Test against Virtual machines
 $ ansible-playbook main.yml -l local

  • 23. blue-bag BASIC SECURITY: FIRST 5 Lock down SSH Create users and groups Limit SSH to RSA key: 
 (no passwords / no root)
 Limit to AllowGroups Update APT - unattended updates security Configure hostname Configure IPTables Configure Fail2ban
  • 24. blue-bag BASIC SECURITY: FIRST 5 Take what we know Research Create templates / vars Consistent across infrastructure Use tasks to implement Idempotent Version controlled Documented
  • 25. blue-bag FIRST5: SSH - EXAMPLE "## README.md "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 %## vars %## main.yml
  • 26. blue-bag FIRST5: SSH - EXAMPLE "## README.md "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 %## vars %## main.yml # Package generated configuration file # See the sshd_config(5) manpage for details ! # What ports, IPs and protocols we listen for Port 22 # Use these options to restrict which interfaces/protocols sshd will bind to #ListenAddress :: #ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key HostKey /etc/ssh/ssh_host_ecdsa_key #Privilege Separation is turned on for security UsePrivilegeSeparation yes ! # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits 768 # Logging SyslogFacility AUTH LogLevel INFO # Authentication: LoginGraceTime 120
  • 27. blue-bag FIRST5: SSH - EXAMPLE "## README.md "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 %## vars %## main.yml # {{ ansible_managed }} # See the sshd_config(5) manpage for details ! # What ports, IPs and protocols we listen for Port {{ ssh_port }} # Use these options to restrict which interfaces/protocols sshd will bind to #ListenAddress :: #ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key HostKey /etc/ssh/ssh_host_ecdsa_key #Privilege Separation is turned on for security UsePrivilegeSeparation yes ! # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits {{ ssh_server_key_bits }} # Logging SyslogFacility AUTH LogLevel INFO # Authentication: LoginGraceTime {{ ssh_login_grace_time }}
  • 28. blue-bag FIRST5: SSH "## README.md "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 %## vars %## main.yml # SSH Settings # optionally change port ssh_port: 22034 # reduce login grace time from 120 (2 minutes) ssh_login_grace_time: 20 # increase the server key bit encryption default=768 ssh_server_key_bits: 4096 # control the parallelism ssh_max_startups: 3:50:10 # control forwarding ssh_allow_tcp_forwarding: “no” ssh_allow_x11_forwarding: “no”
  • 29. blue-bag FIRST5: SSH "## README.md "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 %## vars %## main.yml --- - name: SSH Config | add ssh config file template: > src=ssh_config.j2 dest=/etc/ssh/sshd_config backup=yes notify: restart ssh
  • 30. blue-bag BASIC SECURITY: FIRST 5--- - hosts: all gather_facts: true sudo: true ! pre_tasks: - name: Pretask | Update apt cache apt: update_cache=yes cache_valid_time=3600 ! - name: Pretask | Run apt-get upgrade apt: upgrade=dist roles: - { role: base , tags: ["base"] } - { role: ntp , tags: ["ntp"] } - { role: user, tags: ["user"] } - { role: ssh , tags: ["ssh"] } - { role: fail2ban, tags: ["fail2ban"] } - { role: logwatch , tags: ["logwatch"] } - { role: exim , tags: ["exim"] } - { role: geerlingguy.firewall, tags: ["iptables"] } handlers: - include: common/handlers/main.yml Don’t reinvent the wheel
 Use Galaxy Roles
 where relevant
  • 31. blue-bag ANSIBLE BASICS: PROVISION --- - hosts: all roles: - geerlingguy.mysql - geerlingguy.apache - geerlingguy.php A LAMP SERVER IN SIX LINES OF YAML Jeff Geerling - Ansible for Devops
  • 32. blue-bag BASIC SECURITY: SSL POODLE - name: Apache security | SSL Poodle update lineinfile: dest=/etc/apache2/mods-available/ssl.conf regexp="{{ item.regexp }}" line="{{ item.line }}" backup=yes with_items: - { regexp: "^# enable only secure protocols:", line: "# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3" } - { regexp: "^SSLProtocol", line: "SSLProtocol all -SSLv2 -SSLv3" } notify: restart apache tags: - poodle
  • 33. blue-bag BASIC SECURITY: SSL POODLE - name: Apache security | SSL Poodle update template: src=ssl-poodle.conf.j2 dest=/etc/apache2/conf.d/ssl.conf notify: restart apache tags: - poodle # {{ ansible_managed }} <IfModule mod_ssl.c> ! # enable only secure protocols: TLSv1, but not SSLv2 & SSLv3 SSLProtocol all -SSLv2 -SSLv3 ! </IfModule> SSL-POODLE.CONF.J2
  • 34. blue-bag BASIC SECURITY: DRUPAL SECURITY Ansible role quickly address 
 security issues such as 
 SA-CORE-2014-005 Inventory of Drupal sites Task to apply patch Test for vulnerability Re-use for future patches
  • 35. blue-bag BASIC SECURITY: DRUPAL SECURITY "## common &   "## handlers &   &   %## main.yml &   %## vars &   "## debian.yml &   %## os_default.yml "## group_vars &   "## all "## host_vars &   %## local &   %## example.server.net "## hosts "## log &   %## ansible.log "## patch.yml "## roles    %## SA-CORE-2014-005    "## README.md    "## files    &   "## SA-CORE-2014-005-D7.patch    &   %## dbtng.patch    "## meta    &   %## main.yml    %## tasks    %## main.yml SA-CORE-2014-005
  • 36. blue-bag BASIC SECURITY: DRUPAL SECURITY %## SA-CORE-2014-005 "## README.md "## files &   "## SA-CORE-2014-005-D7.patch &   %## dbtng.patch "## meta &   %## main.yml %## tasks %## main.yml SA-CORE-2014-005
  • 37. blue-bag BASIC SECURITY: DRUPAL SECURITY--- ! ansible_ssh_host: 123.123.123.123 ! webroot: "/var/www" ! drupal7_sites: - { drupal_docroot: "{{ webroot }}/example.d7.site/live/htdocs”, vhost_name: "example.d7.site.live" } - { drupal_docroot: "{{ webroot }}/example.d7.site/dev/htdocs", vhost_name: “example.d7.site.dev" } - { drupal_docroot: "{{ webroot }}/example.d7.site/staging/htdocs", vhost_name: "example.d7.staging" } drupal6_sites: - { drupal_docroot: "{{ webroot }}example.d6.site", vhost_name: "example.d6.site" } host_vars: example.server.net
  • 38. blue-bag BASIC SECURITY: DRUPAL SECURITY --- # tasks file for SA-CORE-2014-005-D7 ! - name: Drupal7 patch | get patch | SA-CORE-2014-005-D7. get_url: url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch dest: /tmp/SA-CORE-2014-005-D7.patch ! - name: Drupal7 patch | Apply the patch from the drupal docroot. shell: "patch -p1 < /tmp/SA-CORE-2014-005-D7.patch chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal7_sites }}" ! - name: Drupal7 patch | Clear Drupal caches. command: "drush cc all chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal7_sites }}" Role: SA-CORE-2014-005-D7 - main.yml see blog post: http://www.midwesternmac.com/blogs/jeff-geerling/fixing-drupal-fast-using
  • 39. blue-bag BASIC SECURITY: DRUPAL SECURITY ! - name: Drupal6 patch | get patch | SA-CORE-2014-005-D7. copy: src=dbtng.patch dest=/tmp/SACORE2014005D6-dbtng.patch owner=root group=root mode=0644 - name: Drupal6 patch | Apply the patch from the drupal docroot. shell: "patch -p1 < /tmp/SACORE2014005D6-dbtng.patch chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal6_sites }}" tags: D6 Role: SA-CORE-2014-005-D7 - main.yml - (cont..)
  • 40. blue-bag BASIC SECURITY: DRUPAL SECURITY Subscribe to the security bulletins Follow key Drupal people on twitter Harden your Drupal installations Use version control Leave Wednesday nights free
  • 41. blue-bag Don’t use .htaccess Migrate all .htaccess to vhosts Get a static IP Use custom robots.txt BASIC SECURITY: DRUPAL SECURITY
  • 42. blue-bag CUSTOM ROBOTS.TXT Won’t get overwritten by Drupal update RewriteCond %{REQUEST_URI} ^/robots.txt$ [NC]
 RewriteRule robots.txt$ myrobots.txt [L]
  • 43. blue-bag DRUPAL & VHOSTS Disallow all txt other than robots.txt Disallow all php other than index.php Don’t use .htaccess Migrate all .htaccess to vhosts Get a static IP Use custom robots.txt
  • 44. blue-bag NO PHP FILES OTHER THAN INDEX.PHP <FilesMatch "([^index].php|[^myrobots|robots].*.txt)$"> AuthName "Development" AuthUserFile /etc/apache2/.htpasswds/passwd AuthType basic Require valid-user Order deny,allow Deny from all Allow from 123.123.123.123 Allow from 127.0.0.1 Satisfy Any </FilesMatch>
  • 45. blue-bag DRUPAL & VHOSTS Restrict file permissions (640 / 440) Don’t use .htaccess Migrate all .htaccess to vhosts Get a static IP Use custom robots.txt Disallow all txt other than robots.txt Disallow all php other than index.php
  • 46. blue-bag TEST BEFORE RESTART $ apachectl configtest Syntax error on line 46 of example.local.conf: RewriteRule: bad flag delimiters $ apachectl configtest
 $ apachectl graceful RewriteRule ^ index.php [L, NC] RewriteRule ^ index.php [L,NC] 46 # If you are on RHEL/CentOS: $ apachectl -t $ /etc/init.d/httpd restart
  • 47. blue-bag BASIC SECURITY: DRUPAL SECURITY Review your logs Subscribe to the security bulletins Follow key Drupal people on twitter Harden your Drupal installations Use version control Leave Wednesday nights free
  • 48. blue-bag KEEP YOUR LOGS SLIM ## flag robots.txt requests - allow these to test robot behaviour
 SetEnvIf Request_URI "^/robots.txt$" robots-request=log
 
 ## flag favicon requests
 SetEnvIf Request_URI "^/favicon.ico$" favicon-request=nolog
 
 ## set do_not_log if any of the above flags are set
 SetEnvIf robots-request nolog do_not_log
 SetEnvIf favicon-request nolog do_not_log
 
 ## only log if do_not_log is not set
 CustomLog /var/www/log/mysite-access.log vcommon env=!do_not_log
  • 49. blue-bag KEEP YOUR LOGS SLIM ## flag robots.txt requests - allow these to test robot behaviour
 SetEnvIf Request_URI "^/robots.txt$" robots-request=log
 
 ## flag favicon requests
 SetEnvIf Request_URI "^/favicon.ico$" favicon-request=nolog
 
 ## flag image, Css and JS requests and other static assets
 SetEnvIf Request_URI “.(eot|js|css|woff|gif|png|jp(e)g))$” is_static_asset=nolog
 
 ## flag cron calls
 SetEnvIf Request_URI "^(.*)cron.php$" cron-request=nolog
 
 ## flag monitor calls
 BrowserMatchNoCase NewRelicPinger monitor-request=nolog
 
 ## set do_not_log if any of the above flags are set
 SetEnvIf robots-request nolog do_not_log
 SetEnvIf favicon-request nolog do_not_log
 SetEnvIf image-request nolog do_not_log
 SetEnvIf is_static_asset nolog do_not_log
 SetEnvIf monitor-request nolog do_not_log
 
 ## only log if do_not_log is not set
 CustomLog /var/www/log/mysite-access.log vcommon env=!do_not_log
  • 50. blue-bag BASIC SECURITY: GENERAL SECURITY Mod_security
 Drupal specific rules and exclusions
 manage exclusions with Ansible Know your application ‘surface’
  • 51. blue-bag ## {{ ansible_managed }} ! SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass, setvar:ip.slow_dos_counter=+1, expirevar:ip.slow_dos_counter=60, id:'990002'" ! SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client Connection Dropped due to high number of slow DoS alerts', id:'990003'" ! SecRule ARGS "ansibletesttoseeifmodsisworking" "phase:1,log,deny,status: 503,msg:'Test Rule',id:'990004'" ! # Rules to allow good bots with no request header - Don’t do this!! # SecRule REQUEST_HEADERS:User-Agent "Google|Bing|Yandex" "phase: 1,t:none,allow,nolog,ctl:ruleRemoveById=960015" # Do this (use Ansible to manage IP list / User-Agents are spoofed) SecRule REMOTE_ADDR "^66.249.64.165" phase: 1,nolog,allow,ctl:ruleEngine=off,id:960015 ! <LocationMatch "/admin/structure/[pages|views|types]"> SecRuleRemoveById 981173 SecRuleRemoveById 960024 SecRuleRemoveById 981231 </LocationMatch> BASIC SECURITY: GENERAL SECURITY ./templates/modsecurity_crs_48_local_exceptions.conf.j2
  • 52. blue-bag BASIC SECURITY: GENERAL SECURITY Mod_security
 Drupal specific rule and exclusions
 manage exclusions with Ansible Fail2Ban
 Drupal specific actions IPTables - Whitelists Remove all unused services Consider log analysis services
 e.g. loggly, paper trail, sumo logic Know your application ‘surface’
  • 53. blue-bag ANSIBLE & DRUPAL Put all sites in maintenance mode
 (Note: better to use maintenance site and re-point traffic to that so you can still work on your site on your ip) Pull latest changes Use in harmony with Drush Clear caches and other Drush actions
 see https://github.com/jenitehan/drupal_update_check Extend Drush / Drupal to output JSON Automate common tasks
  • 54. blue-bag BASIC SECURITY: DRUPAL SECURITY Consider a platform provider Subscribe to the security bulletins Follow key Drupal people on twitter Harden your Drupal installations Use version control Review your logs Use security best practices
  • 55. blue-bag ANSIBLE & DRUPAL: EXAMPLE 2 Use Case: Commerce site Product Image updates Replace the image(s) Clear the Image Cache/ styles
  • 56. blue-bag ansible_ssh_host: 123.123.123.123 ansible_ssh_user: ansible drupal_sites: - “/var/www/mysite/live/htdocs” - “/var/www/mysite/staging/htdocs" - "/var/www/mysite/dev/htdocs" ANSIBLE & DRUPAL: EXAMPLE 2 ./host_vars/server.example.com ./main.yml --- - hosts: all gather_facts: false sudo: true ! roles: - { role: updateimages , tags: ["updateimages"] } ! handlers: - include: common/handlers/main.yml ./hosts [example] server.example.com
  • 57. blue-bag --- # vars file for base files_src_path: “/path/to/local/updated/images” files_dest_path: “/sites/default/files” files_to_update: - “fa9001.jpg" - “fa9002.jpg" image_cache_folders: - “styles/thumbnail/public/product_images" - "styles/product_gallery/public/product_images" - "styles/medium_tall/public/product_images" - "styles/product_full/public/product_images" ANSIBLE & DRUPAL: EXAMPLE 1 ./roles/updateimages/vars/main --- - name: clear drupal cache command: "drush cc all chdir={{ item }}" with_items: - "{{ drupal_sites }}" ./handlers/main.yml
  • 58. blue-bag - name: Image Update | Products copy: src={{ files_src_path }}/{{ item[1] }} dest={{ item[0] }}/{{ files_dest_path }}/product_images/{{ item[1] }} owner=www-data group=www-data mode:0644 with_nested: - drupal_sites - files_to_update tags: images notify: clear drupal cache ! - name: Image Update | Update Cache file: > path={{ item[0] }}/{{ files_dest_path }}/{{ item[1] }}/{{ item[2] }}
 state=absent with_nested: - drupal_sites - image_cache_folders - files_to_update tags: images notify: clear drupal cache ANSIBLE & DRUPAL: EXAMPLE 2 ./roles/updateimages/tasks/main.yml
  • 59. blue-bag RUN UPDATE TASK: [updateimages | Image Update | Products] **************** ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) => {"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/ var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path": “/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size": 193018, "state": "file", "uid": 33} changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg']) => {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/ var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33", "mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/ ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33} changed: [server.example.com] => (item=['/var/www/mysite/dev/htdocs', 'fa9001.jpg']) => {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/var/ www/mysite/dev/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33", "mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/ ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33} $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 60. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg", "state": "absent"} PLAY RECAP ******************************************************************** server.example.com : ok=2 changed=2 unreachable=0 failed=0
  • 61. blue-bag ANSIBLE TOOLS Automate playbook runs Collaborate Log Permissions Scheduled runs Push button deployment Free for < 10 nodes
  • 62. blue-bag SOME USEFUL TOOLS Unicode Code Converter: 
 http://rishida.net/tools/conversion/ PHP Decoder / Hex Decoder: 
 http://ddecode.com/phpdecoder/ Permissions Calculator: http://permissions-calculator.org/ RegEx:
 https://regex101.com/#pcre Network Tools: 
 CIDR masks etc
 http://pingtool.org/
  • 63. blue-bag CONCLUSION / QUESTIONS For all local development use VLAD http://git.io/DCB-Vlad For Ansible get the books https://leanpub.com/ ansible-for-devops http://www.ansible.com/ ansible-book

Editor's Notes

  1. Context: Self hosted sites - any server you ssh to and undertake repeatable tasks if you write shell scripts, scp, have build specs for your servers take home: sticker and t shirt
  2. Trends in security exploits in 2014 - Common Vulnerabilities and Exposures (CVE) system Whereas CVE numbers used to be the preserve of Sysadmins - now they get branded and have gone mainstream - even the Drupal exploit ‘Drupalgeddon’ made it to the BBC web site.
  3. Although probably not significantly more exploits - probably affect more people. Previously shared hosting - now everyone can have a server Easy to get - easy to not secure! Provider no longer cares as being a VM it is sandboxed from their infrastructure (to a degree) We are seeing more manual exploits - human captcha on comments etc more coordinated attacks - wave of similar attacks from set of compromised machines
  4. As soon as you ‘acquire’ a server / public IP you are under attack You need to remove root access, secure all ports and lock down the server - then open up for whatever you are going to use it for.
  5. autodiscovery of variables, handlers, etc
  6. Apache is extremely intolerant of syntax errors. If you restart and there is an error the web server will stop and not restart. Always test before restarting and use graceful restart to allow existing request to complete.