# Binaries
Haskell
code compiled will always produce the same binary file, since the compiler is deterministic
(*).
(*)
GHC
is not fully deterministic yet, but if you keep to the following setup:stack
+LTS
-Data.Unique
, you shouldn’t have any problems. For more info, please look into the ongoing work in order to makeGHC
support fully deterministic builds.
If a secure hash
is applied to these binaries, in this case SHA-256
, we can create what is called a reproducible build hash
. For example:
28066d57da7328899c853a3f6c9ebc1bc7e0fa1a0ce0e9bf05de9c796911aa93
This hexadecimal
number, could be denominated as a warranty seal
since it certifies that a specific code
will always produce
the same binary
.
As there is a link between code
and binaries
, this will allow the relevant authorities
to testify that the application
that is currently being executed
comes
from the source code
and, in addition, to easily perform trustworthy audits
to verify that the applications, really do
what they were designed
to do.
Here is an explanatory video showing how difficult it’s to make changes to a binary
in order to try to recreate the initial 256 bit hash
:
- Safety in numbers: 3Blue1Brown - How secure is 256 bit security?
# Distribution
When using Docker
technology for the distribution
of binaries, as it is a technology that does not
give the same importance to determinism
when it comes to recreating images or containers, it has been necessary to create algorithms
that are capable of producing reproducible build hashes for both of images and containers, and therefore safeguard the guarantees offered by the use of Haskell.
- Docker Image Reproducible Build Hash:
#!/usr/bin/env bash
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
clear
img="./tmp/img.tar"
docker save uniprocess > $img
echo "Docker Image Reproducible Build Hash:"
# exclude all opaque files
pat="*/*.wh..wh..opq"
# find all the layer.tar files
for f in $(tar -tf $img --wildcards "*/layer.tar"); do
# for each of the layer files, SHA256 each the files
for cf in $(tar -xf $img --to-stdout $f | tar --exclude="$pat" -tf -); do
n=${cf##*/}
if [ "" != "$n" ]; then
tar -xf $img --to-stdout $f | tar -xf - --to-stdout $cf |
sha256sum | cut -d " " -f 1
fi
done
# Hash the content of each file, sort them and combine to a single hash
done | sort | sha256sum | cut -d " " -f 1
find ./tmp -name "img.tar" -delete
echo
- Docker Container Reproducible Build Hash:
#!/usr/bin/env bash
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
clear
uid=$1
con="./tmp/con.tar"
docker export $uid > $con
echo "Docker Container Reproducible Build Hash:"
# exclude (running) docker container mount file
pat="etc/mtab"
# find all the layer.tar files
for cf in $(tar --exclude="$pat" -tf $con); do
n=${cf##*/}
if [ "" != "$n" ]; then
tar -xf $con --to-stdout $cf |
sha256sum | cut -d " " -f 1
fi
# Hash the content of each file, sort them and combine to a single hash
done | sort | sha256sum | cut -d " " -f 1
find ./tmp -name "con.tar" -delete
echo
The reason for the use of Docker
, is that it allows to use base containers
of a much smaller size
if we compare it to a standard operating system. The base container used is fpco/haskell-scratch:integer-gmp of only 2 MB
in size, producing container images of about 7.5 - 15 MB
.
- Dockerfile.builder:
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
FROM haskell:8.2
# Install dependecies needed to compile Haskell libraries
RUN apt-get update && apt-get install --yes \
xz-utils \
make
RUN stack --resolver lts-11.11 install base \
# A time & space-efficient byte arrays (Word8)
bytestring \
# Date and time stamps
time \
# Linux's kernel random number generator
random \
# Berkeley sockets
network \
# Native implementation of TLS protocol for Server & Client
tls \
# (tls) Default values for TLS parameters
data-default-class \
# (tls) Specify hash for validation (SHA256)
x509 \
# (tls) Accessing and storing X.509 certificates
x509-store \
# (tls) Disable validation for non x509 v3 certificates
x509-validation \
# Validate input. Example: A name shouldn't be "42"
parser-combinators \
# serializer from and to JSON
array \
# serializer from and to JSON
containers \
# serializer from and to JSON
mtl \
# serializer from and to JSON
syb
## References
# - Futtetennismo:
# * Base: https://futtetennismo.me/posts/docker/
# * File: 2017-11-24-docker-haskell-executables.html
- Dockerfile:
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
FROM haskell-base-builder as builder
WORKDIR "../"
# copy the contents of the current directory in the working directory
COPY . .
RUN stack --resolver lts-11.11 install && \
strip /root/.local/bin/uniprocess
FROM fpco/haskell-scratch:integer-gmp
COPY --from=builder /root/.local/bin/uniprocess /bin/
COPY ./der/dev/console /dev/console
COPY ./der/env/.dockerenv /.dockerenv
COPY ./der/etc/hostname /etc/hostname
COPY ./der/etc/hosts /etc/hosts
COPY ./der/etc/resolv.conf /etc/resolv.conf
COPY ./tls/cacert.pem /tls/cacert.pem
COPY ./tls/uniprocess.public.crt /tls/uniprocess.public.crt
COPY ./tls/uniprocess.secret.key /tls/uniprocess.secret.key
# We provide the possibility to specify the port and an argument
# Examle: docker run -d -p 8443:8443 uniprocess 8443
# https://stackoverflow.com/a/40312311
# Cos we need root in order to server 80 and 443 in Linux, we use Tomcats 8080
# (HTTP) and 8443 (HTTPS) approach
#
# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
ENTRYPOINT [ "/bin/uniprocess" ]
CMD [ "8443" ] # Default value but can overriden at runtime
## References
# - Futtetennismo:
# * Base: https://futtetennismo.me/posts/docker/
# * File: 2017-11-24-docker-haskell-executables.html
And since the base container
only includes Linux
components to run Haskell
applications, this will minimize
the attack surface for hackers
.