nginx_configure.rb 8.74 KB
#
# Author:: Earth U (<sysadmin @ chromedia.com>)
# Cookbook Name:: cfe-nginx-php-fpm
# Recipe:: nginx_configure
#
# Copyright 2016, Chromedia Far East, Inc.
#
# 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.
#

include_recipe 'openssl::upgrade'

cb = 'cfe-nginx-php-fpm'

# Create necessary directories
inc_dir  = node[cb]['nginx']['inc_dir']
priv_dir = node[cb]['nginx']['priv_dir']

[ inc_dir, priv_dir ].each do |ndir|
  directory ndir do
    recursive true
  end
end

# Generate config files for each virtual server.
catch_all_def_false = node[cb]['nginx']['sites'].length > 1

# If at least one site is using TLS, configure a high modulus DH param
path_dhparam = "#{priv_dir}/dhparam.pem"
if node[cb]['nginx']['sites'].any? { |site| site[:ssl] }
  openssl_dhparam path_dhparam do
    key_length node[cb]['openssl']['dh_modulus']
    generator  2
  end
end

node[cb]['nginx']['sites'].each do |site|

  if site.is_a?(Array)
    site_sname = site[0]
    site = site[1]
  else
    site_sname = site[:server_name]
  end

  # Assign default values to attributes
  site_index    = site[:index]
  site_port     = site[:port]
  site_lopts    = site[:listen_opts]
  site_aliases  = site[:aliases] || []
  site_doc_root = site[:doc_root]
  site_alo      = site[:access_log_options]
  site_ssl      = site[:ssl]
  site_auth     = site[:auth]
  site_ins      = site[:init_statements] || []
  site_ss1      = site[:server_statements_1] || []
  site_ss2      = site[:server_statements_2] || []

  site_bl_robots = site.has_key?(:block_robots) ? site[:block_robots] : true
  site_bl_fav    = site.has_key?(:block_fav) ? site[:block_fav] : true

  site_logrobots = site.has_key?(:log_robots) ? site[:log_robots] : false
  site_loghidden = site.has_key?(:log_hidden) ? site[:log_hidden] : true

  temp_catch_all = site.has_key?(:catch_all) ? site[:catch_all] : true
  site_catch_all = catch_all_def_false ? false : temp_catch_all
  site_types     = ( site[:types] || [] ).uniq { |e| e[:type] }
  site_aheads    = site[:add_headers] || {
    'X-Frame-Options'                   => 'SAMEORIGIN',
    'X-Content-Type-Options'            => 'nosniff',
    'X-XSS-Protection'                  => '"1; mode=block"',
    'X-Permitted-Cross-Domain-Policies' => 'none'
  }

  path_crt     = ''
  path_key     = ''
  path_pass    = ''

  # If TLS/SSL is enabled, configure it:
  if site_ssl
    if site_ssl[:letsencrypt]
      le_base_dir = site_ssl[:le_base_dir] || '/etc/letsencrypt/live'
      le_sub_dir  = site_ssl[:le_sub_dir] || site_sname
      path_crt    = "#{le_base_dir}/#{le_sub_dir}/fullchain.pem"
      path_key    = "#{le_base_dir}/#{le_sub_dir}/privkey.pem"

    else
      path_crt = "#{priv_dir}/#{site_sname}.crt"
      path_key = "#{priv_dir}/#{site_sname}.key"

      if site_ssl[:cert].nil?
        Chef::Log.error('Missing SSL certificate')
        raise 'Missing SSL certificate'
      end

      if site_ssl[:key].nil?
        Chef::Log.error('Missing SSL key file')
        raise 'Missing SSL key file'
      end

      file path_crt do
        mode      0644
        content   site_ssl[:cert]
        sensitive true
        action    :create_if_missing
      end

      file path_key do
        mode      0644
        content   site_ssl[:key]
        sensitive true
        action    :create_if_missing
      end
    end
  end

  # If basic auth is enabled, create htpasswd file
  if site_auth
    path_pass = "#{priv_dir}/#{site_sname}.htpasswd"
    site_auth[:users].each do |auser, apass|
      execute "Generate #{path_pass}" do
        command   "printf \"#{auser}:"\
                  "$( openssl passwd -apr1 '#{apass}' )"\
                  "\\n\" >> #{path_pass}"
        action    :run
        sensitive true
      end
    end
  end

  site_includes  = []
  site_upstreams = []
  # site_upstreams element:
  # {
  #   :name => 'string',
  #   :servers => [
  #     '127.0.0.1:9000',
  #     '/var/run/php-fpm.sock'
  #   ]
  # }

  # Create necessary include files for each type of this site
  site_types.each do |stype|

    # Set default attributes for each site type
    stype_subp = stype[:subpath] ? stype[:subpath].gsub(/^\/+|\/+$|\s/, '') : ''
    stype_subp = stype_subp.length > 0 ? "#{stype_subp}/" : stype_subp

    stype_ads = stype[:add_statements] || []
    stype_ups = stype[:upstream_name] ||
      "#{stype[:type]}_#{site_sname.gsub('.', '_')}"

    stype_logstatic = stype.has_key?(:log_static) ? stype[:log_static] : false
    stype_statics = if stype.has_key?(:static_types)
      stype[:static_types]
    else
      %w{
        js css ogg ogv svg svgz eot otf woff mp4 ttf rss atom
        jpg jpeg gif png ico zip tgz gz rar bz2
        doc xls exe ppt tar mid midi wav bmp rtf
      }
    end

    vars = {
      :subpath        => stype_subp,
      :upstream_name  => stype_ups,
      :add_statements => stype_ads,
      :static_types   => stype_statics,
      :log_static     => stype_logstatic
    }

    if stype[:upstream_servers] && stype[:upstream_servers].length > 0
      site_upstreams.push( {
        :name    => stype_ups,
        :servers => stype[:upstream_servers] || []
      } )
    end

    case stype[:type]
    # BASIC PHP SITE
    when 'basic', 'basic_php'
      vars[:index] = site_index
      vars[:fastcgi_intercept_errors] = 
        stype.has_key?(:fastcgi_intercept_errors) ?
        stype[:fastcgi_intercept_errors] : false

      template "#{inc_dir}/type_basic_php_#{site_sname}" do
        source 'inc_type_basic.erb'
        mode   0644
        variables vars
      end
      site_includes.push("#{inc_dir}/type_basic_php_#{site_sname}")

    # STANDARD WORDPRESS SITE
    when 'wordpress'
      vars[:index] = site_index
      vars[:loginpage_statements] = stype[:loginpage_statements] || []
      vars[:fastcgi_intercept_errors] = 
        stype.has_key?(:fastcgi_intercept_errors) ?
        stype[:fastcgi_intercept_errors] : false

      template "#{inc_dir}/inc_type_wordpress_#{site_sname}" do
        source 'inc_type_wordpress.erb'
        mode   0644
        variables vars
      end
      site_includes.push("#{inc_dir}/inc_type_wordpress_#{site_sname}")

    # REVERSE PROXY WEBSERVER WITH WEBSOCKET
    when 'webserver', 'webserver_ws'
      template "#{inc_dir}/inc_type_webserver_ws_#{site_sname}" do
        source 'inc_type_webserver.erb'
        mode   0644
        variables vars
      end
      site_ins.push("map $http_upgrade $connection_upgrade {\n"\
                    "    default upgrade;\n"\
                    "    ''      close;\n"\
                    "}")
      site_includes.push("#{inc_dir}/inc_type_webserver_ws_#{site_sname}")

    # GENERIC REVERSE PROXY WEBSERVER
    when 'webserver_basic'
      vars[:cparams] = stype[:custom_params] || {}

      template "#{inc_dir}/inc_type_webserver_basic_#{site_sname}" do
        source    stype[:source] || 'inc_type_webserver_basic.erb'
        cookbook  stype[:cookbook] || cookbook_name
        mode      0644
        variables vars
      end
      site_includes.push("#{inc_dir}/inc_type_webserver_basic_#{site_sname}")

    else
      Chef::Log.error("Unknown site type: #{stype[:type]}")
      raise 'Unknown site type'
    end
  end

  # Create the main config file for this site
  template "#{node['nginx']['dir']}/sites-available/#{site_sname}" do
    source   'nginx_site.conf.erb'
    mode     0644
    notifies :restart, 'service[nginx]', :delayed
    variables(
      :server_name => site_sname,
      :port        => site_port,
      :listen_opts => site_lopts,
      :aliases     => site_aliases,
      :doc_root    => site_doc_root,
      :index       => site_index,
      :ssl         => site_ssl,
      :auth        => site_auth,

      :catch_all          => site_catch_all,
      :access_log_options => site_alo,
      :log_robots         => site_logrobots,
      :log_hidden         => site_loghidden,

      :block_robots => site_bl_robots,
      :block_fav    => site_bl_fav,

      :path_crt     => path_crt,
      :path_key     => path_key,
      :path_pass    => path_pass,
      :path_dhparam => path_dhparam,

      :upstreams           => site_upstreams,
      :includes            => site_includes,
      :init_statements     => site_ins,
      :add_headers         => site_aheads,
      :server_statements_1 => site_ss1,
      :server_statements_2 => site_ss2,

      :log_dir => node['nginx']['log_dir']
    )
  end

  nginx_site site_sname do
    enable true
  end
end