Simple web server in Bash using Ncat

Introduction

If you need to create a very simple web server and cannot find a suitable piece of software, e.g. for some specific purpose, you can use Bash to implement it by yourself. An example might be a custom Prometheus Exporter as mentioned below.

Prerequisities

The most important tool besides Bash that is used for listening on the specified port is Ncat. Ncat is started from the main web server script and if a request arrives, it invokes a Bash function exported from the main script. This function than handles the request and it's output is sent back to the connected client by Ncat.

An important prerequisity besides the ncat command is that the /bin/sh command is a symlink to /bin/bash, not to another shell. Otherwise the Bash handler function export doesn't work properly (note the export -f ... commands in the script).

Hello world web server

Let's assume that we want to create a web server with name baxic-web-server located in /opt that is able to print static preconfigured strings.

Let's create a directory /opt/baxic-web-server and place there the following executable script with name baxic-web-server.sh.

  • #!/bin/bash

  • PROGNAME='baxic-web-server.sh'

  • CONFFILE="/opt/${PROGNAME%.sh}/${PROGNAME%.sh}.conf"

  • export PROGNAME CONFFILE


  • # read_config_file
  • # read configuration file
  • read_config_file() {

  • unset PORT
  • unset STRING

  • if [[ ! -f "$CONFFILE" || ! -r "$CONFFILE" ]]; then
  • echo "$PROGNAME: configuration file '$CONFFILE' not found"
  • exit 2
  • fi

  • . "$CONFFILE"

  • if [[ -z "$PORT" ]]; then
  • echo "$PROGNAME: port not configured"
  • exit 2
  • fi

  • }

  • read_config_file


  • # handler
  • # handler for separate requests
  • handler() {

  • local httpget

  • local response response_len

  • read httpget

  • response="$(
  • echo "${STRING[@]}"
  • )"

  • response_len="$(echo "$response" | wc -c)"

  • echo 'HTTP/1.1 200 OK'
  • echo "Content-Length: $response_len"
  • echo 'Content-Type: text/plain'
  • echo "Date: $(date -R -u | sed -r 's#\+0000$#GMT#')"
  • echo
  • echo "$response"

  • return 0

  • }


  • export -f handler
  • export -f read_config_file


  • # handle requests on port
  • ncat -k -l "$PORT" -c 'read_config_file && handler'


  • # exit
  • exit 0

Then let's add it's configuration file baxic-web-server.conf to the same directory.

  • PORT='8080'

  • STRING[0]='Hello world!'
  • STRING[1]='And bye!'

Now the web server can be started.

  • /opt/baxic-web-server/baxic-web-server.sh

And tested from a browser or using curl.

  • curl -i http://localhost:8080
Hello world SSL web server

To create a SSL script version, the script can be modified just a little bit to run Ncat with SSL and specify the certificate and key.

  • #!/bin/bash

  • PROGNAME='baxic-web-server-ssl.sh'

  • CONFFILE="/opt/${PROGNAME%.sh}/${PROGNAME%.sh}.conf"

  • PORT='8443'

  • export PROGNAME CONFFILE
  • export PORT


  • # read_config_file
  • # read configuration file
  • read_config_file() {

  • unset PORT
  • unset STRING
  • unset SSL_CRT
  • unset SSL_KEY

  • if [[ ! -f "$CONFFILE" || ! -r "$CONFFILE" ]]; then
  • echo "$PROGNAME: configuration file '$CONFFILE' not found"
  • exit 2
  • fi

  • . "$CONFFILE"

  • if [[ -z "$PORT" ]]; then
  • echo "$PROGNAME: port not configured"
  • exit 2
  • fi

  • if [[ -z "$SSL_CRT" || -z "$SSL_KEY" ]]; then
  • echo "$PROGNAME: ssl certificate or key not configured"
  • exit 2
  • fi

  • }

  • read_config_file


  • # handler
  • # handler for separate requests
  • handler() {

  • local httpget

  • local response response_len

  • read httpget

  • response="$(
  • echo "${STRING[@]}"
  • )"

  • response_len="$(echo "$response" | wc -c)"

  • echo 'HTTP/1.1 200 OK'
  • echo "Content-Length: $response_len"
  • echo 'Content-Type: text/plain'
  • echo "Date: $(date -R -u | sed -r 's#\+0000$#GMT#')"
  • echo
  • echo "$response"

  • return 0

  • }


  • export -f handler
  • export -f read_config_file


  • # handle requests on port
  • ncat --ssl --ssl-cert "$SSL_CRT" --ssl-key "$SSL_KEY" -k -l "$PORT" -c 'read_config_file && handler'


  • # exit
  • exit 0

This example adds the paths to the certificate and key to the configuration file.

  • PORT='8443'

  • SSL_CRT='/etc/ssl/certs/ssl-cert-snakeoil.pem'
  • SSL_KEY='/etc/ssl/private/ssl-cert-snakeoil.key'

  • STRING[0]='Hello world!'
  • STRING[1]='And bye!'

It also assumes, that both the script and configuration file are located in the directory /opt/baxic-web-server-ssl or in /opt/baxic-web-server with a symlink from /opt/baxic-web-server-ssl to /opt/baxic-web-server.

Summary

The Hello world web server script shown above is very simple and prints just a static message, doesn't log anything, separates the SSL and non-SSL version etc. However, it can be easily extended depending on the expected functionality - mainly the handler and read_config functions.

As already mentioned, for example the Baxic Command Exporter or Baxic SNMP Exporter scripts are based on this approach.

 

Inserted: 2021-04-17 21:44:54
Last updated: 2021-05-02 07:11:24