Bart Simons

getting started

A 3 post collection


Pretty URLs in Apache - Getting Started Tutorial

 •  Filed under getting started, tutorial, Pretty URLs, Apache, mod_rewrite

Ever wanted to create beautiful URLs for your self-hosted websites? This page has got you covered with all just about the information you need to get started. All functionality which is demonstrated on this page is made possible by mod_rewrite, also known as the 'rewrite' module. This module gives system administrators the possibility to rewrite URL paths, as I shall demonstrate:

Let's say Mr. X has a website called mywebsite.corp with a single page called test.php. He currently visits his page like this:

mywebsite.corp before any modification

Mr.X is not happy with how the php file extension is exposed to the user, and wants to make his page URL look better by removing the php extension. The first thing he has to do is to enable the rewrite module which is fairly simple:

a2enmod rewrite  

Now, all that is left to do is to write the rewrite rule inside a Directory tag in apache.conf like this:

<Directory /var/www/html>  
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        RewriteEngine On
        RewriteRule ^test$ /test.php
</Directory>  

Restart your web server with service apache2 restart and let's see if things work the way we want:

mywebsite.corp after modification

It works! Now, let's dive deeper into what the RewriteRule line does

^ is the character to indicate the root URL of your website
$ is the character to indicate the end of the URL to match

This has been a simple demonstration of what you can do and accomplish with mod_rewrite. However, you can do so much more with this module. Let's take a look at regular expressions and how to use with rewrite rules:

mywebsite.corp before second modification

With this directory block:

<Directory /var/www/html>  
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        RewriteEngine On
        RewriteRule ^users/(\d+)$ /users.php?id=$1
</Directory>  

We are able to create an URL rewrite that functions like this:

mywebsite.corp after second modification

As you can see, you can use capture groups to capture URL parameters which you can use like in the previous example. Pretty handy!

This is just a handful of possibilities, I hope that you find 'em useful. Thanks for reading!

WireGuard.io Getting Started Guide/Tutorial

 •  Filed under getting started, guide, tutorial, WireGuard, WireGuard.io

In todays networking world, virtual private networks are unmissable. With IT needs growing exponentially in the current modern era, it is essential to make the right choices on what VPN software you are going to use. While IPSec tunnels are commonly deployed and proven to deliver good performance while being stable at the same time, are there any other alternatives?

Yes there are. Here are some VPN solutions I have deployed in the past:

  • OpenVPN, both in peer-to-peer and remote access configurations
  • PPTP (with pptpd on Linux)
  • SoftEther (Has its own VPN protocol over an SSL connection)

Recently - on a long journey on Google - I came across WireGuard. They claim to have the networking code of their VPN software running in kernel-space for optimal performance, so that seems all good. I decided to dig deeper into WireGuard, so I could write a guide/tutorial on the getting started and configuration process.

My test environment

My test environment consists over two Linux servers in the cloud, they are directly connected to each other over a private network:

  • server-01: 10.129.29.151
  • server-02: 10.129.30.154

For benchmarking networking speeds I used iperf, and this is the traffic speed test result I got over this private network:

iperf raw network speeds

Installing WireGuard

This step is pretty straight forward, just copy and paste this code into your terminal:

add-apt-repository -y ppa:wireguard/wireguard  
apt update  
apt install -y wireguard-dkms wireguard-tools  

If you don't use Ubuntu on your servers, check out this page on the WireGuard website to find out how to install it on your Linux distribution of preference.

Initialisation of WireGuard's virtual interfaces

Configuring a simple peer-to-peer tunnel on WireGuard is not that complicated.

First of all, let's create the wg0 interface on both servers - this will be the virtual interface for your virtual private network between both servers:

ip link add dev wg0 type wireguard  

Your virtual network also needs an IP address for each node so that machines can communicate between each other over IP:

# For server-01:
ip address add dev wg0 192.168.2.1/24

# For server-02:
ip address add dev wg0 192.168.2.2/24  


Generating a configuration for each node

WireGuard uses a key-based VPN solution for communication between nodes. This system insists of a private key and a public key for each node. You can generate these keys on each node with the following command:

# For server-01:
wg genkey | tee privatekey01 | wg pubkey > publickey01

# For server-02
wg genkey | tee privatekey02 | wg pubkey > publickey02  

Create a configuration file named wireguard.conf and store it somewhere safe with the right Linux permissions applied on this file (chown/chmod). Here's what you need to put in this configuration file:

# On server-01:

[Interface]
ListenPort = 4820  
PrivateKey = privatekey01's content goes here

[Peer]
Endpoint = ip:port of endpoint (10.129.30.154:4820)  
PublicKey = publickey02's content goes here  
AllowedIPs = 0.0.0.0/0  
# On server-02:

[Interface]
ListenPort = 4820  
PrivateKey = privatekey02's content goes here

[Peer]
Endpoint = ip:prt of endpoint (10.129.29.151:4820)  
PublicKey = publickey01's content goes here  
AllowedIPs = 0.0.0.0/0  

Link the configuration to the interface on all nodes:

wg setconf wg0 wireguard.conf  

Bring the interface up on all nodes:

ip link set up dev wg0  

You are now connected, you can test connectivity by sending ICMP echo packets:

WireGuard ICMP connectivity test


Benchmarking performance

Run this command on the first node (server-01 in my case):

iperf -s  

Run this command on the second node (server-02 in my case):

iperf -c 192.168.2.1  

These are the results I got over the tunnel:

Pretty good results for just a dual-core server. I'm sure that there are possibilities/tweaks to make WireGuard perform even better, we'll see...

Ghost.org API - Getting Started

 •  Filed under ghost.org, api, tutorial, basics, ghost, getting started

The Ghost blogging software platform makes great use of top-of-the-line technologies, and with that comes a great public API that you can use to integrate Ghost with your current software. It does only take about half an hour to learn the basics, and could be a great new method for you to publish your content!

The demo project

To showcase the possibilities of Ghost, we need a little demo project to get the fire started with examples. This project will cover the following API related stuff:

  • Logging into Ghost from the command line;
  • Publishing a new page with automatically generated data.
Logging into Ghost from the command line

When you create an API request to a Ghost blog it expects a specially crafted so-called access token. An access token is used for authentication purposes on the API side and can be generated by sending your username and password to this location in an HTTP POST request:

/api/v0.1/authentication/token

It expects the following body data:

grant_type=password&username=dummyusername&password=dummypassword&client_id=ghost-admin&client_secret=dummyclientsecret

The client secret of your Ghost blog can be extracted from the login page of your website. You can search through the DOM of the login page or use curl together with grep to fetch it for you:

Fetch Ghost Client Data

I also made a PowerShell wrapper to automate the API login process:

<#  
    .SYNOPSIS
        Logs into a Ghost blog.
    .DESCRIPTION
        Ghost blog login from PowerShell, for demonstration purposes.
    .LINK
        https://bartsimons.me
#>

Function Invoke-GhostLogin {  
    [CmdletBinding()]
        [OutputType(
            [PSCustomObject]
        )]

    Param (
        [Parameter(
            Mandatory = $true
        )]
        [String]
        $AdminURL,

        [Parameter(
            Mandatory = $true
        )]
        [String]
        $User,

        [Parameter(
            Mandatory = $true
        )]
        [String]
        $Password
    )

    $LoginPageContent = (Invoke-WebRequest $AdminURL).AllElements

    $ClientID =     ($LoginPageContent | where { $_.name -eq "env-clientId" } | Select-Object content).content
    $ClientSecret = ($LoginPageContent | where { $_.name -eq "env-clientSecret" } | Select-Object content).content

    If ($AdminURL[$AdminURL.length-1] -ne "/") {
        $AdminURL+="/"
    }

    $LoginAPIResponse = (Invoke-WebRequest ($AdminURL+"api/v0.1/authentication/token") -Method Post -Body "grant_type=password&username=$User&password=$Password&client_id=$ClientID&client_secret=$ClientSecret").Content | ConvertFrom-Json

    return New-Object PSObject -Property @{
        access_token  = $LoginAPIResponse.access_token
        expires_in    = $LoginAPIResponse.expires_in
        refresh_token = $LoginAPIResponse.refresh_token
        token_type    = $LoginAPIResponse.token_type
    }
}

And just for fun, I also made a wrapper for Bash if you need a automation solution for Linux and macOS:

#!/bin/bash

# Ghost Blog Login Script
# Usage:   ./ghostlogin.sh url username password
# Example: ./ghostlogin.sh https://ghost-example.com/ghost/ admin 123456

if [[ $# -eq 3 ]]; then

    url="$1"
    length_url_end=${#1}
    length_url_start=length_url_end-1
    if [[ ${1:length_url_start:length_url_end} != "/" ]]; then
        url="$url/"
    fi

    clientdata=($(curl -s $url | grep '<meta name="env-client' | cut -d '"' -f4))

    eval "curl -s ${url}api/v0.1/authentication/token --data 'grant_type=password&username=$2&password=$3&client_id=${clientdata[0]}&client_secret=${clientdata[1]}'"

fi  

Ghost Login Bash Script

And that makes up for the authentication part of this guide. All you need for you is the access_token token to authenticate all the API requests that require authentication. Now, the next step is to actually post something on your website by using the API. Weird enough, I was not able to find any information nor examples about the POST and PUT request methods in the Ghost official documentation. I found out about how to use it by inspecting the network traffic going out to my testing/demo server. This is how it works:

  • You make a POST request with the necessary details/content, the draft gets created;
  • The response data contains the body data that's needed for the next request;
  • You make a PUT request with the response data from the response of the previous request and modify the needed stuffs;

Here's how to initially create a post via PowerShell:

<#  
    .SYNOPSIS
        Creates a new post on a Ghost blog.
    .DESCRIPTION
        Create a new post on a Ghost blog, to
    .LINK
        https://bartsimons.me
#>

Function New-GhostPost {  
    [CmdletBinding()]
        [OutputType(
            [String]
        )]

    Param (
        [Parameter (
            Mandatory = $true
        )]
        [String]
        $AccessToken,

        [Parameter (
            Mandatory = $true
        )]
        [String]
        $Title,

        [Parameter (
            Mandatory = $true
        )]
        [String]
        $AdminURL,

        [Parameter (
            Mandatory = $true
        )]
        [String]
        $Slug,

        [Parameter (
            Mandatory = $true
        )]
        [String]
        $Content,

        [Parameter (
            Mandatory = $true
        )]
        [Boolean]
        $Public
    )

    If ($AdminURL[$AdminURL.length-1] -ne "/") {
        $AdminURL+="/"
    }

    $NewPostDataTemplate = '{"posts":[{"title":"<%TITLE%>","slug":"<%SLUG%>","markdown":"<%CONTENT%>","image":null,"featured":false,"page":false,"status":"draft","language":"en_US","meta_title":null,"meta_description":null,"author":"1","publishedBy":null,"tags":[]}]}' -Replace "<%TITLE%>","$Title" -Replace "<%SLUG%>","$Slug" -Replace "<%CONTENT%>","$Content"
    $PageCreationResponse = (Invoke-WebRequest ($AdminURL+"api/v0.1/posts/?include=tags") -Method Post -Headers @{"Authorization"="Bearer $AccessToken";"Content-Type"="application/json; charset=UTF-8"} -Body $NewPostDataTemplate).Content

    If ($Public -eq $true) {
        $PostID = ($PageCreationResponse | ConvertFrom-Json).posts.id
        $PageCreationResponse = (Invoke-WebRequest ($AdminURL+"api/v0.1/posts/$PostID/?include=tags") -Method Put  -Headers @{"Authorization"="Bearer $AccessToken";"Content-Type"="application/json; charset=UTF-8"} -Body ($PageCreationResponse -Replace "draft","published")).Content
    }

    return $PageCreationResponse
}

You can do the same thing in Bash if you feel more at home with UNIX tools just like me. The code on this one might be a bit messy but it works. And it's not PowerShell. 😉

#!/bin/bash

# Ghost Blog Post Script
# Usage:   ./ghostmakepost.sh url accesstoken title slug content public
# Example: ./ghostmakepost.sh https://ghost-example.com/ghost/ "fEwkJhfew7j....fe31" "Just testing :)" "just-testing" "######It works!" true 

if [[ $# -eq 6 ]]; then  
    url="$1"
    length_url_end=${#1}
    length_url_start=length_url_end-1
    if [[ ${1:length_url_start:length_url_end} != "/" ]]; then
        url="$url/"
    fi

    accesstoken="$2"
    title="$3"
    slug="$4"
    content="$5"
    public="$6"

    conceptdata='{"posts":[{"title":"<%TITLE%>","slug":"<%SLUG%>","markdown":"<%CONTENT%>","image":null,"featured":false,"page":false,"status":"draft","language":"en_US","meta_title":null,"meta_description":null,"author":"1","publishedBy":null,"tags":[]}]}'
    conceptdata="${conceptdata/<%TITLE%>/$title}"
    conceptdata="${conceptdata/<%SLUG%>/$slug}"
    conceptdata="${conceptdata/<%CONTENT%>/$content}"
    conceptpostcommand="curl -s ${url}api/v0.1/posts/?include=tags -X POST -H 'Authorization: Bearer $accesstoken' -H 'Content-Type: application/json; charset=UTF-8' --data-binary '$conceptdata'"

    conceptpostresult=$(eval $conceptpostcommand)

    conceptpostid=($(echo $conceptpostresult | cut -d '"' -f5))

    length_conceptpostid=${#conceptpostid}
    length_conceptpostid_start=1
    length_conceptpostid_end=length_conceptpostid-2

    conceptpostid=${conceptpostid:length_conceptpostid_start:length_conceptpostid_end}

    if [[ $public == "true" ]]; then
        conceptpostresult="${conceptpostresult/\"status\":\"draft\"/\"status\":\"published\"}"
        publicpostcommand="curl -s ${url}api/v0.1/posts/$conceptpostid/?include=tags -X PUT -H 'Authorization: Bearer $accesstoken' -H 'Content-Type: application/json; charset=UTF-8' --data-binary '$conceptpostresult'"
        publicpostresult=$(eval $publicpostcommand)
        echo $publicpostresult
    else
        echo $conceptpostresult
    fi
fi  

This is my take on working with the Ghost public API from a command-line and/or scripting perspective. I hope you learnt something from my write-up on Ghost and its beautiful backend work. If you haven't tried Ghost yet, go give it a try: it's open source and free + built on node.js 🙂