Skip to content

Commit

Permalink
[MM-49667] Enable sandboxing (#14)
Browse files Browse the repository at this point in the history
* Enable sandboxing

* Refactor Dockerfile

* Automated pinned packages generation (#15)
  • Loading branch information
streamer45 authored Jan 18, 2023
1 parent 2e24eb0 commit e837227
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 27 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ else
endif
@$(OK) Generating github-release http://github.com/$(GITHUB_ORG)/$(GITHUB_REPO)/releases/tag/$(APP_VERSION) ...

.PHONY: go-pinned-packages
go-pinned-packages: ## to update pinned packages list for the Ubuntu based Docker image
go run ./build/generator.go

.PHONY: clean
clean: ## to clean-up
@$(INFO) cleaning /${GO_OUT_BIN_DIR} folder...
Expand Down
42 changes: 25 additions & 17 deletions build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,8 @@
# A multi stage build, with golang used as a builder
# and ubuntu:22.04 as runner
ARG GO_IMAGE=golang:1.18@sha256:fa71e1447cb0241324162a6c51297206928d755b16142eceec7b809af55061e5
# hadolint ignore=DL3006
FROM ${GO_IMAGE} as builder

#GO_BUILD_PLATFORMS holds the platforms that we will build the docker image against
ARG GO_BUILD_PLATFORMS=linux-amd64

# Setup directories structure and compile
COPY . /src
WORKDIR /src
RUN make go-build

FROM ubuntu:22.04@sha256:817cfe4672284dcbfee885b1a66094fd907630d610cab329114d036716be49ba as runner
COPY --from=builder /src/dist/calls-recorder-linux-amd64 /opt/calls-recorder/bin/calls-recorder
FROM ubuntu:22.04@sha256:817cfe4672284dcbfee885b1a66094fd907630d610cab329114d036716be49ba as base

# Setup system dependencies
WORKDIR /workdir
Expand All @@ -23,21 +12,23 @@ WORKDIR /workdir
# https://stackoverflow.com/questions/71941032/why-i-cannot-run-apt-update-inside-a-fresh-ubuntu22-04
RUN sed -i -e 's/^APT/# APT/' -e 's/^DPkg/# DPkg/' /etc/apt/apt.conf.d/docker-clean

ARG CHROMIUM_VERSION=109.0.5414.74-0
ARG DEBIAN_FRONTEND=noninteractive
COPY ./build/pkgs_list .
# hadolint ignore=DL3008,SC2046
RUN set -ex && \
apt-get update && \
apt-get install --no-install-recommends -y ffmpeg=7:4.4.2-0ubuntu0.22.04.1 pulseaudio=1:15.99.1+dfsg1-1ubuntu2 \
xvfb=2:21.1.3* wget=1.21.2-2ubuntu1 unzip=6.0-26ubuntu3.1 fonts-emojione=2.2.6+16.10.20160804-0ubuntu2 ca-certificates=20211016 && \
apt-get install --no-install-recommends -y $(cat pkgs_list) && \
apt-get update && \
wget --progress=dot:giga -N \
-O chromium-chromedriver.deb \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-chromedriver_108.0.5359.124-0ubuntu0.22.04.1sav0_amd64.deb && \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-chromedriver_${CHROMIUM_VERSION}ubuntu0.22.04.1sav1_amd64.deb && \
wget --progress=dot:giga -N \
-O chromium-codecs-ffmpeg-extra.deb \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-codecs-ffmpeg-extra_108.0.5359.124-0ubuntu0.22.04.1sav0_amd64.deb && \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-codecs-ffmpeg-extra_${CHROMIUM_VERSION}ubuntu0.22.04.1sav1_amd64.deb && \
wget --progress=dot:giga -N \
-O chromium-browser.deb \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-browser_108.0.5359.124-0ubuntu0.22.04.1sav0_amd64.deb && \
https://launchpad.net/~savoury1/+archive/ubuntu/chromium/+files/chromium-browser_${CHROMIUM_VERSION}ubuntu0.22.04.1sav1_amd64.deb && \
apt-get install --no-install-recommends -y ./chromium-chromedriver.deb \
./chromium-codecs-ffmpeg-extra.deb \
./chromium-browser.deb && \
Expand All @@ -46,6 +37,23 @@ RUN set -ex && \
adduser root pulse-access && \
mkdir -pv ~/.cache/xdgr

# Create unprivileged user to run the recorder process
RUN groupadd -r calls && useradd -mr -g calls -G audio,video,pulse-access calls

# hadolint ignore=DL3006
FROM ${GO_IMAGE} as builder

#GO_BUILD_PLATFORMS holds the platforms that we will build the docker image against
ARG GO_BUILD_PLATFORMS=linux-amd64

# Setup directories structure and compile
COPY . /src
WORKDIR /src
RUN make go-build

FROM base AS runner
COPY --from=builder /src/dist/calls-recorder-linux-amd64 /opt/calls-recorder/bin/calls-recorder

# copy binary
COPY ./build/entrypoint.sh .

Expand Down
32 changes: 24 additions & 8 deletions build/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,39 @@ pulseaudio -D --verbose --exit-idle-time=-1 --system --disallow-exit
pactl load-module module-null-sink sink_name="grab" sink_properties=device.description="monitorOUT"

# Forward signals to service
pid=0
RECORDER_PID=0
RECORDER_PID_FILE=/tmp/recorder.pid

# SIGTERM-handler
# SIGTERM handler
term_handler() {
if [ $pid -ne 0 ]; then
kill -SIGTERM "$pid"
wait "$pid"
RECORDER_PID=`cat $RECORDER_PID_FILE`
if [ $RECORDER_PID -ne 0 ]; then
kill -SIGTERM "$RECORDER_PID"
tail --pid="$RECORDER_PID" -f /dev/null
fi
exit 143; # 128 + 15 -- SIGTERM
}

# On callback, kill the last background process, which is `tail -f /dev/null` and execute the specified handler
trap 'kill ${!}; term_handler' SIGTERM

# Run service
XDG_RUNTIME_DIR=$PATH:~/.cache/xdgr /opt/calls-recorder/bin/calls-recorder &
pid="$!"
# DEV_MODE is optional so we need to check whether it's set before relaying it to the recorder app.
DEV="${DEV_MODE:-false}"

RECORDER_USER=calls

# Give permission to write recording files.
chown -R $RECORDER_USER:$RECORDER_USER /recs

# Run service as unprivileged user.
runuser -l $RECORDER_USER -c \
"SITE_URL=$SITE_URL \
AUTH_TOKEN=$AUTH_TOKEN \
CALL_ID=$CALL_ID \
THREAD_ID=$THREAD_ID \
DEV_MODE=$DEV \
XDG_RUNTIME_DIR=/home/$RECORDER_USER/.cache/xdgr \
/opt/calls-recorder/bin/calls-recorder" &

# Wait forever
wait ${!}
137 changes: 137 additions & 0 deletions build/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
)

const (
launchpadAPIBaseURL = "https://api.launchpad.net/1.0"
distroName = "ubuntu"
distroVersion = "jammy"
distroArch = "amd64"

pkgsListPath = "./build/pkgs_list"

requestTimeout = 5 * time.Second
)

type Package struct {
Name string
Version string
}

func GetPublishedPackages(c *http.Client, names []string) ([]Package, error) {
var pkgs []Package

for _, pkgName := range names {
ctx, cancelFn := context.WithTimeout(context.Background(), requestTimeout)
defer cancelFn()

url := fmt.Sprintf("%s/%s/+archive/primary", launchpadAPIBaseURL, distroName)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("request creation: %w", err)
}

q := req.URL.Query()
q.Add("ws.op", "getPublishedBinaries")
q.Add("binary_name", pkgName)
q.Add("exact_match", "true")
q.Add("distro_arch_series", fmt.Sprintf("%s/%s/%s/%s", launchpadAPIBaseURL, distroName, distroVersion, distroArch))
q.Add("status", "Published")
q.Add("order_by_date", "true")
req.URL.RawQuery = q.Encode()

resp, err := c.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}

respData := map[string]any{}
if err := json.NewDecoder(resp.Body).Decode(&respData); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}

entries, ok := respData["entries"].([]any)
if !ok {
return nil, fmt.Errorf("failed to parse response")
}

if len(entries) == 0 {
break
}

entry, ok := entries[0].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("failed to parse entry")
}

name, ok := entry["binary_package_name"].(string)
if !ok {
return nil, fmt.Errorf("failed to parse package name")
}

version, ok := entry["binary_package_version"].(string)
if !ok {
return nil, fmt.Errorf("failed to parse package version")
}

log.Printf("found %s=%s\n", name, version)

pkgs = append(pkgs, Package{Name: name, Version: version})
}

return pkgs, nil
}

func GenPinnedPackages(pkgsNames []string) error {
c := &http.Client{}

pkgs, err := GetPublishedPackages(c, pkgsNames)
if err != nil {
return fmt.Errorf("failed to get packages: %w", err)
}

outFile, err := os.OpenFile(pkgsListPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return fmt.Errorf("failed to open output file: %w", err)
}
defer outFile.Close()

for _, pkg := range pkgs {
fmt.Fprintf(outFile, "%s=%s\n", pkg.Name, pkg.Version)
}

return nil
}

func parsePkgsList(data string) []string {
var pkgs []string
list := strings.Split(data, "\n")
for _, el := range list {
name, _, _ := strings.Cut(el, "=")
pkgs = append(pkgs, name)
}
return pkgs
}

func main() {
data, err := os.ReadFile(pkgsListPath)
if err != nil {
log.Fatalf("failed to read packages file: %s", err)
}

if err := GenPinnedPackages(parsePkgsList(string(data))); err != nil {
log.Fatalf("failed to generate pinned packages: %s", err)
}

log.Printf("done")
}
7 changes: 7 additions & 0 deletions build/pkgs_list
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ca-certificates=20211016ubuntu0.22.04.1
ffmpeg=7:4.4.2-0ubuntu0.22.04.1
fonts-emojione=2.2.6+16.10.20160804-0ubuntu2
pulseaudio=1:15.99.1+dfsg1-1ubuntu2
unzip=6.0-26ubuntu3.1
wget=1.21.2-2ubuntu1
xvfb=2:21.1.3-2ubuntu2.5
6 changes: 6 additions & 0 deletions cmd/recorder/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"log"
"os"
"os/signal"
Expand All @@ -10,6 +11,11 @@ import (
func main() {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)

pid := os.Getpid()
if err := os.WriteFile("/tmp/recorder.pid", []byte(fmt.Sprintf("%d", pid)), 0666); err != nil {
log.Fatalf("failed to write pid file: %s", err)
}

cfg, err := loadConfig()
if err != nil {
log.Fatalf("failed to load config: %s", err)
Expand Down
2 changes: 0 additions & 2 deletions cmd/recorder/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ func (rec *Recorder) runBrowser(recURL string) error {
chromedp.NoFirstRun,
chromedp.NoDefaultBrowserCheck,
chromedp.DisableGPU,
chromedp.NoSandbox,

// puppeteer default behavior
chromedp.Flag("disable-infobars", true),
chromedp.Flag("enable-automation", true),
chromedp.Flag("disable-background-networking", true),
chromedp.Flag("enable-features", "NetworkService,NetworkServiceInProcess"),
chromedp.Flag("disable-background-timer-throttling", true),
Expand Down

0 comments on commit e837227

Please sign in to comment.