Skip to content

Instantly share code, notes, and snippets.

@zloynemec
Created April 15, 2015 12:29
Show Gist options
  • Save zloynemec/1e4680c4c9aa9cee8a6e to your computer and use it in GitHub Desktop.
Save zloynemec/1e4680c4c9aa9cee8a6e to your computer and use it in GitHub Desktop.
Secure adminer nginx setup
# Secure adminer setup
# Author Taras Kozlov
# download adminer to separate directory
mkdir -p /var/www/admin
cd /var/www/admin
wget http://www.adminer.org/latest.php -O adminer.php
echo '<?php phpinfo(); >' > info.php
sudo -i
# Generate self-signed certificate
mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/admin.key -out /etc/nginx/ssl/admin.crt
# Generate htpasswd
apt-get install apache2-utils
mkdir -p /etc/nginx/.htpasswd
htpasswd -c /etc/nginx/.htpasswd/admin admin
# setup site to this directorty
# example site http://admin.example.com
nano /etc/nginx/sites-available/admin
server {
listen 80;
server_name admin.example.com;
return 301 https://$server_name$request_uri;
}
server {
server_name admin.example.com;
listen 443 ssl;
access_log /var/log/nginx/admin.access.log;
error_log /var/log/nginx/admin.error.log;
ssl_certificate /etc/nginx/ssl/admin.crt;
ssl_certificate_key /etc/nginx/ssl/admin.key;
root /var/www/admin;
index index.php;
# Get file here https://codex.wordpress.org/Nginx
include global/restrictions.conf;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd/admin;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# enabling this site
cd /etc/nginx/sites-enabled
ln -s ../sites-available/admin admin
service nginx reload
@GerBreOwn
Copy link

Just followed this post and when I browse "localhost/adminer" I get a 404 error. What have I missed, as I thought I went through all steps.
Thanks

@ezriharmusial
Copy link

ezriharmusial commented Jul 18, 2018

The index file this Config file is refering to is named adminer.php not index.php. So either Change the /var/www/admin/adminer.php to /var/www/admin/index.php or replace the index.php notations in the config file to adminer.php and it will work.

@larrynalzaro
Copy link

Dang ... it works like a dream.

@LucioSaldivar
Copy link

This keeps failing for me. Is there any way to share a screen shot of the server block?

@larrynalzaro
Copy link

This keeps failing for me. Is there any way to share a screen shot of the server block?

Failing? Here's a screenshot:

scr

@LucioSaldivar
Copy link

where did you put the first Serve{}?? the one with the listen 80 and the return?

@larrynalzaro
Copy link

right above it ... here:

scr2

If you followed the setup described above there's no way it wouldn't work.

@ifthakharriyad
Copy link

Since nano /etc/nginx/sites-available/admin creates a directiory named admin in which file do I have to write the code below:

server {
    listen         80;
    server_name    admin.example.com;
    return         301 https://$server_name$request_uri;
}

server {
    server_name	   admin.example.com;
    listen	       443 ssl;
    access_log     /var/log/nginx/admin.access.log;
    error_log      /var/log/nginx/admin.error.log;

    ssl_certificate /etc/nginx/ssl/admin.crt;
    ssl_certificate_key /etc/nginx/ssl/admin.key;

    root           /var/www/admin;
    index          index.php;

    # Get file here https://codex.wordpress.org/Nginx
    include        global/restrictions.conf;

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd/admin;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
    	try_files $uri =404;
    	fastcgi_split_path_info ^(.+\.php)(/.+)$;
    	fastcgi_pass php;
    	fastcgi_index index.php;
    	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    	include fastcgi_params;
    }
}

@ifthakharriyad
Copy link

Since nano /etc/nginx/sites-available/admin creates a directiory named admin in which file do I have to write the code below:

server {
    listen         80;
    server_name    admin.example.com;
    return         301 https://$server_name$request_uri;
}

server {
    server_name	   admin.example.com;
    listen	       443 ssl;
    access_log     /var/log/nginx/admin.access.log;
    error_log      /var/log/nginx/admin.error.log;

    ssl_certificate /etc/nginx/ssl/admin.crt;
    ssl_certificate_key /etc/nginx/ssl/admin.key;

    root           /var/www/admin;
    index          index.php;

    # Get file here https://codex.wordpress.org/Nginx
    include        global/restrictions.conf;

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd/admin;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
    	try_files $uri =404;
    	fastcgi_split_path_info ^(.+\.php)(/.+)$;
    	fastcgi_pass php;
    	fastcgi_index index.php;
    	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    	include fastcgi_params;
    }
}

I have put this code to nginx.conf and followed everything mentioned and nginx is running but getting 404 error.
And localhost/adminer is redirecting to https://admin.example.com/adminer.

@larrynalzaro
Copy link

You got it wrong. It's not supposed to be a directory; it's a file and that's what its contents are supposed to be.

@ifthakharriyad
Copy link

It's supposed to be a file inside the /etc/nginx/sites-available/admin directory?
If so, what is the name of the file?

@larrynalzaro
Copy link

Noooo. It's just a file under /etc/nginx/sites-available.

@ifthakharriyad
Copy link

Okay. let me try with this. By the way, I have also changed the nginx.conf should I also undo the changes?

@ifthakharriyad
Copy link

ifthakharriyad commented Oct 29, 2021

Noooo. It's just a file under /etc/nginx/sites-available.

So, I can give the file name whatever I want to?
What extension the file need to have?

@larrynalzaro
Copy link

You can give it any name you want. No extension.

@ifthakharriyad
Copy link

Note: I am trying to set up adminer in my local machine.
@larrynalzaro Made the changes and nginx is running fine but still getting 404 error and redirected to https://admin.example.com/adminer:
image

@larrynalzaro
Copy link

Dude,

You're not getting it. Read the author's original post at the top of this thread. It's meant to be an example; you're supposed to modify it to conform to your needs. It's the same thing with the server block definition I posted: it's an example. You have to modify it according to your specific requirements.

@parmonov98
Copy link

I'm getting nginx 403 error!

@Navia25
Copy link

Navia25 commented Aug 14, 2022

Thank you for this example configuration,

Literally only tweaked the adminer.conf provided with my own secure TLS setup.

I found that Adminer was much easier to install by reading this, since I already operate a Pterodactyl (Basically a Game Control Panel for game servers) instance I had things such as PHP (php 8.1 fpm) and the webserver (nginx) already.

For all the above who are failing to set it up, don't attempt this unless you already know how to operate a nginx webserver (with extensions)
The tutorial above does not work out of the box and requires you to already have knowledge outside the "box".

Certain requirements for Adminer are also used by Pterodactyl, you may visit their documentation at https://pterodactyl.io/panel/1.0/getting_started.html
(mostly regards the line below which includes Nginx, PHP v8.1 with extensions like FPM and MariaDB)
apt-get install php8.1 php8.1-{common,cli,gd,mysql,mbstring,bcmath,xml,fpm,curl,zip} mariadb-server nginx tar unzip git -y

My installation:
Ubuntu 20.04.4 LTS (Focal Fossa), but note it will probably work on 18.04 and 22.04 as well, including other Debian and Debian based Linux variants.
Nginx is in /etc/nginx/
Adminer Nginx config file is in /etc/nginx/sites-available/
Adminer Nginx config file is named adminer.conf (Full path = /etc/nginx/sites-available/adminer.conf
Adminer Nginx symlink is in /etc/nginx/sites-enabled/
Adminer Nginx symlink is also named adminer.conf and the symlinks points to /etc/nginx/sites-available/adminer.conf
If you do not know how to make a symlink, use a FTP client like FileZilla/WinSCP (with SFTP protocol) or look it up, I'm not gonna chew everything for ya.
Adminer's PHP file is located in /var/www/adminer/
Adminer's PHP file is simply named adminer.php (Full path = /var/www/adminer/adminer.php)

The configuration below is nearly identical to mine, except that I removed my domain name and added comments, BE SURE TO REPLACE IT WITH YOUR DOMAIN NAME! and make sure to point A and AAAA DNS records to whatever subdomain you will use for your Adminer interface, if you do not understand what this is, cancel installing this software now!

example.com is a PLACEHOLDER, replace it with your own domain, whatever that domain may be.
Also ignore that the TLS ciphersuites contain both TLS_ECDHE_ECDSA and TLS_ECDHE_RSA ciphers, I actually use both an ECC and RSA certificate so my webserver supports both types of ciphersuites (nginx will ignore the other type if you got an ECC or RSA TLS cert only!
For the not so techies, Do not use the term SSL, SSL was made obsolete two decades ago, call it by it's proper name, TLS.
SSL certificates => TLS certificates, or very techy term X.509 certificates (but you don't need to remember that)

# The section here enables HTTPS redirects on port 80, meaning all http:// is converted to https:// instead
server {
    listen 80;
    listen [::]:80;
    server_name adminer.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    # listen on port 443 w/ TLS and enable HTTP/2
    listen 443 ssl http2;
    # Also listen on IPv6.
    listen [::]:443 ssl http2;
    server_name adminer.example.com;

    root /var/www/adminer;
    index adminer.php;
    access_log /var/log/nginx/adminer-access.log;
    error_log /var/log/nginx/adminer-error.log;

    # Your TLS certificate chain (chain is basically your cert + intermediate + root certificate, this can end up being a total of 3 or 4)
    # For Let's Encrypt this will be one of the following chains; CERT < R10/R11 < ISRG Root X1 (Certbot default RSA) / CERT < E5/E6 < ISRG Root X1 (Certbot default ECC) / CERT < E5/E6 < ISRG Root X2 (recommended)
    ssl_certificate /etc/letsencrypt/live/adminer.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/adminer.example.com/privkey.pem;

    # This is for OCSP Stapling, recommended to use until 2025/08/01, as OCSP issues occur frequently with clients, and with Stapling, it might be slightly reduced.
    # The reason you are recommended to comment the three lines below in August, is because ISRG will disable OCSP for Let's Encrypt COMPLETELY, so keeping the below lines after August started, may cause your setup to break, and clients may experience TLS errors, which may prove problematic if you use
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    # Enable only TLS 1.2 and TLS 1.3, compatible and secure. do NOT ever enable the fundamentally insecure TLS 1.1 and 1.0 or even SSL 3.0!
    ssl_protocols TLSv1.2 TLSv1.3;
    # Turns off TLS 1.3's 0-RTT, which is weak against replay attacks, you'll get minus points from internet.nl if you turn it on regardless.
    ssl_early_data off;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";

    ssl_ecdh_curve X448:X25519:secp521r1:secp384r1:secp256r1;

    # Uncomment these lines only if you use nginx version 1.19.4 or higher, as this directive is NOT supported by nginx 1.18.0, which is shipped by default on Debian 11 Bullseye and Ubuntu 22,04 LTS Focal Fossa. 
    #ssl_conf_command Options PrioritizeChaCha,NoRenegotiation,NoResumptionOnRenegotiation,AntiReplay,ServerPreference,EncryptThenMac,ECDHSingle,DHSingle,AllowNoDHEKEX,NoTickets,NoCompression,NoClientRenegotiation;
    #ssl_conf_command SignatureAlgorithms ECDSA+SHA512:ECDSA+SHA384:ECDSA+SHA256:RSA-PSS+SHA512:RSA-PSS+SHA384:RSA-PSS+SHA256:RSA+SHA512:RSA+SHA384:RSA+SHA256
    #ssl_conf_command Groups X448:X25519:secp521r1:secp384r1:secp256r1

    ssl_session_cache shared:MozSSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    gzip off;
    etag off;

    # NextDNS, if you do not have IPv6 support, comment the first resolver line and uncomment the second.
    resolver [2a07:a8c0::]:53 [2a07:a8c1::]:53 45.90.28.0:53 45.90.30.0:53;
    #resolver 45.90.28.0:53 45.90.30.0:53;
    resolver_timeout 600s;

    rewrite ^/.well-known/security.txt$ https://securitytxt.org/.well-known/security.txt permanent;

    # See https://hstspreload.org/ before uncommenting the line below.
    # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    # HSTS Preload should be done at the apex domain.
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "0";
    add_header X-Robots-Tag none;
    #add_header Content-Security-Policy "frame-ancestors 'self'; upgrade-insecure-requests";
    add_header Content-Security-Policy "default-src 'self'; frame-src 'self'; frame-ancestors 'self'; script-src 'strict-dynamic'; style-src 'strict-dynamic'; frame-src 'img-src'; connect-src 'self'; font-src 'self'; object-src 'self'; media-src 'self'; upgrade-insecure-requests";
    add_header Content-Security-Policy-Report-Only "default-src 'self'; frame-src 'self'; frame-ancestors 'self'; script-src 'self' example.com example.org; style-src 'self' example.com example.org; frame-src 'img-src'; connect-src 'self'; font-src 'self'; object-src 'self'; media-src 'self'; upgrade-insecure-requests";
    add_header X-Frame-Options DENY;
    add_header Referrer-Policy no-referrer;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
    	try_files $uri =404;
    	fastcgi_split_path_info ^(.+\.php)(/.+)$;
    	fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    	fastcgi_index adminer.php;
    	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    	include fastcgi_params;
    }
}

Note that you can also find the same config file with clearer visibility at this pastebin:
PrivateBin instance no longer running, so link is broken.

Always restart nginx after configuration changes with systemctl restart nginx
If a restart fails then diagnose the issue with systemctl status nginx
It should tell you what the problem is (eg wrong formatting in a nginx config, it will tell you which line and column)

It should be self explanatory that you need to install Certbot, acme.sh or anything of the like, in order to issue TLS certificates in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment