Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions cookbooks/fb_nginx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
fb_nginx Cookbook
=================
Setup nginx

Requirements
------------
On Fedora/RHEL/CentOS, this requires EPEL.

Attributes
----------
* node['fb_nginx']['enable']
* node['fb_nginx']['enable_default_site']
* node['fb_nginx']['config']
* node['fb_nginx']['config'][$CONFIG]
* node['fb_nginx']['sites']
* node['fb_nginx']['sites'][$NAME]
* node['fb_nginx']['sysconfig']
* node['fb_nginx']['sysconfig'][$CONFIG]
* node['fb_nginx']['modules']

Usage
-----
This cookbook setups and runs nginx for you.

### Sites

Each site is configured with a name which is used mostly to modify the node
object later in the run, but also as a comment in the rendered config.

Each key in the sites hash can have a value of the following types:

* String - gets rendered as `#{key} #{value}`
* Array - gets rendered as `#{key} #{value.join(' ')}`
* Hash - the name is used to start a new block, and the nested hash is rendered
similarly to the outer hash.

It is important to note that keys that are repeated need their modifier to be
part of the key, i.e. "location /" and "location /foo"

For example:

```ruby
node.default['fb_nginx']['sites']['test_site'] = {
'listen 443' => 'ssl default_site'
'listen [::]:443]' => 'ssl default_site',
'server_name' => 'test.example.com',
'ssl_certificate' => '/etc/nginx/test.example.com.crt',
'ssl_certificate_key' => '/etc/nginx/test.example.com.key',
'location /' => {
'proxy_pass' => 'http://localhost:8080',
'proxy_set_header Host' => '$host',
'proxy_set_header X-Forwarded-Proto' => 'https',
'proxy_set_header X-Forwarded-For' => '$proxy_add_x_forwarded_for',
'proxy_set_header X-Real-Ip' => '$remote_addr',
},
}
```

Would be rendered like so:

```text
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name test.example.com
ssl_certificate /etc/nginx/test.example.com.crt;
ssl_certificate_key /etc/nginx/test.example.com.key;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-Ip $remote_addr;
}
}
```

Note there is a special `_create_self_signed_cert` setting you can put on any
site and if it's set, and if no certificate is found, `fb_nginx` will create a
self-signed cert for you.

### enable

When `enable` is set to `false`, the server will be shutdown, but the config
file will still be rendered.

### enable_default_site (Debian/Ubuntu only)

By default the Debian and Ubuntu Ngnix packages lay down a default webserver
config for a server listening on :80 and serving up files from `/var/www/html`.

This can be undesirable if you want a custom document root or customizations.
This cookbook provides an attribute, `node['fb_nginx']['enable_default_site']`,
to enable/disable this default configuration. The default is `true`, which
preserves the package default. Setting this to `false` will disable the
default :80 configuration.

Note this only applies to Debian and Ubuntu systems. Other distributions
may/may not have this default behaviour.

### config

`config` works similarly to `sites`, but is meant to be in a top-level config
file to configure things like `events` or the `http` subsystem. There's also a
`_global` key for configs that don't live in a context.

For example, you may do:

```ruby
node.default['fb_nginx']['config']['events'] = {
'multi_accept' => 'on',
}
```

Which would render like:

```text
events {
multi_accept on
}
```

Like in `sites`, the value can be a string, array, or hash and will be handled
the same way.

Note that the config is what goes into the global config file. We hard-code
includes that make the rest of the API work such as `sites` and `modules` into
the template, but everything else is overridable.

### modules

The `modules` entry is simply an array of modules to load. They are assumed to
be in the 'modules' directory and end with a `.so`. In other words:

```ruby
node.default['fb_nginx']['modules'] = ['ngx_http_geoip_module']
```

Would render as:

```text
load_module modules/ngx_http_geoip_module.so
```

### sysconfig

The `sysconfig` populates the variables that the init script or systemd unit
reads (in `/etc/sysconfig` or `/etc/default` depending on your distro). This
one is a simple 1-layer hash that is rendered as shell-readable key-value
pairs. All keys are upcased. Thus:

```ruby
node.default['fb_nginx']['sysconfig']['ulimit'] = '-n 4096'
```

will render as:

```text
ULIMIT="-n 4096"
```
54 changes: 54 additions & 0 deletions cookbooks/fb_nginx/attributes/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#
# Copyright (c) 2025-present, Meta Platforms, Inc.
# Copyright (c) 2025-present, Phil Dibowitz
# All rights reserved.
#
# 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.
#

default['fb_nginx'] = {
'enable' => true,
'enable_default_site' => true,
'manage_packages' => true,
'sites' => {},
'modules' => [],
'config' => {
'_global' => {
'user' => FB::Nginx.user,
'worker_processes' => 'auto',
'pid' => '/run/nginx.pid',
'include' => ::File.join(FB::Nginx.module_dir, 'fb_modules.conf'),
},
'events' => {
'worker_connections' => 768,
},
'http' => {
'sendfile' => 'on',
'tcp_nopush' => 'on',
'keepalive_timeout' => 65,
'types_hash_max_size' => 2048,
'include' => '/etc/nginx/mime.types',
'default_type' => 'application/octet-stream',
'ssl_protocols' => [
'TLSv1',
'TLSv1.1',
'TLSv1.2',
],
'ssl_prefer_server_ciphers' => 'on',
'access_log' => '/var/log/nginx/access.log',
'error_log' => '/var/log/nginx/error.log',
'gzip' => 'on',
},
},
'sysconfig' => {},
}
23 changes: 23 additions & 0 deletions cookbooks/fb_nginx/libraries/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module FB
class Nginx
def self.module_dir
if ::ChefUtils.fedora_derived?
'/etc/nginx/conf.modules.d'
elsif ::ChefUtils.debian?
'/etc/nginx/modules-enabled'
else
fail 'fb_nginx: unknown platform_family'
end
end

def self.user
if ::ChefUtils.fedora_derived?
'apache'
elsif ::ChefUtils.debian?
'www-data'
else
fail 'fb_nginx: unknown platform_family'
end
end
end
end
29 changes: 29 additions & 0 deletions cookbooks/fb_nginx/metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Copyright (c) 2019-present, Vicarious, Inc.
# All rights reserved.
#
# 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.
#

name 'fb_nginx'
maintainer 'Vicarious'
maintainer_email 'noreply@vicarious.com'
license 'Apache-2.0'
source_url 'https://github.com/facebook/chef-cookbooks'
description 'Installs/Configures nginx'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
# never EVER change this number, ever.
version '0.1.0'
supports 'centos'
supports 'debian'
supports 'ubuntu'
109 changes: 109 additions & 0 deletions cookbooks/fb_nginx/recipes/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#
# Cookbook:: fb_nginx
# Recipe:: default
#
# Copyright (c) 2025-present, Meta Platforms, Inc.
# Copyright (c) 2025-present, Phil Dibowitz
# All rights reserved.
#
# 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.
#

package 'nginx' do
Comment thread
jaymzh marked this conversation as resolved.
only_if { node['fb_nginx']['manage_packages'] }
action :upgrade
end

sitesdir = value_for_platform_family(
['rhel', 'fedora'] => '/etc/nginx/conf.d',
['debian'] => '/etc/nginx/sites-enabled',
)

moddir = FB::Nginx.module_dir

sysconfig = value_for_platform_family(
['rhel', 'fedora'] => '/etc/sysconfig/nginx',
['debian'] => '/etc/default/nginx',
)

[sitesdir, moddir].uniq.each do |dir|
directory dir do
owner 'root'
group 'root'
mode '0755'
end
end

file '/etc/nginx/conf.d/fb_nginx.conf' do
action :delete
end

template sysconfig do
owner 'root'
group 'root'
mode '0644'
source 'sysconfig.erb'
notifies :restart, 'service[nginx]'
end

template '/etc/nginx/nginx.conf' do
owner 'root'
group 'root'
mode '0644'
variables({ :sitesdir => sitesdir })
notifies :restart, 'service[nginx]'
end

template "#{moddir}/fb_modules.conf" do
owner 'root'
group 'root'
mode '0644'
notifies :restart, 'service[nginx]'
end

template "#{sitesdir}/fb_sites.conf" do
owner 'root'
group 'root'
mode '0644'
notifies :restart, 'service[nginx]'
end

if node['platform_family'] == 'debian'
# By default the nginx package lays down a 'default' symlink to
# sites-available/default which contains a generic :80 listener.
# This can conflict if we want to control :80 ourselves.
file "#{sitesdir}/default" do
not_if { node['fb_nginx']['enable_default_site'] }
action :delete
end

link "#{sitesdir}/default" do
only_if { node['fb_nginx']['enable_default_site'] }
to '../sites-available/default'
end
end

fb_nginx_create_certs 'do it' do
only_if { node['fb_nginx']['enable'] }
end

service 'nginx' do
only_if { node['fb_nginx']['enable'] }
action [:enable, :start]
end

service 'disable nginx' do
not_if { node['fb_nginx']['enable'] }
service_name 'nginx'
action [:stop, :disable]
end
Loading