megacolorboy

Abdush Shakoor's Weblog

Writings, experiments & ideas.

Resolve permission error while SSH-ing to AWS EC2 instance from a Linux machine

If you are someone who's trying to access a AWS EC2 instance via SSH using a private key from a linux machine, you might have or will come across this error:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'your-aws-private-key.pem' are too open.
It is recommended that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: your-aws-private-key.pem
Permission denied (publickey).

Why am I getting this?

From what I have read, EC2 instances will simply not accept a private key that are publicly visible to others especially if it's somewhere stored in your Desktop or Downloads folder.

So basically, your private key should be accessible to others.

Oh, how can I fix it?

It's pretty straightforward, you just have to make sure that the private key is read-only like this:

chmod 400 your-aws-private-key.pem

After that, try connecting again and it should work fine!

Hope you found this tip useful!

Install PFX certificate on a Linux server

I would consider this as an extended post to my previous post that I had written six months ago.

A PFX Certificate usually contains the following in PKCS#12 format:

  • The actual certificate.
  • The private key to the certificate.
  • The Intermediate authority certificate that ensures the trustworthiness of the certificate.

To extract all those files, here are the steps that I have documented:

Note

If the .PFX file prompts you for a passphrase, please check with your project manager or client regarding this information.

Extract the Encrypted Private Key

openssl pkcs12 -in <filename.pfx> -nocerts -out encrypted.key

Extract RSA Private Key

openssl rsa -in encrypted.key -out private.key

Extract Certificate

openssl pkcs12 -in <filename.pfx> -clcerts -nokeys -out certificate.crt

Extract Combined Chain Certificate (Optional)

openssl pkcs12 -in <filename.pfx> -cacerts -nokeys -chain | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > combined_chain_certificate.crt

Once you are done extracting all the required files, you can add the certificates like this:

Apache configuration:

SSLCertificateFile /path/to/certificate.crt
SSLCertificateKeyFile /path/to/private.key

# Optional, if you have it, else skip.
SSLCertificateChainFile /path/to/combined_chain_certificate.crt

Test if it works:

apachectl configtest
systemctl restart httpd

Nginx configuration:

# If you don't have a combined chain certificate:
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;

# If you have a combined chain certificate:
ssl_certificate /path/to/combined_chain_certificate.crt
ssl_certificate_key /path/to/private.key;

Test if it works:

nginx -t
systemctl restart nginx

Hope you found this tip useful.

Install Microsoft SQL Server Driver for PHP on Amazon Linux 2

Recently, I tried to install Microsoft SQL Server driver for PHP on Amazon Linux and searching on how-to do was really annoying.

I read a few articles on StackOverflow and found some samples on GitHub Gists and thought of sharing on how I installed it.

Prerequisites

  1. Amazon Linux 2 installed.
  2. Ensure the ports 80 and 443 are open on your instance.
  3. PHP >= v5.6 and the following extensions: php-devel, php-pear, php-pdo, and php-xml.
  4. Know-how on using the terminal.

Installation steps

These are the commands used to install the SQL Server driver:

sudo su
sudo yum-config-manager --add-repo https://packages.microsoft.com/config/rhel/7/prod.repo
sudo yum update
sudo ACCEPT_EULA=Y yum install -y msodbcsql mssql-tools unixODBC-devel re2c gcc-c++ gcc
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv

Modify php.ini

You can either to go to your php.ini file and add the extension=sqlsrv extension or add it like this:

echo "extension=sqlsrv" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
echo "extension=pdo_sqlsrv" >> /etc/php.d/30-pdo_sqlsrv.ini

Restart the service

Ensure that the server can connect and restart the service:

sudo setsebool -P httpd_can_network_connect_db 1
sudo systemctl restart httpd && sudo apachectl restart

Next, run the following the command to see that both pdo_sqlsrv and sqlsrv are installed:

php -m | grep "sqlsrv"`

Test if the driver works

Create a test.php file in your root directory and copy-paste this snippet to test if it works:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$serverName = "YOUR_DB_HOST";
$connectionOptions = array(
    "Database" => "YOUR_DB_NAME",
    "Uid" => "YOUR_DB_USER",
    "PWD" => "YOUR_DB_PASSWORD"
);

$conn = sqlsrv_connect($serverName, $connectionOptions);

if($conn) {
    print "Connected successfully."; 
} else {
    print "Error while connecting to server.";
}
?>

Hope you found this tip useful!

Logging client IP addresses on Apache server

If you want to log the actual client IP address, you need to extract the X-Forward-For header from the request and in order to do that, you need to make a tiny edit in your httpd.conf file.

  1. Go to /etc/apache2/conf or /etc/httpd/conf and open httpd.conf file.
  2. Search for the string that starts with: LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined.
  3. Modify the %h to %{X-Forwarded-For}i. Now, it should look like this: LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined.
  4. Test the config to ensure that there are no typos by typing: apachectl configtest.
  5. Save and restart the service by typing: systemctl restart httpd or systemctl restart apache2.
  6. In your terminal, type tail -f /var/log/httpd/access.log and you'll be seeing the client IP being logged in your logs.

Hope you found this tip useful!

Using IIFE in JavaScript

IIFE a.k.a Immediately-Invoked Function Expression — is a way to execute functions as soon as the function is created.

Using IIFE, you can easily isolate declared variables away from the global scope.

This is how the syntax would look like:

// ES5 Standard
let foo = (function(){
    let message = "Hello world"
    console.log(message);
})();

They can be defined with arrow functions as well:

// ES6 Standard
let foo = (() => {
    let message = "Hello world"
    console.log(message);
})();

According to MDN Docs, it's a design pattern a.k.a Self-Executing Anonymous Function. There are two parts to this:

  1. The function that's enclosed within the Grouping Operator (), which would prevent it from polluting the global space and access to the variables within it's scope.

  2. The (); will create the Immediately Invoked Function Expression on-the-fly.

When to use it?

There are many interesting cases such as if you are following the Module Pattern especially if you want to avoid polluting the global namespace or just that you don't want your code to interfere with other code thus ensures code safety.

Readings

Download a webpage along with it's assets using wget

I wanted to download a webpage along with it's assets and scripts, I tried using Chrome's Save as... option but it wasn't working as expected.

I did a little googling and thought of using wget to do the job and it worked. Here's the command I used:

wget -p -k -H --nd https://www.example.com

Let's see what do those flags mean:

  • -p: Downloads all the necessary files to properly display the downloaded HTML page.
  • -k: After the download is complete, it converts the links in the document to make them suitable for local viewing.
  • -H: This would download files that spans different hosts.
  • --nd: While retrieving files recursively, it will not create a hierarchy of directories and downloads the files into a single directory instead.

If you open index.html directly on the browser, the assets might be broken as the -k flag doesn't seem to make the assets relative to the root directory. In order to view it, you can view it on a localhost server like WAMP, XAMPP or if you are using Python, you can type python -m http.server and view your downloaded file.

Might be a bit messy but gets the job done.

Extract private key and certificate from .pfx file

Ever wondered what's a .pfx file? It's just a combination of both private key and the certificate and this file is usually used in Microsoft IIS Servers.

However, you can't use this file in Linux servers as both private key and the certificate are supposed to be individual files.

Here's how you can extract both private key and certificate files:

openssl pkcs12 -in file.pfx -nocerts -out privatekey.pem -nodes
openssl pkcs12 -in file.pfx -nokeys -out certificate.crt

Hope this tip helps you out!

Install NGINX and PHP on Windows

Thought of writing a small tutorial on how to install NGINX and PHP on Windows. You can also use this as a reference if you wanted to install them on Windows Server too.

Note

This tutorial won't teach you how to configure both PHP and NGINX configurations as there are a lot of tutorials focusing on that topic.

Setting up NGINX

  1. Download NGINX for Windows
  2. Go to C:/ directory and create a folder named C:/nginx.
  3. Unzip the downloaded .zip file in C:/nginx directory.
  4. Go to C:/nginx and create two new folders named sites-available and sites-enabled.
  5. To test, if it's working, double-click on C:/nginx/nginx.exe and type localhost in the browser. You should be able to see a Welcome page.
  6. Kill the NGINX process from Task Manager.

Setting up PHP

  1. Download PHP 8.1 for Windows.
  2. Go to C:/ directory and create a folder named C:/php or if you wanted to have multiple versions, C:/php8.1.
  3. Unzip the downloaded .zip file in C:/php directory.
  4. Add PHP to your system environment variables by adding the path C:/php
  5. Open Command Prompt and type php -v. You see should be able to see the version if it's installed correctly.

Run NGINX and PHP as separate services

Generally, it's good practice to run both PHP and NGINX as background services or else, you might have to start the processes manually every time the server goes down.

As I was looking for a way to do that, I stumbled upon this post on StackOverflow and it was quite helpful.

Install NSSM Service Manager

NSSM Service Manager is a free open-source alternative that helps you create a service with the help of GUI. Below are the steps that I followed:

  1. Download NSSM Service Manager
  2. Go to C:/ directory and create a folder named C:/nssm.
  3. Unzip the downloaded .zip file in C:/nssm directory.

Install NGINX as a service

  1. Open Command Prompt as an Administrator.
  2. Go to C:/nssm/win64/ directory.
  3. Type nssm install nginx
  4. Define the path of the nginx.exe file.
  5. Click on Install Service.
  6. Press Win+R and type services.msc.
  7. Look for nginx and start the service.
  8. Open your browser and type localhost to see if it's working correctly.

Install PHP as a service

  1. Open Command Prompt as an Administrator.
  2. Go to C:/nssm/win64/ directory.
  3. Type nssm install php
  4. Define the path of the php-cgi.exe file.
  5. Add the arguments: -b 127.0.0.1:9000
  6. Click on Install Service.
  7. Press Win+R and type services.msc.
  8. Look for php and start the service.

Test to see if it works!

Before you proceed with the following steps, ensure that you include the *.conf files from the sites-enabled folder in your nginx.conf file.

Add the line include "C:/nginx/sites-enabled/*.conf"; in your nginx.conf file and follow the steps below:

  1. Go to C:/nginx/sites-available directory and create example.com.conf.
  2. Go to C:/nginx/html and create directory.
  3. Refer to the sample configuration provided and make the necessary changes.
  4. To enable the site, you have to create a symlink: mklink "C:\nginx\sites-enabled\example.com.conf" "C:\nginx\sites-available\example.com.conf".
  5. Ensure the configuration doesn't have any errors by typing nginx -t in C:/nginx directory.
  6. Restart the NSSM service.
  7. Add an entry in your hosts file: 127.0.0.1 example.com.
  8. And you're done!

Tip

If you get a "Connection Refused" or error that is similar to that, it's most probably due to firewall or maybe the PHP service isn't running.

Useful commands while using NSSM

# Start a service
nssm start "servicename" 

# Restart a service
nssm restart "servicename" 

# Stop a service
nssm stop "servicename" 

# Install a service
nssm install "servicename" 

# Remove/Uninstall a service
nssm remove "servicename" 

Sample NGINX Virtual Host Configuration

server {

    # Your Domain Name
    listen 80;
    server_name example.com;

    autoindex off;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";
    add_header Access-Control-Allow-Origin *;

    if (!-e $request_filename) {
        rewrite  ^/[_0-9a-zA-Z-]+(/wp-(content|admin|includes).*) $1 break;
        rewrite  ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 break;
    }

    # Uncomment these lines if SSL is provided
    #==========================
    #listen 443 ssl;
    #ssl_certificate your-ssl.pem;
    #ssl_certificate_key your-ssl.key;
    #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 
    #ssl_prefer_server_ciphers on;
    #ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    #==========================

    # Log files for Debugging
    access_log logs/example-access.log;
    error_log logs/example-error.log;

    # Directory
    root "C:/nginx/html/example.com";
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ /\.(?!well-known).* {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Deny yaml, twig, markdown, ini file access
    location ~* /.+\.(markdown|md|twig|yaml|yml|ini)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # PHP-FPM Configuration Nginx
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

Conclusion

If you've gotten this far, give yourself a pat in the back.

Hope you've found this tutorial useful and please share it with your friends and colleagues if they really need it!