---
date: 2020-05-28T20:00:11+02:00
description: "To deliver compressed static content (in the deflate, gzip, brotli format) by httpd+slowcgi, natives servers on OpenBSD"
draft: false
include_toc: true
show_comments: false
slug: "httpd-delivers-compressed-files"
tags: ['httpd','slowcgi','OpenBSD','CGI','deflate','gzip','brotli']
title: "httpd: deliver compressed static files (+slowcgi)"
translationKey: "httpd-compressed-static-files"
---
## Description
**OpenBSD** has, by default, in basesystem:
* a webserver, named **httpd**, since 5.7
* a server CGI, named **slowcgi**, since 5.4
* OpenBSD : **6.6, 6.7**
---
**Principle**: if web client accepts datas compression in the deflate, gzip,
brotli format, then **httpd** redirects to **slowcgi** to deliver the
compressed static content.
{{< note warning >}}
This is not means compression dynamicly, or "compression on the fly!"
{{}}
**httpd** is not able to manage delivery about compressed static content.
The tip is to use the server **slowcgi** CGI for.
In facts, by CGI script shell, we'll assume the delivery of those compressed
static content:
* for the format: **atom**, **css**, **html**, **js**, **json**, **svg**, **txt**, **xml**
* to both compression: **gzip**, and **brotli**.
## Installation
You do to [download my script **sbw.cgi**][1].
When it's done, you need to put into the `cgi-bin` folder on web chroot.
For this, use the command `install` as:
`install -o www -g bin -m 0550 sbw.cgi /var/www/cgi-bin/sbw.cgi`
Indeed, we install correctly with minimals/needed rights to the web user
`www` and the group `bin`.
### Dependencies
This script need the installation in the web chroot of several binaries
and some libraries, to run correctly:
Copy from:
* first: **sh**, and binaries: **cat**, **date** and **sha256** —
*which are all in the `/bin` system directory*.
* all others binaries: **basename**, **logger** {{}}1{{}},
**stat** - *which are all in the `/usr/bin` system directory*.
* the shared libc: `/usr/lib/libc.so.xx.0` {{}}2{{}}
* the library to execution: `/usr/libexec/ld.so`
to the respective folders into web chroot `/var/www/`.
All need `root` user and `bin` group rights:
* with 0555 mode for the binaries, e.g.:
* **sh**:
`install -o root -g bin -m 0555 /bin/sh /var/www/bin/`
* **stat**:
`install -o root -g bin -m 0555 /usr/bin/stat /var/www/usr/bin/`
* and 0444 mode for the libraries, as:
* **libc**:
`install -o root -g bin -m 0444 /usr/lib/libc.so.xx.0 /var/www/usr/lib/`
* **ld.so**:
`install -o root -g bin -m 0444 /usr/libexec/ld.so /var/www/usr/libexec/`
Before, you need to create the corresponding directories in the web chroot!
See my [dependencies script][2].
---
{{}}1{{}} about the interest of the binary `logger`:
Normaly, ideally, we don't need the logger. It is about logging some actions,
which in case of failure, have written in the logs `/var/log/{daemon,messages}`.
Also, if the `debug` variable is set to `1`, on the main function, then
the logger will return the values corresponding to the different variables,
for analysis: ensure that variable receive one value, and what value‽
{{}}2{{}} The **libc.so** change name,
at each version of OpenBSD:
* v6.7 : `libc.so.96.0`
* v6.6 : `libc.so.95.1`
This detail is important, can be hardly scripted. You need to modify the
script, at the new version of OpenBSD, to change the name of the library,
otherwise, it will not work!
---
{{< note tip >}}
To known, what are the dependencies from a binary, use the command `ldd`.
{{}}
## Configuration
### httpd
Add at your context `server`, all following needed `location` statements:
{{< code "web-httpd-delivre-fichiers-compresses-location-examples" httpd >}}
The file `sbw.conf` contains the following `fastcgi` statements:
{{< file "web-httpd-delivre-fichiers-compresses-fastcgi-examples" httpd "/etc/httpd.d/sbw.conf" >}}
**Explainations** :
It's important to define, at least:
* **root**: the relative path of script CGI, into the web chroot.
* **realroot**: a parameter for your web root
* and optionals paramaters, specially of the cache.
### slowcgi
The server **slowcgi** does not require any configuration. Only, you enable
and start with the tool `rcctl`.
## History
### Vulnerabilities
In fact, the story of the {{< abbr HTTP "HyperText Transfert Protocol" >}} protocol
reveals us two majors vulnerabilities related to the on-the-fly compression:
**CRIME** and **BREACH** — *the second is forked on the first*.
Thoses vulnerabilities can even impact {{< abbr TLS "Transport Layer Secure" >}}.
Among the countermeasures has been the adoption since HTTP 1.1 of block
encoding transfer — *the famous `Transfer-Encoding: chunked` header*;
similarly, it is strongly recommended to implement a **Referrer** policy
to allow delivery of compressed content ONLY from the current domain,
and to refuse it from any other domain.
### about httpd
Reyk Floeter, the httpd author/developer, [denies support for the compressed
content][3]. Even, one [request][4] had be done to support pre-compressed
content, it's not ready to be integrated.
### brotli
**brotli** is a compression format invented by a team from the company Google.
It is considered to be the successor to gzip because it is faster and has
a better compression ratio.
For more informations, see:
* https://brotli.org/
* Does your web client support it: https://caniuse.com/#search=brotli
**curl**, since v7.57.0, support brotli, by adding the option `--compressed`,
or `-H` — *this manage finely HTTP headers*. (see the [manpage][7])*
#### unsupported clients
* On OpenBSD, **curl** seems not supported brotli.
* Egual, on all OS, **lynx**, **w3m** does not support it.
### Firefox
Since v64, [Firefox does not support Atom or RSS feed][5].
Actually, it's more subtle than it sounds:
* if you deliver Atom or RSS with mime type **text/xml**, both are XML files,
Firefox accepts to read and native display.
* if you deliver it with their mime type, respectively **application/atom+xml**
and **application/rss+xml**, a RFC standard, then Firefox ask you what
to do with.
"A nameless aberration!!!"
### the little story
For the little story, Xavier Cartron @prx is the original author of this
genious idea to deliver compressed static content, by adding the header
`ETag`. *This is an id for the delivered ressource*.
It is in this context, that the binary **sha256** is useful.
---
My work, based on his first version, was to add several things:
About {{< anchor brotli brotli >}}, so I resumed/continued writing in order
to be able to deliver static content previously compressed with this format.
Next, I added code to manage others needed headers:
* `Content-Length`: to send weight of delivered document.
*It is in this context, that the binary **stat** is useful*.
* `Last-Modified`: to get the modification date of the delivered document;
someone considers this header is more relevant than **ETag**.
*It is in this context, that the binaries **date** and **stat** are useful*.
* `Transfer-Encoding`: to send the delivered document with the good compression
format, if necessary.
Then, I wrote the necessary code to detect if the useful dependencies were
in the web chroot, otherwise the script can't work. If it's the case, the
serveur send an error 500, with an explicit HTML message.
**ATTENTION**: the script not installs and can not install the dependancies;
because, it on the web chroot, it can not "view" the OS filesystem.
Next, after some research on the web, I understant that the format **deflate**,
that may be requested by some web clients, is managed by the format **gzip**;
then, the script supports too.
---
But I was confronted with dysfunctions that I couldn't understand, let alone solve.
*I stopped the project* :(
But, two "things" helped seriously to continue:
* Solène Rapenne, from the OpenBSD team, helped to understand I was making
the mistake of sending too many line breaks, when I need only one,
at the right time, one between the sending of the different headers
and the document itself, whether it is compressed or not.
* the idea to implement a variable `debug` and use the binary `logger` to
ensure some differents returns.
One tips that Solène gave me is the local use of this command:
`env HTTP_ACCEPT_ENCODING=br realroot=/var/www/htdocs/domaine.tld/dev/ PATH_INFO=index.html /var/www/cgi-bin/sbw.cgi | less`
explaining me that it is possible to query the CGI server locally directly,
by sending it the different possible values before the call.
Amazing! :D
About this, since we installed the CGI shell script **sbw.cgi** with the
user rights to **0550**, we had this erreur:
`env: /var/www/cgi-bin/sbw2.cgi: Permission denied`
you need to change the other rights to `+x` *(or, `0551`)*. ;-)
---
I improved the detection of the mime type by getting it from the file
called on the filesystem — *this need to use the binary **basename*** -
and the management of the mime type about feed Atom or RSS.
Finally, I wrote the necessary code to detect if the user agent was {{< anchor Firefox firefox >}}:
* If yes, to obtain his version number. A little hack to deliver feed Atom
and RSS to the "false" mime-type **text/xml**. Also, Firefox accepts
to read the feed instead asking to open with another application!
I wrote the first draft of this hack requiring the addition of the binaries
`grep` and `awk` — *but, this functional solution did not satisfy me*.
When, an user on the forum about the french community "OpenBSD pour tous",
@eol makes me [the think to use shell expansion][6].
"Et, voilà!"
---
Actualy, @prx rewrote his script in C:
* the advantage is that there is no need for any dependency; too, it's
"protected" by the system call security measures pledge(2) and unveil(2).
* however, it ONLY supports gzip compression; egual, no header **Last-Modified**,
or brotli, and deflate support, at least not directly|automatically.
## Documentation
For more documentation about [referrer policy][8].
### manpage
* {{< man install >}}
* {{< man slowcgi 8 >}}, {{< man rcctl 8 >}}
* {{< man pledge 2 >}}, {{< man unveil 2 >}}
### Wikipédia
* About the **BREACH** vulnerability: https://en.wikipedia.org/wiki/BREACH
* and the **CRIME** exploit: https://en.wikipedia.org/wiki/CRIME_(security_exploit)
---
[1]: https://framagit.org/hucste/tools/-/raw/master/OpenBSD/slowcgi/sbw.cgi
[2]: https://framagit.org/hucste/tools/-/blob/master/OpenBSD/chroot_deps
[3]: https://github.com/reyk/httpd/issues/21
[4]: https://github.com/reyk/httpd/issues/80
[5]: https://support.mozilla.org/en-US/kb/feed-reader-replacements-firefox
[6]: https://forum.openbsd.fr.eu.org/showthread.php?tid=2620&pid=20898#pid20898
[7]: https://curl.haxx.se/docs/manpage.html
[8]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy