Prolog
Ein Nextcloud Update Check fürs Monitoring welcher die gleiche Update-API verwendet wie das Webfrontend von Nextcloud selbst.
Nextcloud auf verfügbare Updates prüfen
Ich würde jetzt einfach mal vermuten, dass jeder der diesen Artikel tatsächlich liest Nextcloud kennt. Oder zumindest ownCloud. Sicherheitshalber nochmal nachschauen? Bitte, das geht hier https://nextcloud.com/ bzw. hier https://owncloud.org/. Da ich selbst inzwischen nur noch wenig mit ownCloud zu tun habe und fast alle Installation mit Nextcloud betreibe beziehe ich mich in diesem Blogbeitrag ausschließlich auf Nextcloud.
Wie in einem anderen Blogbeitrag beschrieben mag ich keine Mails welche mich auf verfügbare Updates hinweisen (Statusmails? Nein Danke!). Das wären schlichtweg zu viele Systeme die sich da innerhalb kürzester Zeit melden würden. Ebenso, auch wenn das für eine Plattform reichen täte, bin ich kein Fan davon in Nextcloud nachzusehen. Wenn es für die erste installierte Plattform Updates gibt, dann auch für alle anderen. Unterm Strich viel zu mühsam. Zumal wir auch Kunden haben die Ihre Cloud-Installation selbst betreuen. Hier könnte der Check als Hinweis für den Kunden dienen. Oder für mich als Serverbetreiber Grund genug sein mal wieder den Telefonhörer in die Hand zu nehmen und den Kunden gefühlvoll mit dem notwendigen Nachdruck davon zu überzeugen doch bitte Updates zu installieren. Sicherheit und so. Klappt richtig gut 😉
Bleibt die Frage wie prüfen. Ein paar Checks habe ich gefunden welche die lokale Version ermitteln in dem Sie die version.php-Datei im Dateisystem direkt auslesen oder einfach https://<deine.nextcloud>/status.php aufrufen. In diesem JSON-String findet sich die Versionsnummer. Eine aufbereitete Fassung der JSON-Daten siehst Du links im Screenshot. So oder so, der Teil mit der lokalen Versionsnummer ist leicht. Bleibt noch die Frage wie man heraus findet ob es überhaupt Updates gibt. Hierfür habe ich einige verschiedene Ansätze gefunden:
- 1Einer zum Beispiel war die Releases auf dem Nextcloud-Server auszuwerten https://download.nextcloud.com/server/releases/. Klar, das mag funktionieren. Aber richtig warm werde ich mit dieser Lösung nicht. Also weiter suchen.
- 2Sehr interessant fand ich die Variante wie unter https://github.com/janvonde/check_nextcloud beschrieben. Hierfür wird der Nextcloud Security Scan verwendet: https://scan.nextcloud.com/. Neben der Update-Prüfung erhält man hier gleich noch ein Sicherheitsrating oben drauf mit dazu. Super Grundgedanke. Jetzt das aber dabei, dass sich wie folgt auf der zugehörigen GitHub Seite liest: „Please don’t run this check too often. There is an API limit at the scan.nextcloud.com server at the /api/queue endpoint with arround 250 POST requests a day. I personally run it every 24h“. Eine Update-Prüfung einmal am Tag reicht mir absolut aus, jedoch habe ich wohl zu viele verschiedene Instanzen hier am laufen – die Meldung welche auf die Limitüberschreitung hinwies hat nur wenige Tage auf sich warten lassen. Das ist schade. Weil vom Ansatz her war der Check richtig super.
- 3Also musste eine andere Lösung her. Nextcloud selbst weiß doch auch ob es gerade ein Update gibt. Also habe ich mich hier auf die Suche begeben. Unter updater/index.php bin ich fündig geworden.
private function getUpdateServerResponse() { $this->silentLog('[info] getUpdateServerResponse()'); $updaterServer = $this->getConfigOption('updater.server.url'); if($updaterServer === null) { // FIXME: used deployed URL $updaterServer = 'https://updates.nextcloud.org/updater_server/'; } $this->silentLog('[info] updaterServer: ' . $updaterServer); $releaseChannel = $this->getCurrentReleaseChannel(); $this->silentLog('[info] releaseChannel: ' . $releaseChannel); $this->silentLog('[info] internal version: ' . $this->getConfigOption('version')); $updateURL = $updaterServer . '?version='. str_replace('.', 'x', $this->getConfigOption('version')) .'xxx'.$releaseChannel.'xx'.urlencode($this->buildTime).'x'.PHP_MAJOR_VERSION.'x'.PHP_MINOR_VERSION.'x'.PHP_RELEASE_VERSION; $this->silentLog('[info] updateURL: ' . $updateURL); // Download update response $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $updateURL, CURLOPT_USERAGENT => 'Nextcloud Updater', ]);
In Zeile 7 ist der Update-Server angegeben und in Zeile 15 findet sich der Aufbau der URL. Einige dieser Bestandteile finden sich in der bereits angesprochenen version.php, der Rest sind Versionsnummern der lokal verwendeten PHP-Installation. Dies wird mit reichlich „x“ getrennt zu einer langen URL zusammen gesetzt welche z.B. so aussieht:
Diese Adresse geöffnet ergibt folgende Antwort:
<?xml version="1.0" encoding="UTF-8"?> <nextcloud> <version>15.0.0.10</version> <versionstring>Nextcloud 15.0.0</versionstring> <url>https://download.nextcloud.com/server/releases/nextcloud-15.0.0.zip</url> <web>https://docs.nextcloud.com/server/15/admin_manual/maintenance/upgrade.html</web> <changes>https://updates.nextcloud.com/changelog_server/?version=15.0.0</changes> <autoupdater>1</autoupdater> <eol>0</eol> <signature>c4llfwhYTKaEiTWVivJ1NgTIS5q2mxJVJkyew0nd/setTzpXt2H9zXKGQLmjUcy7 fSeW5+wkUfGD2J3XrcLKifGZMjxrhtAW97L1g/o8gp84ZO1WbrOKfneTLDXWkmwg nztf/5z0F0nppOyUX6HR84UhwDbhET7U8JV/1Ik7OO6D361U4sxELUhvg6GyQbdS oJ/t4MvMe1Fs+F5Q7dZzczivxu5oB0n2cVu9WMh8VnV6MiYcKV4/w7poibHMO16k 4IHl6E2hTF3un0obMKy7SRY4xXJ0Ohmj5Ne/8iDRjM7oop5Tzpnf1nPLEJJOZPSN fq4UospVzAmRe4UGPTUiqQ==</signature> </nextcloud>
In diesem Beispiel ist die Version 15.0.0.10 als aktuellere Version zur angefragten 14.0.4.2 verfügbar. Falls keine xml-Datei ausgeliefert wird ist die Nextcloud Installation auf aktuellem Stand.
Das war es mit dem Verfahren zur Prüfung ob es Updates gibt. Bleibt noch die Umsetzung über. Obwohl dies sehr einfach in PHP selbst zu realisieren wäre ist bei mir die Wahl auf Python3 gefallen. Ich wollte schon längst mal wieder was in Python machen um meine angestaubten Python-Kenntnisse aufzufrischen und vor allem zu vertiefen. Also wurde die Sprache der Wahl die aktuelle Version von Python3. Die versions.php ist eine relativ einfach PHP-Datei welche sich gut auslesen lassen sollte. Fehlen noch die benötigten PHP-Versionsnummern. Ein an dieser Stelle gern genommener Trick ist in der vermeintlichen anderen Sprache die benötigten Daten in ein Array zu packen und dieses per JSON auszugeben. Die so umgewandelten Daten lassen sich dann super weiterverarbeiten:
ncVersion = json.loads(subprocess.check_output([ args.php, '-r', 'include \'' + args.file + '\';' + ''' $g = array(); foreach($GLOBALS as $key => $val) { if(preg_match('/^OC_/', $key)) { $g[$key] = $val; } } echo json_encode( array( 'pv' => PHP_VERSION, 'pmav' => PHP_MAJOR_VERSION, 'pmiv' => PHP_MINOR_VERSION, 'prv' => PHP_RELEASE_VERSION, 'g' => $g ) ); ''' ]))
Noch ein kleiner Tipp für alle die PHP an der Stelle nicht so gut kennen: Man könnte einfach das Array $GLOBALS ausgeben, da wären alle Werte drin. Nur gibt es in $GLOABLS eine Referenz auf sich selbst da ja auch $GLOBALS eine globale Variable ist. Und an genau der Stelle steigt json_encode() aus da diese Funktion mit Referenzen nicht umgehen kann. In der versions.php fangen aber alle interessanten Variablen mit „OC_“ an und lassen sich somit super filtern ;-).
Den kompletten Check, eine Makro gestützte Beispiel-Konfig für Icinga2 sowie eine kurze Anleitung findet Ihr in meinem GitLab-Account: https://gitlab.com/Gehirn-Mag.net/icinga-and-nagios-plugins/. Bis jetzt liegt da nur der Nextcloud-Update-Check drin, weitere werden aber folgen.
Und so sieht das dann im icingaweb2 aus:
Was jetzt noch fehlt ist die Prüfung der Addons auf Aktualität. Auch hierfür sollte sich der passende Anfragestring irgendwo im Quellcode von Nextcloud finden lassen. Mir reicht jedoch vorerst die Prüfung von Nextcloud selbst. Ich bin also vorerst zufrieden. Wer weiß, ggf. reiche ich in ein paar Wochen die Erweiterung für die Addons nach.
Viel Spaß beim benutzen. Feedback und Anregungen gerne via Mail oder hier in den Kommentaren hinterlassen.
Achtung:
Nachtrag
An dieser Stelle sind alle Dinge gesammelt die sich seit der Erstellung des Checks und des obigen Beitrages geändert haben. Sollte es zu viel werden wird dieser Beitrag als veraltet markiert und ein neuer verfasst. Bis dahin gilt diese Übersicht.
Geänderter Update Channel
Es ist möglich den Update Channel in Nextcloud zu ändern. Diese Einstellung findet sich jedoch nicht in der versions.php sondern in der config/config.php. Sollte dort der Channel definiert sein wird dieser verwendet, ansonsten der aus der versions.php. Das hat jedoch zur Folge, dass via -c jetzt noch zusätzlich die config.php mit angegeben werden muss.
Anmerkung zur Icinga Config: Die config.php darf vermutlich vom Benutzer welcher Icinga ausführt nicht gelesen werden. Deswegen ist im CheckCommand ein Aufruf von sudo mit enthalten.
Python vor 3.6
Bei Python Versionen bis 3.5.x erscheint die Fehlermeldung „CRITICAL Cannot parse file /var/www/nextcloud/version.php“. Ab Version 3.6 ist dieses Problem behoben.
Falls kein Update auf Pyhton 3.6.x möglich ist kann der Check wie folgt angepasst werden:
// Die import Liste um chardet erweitern - muss ggf. vorher noch installiert werden import chardet // Den ersten try: Block wie folgt bearbeiten try: jsonResponse = subprocess.check_output([ args.php, '-r', 'include \'' + args.file + '\';include \'' + args.config + '\';' + ''' $g = array(); foreach($GLOBALS as $key => $val) { if(preg_match('/^(OC_|CONFIG$)/', $key)) { $g[$key] = $val; } } echo json_encode( array( 'pv' => PHP_VERSION, 'pmav' => PHP_MAJOR_VERSION, 'pmiv' => PHP_MINOR_VERSION, 'prv' => PHP_RELEASE_VERSION, 'g' => $g ) ); ''' ]) print(jsonResponse) ncVersion = json.loads(jsonResponse.decode(chardet.detect(jsonResponse)['encoding']))
Hallo,
dieser vorgestellte Icinga2-Check ist genau das, was ich gesucht habe, vielen Dank! Leider gibt es eine Fehlermeldung beim Parsen der version.php (sowohl bei Nextcloud 15 als auch 16):
./check_nextcloud.py -f /var/www/nextcloud/version.php -v
args=Namespace(file=’/var/www/nextcloud/version.php‘, php=’/usr/bin/php‘, updateServer=’https://updates.nextcloud.org/updater_server/‘, verbose=’verbose‘)
CRITICAL Cannot parse file /var/www/nextcloud/version.php
Python3 ist in der Version 3.5.3 (Debian 9), php in 7.3
Viele Grüße
Stephan
Hallo Stephan.
Ich hab den Check etliche Male am laufen, jedoch unter Ubuntu anstatt Debian. Daran sollte es aber prinzipiell nicht scheitern. Was für eine Ausgabe erhältst Du wenn Du folgenden Befehl in der Konsole ausführst:
/usr/bin/php -r 'include '\''/var/www/nextcloud/version.php'\''; print_r($GLOBALS);'
Hallo Stephan,
wie vorhin bereits besprochen ist die Ursache gefunden und liegt im Python 3.5. Ab Version 3.6 gibt es diese Fehlermeldung nicht mehr. Zur Lösung bei älteren Python-Versionen:
import chardet
*snip*
// Ersetzte den ersten try: Block durch:
try:
jsonResponse = subprocess.check_output([
args.php, '-r',
'include \'' + args.file + '\';' + '''
$g = array();
foreach($GLOBALS as $key => $val) {
if(preg_match('/^OC_/', $key)) {
$g[$key] = $val;
}
}
echo json_encode(
array(
'pv' => PHP_VERSION,
'pmav' => PHP_MAJOR_VERSION,
'pmiv' => PHP_MINOR_VERSION,
'prv' => PHP_RELEASE_VERSION,
'g' => $g
)
);
'''
])
print(jsonResponse)
ncVersion = json.loads(jsonResponse.decode(chardet.detect(jsonResponse)['encoding']))
Damit dies funktioniert muss ggf. die Python Erweiterung chardet installiert werden.
Ggf. mache ich mir demnächst mal die Mühe und baue den Check um bzw. schreibe die Version 2 von dem Check: Bei nextcloud (und ggf. auch bei owncloud?) kann man mittels occ inzwischen prüfen ob es Updates gibt. Liefert zwar immer als exit-code die 0 zurück, muss dann aber halt entsprechend ausgewertet werden. Ebenfalls machbar.
sudo -u www-data php occ update:check; echo $?
Update for files_rightclick to version 0.15.0 is available.
1 update available
0
Das ändert aber nichts daran, dass der im Artikel beschriebene Weg weiterhin funktioniert. Ob man die Frage ins Internet jetzt selbst stellt oder über occ stellen lässt ändert nichts am Ergebnis. Letzteres liefert aber zusätzlich noch die Updates zu den Plugins mit. Das was, wie oben erwähnt, bisher noch fehlte.
Moin Steffen,
das Problem (wenn es denn eins ist) scheint wohl eher Python-relevant zu sein. Das Kommando liefert vernünftige Werte:
…
[GLOBALS] => Array
*RECURSION*
[OC_Version] => Array
(
[0] => 16
[1] => 0
[2] => 3
[3] => 0
)
[OC_VersionString] => 16.0.3
[OC_Edition] =>
[OC_Channel] => stable
[OC_VersionCanBeUpgradedFrom] => Array
(
[nextcloud] => Array
(
[15.0] => 1
[16.0] => 1
)
[owncloud] => Array
(
)
)
[OC_Build] => 2019-07-09T12:01:37+00:00 185087e9bc46c1ef08e165e1ac979827dc05d469
[vendor] => nextcloud
)
Ich habe mal ein „import sys“ eingefügt die „except“-Anweisung umgebaut,
except:
#myExit(2, ‚CRITICAL Cannot parse file ‚ + args.file)
(type, value, traceback) = sys.exc_info()
print(„Type: „, type)
print(„Value: „, value)
print(„traceback: „, traceback)
damit mehr zu sehen ist und folgendes erhalten:
./check_nextcloud.py -f /var/www/nextcloud/version.php -v
args=Namespace(file=’/var/www/nextcloud/version.php‘, php=’/usr/bin/php‘, updateServer=’https://updates.nextcloud.org/updater_server/‘, verbose=’verbose‘)
Type:
Value: the JSON object must be str, not ‚bytes‘
traceback:
Traceback (most recent call last):
File „./check_nextcloud.py“, line 121, in
print(’ncVersion=‘ + str(ncVersion))
NameError: name ’ncVersion‘ is not defined
Das klingt nach einem Codierungs-Problem. Das Skript geht der Annahme, das utf-8 verwendet wird. Auf meinen Maschinen ist LANG=en_US.UTF-8 gesetzt. Was gibt bei Dir „locale“ aus?
Auch UTF-8, aber de_DE:
root@debncl2:~# locale
LANG=de_DE.UTF-8
LANGUAGE=
LC_CTYPE=“de_DE.UTF-8″
LC_NUMERIC=“de_DE.UTF-8″
LC_TIME=“de_DE.UTF-8″
LC_COLLATE=“de_DE.UTF-8″
LC_MONETARY=“de_DE.UTF-8″
LC_MESSAGES=“de_DE.UTF-8″
LC_PAPER=“de_DE.UTF-8″
LC_NAME=“de_DE.UTF-8″
LC_ADDRESS=“de_DE.UTF-8″
LC_TELEPHONE=“de_DE.UTF-8″
LC_MEASUREMENT=“de_DE.UTF-8″
LC_IDENTIFICATION=“de_DE.UTF-8″
LC_ALL=
Ich baue mir zeitnah mal eine Testumgebung mit Debian 9 zusammen und schaue mal nach was das sein kann.
root@a58aa19202bd:/var/www/html/git/icinga-and-nagios-plugins/nextcloud# cat /etc/debian_version ; php -v; python3 --version
16.0.4.1
Nextcloud 16.0.4
https://download.nextcloud.com/server/releases/nextcloud-16.0.4.zip
https://docs.nextcloud.com/server/16/admin_manual/maintenance/upgrade.html
https://updates.nextcloud.com/changelog_server/?version=16.0.4
1
0
s8S0e7yiQR5efWsMLZ+yVQ+QEqQ1h7PFMRsMOn3fUeOPvrZ9zxSD/87NueppRv2c
10.0
PHP 7.3.4-2 (cli) (built: Apr 13 2019 19:05:48) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.4-2, Copyright (c) 1999-2018, by Zend Technologies
Python 3.7.3
root@a58aa19202bd:/var/www/html/git/icinga-and-nagios-plugins/nextcloud# ./check_nextcloud.py -v -f /var/www/html/nextcloud/version.php
args=Namespace(file='/var/www/html/nextcloud/version.php', php='/usr/bin/php', updateServer='https://updates.nextcloud.org/updater_server/', verbose='verbose')
ncVersion={'pv': '7.3.4-2', 'pmav': 7, 'pmiv': 3, 'prv': 4, 'g': {'OC_Version': [16, 0, 3, 0], 'OC_VersionString': '16.0.3', 'OC_Edition': '', 'OC_Channel': 'stable', 'OC_VersionCanBeUpgradedFrom': {'nextcloud': {'15.0': True, '16.0': True}, 'owncloud': []}, 'OC_Build': '2019-07-09T12:01:37+00:00 185087e9bc46c1ef08e165e1ac979827dc05d469'}}
updateURL=https://updates.nextcloud.org/updater_server/?version=16x0x3x0xxxstablexx2019-07-09T12%3A01%3A37%2B00%3A00+185087e9bc46c1ef08e165e1ac979827dc05d469x7x3x4
xml=
Q6BIUK+35aNiD8+92j10nLfSPj0xIGtCu6QOfOK9uF9ZfhLhyfA6WrqrpzMmsl8y
SZuHeX9Xqurc2WzRSS5b+yN6OIY3eYvo6zpJyV2PnBUvbOOyKxgmcmxE/OSuyIKI
husXQEtj/Bu8LFxWYgDCdqtFyLIf2DKx1H7rgmEuN3RcC6zS177IJ2YKxjHPGxJT
H3eyYffBGQLKmjQVeiQTotKEIl0/dwSfIffs4GGa03d6l4rfHyI1EsI44mlcwdLa
Lj2qreseuw1nNqQQ0QA7nw==
CRITICAL update available: 16.0.4.1 (channel=stable php=7.3.4-2)
Habe halt ein Debian 10 genommen da hier php7.3 bereits dabei ist. Läuft bei mir. Gäbe es die Möglichkeit mal unverbindlich gemeinsam auf das System drauf zu schauen?
Ich habe mir etwas Ähnliches gebastelt, allerdings mit einer etwas simpleren Lösung.
Über das Auslesen des Updateservers und der Config-Files hatte ich nachgedacht, allerdings wollte ich die Schritte minimieren. Bei mir laufen Checks auf diverse verschiedene Systeme zeitgleich und da möchte man pro Check natürlich ein Minimum an Rechenleistung und Zeit verwenden.
Die status.php ist mMn. eine wirklich praktische Datei und stellt(Apps ausgenommen) alle Informationen bereit die gebraucht werden. Es wäre doch toll, wenn Nextcloud selbst auch eine solche Datei bereitstellen würde. Tatsächlich wird das auch indirekt getan, auf deren eigenem Demoserver (https://demo1.nextcloud.com/status.php).
Für diesen Check muss man sich allerdings darauf verlassen, dass der Demoserver immer hinreichend aktuell ist, bisher sieht das auch gar nicht schlecht aus.
Das klingt ebenfalls sehr interessant. Solange man keine Entwicklerversionen installiert hat sollte das eigentlich super laufen. Ich selbst habe die Last durch die Checks etwas anderes minimiert: Der Check läuft zum einen nur alle paar Stunden. Zum anderen überprüfe ich nur wenige meiner Nextcloud Instanzen. Ich lasse die Nextcloud Updates automatisch installieren. Dadurch weiß ich wenn ich bei der ersten Nextcloud Installation auf Updates prüfe, dass dies auch für die anderen Instanzen genau so gilt. Somit reicht mir ein Check für x Instanzen.