remileduc / sherver

Pure Bash lightweight web server.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sherver

Pure Bash lightweight web server.

Easy solution to setup a local website without any server configuration!!!

This is inspired by bashttpd. Though, the behavior is entirely different. See below for more information.

Presentation

How to use

How to use (Expert)

Example

About Security

Why Sherver?

License?

Presentation

How to run

Just clone and run ./sherver.sh. Then, you should be able to connect to localhost:8080. You can pass the port to listen on as a parameter: ./sherver.sh 8080 (default is 8080).

Requirements

This is made to run with Bash. It may not work in another shell. The following tools need to be present in the system (note that they are all part of the default installation of Debian):

  • envsubst if you want to do templating
  • socat to run the server.
    • you can use netcat instead, but it doesn't work well with concurrent HTTP requests

Features

Sherver is a web server that implements part of HTTP 1.0. Even if it is written in a few lines of Bash, it is able to do a lot:

  • no configuration needed: you can just add files either in scripts or in file folders
  • serve any HTML page no matter how complexe (with advanced JavaScript and multiple scripts or files to download...)
  • serve files (text or binary, pictures...) with correct mime type
  • dynamic pages
  • templated HTML so you don't have to duplicate headers and footers
  • parse of URL query string
  • support for GET and POST
  • deal with client cache resources
  • easily extandable
    • can run any scripts or executable of any languages as soon as they output something on stdout
    • comes with a library of bash functions to ease the use

All of these makes Sherver the perfect tool to run a small server that will serve few pages on your local network.

Even if it sounds awesome, Sherver still has the following limitations:

  • only support HTTP GET and POST requests, though it would be easy to add the others
  • no concurrency
    • if a page needs to download a lot of files, the files are sent one after the other
    • if 2 users access the website, the second one needs to wait until the first one is fully served
  • no security (see About Security).

This is why Sherver is supposed to remain in a private and controlled environment. Do not expose Sherver on Internet!!! If you want to expose your site on Internet, you should use a tool that knows about security and concurrency (like nginx or other).

Always run Sherver behind a firewall that prevent any intrusions from outside.

How to use

Quick documentation about how to use Sherver for your own use. All variables and functions mentioned here have a full description in scripts/README.md.

Serve static pages

The simplest thing you can do is to serve static pages : pure HTML files that don't need any processing.

To do so, you only need to put your HTML files in the subdirectory file/pages. Then, you can access to your pages through a URL like /file/pages/index.html (if your file name is index.html for instance).

Note that you'll have to give the full file name in the URL so Sherver can find it.

It is as simple as that! If Sherver can find the file, it will serve it. Otherwise, it will return a 404 error.

Serve files

You can serve any type of files from Sherver. From text-based like CSS or JavaScript to binaries like images, videos, zip...

Just put the files in the subdirectory file. You can then reference them through a URL like /file/venise.webp. Note that it is preferable to give full path rather than relative paths.

Sherver will automatically serve the file if it can find it, with the correct mime type. It will even allow the browser to cache the file, and will only serve it again if the file has changed. If Sherver can't find the file, it will return a 404 error.

For resources, like CSS, JavaScript, favicon... it is better to put them in the subfolder file/resources, though you don't have to.

Example on how to link a CSS file:

<link rel="stylesheet" type="text/css" href="/file/resources/ugly.css">

Example on how to integrate a picture in your HTML:

<img src="/file/venise.webp" alt="">

Serve dynamic pages

This is where Sherver becomes useful: it can serve dynamic pages, built server side depending on the context.

To do so, you just need to add executables in the subfolder scripts. Executables can be of any types (bash script, python script, any other scripts, any binary like C++ compiled executable...) as soon as Sherver can execute it (it must have the executable flag set).

As soon as you have an executable there, Sherver will run it and serve its output. Note that index.sh is a particular name as it is the one that will be executed by the dispatcher if you access to the root of the website (see dispatcher section below). If Sherver can't run any files, it will return a 404 error. If the executable fails (return code is not 0), it will return a 500 error.

To link an executable, you have to omit the folderscripts in the URL: /page.sh will look for the executable ./scripts/page.sh.

The executable is ran from the scripts folder.

Bash scripts

Sherver is mainly made to work with bash scripts. If you create a Bash script, the first thing you should do is to run the function init_environment. Then you will have access to all the following variables:

  • REQUEST_FULL_STRING
  • REQUEST_METHOD
  • REQUEST_URL
  • REQUEST_HEADERS
  • REQUEST_BODY
  • URL_BASE
  • URL_PARAMETERS
  • DATE
  • RESPONSE_HEADERS
  • HTTP_RESPONSE

And also a lot of useful functions like:

  • add_header
  • send_response
  • send_file
  • send_error

Check the whole documentation about the SHERVER_UTILS.sh library in scripts/README.md.

Everything written on the standard output will be sent to the client. Here is a very simple script that returns the requests in a text format:

#!/bin/bash

init_environment
if [ "$REQUEST_METHOD" != 'GET' ]; then
	send_error 405
fi

add_header 'Content-Type' 'text/plain'
send_response 200 "$REQUEST_FULL_STRING"

Any other scripts or binaries

If you don't use Bash, you will only have access to the environment variable REQUEST_FULL_STRING that contains the full request as a string. The requested URL (REQUEST_URL) will be passed as first argument.

Everything written on the standart output will be sent to the client. Though, you should write the headers of the response yourself.

Template mechanism

For Bash scripts, there is a basic template engine integrated with Sherver (lol). It actually uses envsubst to replace any occurrence of $VARIABLE by the variable from the environment if there is.

You can put your templates in the subfolder scripts/templates, though it is not mandatory.

Here is a template for a text file template.txt (that improves our previous Bash script example):

You entered the following request:

$REQUEST

And you would use it with the following script:

#!/bin/bash

init_environment
if [ "$REQUEST_METHOD" != 'GET' ]; then
	send_error 405
fi

REQUEST="$REQUEST_FULL_STRING"
# put REQUEST in the environment so we can use it in our template
export REQUEST
# load the template
response=$(envsubst < 'templates/template.txt')

add_header 'Content-Type' 'text/plain'
send_response 200 "$response"

Full HTML example in Example below.

POST requests

Post requests are supported. You can check the value of the variable REQUEST_METHOD that will either be GET or POST, so you can have different behavior based on the type of the request.

The content of the POST request can be retreived in the variable REQUEST_BODY. If the data is url encoded by the client, you can use the function parse_url with some tricks to get an associative array of the parameters.

Any content can be sent back to the client. You can add the correct mime type thanks to the method add_header.

How to use (Expert)

All variables and functions mentioned here have a full description in scripts/README.md.

Logs

Anything written to the standard error can be logged. To ease the logs, you can use the function log.

To keep the logs in a file, you can redirect the error output of sherver.sh in a file:

./sherver.sh 2> logs.txt

By default, the headers of both the requests and the responses are logged, but not the bodies.

Dispatcher

The dispatcher is responsible of asking to either serve a file or run a script, depending on the requested UTL. It is implemented in the file dispatcher.sh.

It currently has 4 actions:

  • if the URL is the root (/), then it executes the script scripts/index.sh
  • if the URL asks for index.htm or index.html, it executes the script scripts/index.sh
  • if the URL starts with /file/, it serves the file asked
  • in any other case, it will run the script provided by the URL, prepending the scripts folder (the URL /test/dummy.sh will run the script scripts/test/dummy.sh if it exists).

All this behaviors can be changed by editing the file dispatcher.sh.

Run as a service (daemon)

First of all, you need to create a specific user that will run sherver.sh with low priviledges. We'll call our user sherver and we'll put the whole website in its hone directory.

We need to add our user to the groups sudo and netdev, so it is able to manage the VPN (it is obviously not a good idea to give sudo to the user, this is why you shouldn't expose the website on Internet).

useradd -mUG sudo,netdev -s /usr/bin/bash sherver
passwd sherver
	...

Note that you can add your current user to the sherver group for practical reasons (you'll have to relog to make it effective):

adduser USER sherver

Now, let's get the website in its home directory

su sherver
cd ~
git clone https://github.com/remileduc/sherver.git
cd sherver
git checkout perso

Finally, we need to enable the service so it starts sherver.sh automatically. To do so, copy the file sherver.service in /usr/share/systemd/ and then enable the service:

cp sherver.service /usr/share/systemd/
ln -s /usr/share/systemd/sherver.service /etc/systemd/system/sherver.service
systemctl daemon-reload
systemctl enable sherver.service

Example

You can see as an example the scripts that I use at home to manage my VPN. It is accessible on the perso branch. Note that you need the script vpn-mgr.sh to be able to use it properly.

About security

See bashttpd. It is obvious to say that this comes without any security features. Do not expose Sherver on Internet.

  • it is not currently able to serve over HTTPS
  • it uses rudimentary bash scripts to parse URL and POST request body, that could lead to security breaches
  • it executes blindly any script in the scripts subfolder

If you need to expose the site on internet, you need a real server that has been built especially to face all these issues.

Though, it is perfect to use on a local network. It will be as secure as are your wifi connection and your firewall.

Why Sherver?

I wanted to set up quickly a server that would serve dynamic pages, and that could execute some bash scripts, in order to control my media center through web pages.

I didn't want to install and configure Apache or nGinx. In fact, I didn't want any configuration.

Sherver is able to run without any configuration. You just need to add files at the right place. It can run without anything to be installed (all tools used are part of the default installation of Debian, except maybe for socat).

You can see my use case in the perso branche.

License

Everything is under MIT License.

We use mimetype script, shipped in scripts/utils/mimetype, which is under Perl license.

About

Pure Bash lightweight web server.

License:MIT License


Languages

Language:Shell 56.0%Language:Perl 38.0%Language:HTML 3.9%Language:CSS 2.1%