Commit c7e0ee91c3812f4359cc3553b53d79d7bdbeada5

Authored by nollieheel
1 parent b928210a

Create version 0.1.0 of the cookbook.

1 1 ---
2 2 driver:
3 3 name: ec2
  4 + aws_ssh_key_id: cfe_stg_20160222
  5 + security_group_ids: ["sg-7f6fda18"]
  6 + region: us-west-2
  7 + availability_zone: b
  8 + subnet_id: subnet-d530d8b1
  9 + instance_type: t2.micro
  10 + associate_public_ip: true
  11 + require_chef_omnibus: true
  12 + shared_credentials_profile: earth
4 13
5 14 provisioner:
6 15 name: chef_solo
7 16
8 17 platforms:
9 18 - name: ubuntu-14.04
10   - - name: centos-7.1
  19 + driver:
  20 + image_id: ami-3d2cce5d
  21 + transport:
  22 + username: ubuntu
  23 + ssh_key: ~/.ssh/cfe_stg_20160222.pem
11 24
12 25 suites:
13 26 - name: default
... ...
1   -Copyright (C) 2016 YOUR_NAME
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
2 4
3   -All rights reserved - Do Not Redistribute
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "{}"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright 2016 Chromedia Far East, Inc.
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
... ...
1 1 # cfe-nginx-php-fpm-cookbook
2 2
3   -TODO: Enter the cookbook description here.
  3 +Installs PHP5-FPM, Nginx, Postfix, and MariaDB client on a server. Also sets up webserver configs for all virtual servers, including TLS and basic auth.
  4 +
  5 +Can also auto-generate config files for certain site types, such as Wordpress, etc. (right now it's just Wordpress).
4 6
5 7 ## Supported Platforms
6 8
7   -TODO: List your supported platforms.
  9 +Ubuntu 14.04
8 10
9 11 ## Attributes
10 12
... ... @@ -16,11 +18,47 @@ TODO: List your supported platforms.
16 18 <th>Default</th>
17 19 </tr>
18 20 <tr>
19   - <td><tt>['cfe-nginx-php-fpm']['bacon']</tt></td>
  21 + <td><tt>['cfe-nginx-php-fpm']['php_fastcgi_socket']</tt></td>
  22 + <td>String</td>
  23 + <td>The socket used by PHP-FPM. Set to boolean false to disable PHP-FPM.</td>
  24 + <td><tt>'127.0.0.1:9000'</tt></td>
  25 + </tr>
  26 + <tr>
  27 + <td><tt>['cfe-nginx-php-fpm']['postfix']['email_domain']</tt></td>
  28 + <td>String</td>
  29 + <td>Email domain to be used by Postfix.</td>
  30 + <td><tt>''</tt></td>
  31 + </tr>
  32 + <tr>
  33 + <td><tt>['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_robots']</tt></td>
  34 + <td>Boolean</td>
  35 + <td>Whether to log crawler access in Nginx.</td>
  36 + <td><tt>false</tt></td>
  37 + </tr>
  38 + <tr>
  39 + <td><tt>['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_hidden']</tt></td>
20 40 <td>Boolean</td>
21   - <td>whether to include bacon</td>
  41 + <td>Whether to log attempted accesses to hidden files/folders in Nginx.</td>
22 42 <td><tt>true</tt></td>
23 43 </tr>
  44 + <tr>
  45 + <td><tt>['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_static']</tt></td>
  46 + <td>Boolean</td>
  47 + <td>Whether to log accesses to static files in Nginx.</td>
  48 + <td><tt>false</tt></td>
  49 + </tr>
  50 + <tr>
  51 + <td><tt>['cfe-nginx-php-fpm']['nginx']['restriction_file']['static_types']</tt></td>
  52 + <td>Array</td>
  53 + <td>An array of strings denoting the file extensions of what will be considered static files by Nginx with regards to logging (see previous attribute).</td>
  54 + <td><tt>(see default attribute file)</tt></td>
  55 + </tr>
  56 + <tr>
  57 + <td><tt>['cfe-nginx-php-fpm']['nginx']['sites']</tt></td>
  58 + <td>Array/Hash</td>
  59 + <td>Values that define the virtual servers to be hosted by Nginx.</td>
  60 + <td><tt>(see default attribute file)</tt></td>
  61 + </tr>
24 62 </table>
25 63
26 64 ## Usage
... ... @@ -32,11 +70,11 @@ Include `cfe-nginx-php-fpm` in your node's `run_list`:
32 70 ```json
33 71 {
34 72 "run_list": [
35   - "recipe[cfe-nginx-php-fpm::default]"
  73 + "recipe[cfe-nginx-php-fpm]"
36 74 ]
37 75 }
38 76 ```
39 77
40 78 ## License and Authors
41 79
42   -Author:: YOUR_NAME (<YOUR_EMAIL>)
  80 +Author:: Earth U. (<sysadmin@chromedia.com>)
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Attribute:: default
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +# The socket used PHP-FPM. If false, fastcgi is not used.
  22 +# Examples:
  23 +# '127.0.0.1:9000'
  24 +# '/var/run/php-fpm.sock'
  25 +# false
  26 +default['cfe-nginx-php-fpm']['php_fastcgi_socket'] = '127.0.0.1:9000'
  27 +
  28 +# Setting 'update_cacert' to true will get the latest cacert from
  29 +# http://curl.haxx.se/ca/cacert.pem and use that as CAFile for postfix.
  30 +default['cfe-nginx-php-fpm']['postfix']['update_cacert'] = true
  31 +default['cfe-nginx-php-fpm']['postfix']['email_domain'] = 'example.com'
  32 +
  33 +# If your php-fpm pool is not named 'www', then delete
  34 +# the default one 'www' automatically installed by php-fpm
  35 +default['cfe-nginx-php-fpm']['php-fpm']['delete_pool_www'] = true
  36 +
  37 +default['cfe-nginx-php-fpm']['nginx']['inc_dir'] =
  38 + "#{node['nginx']['dir']}/sites-available/include"
  39 +default['cfe-nginx-php-fpm']['nginx']['priv_dir'] =
  40 + "#{node['nginx']['dir']}/private"
  41 +
  42 +default['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_robots'] = false
  43 +default['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_hidden'] = true
  44 +default['cfe-nginx-php-fpm']['nginx']['restriction_file']['log_static'] = false
  45 +default['cfe-nginx-php-fpm']['nginx']['restriction_file']['static_types'] = %w{
  46 + js css ogg ogv svg svgz eot otf woff mp4 ttf rss atom
  47 + jpg jpeg gif png ico zip tgz gz rar bz2
  48 + doc xls exe ppt tar mid midi wav bmp rtf
  49 +}
  50 +
  51 +default['cfe-nginx-php-fpm']['nginx']['sites'] = [
  52 + {
  53 + :server_name => 'example.com',
  54 + :aliases => ['www.example.com'],
  55 + :doc_root => '/var/www/example.com',
  56 + :index => 'index.php',
  57 +
  58 + # Access log options as one long string. Default: false
  59 + #:access_log_options => '<some options>'
  60 +
  61 + # Whether to include a default virtual server named '_' or not.
  62 + # If there is more than one server given in this 'sites' array,
  63 + # 'catch_all' value will always be overriden to 'false'.
  64 + # Default: true
  65 + #:catch_all => true
  66 +
  67 + # Necessary values for SSL/TLS setup. Default: :ssl => false
  68 + #:ssl => {
  69 + # :cert => '[contents of chain cert here]',
  70 + # :key => '[contents of cert private key here]',
  71 + # :dh_modulus => 2048,
  72 + # :self_signed => false,
  73 + # :hsts_max_age => '15758000',
  74 + # :hsts_include_subdomains => true
  75 + #}
  76 +
  77 + # Necessary values for Basic Auth setup. Default: :auth => false
  78 + #:auth => {
  79 + # :msg => 'Restricted Area. Please authenticate.',
  80 + # :users => {
  81 + # 'example_user' => 'secretpassword123'
  82 + # }
  83 + #}
  84 +
  85 + # An array of strings that will be included as statements in the main
  86 + # nginx config file for this server. Default: []
  87 + #:custom_statements => []
  88 +
  89 + # Enumerates the different site types this server supports.
  90 + # Possible elements of :types are (only :type is mandatory):
  91 + # {
  92 + # :type => 'basic',
  93 + # :subpath => ''
  94 + # }
  95 + # {
  96 + # :type => 'wordpress',
  97 + # :subpath => '',
  98 + # :fastcgi_intercept_errors => false,
  99 + # :loginpage_statements => [] # An array of strings to be
  100 + # # written on the config for the
  101 + # # /wp-login.php and /wp-admin pages.
  102 + # }
  103 + :types => [ { :type => 'basic' } ]
  104 + }
  105 +]
  106 +
  107 +#
  108 +# php-fpm cookbook
  109 +#
  110 +default['php-fpm']['user'] = node['nginx']['user']
  111 +default['php-fpm']['group'] = node['nginx']['group']
  112 +
  113 +default['php-fpm']['skip_repository_install'] = true
  114 +default['php-fpm']['pools'] = [
  115 + # Most likely, just use one pool for all PHP needs.
  116 + # (But there are exceptions, of course)
  117 + {
  118 + :name => 'example_pool',
  119 + :enable => true,
  120 + :listen => node['cfe-nginx-php-fpm']['php_fastcgi_socket'],
  121 +
  122 + :user => node['nginx']['user'],
  123 + :group => node['nginx']['group'],
  124 +
  125 + :max_requests => 500,
  126 + :max_children => 50,
  127 +
  128 + :access_log => false,
  129 + :catch_workers_output => 'no',
  130 + #:access_log => true,
  131 + #:catch_workers_output => 'yes',
  132 +
  133 + :process_manager => value_for_platform(
  134 + 'ubuntu' => {
  135 + '10.04' => 'dynamic'
  136 + },
  137 + 'default' => 'ondemand'
  138 + ),
  139 +
  140 + # Only used if process_manager is 'dynamic':
  141 + :start_servers => 5,
  142 + :min_spare_servers => 5,
  143 + :max_spare_servers => 35,
  144 +
  145 + :php_options => {
  146 + 'php_admin_value[cgi.fix_pathinfo]' => '0',
  147 + 'php_admin_value[expose_php]' => 'Off',
  148 + 'php_value[upload_max_filesize]' => '5M',
  149 + 'php_value[post_max_size]' => '10M'
  150 + }
  151 + }
  152 +]
  153 +
  154 +#
  155 +# mariadb cookbook
  156 +#
  157 +default['mariadb']['install']['type'] = 'package'
  158 +default['mariadb']['install']['version'] = '5.5'
  159 +
  160 +#
  161 +# postfix cookbook
  162 +#
  163 +default['postfix']['main']['myorigin'] = '$mydomain'
  164 +default['postfix']['main']['myhostname'] =
  165 + node['cfe-nginx-php-fpm']['postfix']['email_domain']
  166 +default['postfix']['main']['mydomain'] =
  167 + node['cfe-nginx-php-fpm']['postfix']['email_domain']
  168 +default['postfix']['main']['mydestination'] =
  169 + ['localhost.localdomain', 'localhost']
  170 +
  171 +#
  172 +# nginx cookbook
  173 +#
  174 +default['nginx']['version'] = '1.9.14'
  175 +default['nginx']['install_method'] = 'package'
  176 +default['nginx']['package_name'] = 'nginx'
  177 +default['nginx']['repo_source'] = 'nginx'
  178 +default['nginx']['upstream_repository'] =
  179 + 'http://nginx.org/packages/mainline/ubuntu'
  180 +# Set pid file initially in accordance with Ubuntu 14.04
  181 +# nginx package's pid file. Otherwise, it fails to restart.
  182 +default['nginx']['pid'] = '/var/run/nginx.pid'
  183 +default['nginx']['default_site_enabled'] = false
  184 +default['nginx']['client_max_body_size'] = '10m'
  185 +default['nginx']['event'] = 'epoll'
  186 +default['nginx']['worker_processes'] = 'auto'
  187 +default['nginx']['worker_connections'] = 1_024
  188 +default['nginx']['keepalive_timeout'] = 15
  189 +default['nginx']['keepalive_requests'] = 200
  190 +default['nginx']['disable_access_log'] = false
  191 +default['nginx']['server_tokens'] = 'off'
  192 +default['nginx']['gzip_comp_level'] = '5'
  193 +default['nginx']['extra_configs'] = {
  194 + 'reset_timedout_connection' => 'on'
  195 +}
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Definition:: php_ext
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +# Install a PHP extension
  22 +
  23 +define :php_ext, :action => :install do
  24 + # Update these lists as needed.
  25 + rhel_only = [
  26 + 'xml',
  27 + 'mbstring'
  28 + ]
  29 + debian_enmod_required = [
  30 + 'mcrypt'
  31 + ]
  32 +
  33 + case node['platform_family']
  34 + when 'rhel', 'fedora'
  35 + package "php-#{params[:name]}" do
  36 + action params[:action]
  37 + end
  38 +
  39 + when 'debian'
  40 + unless rhel_only.include?(params[:name])
  41 + package "php5-#{params[:name]}" do
  42 + action params[:action]
  43 + end
  44 +
  45 + if debian_enmod_required.include?(params[:name])
  46 + execute "php5enmod #{params[:name]}" do
  47 + notifies :restart, 'service[php-fpm]', :immediately
  48 + end
  49 + end
  50 + end
  51 + end
  52 +end
... ...
1 1 name 'cfe-nginx-php-fpm'
2   -maintainer 'YOUR_NAME'
3   -maintainer_email 'YOUR_EMAIL'
4   -license 'All rights reserved'
5   -description 'Installs/Configures cfe-nginx-php-fpm'
6   -long_description 'Installs/Configures cfe-nginx-php-fpm'
  2 +maintainer 'Chromedia Far East, Inc.'
  3 +maintainer_email 'sysadmin@chromedia.com'
  4 +license 'Apache License'
  5 +description 'Simplifies setup of Nginx+PHP-FPM in Chromedia.'
  6 +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
7 7 version '0.1.0'
  8 +
  9 +{
  10 + 'php-fpm' => '0.7.5',
  11 + 'mariadb' => '0.2.12',
  12 + 'postfix' => '3.6.2',
  13 + 'nginx' => '2.7.6'
  14 +}.each { |cb, ver| depends cb, '~> ' + ver }
  15 +
  16 +supports 'ubuntu', '>= 14.04'
... ...
1 1 #
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
2 3 # Cookbook Name:: cfe-nginx-php-fpm
3 4 # Recipe:: default
4 5 #
5   -# Copyright (C) 2016 YOUR_NAME
  6 +# Copyright 2016, Chromedia Far East, Inc.
6 7 #
7   -# All rights reserved - Do Not Redistribute
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
8 19 #
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Recipe:: mariadb_client
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +include_recipe 'mariadb::client'
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Recipe:: nginx
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +include_recipe 'nginx'
  22 +
  23 +attribs = node['cfe-nginx-php-fpm']['nginx']
  24 +inc_dir = attribs['inc_dir']
  25 +priv_dir = attribs['priv_dir']
  26 +
  27 +[ inc_dir, priv_dir ].each do |ndir|
  28 + directory ndir do
  29 + recursive true
  30 + end
  31 +end
  32 +
  33 +# The restrictions.conf file containing default rules for virtual servers.
  34 +path_rest = "#{inc_dir}/restrictions.conf"
  35 +template path_rest do
  36 + action :create_if_missing
  37 + mode 0644
  38 + variables(
  39 + :log_robots => attribs['restriction_file']['log_robots'],
  40 + :log_hidden => attribs['restriction_file']['log_hidden'],
  41 + :log_static => attribs['restriction_file']['log_static'],
  42 + :static_types => attribs['restriction_file']['static_types']
  43 + )
  44 +end
  45 +
  46 +# Some basic "include" files for PHP
  47 +path_bpf = "#{inc_dir}/inc_basic_php_fastcgi"
  48 +template path_bpf do
  49 + mode 0644
  50 + variables(
  51 + :socket => node['cfe-nginx-php-fpm']['php_fastcgi_socket']
  52 + )
  53 + only_if { node['cfe-nginx-php-fpm']['php_fastcgi_socket'] }
  54 +end
  55 +
  56 +catch_all_def_false = attribs['sites'].length > 1
  57 +
  58 +attribs['sites'].each do |site|
  59 +
  60 + if site.is_a?(Array)
  61 + site_sname = site[0]
  62 + site = site[1]
  63 + else
  64 + site_sname = site[:server_name]
  65 + end
  66 +
  67 + site_index = site[:index] || 'index.php'
  68 + site_aliases = site[:aliases] || []
  69 + site_doc_root = site[:doc_root] || ''
  70 + site_index = site[:index] || 'index.html'
  71 + site_ssl = site[:ssl] || false
  72 + site_auth = site[:auth] || false
  73 + site_alo = site[:access_log_options] || false
  74 + site_cs = site[:custom_statements] || []
  75 +
  76 + site_types = ( site[:types] || [] ).uniq { |e| e[:type] }
  77 +
  78 + temp_catch_all = site.has_key?(:catch_all) ? site[:catch_all] : true
  79 + site_catch_all = catch_all_def_false ? false : temp_catch_all
  80 +
  81 + path_crt = "#{priv_dir}/#{site_sname}.crt"
  82 + path_key = "#{priv_dir}/#{site_sname}.key"
  83 + path_pass = "#{priv_dir}/#{site_sname}.htpasswd"
  84 + path_dhparam = "#{priv_dir}/#{site_sname}.dhparam"
  85 +
  86 + # If TLS/SSL is enabled, create necessary files
  87 + if site_ssl
  88 + if site_ssl[:cert].nil?
  89 + Chef::Log.error('Missing SSL certificate')
  90 + raise 'Missing SSL certificate'
  91 + end
  92 +
  93 + if site_ssl[:key].nil?
  94 + Chef::Log.error('Missing SSL key file')
  95 + raise 'Missing SSL key file'
  96 + end
  97 +
  98 + file path_crt do
  99 + mode 0644
  100 + content site_ssl[:cert]
  101 + sensitive true
  102 + action :create_if_missing
  103 + end
  104 +
  105 + file path_key do
  106 + mode 0644
  107 + content site_ssl[:key]
  108 + sensitive true
  109 + action :create_if_missing
  110 + end
  111 +
  112 + dh_modulus = site_ssl[:dh_modulus] || 2048
  113 + execute "openssl dhparam -out #{path_dhparam} #{dh_modulus}" do
  114 + not_if { ::File.exist?(path_dhparam) }
  115 + end
  116 + end
  117 +
  118 + # If basic auth is enabled, create htaccess file
  119 + if site_auth
  120 + site_auth[:users].each do |auser, apass|
  121 + execute "Generate #{path_pass}" do
  122 + command "printf \"#{auser}:"\
  123 + "$( openssl passwd -apr1 '#{apass}' )"\
  124 + "\\n\" >> #{path_pass}"
  125 + action :run
  126 + sensitive true
  127 + not_if { ::File.exist?(path_pass) }
  128 + end
  129 + end
  130 + end
  131 +
  132 + site_includes = []
  133 +
  134 + # Create necessary include files for each type of this site
  135 + site_types.each do |stype|
  136 + stype_subpath = stype[:subpath] || ''
  137 +
  138 + case stype[:type]
  139 + # BASIC PHP SITE
  140 + when 'basic'
  141 + template "#{inc_dir}/inc_type_basic_#{site_sname}" do
  142 + source 'inc_type_basic.erb'
  143 + mode 0644
  144 + action :create_if_missing
  145 + variables(
  146 + :index => site_index,
  147 + :subpath => stype_subpath,
  148 + :basic_php_fastcgi => path_bpf
  149 + )
  150 + end
  151 + site_includes.push("#{inc_dir}/inc_type_basic_#{site_sname}")
  152 +
  153 + # STANDARD WORDPRESS SITE
  154 + when 'wordpress'
  155 + template "#{inc_dir}/inc_type_wordpress_#{site_sname}" do
  156 + source 'inc_type_wordpress.erb'
  157 + mode 0644
  158 + action :create_if_missing
  159 + variables(
  160 + :index => site_index,
  161 + :subpath => stype_subpath,
  162 + :basic_php_fastcgi => path_bpf,
  163 + :loginpage_statements => stype[:loginpage_statements] || [],
  164 + :fastcgi_intercept_errors => stype[:fastcgi_intercept_errors] || false
  165 + )
  166 + end
  167 + site_includes.push("#{inc_dir}/inc_type_wordpress_#{site_sname}")
  168 +
  169 + else
  170 + Chef::Log.error("Unknown site type: #{stype[:type]}")
  171 + raise 'Missing SSL key file'
  172 + end
  173 + end
  174 +
  175 + # Create the main config file for this site
  176 + template "#{node['nginx']['dir']}/sites-available/#{site_sname}" do
  177 + source 'nginx_site.conf.erb'
  178 + action :create_if_missing
  179 + mode 0644
  180 + notifies :restart, 'service[nginx]'
  181 + variables(
  182 + :server_name => site_sname,
  183 + :aliases => site_aliases,
  184 + :doc_root => site_doc_root,
  185 + :index => site_index,
  186 + :ssl => site_ssl,
  187 + :auth => site_auth,
  188 +
  189 + :access_log_options => site_alo,
  190 + :catch_all => site_catch_all,
  191 +
  192 + :path_crt => path_crt,
  193 + :path_key => path_key,
  194 + :path_pass => path_pass,
  195 + :path_dhparam => path_dhparam,
  196 + :path_rest => path_rest,
  197 +
  198 + :includes => site_includes,
  199 + :custom_statements => site_cs
  200 + )
  201 + end
  202 +
  203 + nginx_site site_sname do
  204 + enable true
  205 + end
  206 +end
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Recipe:: php-fpm
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +package 'openssl'
  22 +include_recipe 'php-fpm'
  23 +
  24 +# For Ubuntu 14.04 Upstart bug where PHP service does not respond
  25 +# well to the reload command.
  26 +# (Or maybe just delete /etc/init.d/php5-fpm?)
  27 +if node['platform'] == 'ubuntu' and node['platform_version'].to_f == 14.04
  28 + file '/etc/init/php5-fpm.override' do
  29 + content "reload signal USR2\n"
  30 + action :create_if_missing
  31 + end
  32 +end
  33 +
  34 +if node['cfe-nginx-php-fpm']['php-fpm']['delete_pool_www']
  35 + www = "#{node['php-fpm']['pool_conf_dir']}/www.conf"
  36 + file 'delete_pool_www' do
  37 + path www
  38 + action :delete
  39 + only_if { ::File.exist?(www) }
  40 + notifies :restart, 'service[php-fpm]'
  41 + end
  42 +end
  43 +
  44 +# Some default PHP extensions
  45 +php_ext 'mysql'
  46 +php_ext 'cli'
  47 +php_ext 'curl' do
  48 + action :upgrade
  49 +end
... ...
  1 +#
  2 +# Author:: Earth U (<sysadmin@chromedia.com>)
  3 +# Cookbook Name:: cfe-nginx-php-fpm
  4 +# Recipe:: postfix
  5 +#
  6 +# Copyright 2016, Chromedia Far East, Inc.
  7 +#
  8 +# Licensed under the Apache License, Version 2.0 (the "License");
  9 +# you may not use this file except in compliance with the License.
  10 +# You may obtain a copy of the License at
  11 +#
  12 +# http://www.apache.org/licenses/LICENSE-2.0
  13 +#
  14 +# Unless required by applicable law or agreed to in writing, software
  15 +# distributed under the License is distributed on an "AS IS" BASIS,
  16 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17 +# See the License for the specific language governing permissions and
  18 +# limitations under the License.
  19 +#
  20 +
  21 +include_recipe 'postfix'
  22 +
  23 +remote_file node['postfix']['cafile'] do
  24 + source 'http://curl.haxx.se/ca/cacert.pem'
  25 + action :create
  26 + only_if { node['cfe-nginx-php-fpm']['postfix']['update_cacert'] }
  27 +end
... ...
  1 +# Generated by Chef
  2 +#
  3 +
  4 +# No need to enable this if PHP and Nginx share the same filesystem
  5 +#fastcgi_index index.php;
  6 +
  7 +# You should have "cgi.fix_pathinfo = 0;" in php.ini
  8 +fastcgi_split_path_info ^(.+\.php)(/.+)$;
  9 +include fastcgi_params;
  10 +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  11 +
  12 +<% socket = @socket[0] == '/' ? "unix:#{@socket}" : @socket -%>
  13 +fastcgi_pass <%= socket %>;
... ...
  1 +<%
  2 +subp = @subpath ? @subpath.gsub(/^\/+|\/$|\s/, '') : ''
  3 +subp = subp.length > 0 ? "#{subp}/" : subp
  4 +-%>
  5 +# Generated by Chef
  6 +#
  7 +# A basic PHP site config.
  8 +
  9 +# Pass all .php files onto a php-fpm/php-fcgi server.
  10 +#location ~ [^/]\.php(/|$) {
  11 +# Customized location directive to account for URL subpathing:
  12 +location ~ ^/<%= subp %>.+\.php(/|$) {
  13 + try_files $uri =404;
  14 +
  15 + # Enable only if implementing custom error pages
  16 + #fastcgi_intercept_errors on;
  17 +
  18 + include <%= @basic_php_fastcgi %>;
  19 +}
  20 +
  21 +location ~ ^/<%= subp %> {
  22 + try_files $uri $uri/ /<%= subp %><%= @index %>?$args;
  23 +}
... ...
  1 +<%
  2 +subp = @subpath ? @subpath.gsub(/^\/+|\/$|\s/, '') : ''
  3 +subp = subp.length > 0 ? "#{subp}/" : subp
  4 +-%>
  5 +# Generated by Chef
  6 +#
  7 +# WordPress single blog rules.
  8 +# Designed to be included in any server {} block.
  9 +
  10 +# Add trailing slash to */wp-admin requests.
  11 +rewrite /<%= subp %>wp-admin$ $scheme://$host$uri/ permanent;
  12 +
  13 +# Deny access to any files with a .php extension in the uploads directory
  14 +# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
  15 +location ~* /<%= subp %>(.+/)*(?:uploads|files)/.*\.php$ {
  16 + deny all;
  17 +}
  18 +
  19 +<% if @loginpage_statements.length > 0 -%>
  20 +location ~ ^/<%= subp %>(wp-admin|wp-login\.php) {
  21 +<% @loginpage_statements.each do |statement| -%>
  22 + <%= statement %>
  23 +<% end -%>
  24 +
  25 + location ~ ^/<%= subp %>.+\.php(/|$) {
  26 + try_files $uri =404;
  27 +
  28 +<% if @fastcgi_intercept_errors -%>
  29 + # Enable if implementing custom error pages
  30 + fastcgi_intercept_errors on;
  31 +
  32 +<% end -%>
  33 + include <%= @basic_php_fastcgi %>;
  34 + }
  35 +}
  36 +
  37 +<% end -%>
  38 +# Pass all PHP files to the fastcgi proxy
  39 +location ~ ^/<%= subp %>.+\.php(/|$) {
  40 + try_files $uri =404;
  41 +
  42 +<% if @fastcgi_intercept_errors -%>
  43 + # Enable if implementing custom error pages
  44 + fastcgi_intercept_errors on;
  45 +
  46 +<% end -%>
  47 + include <%= @basic_php_fastcgi %>;
  48 +}
  49 +
  50 +location ~ ^/<%= subp %> {
  51 + try_files $uri $uri/ /<%= subp %><%= @index %>?$args;
  52 +}
... ...
  1 +# Generated by Chef
  2 +#
  3 +<%
  4 +servers = [@server_name]
  5 +@aliases.each do |aname|
  6 + servers << aname
  7 +end
  8 +servers.uniq!
  9 +
  10 +if @catch_all
  11 +-%>
  12 +server {
  13 + listen 80 default_server;
  14 + server_name _;
  15 + return 444;
  16 +}
  17 +
  18 +<%
  19 +end
  20 +if @ssl
  21 + if @catch_all
  22 +-%>
  23 +server {
  24 + listen 443 default_server;
  25 + server_name _;
  26 + return 444;
  27 +}
  28 +
  29 +<%
  30 + end
  31 +-%>
  32 +server {
  33 + listen 80;
  34 + server_name <%= servers.join(' ') %>;
  35 + return 301 https://$server_name$request_uri;
  36 +}
  37 +
  38 +server {
  39 + listen 443 ssl;
  40 +
  41 + ssl_certificate <%= @path_crt %>;
  42 + ssl_certificate_key <%= @path_key %>;
  43 +<% unless @ssl[:self_signed] -%>
  44 +
  45 + # Modern cipher suite:
  46 + #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK;
  47 + # Medium compatibility cipher suite (compatible with IE7 WinXP):
  48 + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
  49 + ssl_prefer_server_ciphers on;
  50 + ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  51 + ssl_session_cache shared:SSL:10m;
  52 + ssl_dhparam <%= @path_dhparam %>;
  53 +<% end -%>
  54 +
  55 +<%
  56 + hsts = "max-age=#{@ssl[:hsts_max_age]};"
  57 + hsts << 'includeSubDomains;' if @ssl[:hsts_include_subdomains]
  58 +-%>
  59 + add_header Strict-Transport-Security "<%= hsts %>";
  60 +<%
  61 +else
  62 +-%>
  63 +server {
  64 + listen 80;
  65 +
  66 +<%
  67 +end
  68 +-%>
  69 + add_header X-Frame-Options SAMEORIGIN;
  70 + add_header X-Content-Type-Options nosniff;
  71 +
  72 + server_name <%= servers.join(' ') %>;
  73 +
  74 + root <%= @doc_root %>;
  75 + index <%= @index %>;
  76 +
  77 +<% if @auth -%>
  78 + auth_basic "<%= @auth[:msg] %>";
  79 + auth_basic_user_file <%= @path_pass %>;
  80 +
  81 +<% end -%>
  82 + access_log <%= node['nginx']['log_dir'] %>/<%= @server_name %>.access.log<% if @access_log_options %> <%= @access_log_options %><% end %>;
  83 + error_log <%= node['nginx']['log_dir'] %>/<%= @server_name %>.error.log;
  84 +
  85 + include <%= @path_rest %>;
  86 +<% @includes.each do |inc| -%>
  87 + include <%= inc %>;
  88 +<% end -%>
  89 +
  90 +<% @custom_statements.each do |sm| -%>
  91 + <%= sm %>
  92 +<% end -%>
  93 +}
... ...
  1 +# Generated by Chef
  2 +#
  3 +# Global restrictions configuration file.
  4 +# Designed to be included in any server {} block.
  5 +location = /favicon.ico {
  6 + log_not_found off;
  7 + access_log off;
  8 +}
  9 +
  10 +location = /robots.txt {
  11 + allow all;
  12 +<% unless @log_robots -%>
  13 + access_log off;
  14 + log_not_found off;
  15 +<% end -%>
  16 +}
  17 +
  18 +# Deny all attempts to access hidden files and folders
  19 +location ~ (^|/)\. {
  20 + deny all;
  21 +<% unless @log_hidden -%>
  22 + access_log off;
  23 + log_not_found off;
  24 +<% end -%>
  25 +}
  26 +
  27 +# Directives to send expires headers and (probably?) turn off 404 error logging.
  28 +location ~* ^.+\.(<%= @static_types.join('|') %>)$ {
  29 + expires max;
  30 +<% unless @log_static -%>
  31 + access_log off;
  32 + log_not_found off;
  33 +<% end -%>
  34 +}
... ...