nginx_configure.rb 8.19 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.
#

# Create necessary directories
inc_dir  = node['cfe-nginx-php-fpm']['nginx']['inc_dir']
priv_dir = node['cfe-nginx-php-fpm']['nginx']['priv_dir']

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

# The restrictions file containing default rules for virtual servers.
path_rest = "#{inc_dir}/inc_restrictions"
restfa    = node['cfe-nginx-php-fpm']['nginx']['restriction_file']
template path_rest do
  action :create_if_missing
  mode   0644
  variables(
    :log_robots   => restfa['log_robots'],
    :log_hidden   => restfa['log_hidden'],
    :log_static   => restfa['log_static'],
    :static_types => restfa['static_types']
  )
end

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

node['cfe-nginx-php-fpm']['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] || nil
  site_aliases  = site[:aliases] || []
  site_doc_root = site[:doc_root] || nil
  site_alo      = site[:access_log_options] || nil
  site_ssl      = site[:ssl] || nil
  site_auth     = site[:auth] || nil
  site_ins      = site[:init_statements] || []
  site_ss1      = site[:server_statements_1] || []
  site_ss2      = site[:server_statements_2] || []

  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'
  }

  # 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

    # Configure a high modulus DH param
    path_dhparam = "#{priv_dir}/#{site_sname}.dhparam"
    dh_modulus   = site_ssl[:dh_modulus] || 4096
    execute "openssl dhparam -out #{path_dhparam} #{dh_modulus}" do
      not_if { ::File.exist?(path_dhparam) }
    end

  else
    path_crt     = ''
    path_key     = ''
    path_dhparam = ''
  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
        not_if    { ::File.exist?(path_pass) }
      end
    end
  else
    path_pass = ''
  end

  site_includes = [path_rest]
  upstreams     = []
  # 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|
    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('.', '_')}"

    upstreams.push( {
      :name    => stype_ups,
      :servers => stype[:upstream_servers] || []
    } )

    case stype[:type]
    # BASIC PHP SITE
    when 'basic'
      stype_intererror = stype.has_key?(:fastcgi_intercept_errors) ?
        stype[:fastcgi_intercept_errors] : false

      template "#{inc_dir}/inc_type_basic_#{site_sname}" do
        source 'inc_type_basic.erb'
        mode   0644
        action :create_if_missing
        variables(
          :index                    => site_index,
          :subpath                  => stype_subp,
          :upstream_name            => stype_ups,
          :add_statements           => stype_ads,
          :fastcgi_intercept_errors => stype_intererror
        )
      end
      site_includes.push("#{inc_dir}/inc_type_basic_#{site_sname}")

    # STANDARD WORDPRESS SITE
    when 'wordpress'
      stype_intererror = 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
        action :create_if_missing
        variables(
          :index                    => site_index,
          :subpath                  => stype_subp,
          :upstream_name            => stype_ups,
          :add_statements           => stype_ads,
          :loginpage_statements     => stype[:loginpage_statements] || [],
          :fastcgi_intercept_errors => stype_intererror
        )
      end
      site_includes.push("#{inc_dir}/inc_type_wordpress_#{site_sname}")

    # REVERSE PROXY WEBSERVER
    when 'webserver'
      template "#{inc_dir}/inc_type_webserver_#{site_sname}" do
        source 'inc_type_webserver.erb'
        mode   0644
        action :create_if_missing
        variables(
          :subpath        => stype_subp,
          :upstream_name  => stype_ups,
          :add_statements => stype_ads
        )
      end
      site_ins.push("map $http_upgrade $connection_upgrade {\n"\
                    "    default upgrade;\n"\
                    "    ''      close;\n"\
                    "}")
      site_includes.push("#{inc_dir}/inc_type_webserver_#{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'
    action   :create_if_missing
    mode     0644
    notifies :restart, 'service[nginx]', :delayed
    variables(
      :server_name => site_sname,
      :aliases     => site_aliases,
      :doc_root    => site_doc_root,
      :index       => site_index,
      :ssl         => site_ssl,
      :auth        => site_auth,

      :access_log_options => site_alo,
      :catch_all          => site_catch_all,

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

      :upstreams           => 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