https://github.com/triska/letswicrypt
Prolog HTTPS Servers
https://github.com/triska/letswicrypt
https https-server prolog swi-prolog
Last synced: 7 months ago
JSON representation
Prolog HTTPS Servers
- Host: GitHub
- URL: https://github.com/triska/letswicrypt
- Owner: triska
- Created: 2016-05-06T16:19:23.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2018-02-11T20:33:23.000Z (over 7 years ago)
- Last Synced: 2024-03-20T03:10:32.337Z (over 1 year ago)
- Topics: https, https-server, prolog, swi-prolog
- Language: Prolog
- Homepage: https://www.metalevel.at/letswicrypt/
- Size: 26.4 KB
- Stars: 35
- Watchers: 6
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# LetSWICrypt — Prolog HTTPS servers
Prolog is extremely well suited for writing
[**web applications**](https://www.metalevel.at/prolog/web).This repository explains how to set up and run secure (**HTTPS**)
web servers using SWI-Prolog with *Let's Encrypt* and other
certificate authorities.**Project page**:
[**https://www.metalevel.at/letswicrypt/**](https://www.metalevel.at/letswicrypt/)
# Requirements
SWI-Prolog 7.5.8 or later ships with everything that is
necessary to run HTTPS servers as described in the following.# Obtaining a certificate
For the sake of concreteness, assume that we want to set up an
HTTPS server that is reachable at `xyz.com` and
`www.xyz.com`. These names are chosen also because they are easy to
search for and do not occur anywhere else in the configuration files.## Variant A: Use *Let's Encrypt*
[**Let's Encrypt**](https://letsencrypt.org/) is a free certificate
authority (CA).The tool is easy to install and run. Follow the instructions on their
page, and then execute the following command on the host machine:$ sudo certbot certonly --standalone -d xyz.com -d www.xyz.com
**Note**: This requires that you *stop* any server that listens on
port 80 or port 443, until the certificate is obtained. There
are also other ways to obtain a certificate that allow you to keep
existing servers running. See below for more information.After this is completed, you obtain 4 files in `/etc/letsencrypt/live/xyz.com/`:
/etc/letsencrypt/live/xyz.com/cert.pem
/etc/letsencrypt/live/xyz.com/chain.pem
/etc/letsencrypt/live/xyz.com/fullchain.pem
/etc/letsencrypt/live/xyz.com/privkey.pemWe only need two of them:
- `privkey.pem`: the server's private key
- `fullchain.pem`: the certificate and certificate chain.## Variant B: Use a different certificate authority
You can also use a different CA. To do that, you first create a new
private key and certificate signing request (CSR). The file
[openssl.cnf](openssl.cnf) shows you what is necessary to create
a CSR for both `xyz.com` and `www.xyz.com`. The
`alt_names` section is relevant to cover both domains:[ alt_names ]
DNS.1 = www.xyz.com
DNS.2 = xyz.comUsing `openssl.cnf`, you can create the key (`server.key`)
and CSR (`server.csr`) for example with:$ openssl req -out server.csr -new -newkey rsa:2048 -nodes -keyout server.key -config openssl.cnf
You can inspect the created CSR with:
$ openssl req -text -noout -verify -in server.csr
To obtain a certificate, you have again two options: Either use a
trusted CA (simply supply `server.csr`), or self-sign the
key using for example:$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt -extensions v3_req -extfile openssl.cnf
In both cases, the files that are important for the following are:
- `server.key`: the server's private key
- `server.crt`: the certificate and certificate chain.Note that—up to naming—this corresponds to the files
obtained in Variant A.# Running an HTTPS server with SWI-Prolog
In the previous section, we have seen two ways to obtain a private key
and a certificate. For clarity, we have used different file names to
distinguish the variants. We now assume the following files are
available in `/var/www/xyz.com/`, no matter which variant you used to
obtain them:- `server.key`: the server's private key
- `server.crt`: the certificate and certificate chain.**Note**: You can store the certificate and key in any location,
and also *leave* the files in `/etc/letsencrypt/live/` if you used
*Let's Encrypt* to obtain them. This is because SWI-Prolog reads
these files *before* dropping privileges when starting an
HTTPS server.As the name suggests, the private key is meant to be kept
*private*. Therefore, make sure to use suitable file permissions.You can inspect the issued certificate with:
$ openssl x509 -in server.crt -text -noout
## Preliminaries: SWI-Prolog web server as Unix daemon
The file [server.pl](server.pl) contains a very simple web server that
is written using SWI-Prolog. In its current form, it simply replies
with `Hello!` to any request. In a more realistic scenario, you
would of course supply a more suitable definition
of `handle_request/1`, so that the server replies with more
useful content. Still, this basic server suffices to illustrate the
principle for running an HTTPS server with any of the
certificates we obtained in the previous steps.First, note that this server uses the [`http_unix_daemon`
library](http://eu.swi-prolog.org/pldoc/doc/_SWI_/library/http/http_unix_daemon.pl).
This library makes it extremely easy to run the web server as a
Unix daemon by implicitly augmenting the code to let you
configure the server using command line options. If you have an
existing web server that you want to turn into a Unix daemon,
simply add the following directive at the beginning:
:- use_module(library(http/http_unix_daemon)).Once you have done this, you can run the server with:
$ swipl server.pl --port=PORT
and it will automatically launch as a daemon process. During
development, is is easier to work with the server on the terminal
using an interactive Prolog toplevel, which you can enable with:
$ swipl server.pl --port=PORT --interactivewhere `PORT` is any free port on your system. Try for
example `--port=3041`.To find out more available command line options, use:
$ swipl server.pl --help
## Starting a Prolog HTTPS server
To start an HTTPS server with SWI-Prolog, the following 3 command line
options of the Unix daemon library are of particular relevance:- `--https`: enables HTTPS, using port 443 by default.
- `--keyfile=FILE`: `FILE` contains the server's private key.
- `--certfile=FILE`: `FILE` contains the certificate and certificate chain.So, in our case, we can launch the HTTPS server for example with:
$ sudo swipl server.pl --https --user=you --keyfile=/var/www/xyz.com/server.key --certfile=/var/www/xyz.com/server.crt
Note that running the server on port 443 requires root privileges. The
`--user` option is necessary to drop privileges to the specified
user after forking.## Launching the HTTPS server on system startup
To launch the HTTPS server on system startup, have a look at the
`systemd` sample service file [`https.service`](https.service).Adjust the file as necessary, copy it to `/etc/systemd/system` and enable it with
$ sudo systemctl enable /etc/systemd/system/https.service
then start the service with:
$ sudo systemctl start https.service
# Making your server more secure
Once your server is running, use for example
[SSL Labs](https://www.ssllabs.com/) to assess the quality of its
encryption settings.As of 2017, it is possible to obtain an **A+** rating with SWI-Prolog
HTTPS servers, by using:- as ciphers (see command line option `--cipherlist`): `EECDH+AESGCM:EDH+AESGCM:EECDH+AES256:EDH+AES256:EECDH+CHACHA20:EDH+CHACHA20`
- the `Strict-Transport-Security` header field, to enable HSTS.For additional security, you can encrypt the server's private key,
using for example:$ openssl rsa -des -in server.key -out server.enc
To use an encrypted key when starting the server, use the
`--pwfile=FILE` command line option of the HTTP Unix daemon,
where `FILE` stores the password and has suitably restrictive access
permissions.# Renewing the certificate
Once you have a web server running, you can use *Let's Encrypt* to
obtain and *renew* your certificate *without stopping* the server.To use this feature, you must configure your web server to serve any
files located in the directory **`.well-known`**. With the
SWI-Prolog HTTP infrastructure, you can do this by adding the
following directives to your server::- use_module(library(http/http_files)).
:- http_handler(root('.well-known/'), http_reply_from_files('.well-known', []), [prefix]).Restart the server and use the `--webroot` option as in the following
example:
$ sudo certbot certonly --webroot -w /var/www/xyz.com -d xyz.com -d www.xyz.comPlease see `man certbot` for further options. For example, using
`--logs-dir`, `--config-dir` and `--work-dir`, you can configure paths
so that you can run `certbot` *without* root privileges. In the
example above, it is assumed that your web content is located in the
directory `/var/www/xyz.com`.In this mode of operation, *Let's Encrypt* uses the existing web
server and file contents to verify that you control the domain.After you have done this, you can renew the certificate any time with:
$ certbot renew
This automatically renews certificates that will expire within
30 days, again using the existing web server to establish you as
the owner of the domain. You can run this command as a cronjob.After your certificate is renewed, you must restart your web server
for the change to take effect. Alternatively, you can exchange
certificates while the server keeps running, which is described below.# Exchanging certificates
SWI-Prolog makes it possible to *exchange* certificates while
the server *keeps running*.One way to do this is as follows:
1. Start your server *without* specifying a certificate or key.
2. Use the extensible predicate `http:ssl_server_create_hook/3` to add
a certificate and key upon launch, while storing the original
SSL context. See `ssl_add_certificate_key/4`.
3. When necessary, renew the certificate as explained above. Use
`ssl_add_certificate_key/4` to add the new certificate to the
original SSL context, obtaining a new context that is
associated with the updated certificate.
4. Use the extensible predicate `http:ssl_server_open_client_hook/3`
to use the new context when negotiating client connections.See the [SSL documentation](http://eu.swi-prolog.org/pldoc/doc_for?object=section(%27packages/ssl.html%27))
for more information.Using the original context as a baseline ensures that all command
line options are adhered to and copied to new contexts that are
created. For example, any specified *password* is securely retained in
contexts and can therefore be used also for newly created keys.Note how [**logical purity**](https://www.metalevel.at/prolog/purity)
of these predicates allows the thread-safe implementation of a feature
that is not available in most other web servers.# Server Name Indication (SNI)
To host multiple domains from a single IP address, you need **Server
Name Indication** (SNI). This TLS extension lets you
indicate different certificates and keys depending on the
*host name* that the client accesses.The HTTP Unix daemon can be configured to use SNI by providing
suitable clauses of the predicate `http:sni_options/2`. The first
argument is the *host name*, and the second argument is a list of
SSL options for that domain. The most important options are:- `certificate_file(+File)`: file that contains the **certificate**
and certificate chain
- `key_file(+File)`: file that contains the **private key**.For example, to specify a certificate and key for `abc.com`
and `www.abc.com`, we can use:
http:sni_options('abc.com', [certificate_file(CertFile),key_file(KeyFile)]) :-
CertFile = '/var/www/abc.com/server.crt',
KeyFile = '/var/www/abc.com/server.key'.
http:sni_options('www.abc.com', Options) :-
http:sni_options('abc.com', Options).# Doing it all manually
Instead of relying on the Unix daemon library, you can also _manually_
start an HTTPS server via `http_server/2`. This gives you total
control over all aspects of the server, including those that cannot be
specified as command line options. The options for `ssl_context/3` are
specified as `ssl(+Options)`.For example:
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_ssl_plugin)).https_server(Port, Options) :-
http_server(reply,
[ port(Port),
ssl([ certificate_file('/var/www/xyz.com/server.crt'),
key_file('/var/www/xyz.com/server.key')
])
| Options
]).
reply(_) :-
format("Content-type: text/plain~n~n"),
format("Hello!").Typical use cases do *not* require this. A better way to obtain the
same effect is to rely on the HTTP Unix daemon library, and use the
available hooks for more fine-grained control of SSL parameters.# Related topics
Check out [**Proloxy**](https://github.com/triska/proloxy): It is a
*reverse proxy* that is written entirely in SWI-Prolog. Use
Proloxy if you want to provide access to different web services
under a common umbrella URL.Importantly, you can run Proloxy as an HTTPS server and thus
encrypt traffic of all hosted services at once.For more cryptographic functionality of SWI-Prolog, check out
[**`library(crypto)`**](http://eu.swi-prolog.org/pldoc/man?section=crypto). This
library provides predicates for reasoning about secure hashes,
symmetric and asymmetric encryption, and **digital signatures**.See also the
[**Cryptography**](https://www.metalevel.at/prolog/cryptography)
chapter in
[*The Power of Prolog*](https://www.metalevel.at/prolog).# Acknowledgments
All this is is made possible thanks to:
[**Jan Wielemaker**](http://eu.swi-prolog.org) for providing the
Prolog system that made all this possible in the first place.[**Matt Lilley**](https://github.com/thetrime) for `library(ssl)`, the
SSL wrapper library that ships with SWI-Prolog. The SWI-Prolog HTTPS
server uses this library for secure connections.[**Charlie Hothersall-Thomas**](https://charlie.ht/) for
implementation advice to enable more secure ciphers
in `library(ssl)`.