Alkemist by RunSafe
SELF-SERVICE PORTAL

Summary

The majority of this document describes how to apply LFR to your own software. If you are interested in using software that has already been immunized by RunSafe, please refer to the ESP section.
If you are logged in, the examples on this page will automatically update to use your ALKEMIST_LICENSE_KEY.

General Steps

For most Docker build processes there are five steps necessary to integrate Alkemist®:
  1. Pull Alkemist LFR image from Docker Hub
  2. Include Alkemist LFR image as stage in existing build image
  3. Copy Alkemist LFR files out of LFR stage
  4. Build with Alkemist LFR
  5. Remove Alkemist LFR helper scripts from final image
Docker Integration Process

Hello World Example

The clearest way to show how to integrate Alkemist LFR with code is to show an example. We will use a simple "Hello World" program for this example. Adding LFR protection to the binary compiled in this Dockerfile is as simple as making five short changes, all detailed below. This same process is also available as a live demo which builds a common open-source program (redis) with LFR.
Below is a simple hello_world.c program and a Dockerfile which builds it into a binary.
hello_world.c
#include <stdio.h>

int main() {
    printf("Hello, World!");
    return 0;
}
Dockerfile
FROM alpine:3.11

RUN apk update && apk add --virtual .build-deps \
  build-base \
  gcc

COPY hello_world.c .

RUN gcc -o hello_world hello_world.c

CMD ["./hello_world"]
Below is a diff of the same Dockerfile after applying LFR. The only changes needed were:
  1. Import Alkemist LFR as a Docker build stage, using the tag which corresponds with your target environment
    • The image contains two docker ONBUILD commands which handle license verification and other pre-build steps
    • When building your image be sure to include a license key as detailed in the Specify License section below
  2. Decide where to put the LFR files by specifying the LFR_ROOT_PATH environment variable. This directory will be deleted at the end, so make sure it is not used by anything else
  3. Copy the LFR files from the LFR image into that directory
  4. Utilize the LFR helper script (lfr-helper.sh) to detect the compiler. It will:
    • add arguments as necessary for LFR to function
    • ensure that the compiler will call LFR's linker wrapper
    • otherwise maintain the original linker
    • Note: gcc is used in this example, but most compilers and linkers are supported and no proprietary compilers or linkers - or specific versions of either - are needed to make LFR function
  5. Utilize the LFR cleanup script (lfr-cleanup.sh) to remove files that were needed by LFR during the build process, but will not be needed at run-time
+FROM runsafesecurity/alkemist-lfr:alpine-3.11 AS lfr-files

FROM alpine:3.11

RUN apk update && apk add --virtual .build-deps \
  build-base \
  gcc

COPY hello_world.c .

+ENV LFR_ROOT_PATH=/lfr
+COPY --from=lfr-files /usr/src/lfr ${LFR_ROOT_PATH}

-RUN gcc -o hello_world hello_world.c
+RUN ${LFR_ROOT_PATH}/scripts/lfr-helper.sh gcc -o hello_world hello_world.c \
+ && ${LFR_ROOT_PATH}/scripts/lfr-cleanup.sh 

CMD ["./hello_world"]

Specifying License Key

Alkemist LFR expects your license key to be provided as an ALKEMIST_LICENSE_KEY build argument. The preferred way to do this is to specify your key as a build argument from the command line along with the current date. Dockerfile in the command below is assumed to point to a Dockerfile infused with the differences listed above.

$ docker build -t hello-world-lfr -f Dockerfile-lfr --build-arg ALKEMIST_LICENSE_KEY="<insert Alkemist license here> $(date)" .

Verify LFR Functionality

Verification that Alkemist LFR has been added to the finished binary can be done in a variety of ways. Here are two common approaches:
  1. Check the .txtrp section of the binary with the readelf utility
    • If LFR is enabled on the binary the readelf command will return 5 lines of hex data, which will not match the ones you see below
    • If it is not enabled the command would return readelf: Warning: Section '.txtrp' was not dumped because it does not exist!
    $ docker run --rm hello-world-lfr /bin/sh -c "apk add --update binutils &> /dev/null && readelf -x .txtrp ./hello_world | grep 0x -m5"
      0x0000200e 01d10b00 4065f0ff ffffffff ff020000 [email protected]
      0x0000201e 0609027c 09047c16 046bf0ff ffffffff ...|..|..k......
      0x0000202e ff020000 0c0d2a7c 072a7c07 2a7c0504 ......*|.*|.*|..
      0x0000203e 7c240479 f0ffffff ffffff02 00004203 |$.y..........B.
      0x0000204e 027c0702 7c0c2a7c 0f027c07 027c232a .|..|.*|..|..|#*
    
  2. Check for liblfr.so with ldd
    • If LFR is linked into the compiled binary, ldd will show liblfr.so in its list of libraries
    • The output should look similar to the one below, but the address shown will change as a result of ASLR being enabled on the system
    $ docker run --rm hello-world-lfr /bin/sh -c 'ldd ./hello_world' | grep lfr
            liblfr.so => /usr/local/lib/liblfr.so (0x7f659d514000)
    

Enterprise Software Protection

RunSafe Security follows the process above to build a collection of immunized software that can be easily deployed into production environments. The complete collection can be found at our alkemist-esp repository on Docker Hub.
Using the ESP images is as simple as substituting the FROM line in your Dockerfiles to use the runsafesecurity/alkemist-esp images that correspond with your current base images and providing your ALKEMIST_LICENSE_KEY as an environment variable to your containers based on the modified image.
  • Modify Dockerfile
    -FROM httpd:2.4.41-alpine
    +FROM runsafesecurity/alkemist-esp:httpd-2.4.41-alpine-3.11-lfr
  • Provide ALKEMIST_LICENSE_KEY as environment variable
    $ docker run -e ALKEMIST_LICENSE_KEY=<insert Alkemist license here> <your image>