Nethence Newdoc Olddoc Lab Your IP BBDock  

Setting up NGINX

Introduction

I am considering two kinds of platform (system) setup. The package provided NGINX (being RHEL or Debian systems) or NGINX from scratch. T

To make this guide valid for both situations, NGINX as package versus from source/scratch, I am using variables,

For RHEL and Debian provided NGINX,

confdir=/etc/nginx/conf.d
logdir=/var/log/nginx

For NGINX from scratch,

#confdir=/usr/local/nginx/conf.d
#logdir=logs

Installation

On Debian systems,

    apt install nginx
    netstat -antupe --inet --inet6 | grep LISTEN | grep 80
systemctl status nginx

    cp -pi /etc/nginx/sites-available/default /etc/nginx/sites-available/default.dist
    cp -pi /etc/nginx/nginx.conf /etc/nginx/nginx.conf.dist
    ls -lhF /var/www/html/index.nginx-debian.html
    rm -f /var/www/html/index.nginx-debian.html
echo "<p>nothing here" > /var/www/html/index.html

On RHEL systems, make sure the EPEL repo is available and proceed,

    yum install nginx
    netstat -antupe --inet --inet6 | grep LISTEN | grep 80
systemctl start nginx
systemctl enable nginx

    cp -pi /etc/nginx/nginx.conf /etc/nginx/nginx.conf.dist
ls -alhF /usr/share/nginx/html/

Configuration

It is a good practice to split virtual host configurations into files so those are easily maintainable. I am even taking the server {} stanza out of the main nginx.conf to have those only in the conf.d/ folder. The special catch-all-not-a-virtual-host-per-say will live there too.

The overall http stanza

First, I am copying the part that has to be removed from the main configuration file to some template file,

cd ~/nginx/conf/
sed -n '/server {/,$p; $d' nginx.conf | sed '$d'
sed -n '/server {/,$p; $d' nginx.conf | sed '$d' > ../conf.d/TEMPLATE.conf.disabled

to then get rid of the server {} stanzas from the global configuration,

cp -pi nginx.conf nginx.conf.dist
vi nginx.conf

(REMOVING SERVER STANZAS)

I now have a clean http stanza for the global parameters and there are a few tweaks to setup there.

The www-data (UID 33) and group (GID 33) are already at my disposal as those were created during the package installation. Making sure those existing user and group will be used for the NGINX processes,

#user  nobody;
user www-data;

Allowing NGINX to scale/downscale by itself,

worker_processes  auto;

Enabling compression and defining a custom log format to display the compression ratio,

    #gzip  on;
    gzip  on;
    log_format compression '$remote_addr - $remote_user [$time_local] '
            '"$request" $status $body_bytes_sent '
            '"$http_referer" "$http_user_agent" "$gzip_ratio"';

And of course enabling the conf.d/ folder by adding a pointer in the main NGINX configuration file,

    include /usr/local/nginx/conf.d/*.conf;

The catch-all-not-a-virtual-host-per-say

Note the server_name is _. Redirecting both http and https requests to e.g. https://stdpierre.os3.site/ no matter what, so there will be no unwanted URLs nor URLs spreading around (including heretic URLs containing IP addresses). People will be forced to use and will know about the right URL as for the default HTTP service on this host.

cd /usr/local/nginx/conf.d/
vi CATCHALL.conf 

server {
        listen 80;
        listen [::]:80;

        server_name _;
        access_log logs/CATCHALL.access.log compression;
        error_log  logs/CATCHALL.error.log warn;

        return 301 https://stdpierre.os3.site/;
}

server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_certificate /etc/letsencrypt/live/stdpierre.os3.site/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/stdpierre.os3.site/privkey.pem;
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
        ssl_session_cache shared:SSL:20m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";

        server_name _;
        access_log logs/CATCHALL.access.ssl.log compression;
        error_log  logs/CATCHALL.error.ssl.log warn;

        return 301 https://stdpierre.os3.site/;
}

The main virtual host

This virtual host has the advantage of using a fully valid SSL certificate by Let’s Encrypt (right FQDN).

The location > root parameter here is pointing to the html folder, meaning either /var/www/html/ (as package) or /usr/local/nginx/html/ (from scratch).

cd ~/nginx/conf.d/
vi stdpierre.os3.site.conf

server {
        listen 80;
        listen [::]:80;

        server_name stdpierre.os3.site;
        access_log logs/stdpierre.os3.site.access.log compression;
        error_log  logs/stdpierre.os3.site.error.log warn;

        return 301 https://$host$request_uri;
}

server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_certificate /etc/letsencrypt/live/stdpierre.os3.site/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/stdpierre.os3.site/privkey.pem;
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:DH+3DES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS;
        ssl_session_cache shared:SSL:20m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";

        server_name stdpierre.os3.site stdpierre;
        access_log logs/stdpierre.os3.site.access.ssl.log compression;
        error_log  logs/stdpierre.os3.site.error.ssl.log warn;

        autoindex on;
        location / {
                root   html;
                index  index.html index.htm;
                try_files $uri $uri/ =404;
        }
}

Note. Redirecting HTTP to HTTPS.

Getting rid of the default index (which finally enables directory listing on the root folder),

cd ~/nginx/html/
mv index.html index.html.dist
mv 50x.html 50x.html.dist

The other virtual hosts

The location > root here is will be /data/www/VIRTUAL_HOST/.

Creating virtual hosts (escaping the $host $request_uri $server_name NGINX variables while keeping our own defined shell variables for use with cat),

confdir=/usr/local/nginx/conf.d
logdir=logs

cd $confdir/

for vhost in {qweqwe,asdasd,qweasd}.stdpierre.os3.site; do

cat > $vhost.conf <<-EOF
server {
        listen 80;
        listen [::]:80;

        server_name $vhost ${vhost%%\.*};
        access_log $logdir/$vhost.access.log compression;
        error_log  $logdir/$vhost.error.log warn;

        return 301 https://\$host\$request_uri;
}

server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_certificate /etc/letsencrypt/live/stdpierre.os3.site/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/stdpierre.os3.site/privkey.pem;
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:DH+3DES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS;
        ssl_session_cache shared:SSL:20m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";

        server_name $vhost ${vhost%%\.*};
        access_log $logdir/$vhost.access.ssl.log compression;
        error_log  $logdir/$vhost.error.ssl.log warn;
            root   /data/www/\$server_name/;
        autoindex on;
}
EOF
done; unset vhost

ls -lhF {qweqwe,asdasd,qweasd}.stdpierre.os3.site.conf
grep server_name {qweqwe,asdasd,qweasd}.stdpierre.os3.site.conf

Note: for internal networks, the site can be reached without the domain part, this is the purpose of the additional ${vhost%%\.*} alias for $server_name.

Creating some content for those virtual hosts to serve,

mkdir -p /data/www/qweqwe.stdpierre.os3.site/
mkdir -p /data/www/asdasd.stdpierre.os3.site/
mkdir -p /data/www/qweasd.stdpierre.os3.site/

echo '<p>qweqwe' > /data/www/qweqwe.stdpierre.os3.site/index.html
echo '<p>asdasd' > /data/www/asdasd.stdpierre.os3.site/index.html
echo '<p>qweasd' > /data/www/qweasd.stdpierre.os3.site/index.html

apply with systemctl restart nginx or /usr/local/nginx/sbin/nginx -s reload and check,

curl -v https://asdasd.stdpierre.os3.site/

Note. Eventually overcome the SSL hostname mismatch adding -k to the curl command.

Note. You cannot use $server_name for access_log and error_log unless it’s fine with you to change the log folder perms accordingly (www-data needs to write in it).

Note. Redirecting HTTP to HTTPS.

Reverse Proxy

To setup an http reverse proxy, simply change location in the server stanza,

    location / {
        proxy_pass http://APPLICATION_ADDRESS:PORT;
    }

apply with systemctl restart nginx or /usr/local/nginx/sbin/nginx -s reload.

Be careful, some applications need to know about the vhost and port that are called. So you shall play with those settings. Here is some specific conf for GitLab,

    proxy_set_header Host $http_host;

here’s a working specific conf for Gollum, assuming you’re running SSL only on the nginx side,

            proxy_pass http://localhost:4567;
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Proto https;

also jenkins conf.

some other possible configs,

    #proxy_set_header Host $host; #instead of http_host
            #proxy_set_header X-Real-IP $remote_addr;
    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #proxy_redirect off;

and here is another one,

        proxy_set_header X-Real-IP $remote_addr;

Authentication

in the server or http stanza,

            location ^~ /private/ {
                    auth_basic "Restricted Area";
                    auth_basic_user_file htpasswd;
            }

then create or edit a password file,

cd /usr/local/nginx/conf/
#apt install apache2-utils
#yum install httpd-?
htpasswd -c htpasswd NEW_USER
#DO NOT chmod 600 htpasswd as the www-data user needs to read it

if files exists already,

htpasswd htpasswd EXISTING_USER

apply with systemctl restart nginx or /usr/local/nginx/sbin/nginx -s reload.

CGI

Install and run the FastCGI helper,

apt -y install fcgiwrap
systemctl list-unit-files | grep fcgi
systemctl status fcgiwrap.socket
ls -lhF /var/run/fcgiwrap.socket
systemctl status fcgiwrap.service

Make sure NGINX is ready for that,

ls -lhF $confdir/../fastcgi_params
ls -lhF $confdir/../fastcgi.conf

Make sure your script is executable and test it,

vhost=host.example.com

mkdir -p /data/www/$vhost/

cat > /data/www/$vhost/index.cgi <<-EOF9
#!/bin/ksh

cat <<EOF
Content-type: text/html

<p>hello \`host \$REMOTE_ADDR | awk '{print \$NF}'\` (\$REMOTE_ADDR)
EOF
EOF9

chmod +x /data/www/$vhost/index.cgi
ls -lhF /bin/ksh
export REMOTE_ADDR=::1
/data/www/$vhost/index.cgi
unset REMOTE_ADDR

and setup those parms into the vhost server stanza e.g.,

vi $confdir/$vhost.conf

root /data/www/$server_name;
    index index.cgi maintenance.html;

location ~ (\.cgi|\.py|\.sh|\.pl|\.lua)$ {
    gzip off;
    fastcgi_pass unix:/var/run/fcgiwrap.socket;
    include fastcgi_params;
    fastcgi_param DOCUMENT_ROOT /data/www/$server_name;
    fastcgi_param SCRIPT_FILENAME /data/www/$server_name$fastcgi_script_name;
}

Note. Be careful with $server_name in the fastcgi parameters: if you do not use a vhost the server name becomes _.

Note. include fastcgi_params points to conf/fastcgi_params already

apply with systemctl restart nginx or /usr/local/nginx/sbin/nginx -s reload.

Cache Control

into the http stanza and before the Virtual Host Configs server stanzas,

vi nginx.conf

# Expires map
map $sent_http_content_type $expires {
    default                    off;
    text/html                  epoch;
    text/css                   max;
    application/javascript     max;
    ~image/                    max;
}

refs.

Conclusion

Clean up your UNIX™ env variables when you have finished,

unset confdir logdir

Troubleshooting

If you get a 301 Moved Permanently instaed of the actual content,

curl $vhost

Refs.

References


Home | GitHub | Donate | Contact