Zum Inhalt

Newsboat und Wallabag - Mehrere Smart Cards und unterschiedliche Betriebssystem verwenden

Papierschiff mit roter Fahne fährt auf dem Wasser

Newsboat kann über die API Artikel direkt an Wallabag senden. Dort können die Beiträge bequem und übersichtlich verwaltet, mit Tags versehen und im Webbrowser oder der Wallabag-Apps auf allen Endgeräten gelesen werden.

In diesem Beitrag werden mehrere Clients und Smartcards (YubiKey und Nitrokey (Affiliate Link 🛒)) verwendet, um interessante Artikel an die zentrale Wallabag-Instanz zum späteren lesen zu senden.

Dieser Beitrag baut auf Read it Later - Sende Artikel aus Newsboat direkt an Wallabag auf. Ein paar kleinere Anpassungen im Skript sind notwendig.


Ausgangssituation

Es werden mehrere Clients mit unterschiedlichen Betriebssystemen verwendet, u.a. macOS , Kubuntu und Qubes OS .

An diesen Clients werden mehrere Yubikeys und Nitrokey (Affiliate Link 🛒) angeschlossen. Diese Smartcards haben jeweils unterschiedliche Seriennummern und werden mit verschiedenen GPG-Schlüssen verwendet.

Wichtige Konfigurationsdateien werden zwischen den einzelnen Clients über ein Git-Repository ausgetauscht und aktuell gehalten. Eine genaue Beschreibung des Setups ist in Newsboat Konfigurationsdateien in macOS und Linux verwenden zu finden. Dateien mit sensiblen Informationen, wie Zugangsdaten, werden mit GPG verschlüsselt.

Auf allen Clients wird der RSS Reader Newsboat verwendet. Die abonnierten Feeds werden über das Git-Repository verwaltet. Von jedem Client, aus jeder Newsboat-Instanz sollen interessante Artikel für das spätere Lesen an das zentrale Wallabag gesendet werden.

Syncthing - Alternative zu Git

Eine Alternative zu Git stellt Syncthing dar. Damit lassen sich Ordner und Dateien zwischen allen möglichen Geräten teilen. Für dieses Tool wird kein eigener Server benötigt.

Ich selbst verwende eine Hybrid-Lösung aus Git und Syncthing . Die Konfigurationsdateien werden über einen Git-Server verwaltet, da diese sich nicht so häufig ändern.

Die Token-Dateien werden über Syncthing übertragen, da der refresh_token sehr häufig gewechselt wird und dadurch sehr viele "unnötige" Änderungen in Git zu verwalten wären.


Das Problem

In meinem bisherigen Setup werden die Zugangsdaten zu Wallabag mit einem öffentlichen GPG-Schlüssel auf dem Client abgespeichert. Den Zugriff erhält Newsboat auf die Anmeldedaten nur, wenn die korrekte Smartcard angeschlossen ist. Das funktioniert wunderbar zuverlässig.

1. Problem: Smartcard ist nicht angeschlossen

Bei der Verwendung von mehreren Clients muss der richtige Yubikey / Nitrokey (Affiliate Link 🛒) am Gerät angeschlossen sein. Wenn die Smartcard nun an einem anderen Client steckt, muss man umstecken. Das ist umständlich.

2. Wallabag verwendet OAuth

Wallabag verwendet OAuth und erstellt nach jeder erfolgreichen Anmeldung über die API einen sogenannten referesh_token, der im Skript in der automatisch in der verschlüsselten Passwortdatei hinterlegt wird.

Verwendet man nun die gleiche Passwortdatei auf unterschiedlichen Clients, stellt sich irgendwann die Frage: "Wo ist der richtige referesh_token abgelegt?

Ohne referesh_token keinen Zugriff auf Wallabag und kein Speichern des interessanten Artikels aus Newsboat.


Lösung

1. Lösung

Es werden einfach mehrere Yubikeys und Nitrokey (Affiliate Link 🛒) verwendet.

  • Yubikey ist an Client 1 angeschlossen.

  • Nitrokey verbleibt an Client 2.

Die Smart Cards können aber auch gewechselt werden. Dann ist es jedoch erforderlich, dass man die Wallabag-Passwort-Datei per Git an alle Clients verteilt, sonst gibt es ein Problem mit dem referesh_token.

2. Lösung

Es werden zwei Passwortdateien mit unterschiedlichen Tokens erstellt. (Wallabag Token erstellen)

Jeder Smartcard wird eine Datei zugeordnet.

  • Yubikeywallabag_token1.json.gpg
  • Nitrokeywallabag_token2.json.gpg

Das Skript erkennt auf Grundlage der Seriennummer der Smartcard, ob ein YubiKey oder ein Nitrokey angeschlossen ist. Dann wird die richtige Passwortdatei entschlüsselt und Newsboat kann den Artikel an Wallabag senden.

Installiere deinen eigenen Git-Server

Du kannst sehr einfach deinen eigenen Git-Server in einer Docker-Umgebung installieren.

In Gitea Installation via Docker auf einer Synology Disk Station wird das am Beispiel von Gitea beschrieben.

Du kannst aber auch Forgejo verwenden. (Empfehlung)


Gib mir gerne einen Kaffee ☕ aus 😀

Gib mir gerne einen Kaffee ☕ aus !

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address:

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Weitere Möglichkeiten mich zu unterstützen findest du 👉 hier

Die Skripte

  • Git - Newsboat-Konfigurations- und URL-Dateien
  • Syncthing - wallabag_token1.json.gpg & wallabag_token1.json.gpg

Der wichtige Abschnitt ist ab Zeile 33 anzupassen.

Die Seriennummer der Smartcard wird mit dem Befehl gpg --card-status abgefragt.

Die gpg_key_id_1 ist die Schlüssel-ID des eigenen GPG-Schlüssels.

Die restliche Einrichtung des Skripts bleibt gleich wie in Newsboat Konfigurationsdateien in macOS und Linux verwenden beschrieben.

bookmark.sh
#!/usr/bin/env bash
set -euo pipefail

export GPG_TTY=$(tty)

###################################
# ASYNCHRONER START (robust)
###################################
# Absoluten Pfad des Skripts ermitteln
SCRIPT_PATH="$(readlink -f "$0")"

if [[ "${ASYNC:-0}" -eq 0 ]]; then
    ASYNC=1 bash "$SCRIPT_PATH" "$@" &
    exit 0
fi

###################################
# Arguments
###################################
url="$1"
title="$2"
description="$3"
feed_title="$4"
date_time=$(date "+%Y-%m-%d")

###################################
# Bookmark files
###################################
bookmarks_folder="$HOME/Arbeitsordner/newsboat-bookmarks"
bookmarks_file_md="$HOME/Arbeitsordner/newsboat-bookmarks/bookmarks.md"
bookmarks_file_txt="$HOME/Arbeitsordner/newsboat-bookmarks/bookmarks.txt"

###################################
# Wallabag
###################################
wallabag_url="https://wallabag.exampel.com"
tags="newsboat,it"

###################################
# YubiKey / Nitrokey and GPG Configuration
###################################
# Command to get serial number of smart card
# gpg --card-status

# YubiKey 1
# Make sure serial key and gpg key match
serial_1="11111111"
gpg_key_id_1="GPG11111111"

# Nitrokey 1
# Make sure serial key and gpg key match
serial_2="222222222"
gpg_key_id_2="GPG222222222"

# Local Wallabag Token Files
#encrypted_file_1="$HOME/.newsboat/wallabag_token1.json.gpg"
#encrypted_file_2="$HOME/.newsboat/wallabag_token2.json.gpg"

# Syncthing - Wallabag Token Files via Syncthing
encrypted_file_1="$HOME/Sync/wallabag-via-synology/wallabag_token1.json.gpg"
encrypted_file_2="$HOME/Sync/wallabag-via-synology/wallabag_token2.json.gpg"


###################################
# Create folder and files if not present
###################################

# Sicherstellen, dass das Verzeichnis existiert
mkdir -p "$bookmarks_folder"

# Sicherstellen, dass die Dateien existieren
touch "$bookmarks_file_md"
touch "$bookmarks_file_txt"

###################################
# Newsboat Bookmarking
###################################
last_date=$(tail -n 1 "$bookmarks_file_md" 2>/dev/null | cut -d' ' -f2)

if [[ "$last_date" != "$date_time" ]]; then
  echo "## Date: ${date_time}" >> "$bookmarks_file_md"
fi

echo "- ***${feed_title}***; [${title}](${url}); ${date_time}; 📝 ${description}" >> "$bookmarks_file_md"

if [[ "$last_date" != "$date_time" ]]; then
  echo "Date: ${date_time}" >> "$bookmarks_file_txt"
fi

echo "- ${feed_title}; ${title}; ${url}; 📝 ${description}; ${date_time}" >> "$bookmarks_file_txt"

###################################
# Detect smartcard
###################################
card_serial=$(gpg --card-status 2>/dev/null | awk -F': ' '/Serial number/ {gsub(/ /,"",$2); print $2}')

if [[ -z "$card_serial" ]]; then
    echo "Error: No smartcard detected."
    exit 1
fi

echo "Detected card serial: $card_serial"

if [[ "$card_serial" == "$serial_1" ]]; then
    gpg_key_id="$gpg_key_id_1"
    encrypted_file="$encrypted_file_1"
elif [[ "$card_serial" == "$serial_2" ]]; then
    gpg_key_id="$gpg_key_id_2"
    encrypted_file="$encrypted_file_2"
else
    echo "Error: Unknown YubiKey inserted."
    exit 1
fi

###################################
# Wallabag Token Handling
###################################
backup_file="${encrypted_file}.bak"
cp "$encrypted_file" "$backup_file"

# Decrypt token file (loopback mode avoids GUI pinentry)
decrypted_data=$(gpg --decrypt "$encrypted_file" 2>/dev/null)

if [[ -z "$decrypted_data" ]]; then
  echo "Error: Failed to decrypt token file."
  exit 1
fi

# Extract values
access_token=$(echo "$decrypted_data" | jq -r .access_token)
refresh_token=$(echo "$decrypted_data" | jq -r .refresh_token)
client_id=$(echo "$decrypted_data" | jq -r .client_id)
client_secret=$(echo "$decrypted_data" | jq -r .client_secret)
wallabag_username=$(echo "$decrypted_data" | jq -r .username)
wallabag_password=$(echo "$decrypted_data" | jq -r .password)

###################################
# Check access token
###################################
response_body=$(mktemp)

response=$(curl -s -w "%{http_code}" -o "$response_body" \
  -H "Authorization: Bearer $access_token" \
  "$wallabag_url/api/user")

http_status="${response: -3}"

if [[ "$http_status" == "401" ]]; then

  response=$(curl -s -X POST "$wallabag_url/oauth/v2/token" \
    -d "grant_type=refresh_token" \
    -d "client_id=$client_id" \
    -d "client_secret=$client_secret" \
    -d "refresh_token=$refresh_token")

  if echo "$response" | jq -e '.access_token' >/dev/null 2>&1; then
    access_token=$(echo "$response" | jq -r .access_token)
    refresh_token=$(echo "$response" | jq -r .refresh_token)
  else
    response=$(curl -s -X POST "$wallabag_url/oauth/v2/token" \
      -d "grant_type=password" \
      -d "client_id=$client_id" \
      -d "client_secret=$client_secret" \
      -d "username=$wallabag_username" \
      -d "password=$wallabag_password")

    if echo "$response" | jq -e '.access_token' >/dev/null 2>&1; then
      access_token=$(echo "$response" | jq -r .access_token)
      refresh_token=$(echo "$response" | jq -r .refresh_token)
    else
      exit 1
    fi
  fi

  updated_data=$(echo "$decrypted_data" | jq \
    --arg access_token "$access_token" \
    --arg refresh_token "$refresh_token" \
    '.access_token = $access_token | .refresh_token = $refresh_token')

  echo "$updated_data" | gpg --batch --yes --encrypt --recipient "$gpg_key_id" > "$encrypted_file"
fi

###################################
# Send article to Wallabag
###################################
curl -s -X POST "$wallabag_url/api/entries.json" \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "'"${url}"'",
        "tags": "'"${tags}"'",
        "starred": 1,
        "archive": 0
      }' >/dev/null

rm -f "$response_body"
exit 0

Das Skript ist noch ausführbar zu machen.

chmod +x bookmark.sh

Troubleshooting

Bricht das Skript gleich zu Beginn ab, dann sind die folgenden Punkte zu prüfen. Damit wird vielleicht eine lange Fehlersuche und ein Debuggin vermieden.

  1. Ist das Skript ausführbar?
  2. Ist der Ordner und die Dateien vorhanden? bookmarks_folder, bookmarks_file_md, bookmarks_file_txt
  3. Ist der korrekte pinentry-Eintrag in der ~/.gnupg/gpg-agent.conf gesetzt.

Newsboat config

Die Newsboat-Konfigurationsdatei ist im Verzeichnis ~/.newsboat oder einer anderen Instanz ~/.newsboat-<instanz>/newsboat abzulegen.

Makros ausführen

Makros in Newsboat lassen sich mit der Tastenkombination ,+macro-kürzel.

Beispiele:
macro o open-in-browser; toggle-article-read "read",o
macro c set browser "mpv --player-operation-mode=pseudo-gui -- %u &"; one; set browser firefox,c

config
auto-reload yes
reload-time 60
reload-threads 0
history-limit 60
cleanup-on-quit yes

prepopulate-query-feeds yes
show-read-feeds yes
text-width 80
articlelist-format "%4i %f %D  %?T?|%-17T| ?%t"

always-display-description true
notify-screen yes
save-path "$HOME/arbeitsordner/"

### MACROS ###
macro k open; reload; quit  -- "enter feed to reload it"
macro o open-in-browser; toggle-article-read "read"

# DOWNLOAD #
macro x set browser "~/.newsboat/yt-dlp-audio.sh %u &"; open-in-browser-noninteractively
macro y set browser "~/.newsboat/yt-dlp-video.sh %u &"; open-in-browser-noninteractively

# MACOS STREAM #
macro v set browser "~/.newsboat/yt-dlp-stream.sh %u &"; open-in-browser-noninteractively

# MACOS  AND LINUX STREAM #
macro c set browser "mpv --player-operation-mode=pseudo-gui -- %u &"; one; set browser firefox

###BROWSER ###
#browser "open -g -a 'Firefox' %u"
#browser "open -g -a 'Mullvad\ Browser' %u"

# Use command on macOS and Linux
# Checks for operation system with "uname"
# depending on result
# Darwin = macOS --> open -g -a \"Mullvad\ Browser\" %u
# Linux = mullvad-browser %u
#browser "sh -c 'if [ \"$(uname)\" = \"Darwin\" ]; then open -g -a \"Mullvad\ Browser\" %u; else ~/.local/share/mullvad-browser/start-mullvad-browser.desktop %u; fi'"
browser "sh -c 'if [ \"$(uname)\" = \"Darwin\" ]; then open -g -a \"Mullvad\ Browser\" %u; else xdg-open %u; fi'"

### NEXTCLOUD ###
#urls-source "ocnews"
#ocnews-url "https://next.example.com/"
#ocnews-login "<benutzername>
#ocnews-passwordeval "gpg --decrypt ~/.newsboat/nextcloud-password.gpg"

### BOOKMARK SEND TO WALLABAG ###
bookmark-cmd "$HOME/.newsboat/bookmark.sh"

### COLORS ###
# Works on Linux
#include /usr/share/doc/newsboat/contrib/colorschemes/cyanism

color background         white black
color listnormal         cyan black
color listnormal_unread  cyan black bold
color listfocus          cyan black reverse
color listfocus_unread   cyan black reverse bold
#color title             color46    default reverse bold
color info               white   blue
#color hint-description  magenta default
color article            color229 default
#color end-of-text-marker color8  default

### Highlight URLs with regex ###
highlight article "^(Feed|Link):.*$" color46 default
highlight article "^(Title|Date|Author):.*$" color39 default bold
highlight article "https?://[^ ]+" color46 default underline
highlight article "\\[[0-9]+\\]" color63 default bold
highlight article "\\[image\\ [0-9]+\\]" color63 default bold
highlight feedlist "^─.*$" color61 color235 bold

### HEADLINES ###
highlight feedlist "---.*---" green default bold
highlight feedlist ".*0/0.." default default invis

Mit diesem Skript verwendet jeder Client einen eigenen Cache, die cache.db. Möchte man für alle Clients den gleichen Cache verwenden, kann diese ausgelagert werden.

Newsboat bietet die Option cache-file an. Es besteht damit die Mögklichkeit die cache.db über Syncthing zu verteilen.

cache.db <pfad-zu-cache.db-im-syncthing-ordner>

yt-dlp-audio.sh

Die Datei ist im Verzeichnis ~/.newsboat abzulegen. Das Skript ist anschließend ausführbar zu machen chmod +x.

yt-dlp-audio.sh
#!/usr/bin/env bash

# Script to download audio via yt-dlp
# Triggered by a macro in Newsboat

###################################
# Wallabag Arguments (passed by Newsboat)
###################################
url="$1"  # The URL of the video
title="$2"  # The title of the article (optional, from Newsboat)
description="$3"  # The description (optional, from Newsboat)
feed_title="$4"  # Feed title (optional, from Newsboat)
date_time=$(date "+%Y-%m-%d")  # Date and time of download


# Ensure cookies file exists
COOKIES_FILE="$HOME/.newsboat/www.youtube.com_cookies.txt"
if [ ! -f "$COOKIES_FILE" ]; then
    echo "Cookies file $COOKIES_FILE not found. Please provide a valid cookies file."
    exit 1
fi

# Output directory for audio
OUTPUT_DIR="$HOME/Downloads/audio"

mkdir -p "$OUTPUT_DIR"
cd "$OUTPUT_DIR"

# Run yt-dlp with the passed URL and options
yt-dlp --cookies "$COOKIES_FILE" -x --embed-thumbnail mp3 --audio-quality 0 "$url"

yt-dlp-video.sh

Die Datei ist im Verzeichnis ~/.newsboat abzulegen. Das Skript ist anschließend ausführbar zu machen chmod +x.

yt-dlp-video.sh
#!/usr/bin/env bash

# Script to download videos via yt-dlp
# Triggered by a macro in Newsboat

###################################
# Wallabag Arguments (passed by Newsboat)
###################################
url="$1"  # The URL of the video
title="$2"  # The title of the article (optional, from Newsboat)
description="$3"  # The description (optional, from Newsboat)
feed_title="$4"  # Feed title (optional, from Newsboat)
date_time=$(date "+%Y-%m-%d")  # Date and time of download

# Output directory for audio
OUTPUT_DIR="$HOME/Downloads/video"

mkdir -p "$OUTPUT_DIR"
cd "$OUTPUT_DIR"

# Run yt-dlp with the passed URL and options
yt-dlp 'bestvieo[height<=480]+bestvideo/best[heigt<=480]' "$url"

yt-dlp-stream.sh

Die Datei ist im Verzeichnis ~/.newsboat abzulegen. Das Skript ist anschließend ausführbar zu machen chmod +x.

yt-dlp-stream.sh
#!/usr/bin/env bash

# Script to stream yt-dlp

###################################
# Newsboat)
###################################
url="$1"  # The URL of the video
title="$2"  # The title of the article (optional, from Newsboat)
description="$3"  # The description (optional, from Newsboat)
feed_title="$4"  # Feed title (optional, from Newsboat)
date_time=$(date "+%Y-%m-%d")  # Date and time of download

# Run yt-dlp with the passed URL and options
yt-dlp -o - "$url"  | vlc -

Gib mir gerne einen Kaffee ☕ aus 😀

Gib mir gerne einen Kaffee ☕ aus !

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address:

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Weitere Möglichkeiten mich zu unterstützen findest du 👉 hier

Source

Photo by Artak Petrosyan on Unsplash