commit 22522a6607c60196afd03946b18fd8efaef93d25 Author: Radek Goláň jr. Date: Fri Sep 12 09:39:37 2025 +0200 Initial commit diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml new file mode 100644 index 0000000..307c4d8 --- /dev/null +++ b/.woodpecker/build.yml @@ -0,0 +1,98 @@ +variables: + - &file Containerfile + - &repo dev.shielddagger.com/opensource/talk-recording + +when: + - event: [push, pull_request, cron] + +steps: + - name: dryrun + image: woodpeckerci/plugin-docker-buildx:5 + backend_options: + kubernetes: + securityContext: + privileged: true + settings: + dockerfile: *file + platforms: linux/arm64,linux/amd64 + cache_from: type=registry,ref=dev.shielddagger.com/opensource/talk-recording + cache_to: type=inline + dry_run: true + repo: *repo + tags: latest + registry: dev.shielddagger.com + username: + from_secret: registry_username + password: + from_secret: registry_password + when: + - event: pull_request + - name: publish + image: woodpeckerci/plugin-docker-buildx:5 + backend_options: + kubernetes: + securityContext: + privileged: true + settings: + dockerfile: *file + platforms: linux/arm64,linux/amd64 + cache_from: type=registry,ref=dev.shielddagger.com/opensource/talk-recording + cache_to: type=inline + repo: *repo + auto_tag: true + tags: ${CI_COMMIT_SHA:0:8} + registry: dev.shielddagger.com + username: + from_secret: registry_username + password: + from_secret: registry_password + when: + - event: push + branch: main + - name: gather-digests + image: quay.io/skopeo/stable:latest + environment: + DOCKER_USER: + from_secret: registry_username + DOCKER_PASS: + from_secret: registry_password + DOCKER_REPO: *repo + when: + - event: [push, cron] + branch: main + cron: "security-scan" + commands: + - dnf install -y jq + - skopeo login dev.shielddagger.com --username $DOCKER_USER --password $DOCKER_PASS + - skopeo inspect --raw docker://$DOCKER_REPO:latest | jq -r .'manifests[] | select(.platform.architecture=="arm64").digest' > digest-arm64 + - skopeo inspect --raw docker://$DOCKER_REPO:latest | jq -r .'manifests[] | select(.platform.architecture=="amd64").digest' > digest-amd64 + - name: image-scan + image: aquasec/trivy + environment: + TRIVY_USER: + from_secret: registry_username + TRIVY_PASSWORD: + from_secret: registry_password + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db + TRIVY_CHECKS_BUNDLE_REPOSITORY: public.ecr.aws/aquasecurity/trivy-checks + DOCKER_REPO: *repo + commands: + - export ARM64_DIGEST=$(cat digest-arm64) + - trivy image --platform linux/arm64 --debug ${DOCKER_REPO}@$ARM64_DIGEST --exit-code 1 --username $TRIVY_USER --severity HIGH,CRITICAL + when: + - event: [push, cron] + branch: main + cron: "security-scan" + - name: notify + image: dev.shielddagger.com/opensource/discord-notifier + failure: ignore + settings: + webhook_url: + from_secret: discord_webhook + woodpecker_url: https://ci.shielddagger.com/api + woodpecker_token: + from_secret: woodpecker_token + icon_url: https://discord.com/api/webhooks/1231848304694919270/1ApQzOPMfNosxhQ62HbYScBT5s94m0bIUn1IFGQlT6d8Ru2ImcHHjjkFA_SaonBNU3yz + when: + - status: [success, failure] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bcc00fc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +# syntax=docker/dockerfile:latest +FROM python:3.13.7-alpine3.22 + +COPY --chmod=775 start.sh /start.sh +COPY --chmod=775 healthcheck.sh /healthcheck.sh + +ENV RECORDING_VERSION=v0.1 +ENV ALLOW_ALL=false +ENV HPB_PROTOCOL=https +ENV NC_PROTOCOL=https +ENV SKIP_VERIFY=false +ENV HPB_PATH=/standalone-signaling/ + +RUN set -ex; \ + apk upgrade --no-cache -a; \ + apk add --no-cache \ + ca-certificates \ + tzdata \ + bash \ + xvfb \ + ffmpeg \ + firefox \ + bind-tools \ + netcat-openbsd \ + git \ + wget \ + shadow \ + pulseaudio \ + openssl \ + build-base \ + linux-headers \ + geckodriver; \ + useradd -d /tmp --system recording -u 122; \ +# Give root a random password + echo "root:$(openssl rand -base64 12)" | chpasswd; \ + git clone --recursive https://github.com/nextcloud/nextcloud-talk-recording --depth=1 --single-branch --branch "$RECORDING_VERSION" /src; \ + python3 -m pip install --no-cache-dir /src; \ + rm -rf /src; \ + touch /etc/recording.conf; \ + chown recording:recording -R \ + /tmp /etc/recording.conf; \ + mkdir -p /conf; \ + chmod 777 /conf; \ + chmod 777 /tmp; \ + apk del --no-cache \ + git \ + wget \ + shadow \ + openssl \ + build-base \ + linux-headers; + +VOLUME /tmp +WORKDIR /tmp +USER 122 +ENTRYPOINT ["/start.sh"] +CMD ["python", "-m", "nextcloud.talk.recording", "--config", "/conf/recording.conf"] + +HEALTHCHECK CMD /healthcheck.sh +LABEL com.centurylinklabs.watchtower.enable="false" \ + org.label-schema.vendor="Nextcloud" \ No newline at end of file diff --git a/healthcheck.sh b/healthcheck.sh new file mode 100755 index 0000000..16cc119 --- /dev/null +++ b/healthcheck.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +nc -z 127.0.0.1 1234 || exit 1 \ No newline at end of file diff --git a/recording.conf b/recording.conf new file mode 100644 index 0000000..8bb7df6 --- /dev/null +++ b/recording.conf @@ -0,0 +1,123 @@ +[logs] +# Log level based on numeric values of Python logging levels: +# - Critical: 50 +# - Error: 40 +# - Warning: 30 +# - Info: 20 +# - Debug: 10 +# - Not set: 0 +#level = 20 + +[http] +# IP and port to listen on for HTTP requests. +#listen = 127.0.0.1:8000 + +[backend] +# Allow any hostname as backend endpoint. This is extremely insecure and should +# only be used during development. +#allowall = false + +# Common shared secret for requests from and to the backend servers if +# "allowall" is enabled. This must be the same value as configured in the +# Nextcloud admin ui. +#secret = the-shared-secret + +# Comma-separated list of backend ids allowed to connect. +#backends = backend-id, another-backend + +# If set to "true", certificate validation of backend endpoints will be skipped. +# This should only be enabled during development, e.g. to work with self-signed +# certificates. +# Overridable by backend. +#skipverify = false + +# Maximum allowed size in bytes for messages sent by the backend. +# Overridable by backend. +#maxmessagesize = 1024 + +# Width for recorded videos. +# Overridable by backend. +#videowidth = 1920 + +# Height for recorded videos. +# Overridable by backend. +#videoheight = 1080 + +# Temporary directory used to store recordings until uploaded. It must be +# writable by the user running the recording server. +# Overridable by backend. +#directory = /tmp + +# Backend configurations as defined in the "[backend]" section above. The +# section names must match the ids used in "backends" above. +#[backend-id] +# URL of the Nextcloud instance +#url = https://cloud.domain.invalid + +# Shared secret for requests from and to the backend servers. This must be the +# same value as configured in the Nextcloud admin ui. +#secret = the-shared-secret + +#[another-backend] +# URL of the Nextcloud instance +#url = https://cloud.otherdomain.invalid + +# Shared secret for requests from and to the backend servers. This must be the +# same value as configured in the Nextcloud admin ui. +#secret = the-shared-secret + +[signaling] +# Common shared secret for authenticating as an internal client of signaling +# servers if a specific secret is not set for a signaling server. This must be +# the same value as configured in the signaling server configuration file. +#internalsecret = the-shared-secret-for-internal-clients + +# Comma-separated list of signaling servers with specific internal secrets. +#signalings = signaling-id, another-signaling + +# Signaling server configurations as defined in the "[signaling]" section above. +# The section names must match the ids used in "signalings" above. +#[signaling-id] +# URL of the signaling server +#url = https://signaling.domain.invalid + +# Shared secret for authenticating as an internal client of signaling servers. +# This must be the same value as configured in the signaling server +# configuration file. +#internalsecret = the-shared-secret-for-internal-clients + +#[another-signaling] +# URL of the signaling server +#url = https://signaling.otherdomain.invalid + +# Shared secret for authenticating as an internal client of signaling servers. +# This must be the same value as configured in the signaling server +# configuration file. +#internalsecret = the-shared-secret-for-internal-clients + +[ffmpeg] +# The ffmpeg executable (name or full path) and the global options given to +# ffmpeg. The options given here fully override the default global options. +#common = ffmpeg -loglevel level+warning -n + +# The options given to ffmpeg to encode the audio output. The options given here +# fully override the default options for the audio output. +#outputaudio = -c:a libopus + +# The options given to ffmpeg to encode the video output. The options given here +# fully override the default options for the video output. +#outputvideo = -c:v libvpx -deadline:v realtime -crf 10 -b:v 1M + +# The extension of the file for audio only recordings. +#extensionaudio = .ogg + +# The extension of the file for audio and video recordings. +#extensionvideo = .webm + +[recording] +# Browser to use for recordings. Please note that the "chrome" value does not +# refer to the web browser, but to the Selenium WebDriver. In practice, "chrome" +# will use Google Chrome, or Chromium if Google Chrome is not installed. +# Allowed values: firefox, chrome +# Defaults to firefox +# browser = firefox \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..8afa151 --- /dev/null +++ b/start.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Variables +if [ -z "$NC_DOMAIN" ]; then + echo "You need to provide the NC_DOMAIN." + exit 1 +elif [ -z "$RECORDING_SECRET" ]; then + echo "You need to provide the RECORDING_SECRET." + exit 1 +elif [ -z "$INTERNAL_SECRET" ]; then + echo "You need to provide the INTERNAL_SECRET." + exit 1 +fi + +if [ -z "$HPB_DOMAIN" ]; then + export HPB_DOMAIN="$NC_DOMAIN" +fi + +# Delete all contents on startup to start fresh +rm -fr /tmp/{*,.*} + +cat << RECORDING_CONF > "/conf/recording.conf" +[logs] +# 30 means Warning +level = 30 + +[http] +listen = 0.0.0.0:1234 + +[backend] +allowall = ${ALLOW_ALL} +# The secret below is still needed if allowall is set to true, also it doesn't hurt to be here +secret = ${RECORDING_SECRET} +backends = backend-1 +skipverify = ${SKIP_VERIFY} +maxmessagesize = 1024 +videowidth = 1920 +videoheight = 1080 +directory = /tmp + +[backend-1] +url = ${NC_PROTOCOL}://${NC_DOMAIN} +secret = ${RECORDING_SECRET} +skipverify = ${SKIP_VERIFY} + +[signaling] +signalings = signaling-1 + +[signaling-1] +url = ${HPB_PROTOCOL}://${HPB_DOMAIN}${HPB_PATH} +internalsecret = ${INTERNAL_SECRET} + +[ffmpeg] +# common = ffmpeg -loglevel level+warning -n +# outputaudio = -c:a libopus +# outputvideo = -c:v libvpx -deadline:v realtime -crf 10 -b:v 1M +extensionaudio = .ogg +extensionvideo = .webm + +[recording] +browser = firefox +driverPath = /usr/bin/geckodriver +RECORDING_CONF + +exec "$@" \ No newline at end of file