Skip to content

Instantly share code, notes, and snippets.

@zymawy
Last active January 14, 2023 05:40
Show Gist options
  • Save zymawy/e146f9ada00318e08b0ba202356e0550 to your computer and use it in GitHub Desktop.
Save zymawy/e146f9ada00318e08b0ba202356e0550 to your computer and use it in GitHub Desktop.

We can see we have php 7.0 available out of the box:

sudo apt-cache show php-cli

Instead of using that, we'll start by installing the latest PHP 7.1, via the populate PHP repository.

# Add repository and update local cache of available packages
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get update

# Search for packages starting with PHP, 
# we'll see php7.1-* packages available
sudo apt-cache search -n php*

# Install PHP-FPM, PHP-CLI and modules php7.3-mcrypt
sudo apt-get install -y php7.3-fpm php7.3-cli php7.3-curl php7.3-mysql php7.3-sqlite3 \
    php7.3-gd php7.3-xml php7.3-mbstring php7.3-common

Once that's installed, we can see some similar conventions from Nginx (and other software in Debian/Ubuntu).

SAPI

PHP on Debian/Ubuntu is divided by version and Server Application Programming Interface. A SAPI is the context in which PHP is run. The most common are:

  • cli - when running on the command line
  • fpm - when fulfilling a web request via fastcgi
  • apache2 - when run in Apache's mod-php

Configuration

We can see the configuration split between version and SAPI by checking the file paths within /etc:

cd /etc/php
ls -lah

> ... 5.6/
> ... 7.0/
> ... 7.1/

cd 7.1
ls -lah

> ... cli/
> ... fpm/

Within each SAPI directory (e.g. cli or fpm), there is a php.ini file and a conf.d directory. We can edit php.ini per SAPI and use symlinks within the conf.d directory to enable or disable modules per SAPI.

Modules

PHP on Debian/Ubuntu use Symlinks to decide which ones are loaded per SAPI. All module configuration files are located in /etc/php/<version>/mods-available, and then loaded in via symlinks at /etc/php/<version>/<sapi>/conf.d.

Step 7: Configure PHP With the stack installed, it is now time to configure everything to get it working. There isn’t much to configure with PHP, but there is one small security fix we need to make.

In your terminal, open up your php.ini file in whatever text editor you wish (VIM, or eMacs) but for simplicity, we will use Nano in this tutorial.

sudo nano /etc/php/7.3/fpm/php.ini The line we need to edit is cgi.fix_pathinfo=0 so you can either search for it like a needle in a haystack, or you can search for it using Ctrl+W , I personally recommend searching for it.

Press Ctrl+W and now type in cgi.fix_pathinfo= and click enter. This will take you to the right line right away. You will see a semicolon the left of this line. Delete the semi colon and then change the 1 into a 0 and save the file. The file should look like this upon saving:

To save something in Nano, just press Ctrl+X and type Y and then press Enter.

Before the changes can take effect we need to restart php-fpm by typing in this command:

Shell sudo systemctl restart php7.3-fpm Now our change has taken effect.

Nginx

sudo vim /etc/nginx/sites-available/default

Once PHP is installed, we can configure Nginx to send PHP requests off to PHP-FPM:

server {
    listen 80;

    root /var/www/html;

    server_name _;

    index index.html index.htm index.debian-default.html index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        incude snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.1-fpm.sock;
    }
}

Once edits are complete we can test Nginx and reload:

sudo nginx -t
sudo service nginx reload

In the video, I show you some behavior around the above configuration. Most notably, the try_files configuration allows for "pretty URLs", meaning we don't need to add index.php into the URL within our browser for Nginx to use the index.php file.

The above configuration file will search for php files within the /var/www/html directory and send requests to PHP-FPM if a file is requested that ends in the .php extension.

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

Beyond Permissions: Linux ACLs

Here is a rundown of the basic commands we're using:

$ getfacl /var/www

Get current ACL's for the given directory or file
$ setfacl -R -m u:johndoe:rwx /var/www

setfacl - Set ACL
-R - Recursive down into files and directories
-m - Modifying ACL's (vs removing them)
u:johndoe:rwx - The user johndoe will get rwx permissions
/var/www - Give these permissions to the /var/www directory (and sub files/dirs, since this is a recursive operation via the -R flag)
$ setfacl -R -m g:www-data:rwx /var/www

The same as above, except:

g:www-data:rwx - Allow the group www-data to rwx the /var/www directory
$ setfacl -x g:www-data /var/www

-x - Remove ACL's defined for g:www-data at location /var/www
Here are the commands used in the video:

Create the users we'll use:

# Create user jane
root@server $ adduser jane
# Give jane the ability to use "sudo"
root@server $ usermod -a -G sudo jane

# Create user bob
root@server $ adduser bob
# Bob, a user who can deploy web sites,
# is part of group www-data, the same group as
# our website files
root@server $ usermod -a -G www-data bob

We'll ensure files in our web root are of group "www-data". This is not necessary for ACL permissions, but we so do to keep things consistent.

# Ensure the site files have user/group "www-data"
# This is not necessary for ACL's
root@server $ chown -R www-data:www-data /var/www

Start using some ACL basics. Here we give a user permissiont to read/write/execute files and directories using ACLs instead of the classic Linux permissions.

# Check out the ACL's set by default
# These are separate from the usual user/group permissions
root@server $ getfacl /var/www

# Give use jane the ability to rwx web files at
#   directory /var/www
# Technically she wouldn't *need* this,
# since she could use her sudo abilities
root@server $ setfacl -R -m u:jane:rwx /var/www

# Above we set ACL for existing files/dirs
# Here we will recursively (-R) set the
# defaults (-d flag) for future files/dirs
root@server $ setfacl -Rd -m u:jane:rwx /var/www

# Check new permissions added
# (current dir/files and defaults)
root@server $ getfacl /var/www

Next we give group-based permissions via ACL's to the web files. This is (arguably) more useful, a we can then give any user a secondary group (www-data in this case) to allow them to edit the web files, despite what the owner or group of those files are.

# Add group-based permissions, instead of user-specific
# This allows anyone in group "www-data" (like bob) to
# rwx files in /var/www
# We're setting the defaults here
root@server $ setfacl -R -m g:www-data:rwx /var/www

# Recursively (-R) set the defaults (-d)
for future files/dirs as well
root@server $ setfacl -Rd -m g:www-data:rwx /var/www

# View changes
root@server $ getfacl /var/www

To reiterate: These files/dirs don't need group "www-data" to be editable by bob. ANY USER part of group "www-data" can now edit files/dirs in directory /var/www!

Here are some things worth noting on using ACLs:

# Note to keep setfacl flags/options in two groups
# to avoid errors:
root@server $ setfacl
> Usage: setfacl [-bkndRLP] { -m|-M|-x|-X ... } file ...

Now test our new settings to see how they behave:

# Some testing:
# Jane creates a dir
jane@server $ mkdir -p /var/www/site/styles
# Ensure bob can write to that location:
bob@server $ touch /var/www/site/styles/styles.css

# See the ACL's on new dir - they are inherited from parent dir
# Thanks to ACL defaults set
root@server $ getfacl /var/www/site/styles

# Just to be "clean", make site files all
# part of group www-data. Again, this is
# unnecessary for ACL permissions
root@server $ chgrp -R www-data /var/www

# Set the gid (group id) used for new dirs/files
# in the /var/www directory - new ones will have
# group "www-data" just like their parent directory
#
# This technically isn't necessary, as ACL gorup permissions
# for group "www-data" will function based on the user's assigned
# group rather than the group that is asssigned to the site files
# ...But this is handy to know still
root@server $ chmod -R g+s /var/www

# Laslty, check set permissions
# Note the "s" where you usually see "x"
# in the group permissions
bob@server $ ls -lah /var/www
> drwxrwsr-x 3 www-data www-data 4096 Jan 12 20:34 Site

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

` shell sudo vim /etc/nginx/sites-available/default
`

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

Install Git && Setting Up Git Hooks

cd /var
mkdir repo && cd repo
sudo mkdir site.git && cd site.git
sudo git init --bare
cd hooks && sudo touch post-receive && sudo vim post-receive
#!/bin/sh
git --work-tree=/var/www/folderto --git-dir=/var/repo/site.git checkout -f
sudo chmod +x post-receive
shell

sudo mkdir -p /var/www/

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

Create Remote On Local Repo

git remote add production ssh://zymawy@/var/repo/.git

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

Mysql

Learn how to install MySQL 5.5 or 5.6. We'll cover install MySQL non-interactively (no prompts), useful for scripting installs. This will show the use of debconf-set-selections and debconf-get-selections to configure MySQL setting that might normally be prompted for during installation.### Installing MySQL

We're using an Ubuntu server here, but this should work for Debian as well.

We can install mysql-server to get MySQL 5.5, but the MySQL 5.6 package is likely also available:

shell
sudo apt-get install -y mysql-server-5.6

We can see more information about the MySQL package using apt-cache:

sudo apt-cache show mysql-server

Installing Without Prompts (Scriptable)

Before we go and install MySQL, however, let's see how to remote prompts, so we aren't asked a password during installation. This is useful for scripting the installation of MySQL.

In your terminal export the DEBIAN_FRONTEND variable:

shell
export DEBIAN_FRONTEND="noninteractive"

This will turn off "frontend" (prompts) during installations. If we install MySQL with this set to noninteractive, then no root password will be set, and we'll be able to log into MySQL as root without a password.

We can take this a step forward and also set a root password for MySQL to use ahead of time:

export DEBIAN_FRONTEND="noninteractive"

sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password root"

sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password root"

If you're on a system using yum over apt-get, you just won't get a password prompt. You'll need to set a root password post-install.

Securing/Cleaning up the install

This should always be run after installing MySQL, especially on systems using yum, where you generally don't set a root password ahead of time.

shell
mysql_secure_installation

Whole Thing
The whole process put together is simply this:

export DEBIAN_FRONTEND="noninteractive"

sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password rootpw"
sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password rootpw"

sudo apt-get install -y mysql-server-5.6

mysql_secure_installation

@zymawy
Copy link
Author

zymawy commented Jan 12, 2019

MySQL User

CREATE DATABASE  database_name;

Lastly we need a user that can be connected to. MySQL users are created per host. The host can be a wildcard (any host) % or a specific host, such as localhost or perhaps the IP address of your application server at 192.169.33.11. We'll create a user that can be connected from a remote host - our application server.

Note, I'll grant all privileges to his user when connected from all hosts. You may want to be more restrictive.

Here's what it would look like if creating a user who can be connected to from anywhere:

CREATE USER 'zymawy'@'%' IDENTIFIED BY 'some_secure_password';
GRANT ALL PRIVILEGES on your_db.* TO 'zymawy'@'%';

Here's what it would look like if creating a user who can be connected to from a specific host, and with specific grant permissions:

CREATE USER 'zymawy'@'192.168.33.11' IDENTIFIED BY 'some_secure_password';
GRANT SELECT, DELETE, UPDATE, INSERT, INDEX, LOCK TABLES on your_db.* TO 'zymawy'@'192.168.33.11';

Here's more information on grants needed for mysqldump here.

@zymawy
Copy link
Author

zymawy commented Feb 10, 2019

Ufw Proploms

Normally boils down to the firewall blocking your connection. This can happen for a few reasons, one of which is excessive login failures.

From what I recall, most of the one-click images for Ubuntu are setup with ufw, a firewall that acts as an overlay to iptables and simplifies the process of setting up most rule types.

If you're blocked, the only way to get back in is via Console which you can access from the DO CP.

Click on the name of the Droplet in question. From the left side menu, click Access. Now click on the button that says Launch Console.

You'll need your root password to login. If you only deployed with SSH Keys, the root password is disabled -- in such a case, you're effectively locked out of the server as the Console doesn't allow pubkey authentication.

If you do have a root password set, you'll be prompted to enter it in. You won't see anything when entering the password, so you'll need to type it in carefully. Once you have, hit enter.

Once logged in, you can run:

ufw disable
That'll turn ufw off. Keep in mind, it's not really recommended to run without a firewall active, so the best thing to do would be to simply reset the firewall and then setup new rules.

Once the firewall is disabled, try to login via SSH once again. If you're able to login, I would use the following to setup the firewall.

Double-Check ufw is disabled:

ufw disable
Reset the rules:

ufw reset
Setup Default Policies:

ufw default deny incoming && ufw default allow outgoing
We're setting the default policy to deny any incoming connection. This is preferred as we want to setup a deny all, except what we explicitly allow type policy. This is the first part.

Now, we'll add rules for SSH (Port 22), HTTP (Port 80), and HTTPS/SSL (Port 443). These are the ports we want to allow connections on. This ensures that only these three ports are open, no more, no less.

ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp
Now, I noticed that you're using Sequel Pro, which means we need to open another port, and that'd be 3306 as that's the port MySQL/MariaDB communicate on by default. The issue here is that with the base configuration for MySQL (which is what DigitalOcean uses), remote access to MySQL is not enabled, so you need to enable it.

You should have this file:

/etc/mysql/my.cnf
We need to open that file using nano and find bind-address. Normally this is set to 127.0.0.1, which is localhost. If you try to connect with bind-address set to that IP, it'll fail.

You need to change bind-address to either 0.0.0.0 or the IP address of your Droplet (the IPv4 IP) and then restart MySQL.

service mysql restart
To add MySQL to the firewall, we'd issue one more allow command, like so:

ufw allow 3306/tcp
With SSH, HTTP, HTTPS, and MySQL now added, we can enable ufw using:

ufw enable
It'll ask you to confirm -- do so -- and logout, then try to login again. Then try to login with Sequel Pro, etc.

...

Note: It's really not a good security practice to allow open access to port 3306 as is being done in the above guide. Ideally you would restrict access to a single IP, or set of IP's (if multiple people need access). Allowing open access, such as what is being done here allows anyone to attempt to login to MySQL on your server, whether the user(s) exist or not.

@zymawy
Copy link
Author

zymawy commented Feb 10, 2019

shell service ssh start

@zymawy
Copy link
Author

zymawy commented Apr 11, 2019

New User


# Create a new user, give it a password
# set any additional values you'd like
sudo adduser zymawy

# Log in as new user, create
# and go into a ~/.ssh directory
sudo su zymawy
mkdir ~/.ssh
cd ~/.ssh

# Create/edit ~/.ssh/authorized_keys dir
# and paste in the public key we put into
# our clipboard when we first created it
vim authorized_keys

Then you can log in as that user from your local computer!

# If you only have a few SSH keys, you won't hit the
# max attempts limit and can just do this:
ssh zymawy@server-ip

# If you want to explicitly say which ssh key to use, or
# have enough keys (like me!) to hit the max attempt limit:
ssh -o "IdentitiesOnly yes" -i ~/.ssh/id_whatever zymawy@server-ip

@zymawy
Copy link
Author

zymawy commented Aug 31, 2019

sudo apt-get install nginx

@zymawy
Copy link
Author

zymawy commented Aug 31, 2019

sudo apt-get install mysql-server

@zymawy
Copy link
Author

zymawy commented Aug 31, 2019

sudo apt-get install php-fpm php-mysql php-mbstring

@zymawy
Copy link
Author

zymawy commented Dec 31, 2019

@zymawy
Copy link
Author

zymawy commented Jan 3, 2020

sudo apt-get install nginx

@zymawy
Copy link
Author

zymawy commented Mar 2, 2021

sudo apt-get install -y php7.4-fpm php7.4-cli php7.4-curl php7.4-mysql php7.4-sqlite3 \
    php7.4-gd php7.4-xml php7.4-mbstring php7.4-common

@zymawy
Copy link
Author

zymawy commented Mar 2, 2021

apt install zip unzip php-zip

@zymawy
Copy link
Author

zymawy commented Dec 18, 2021

ocrmypdf -l eng+ara --rotate-pages --deskew --title "My PDF" --jobs 4 20211218_163306_invoices_000069.pdf output_searchable.pdf

@zymawy
Copy link
Author

zymawy commented Dec 18, 2021

ocrmypdf -l eng+ara --rotate-pages --deskew --title "My PDF" --jobs 4 20211218_163306_invoices_000069.pdf output_searchable.pdf

@zymawy
Copy link
Author

zymawy commented Feb 9, 2022

If you use yarn, try yarn --ignore-engines, after yarnpkg/yarn@b880d40 lands in (probably) 15.2.

@zymawy
Copy link
Author

zymawy commented Feb 9, 2022

 yarn --ignore-engines

@zymawy
Copy link
Author

zymawy commented Mar 6, 2022

sudo apt-get install -y php8.1-fpm php8.1-cli php8.1-curl php8.1-mysql php8.1-sqlite3
php8.1-gd php8.1-xml php8.1-mbstring php8.1-common

@zymawy
Copy link
Author

zymawy commented Jan 14, 2023

CREATE USER 'zymawy'@'%' IDENTIFIED BY 'some_secure_password';
grant all privileges on . to 'zymawy'@'%';

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