Raspberry Reverse SSH Tunnel

Wird der Raspberry zum Beispiel mit einem Surfstick oder hinter einer Firewall betrieben und möchte man remote auf diesen per SSH oder mit anderen Diensten zugreifen gibt es die Möglichkeit vom Raspberry sich mit einem anderen SSH Server zu verbinden und bestimmte Ports zu tunneln.

Für den SSH Server kann man entweder einen eigenen Raspberry nutzen oder auch zb. Windows Home Server mit BITVISE SSH Server.

AutoSSH

Autossh wird dazu verwendet, die SSH Verbindung immer wieder automatisch herzustellen, wenn sie unterbrochen wurde.

[sourcecode language=“plain“]sudo apt-get install autossh[/sourcecode]

Keypairs erstellen

Auf dem Remote RPI Keys generieren (angemeldeter user : pi)

[sourcecode language=“plain“]cd ~
ssh-keygen -t rsa[/sourcecode]

Public Key auf den Server kopieren und dort installieren bei Windows BitVise SSH Server mit WINSCP übertragen. Ansonsten kann man das auch mit linux mitteln druchführen. (Vorher muss natürlich auf dem Server ein entsprechender User eingerichtet werden)

[sourcecode language=“plain“]ssh-copy-id -i ~/.ssh/id_rsa.pub user@remote-system[/sourcecode]

SSH Test-Verbindung testen

Port (-p) ggf anpassen oder bei Defaultbetrieb weglassen.

[sourcecode language=“plain“]ssh -p443 user@remote-system[/sourcecode]

Login sollte nun automatisch funktionieren.
SSH mit Remote Port forwarding testen:

[sourcecode language=“plain“]ssh -p443 -fNC -R 10022:localhost:22 user@remote-system[/sourcecode]

Komplexe Variante mit folgenden Ports:

[sourcecode language=“plain“]ssh -p443 -fNC -R 11022:localhost:22 -R 11080:localhost:80 -L 18080:localhost:8080 user@remote-system[/sourcecode]

Erklärung:

  • Remote Port 11022 wird zu lokal Raspberry 22 (SSH) geforwardet
  • Remote Port 11080 wird zu lokal Raspberry 80 (HTTP) geforwardet
  • Und vom Raspberry möchten wir Daten an den Server schicken
    Lokaler Port 18080 wird an den Remote Port 8080 geforwardet (HTTP IPS)

Auto SSH Setup

autoSSH baut die verbindungen automatisch wieder auf (Achtung Traffic)

[sourcecode language=“plain“]autossh -p443 -fNC -R 11022:localhost:22 -R 11080:localhost:80 -L 18080:localhost:8080 user@remote-system[/sourcecode]

autoSSH muss aber bei jedem Start geladen werden – hinzu kommt, dass die Key – Dateien beim PI User liegen.

Einfache Lösung wie folgt mit der Crontab des PI Users, dazu ein Startscript schreiben und das in die crontab des pi users eintragen:

[sourcecode language=“plain“]nano ~/autossh_start[/sourcecode]

Folgender Inhalt in autossh_start

[sourcecode language=“plain“]#!/bin/bash
killall ssh
autossh -p443 -fNC -R 11022:localhost:22 -R 11080:localhost:80 -L 18080:localhost:8080 user@remote-system[/sourcecode]

Das Script noch ausführbar machen:

[sourcecode language=“plain“]chmod u+x ~/autossh_start[/sourcecode]

Crontab schreiben:

[sourcecode language=“plain“]crontab -e -u pi[/sourcecode]

Am Ende eintragen:

[sourcecode language=“plain“]@reboot ~/autossh_start[/sourcecode]

Test ob auch in ctrontab drin:

[sourcecode language=“plain“]crontab -l -u pi[/sourcecode]


 

Serienbriefe in Word mit QR-Codes oder Bildern aus dem Web

Es gibt einen einfachen Weg, Serienbriefe mit QR-Codes oder anderen Bildern aus dem Web zu erstellen.

Dies kann für Eintrittskarten oder sonstige Etiketten notwendig sein.

Hier der Weg:

Excel Datenquelle erstellen.

Beispiel:

excel

 

In Spalte Number eine beliebige Zahl, Nummer oder auch Text. In der Spalte Image wird die URL mit dem VERKETTEN Befehl generiert.

Dieser sieht z.B. So aus: =VERKETTEN(„http://chart.apis.google.com/chart?cht=qr&chs=350×350&chl=“;B2)

Hier wird die Google API genutzt um QR-Codes zu generieren. Man kann hier beliebige URLS hinterlegen oder generieren.

Serienbrief oder Etiketten in Word

Neue Word-Datei erstellen und über Sendungen -> SerienDruck Starten -> Etiketten (oder was Anderes) auswählen und erstellen.

Nun die Excel-Datei als Datenquelle einbinden (Sendungen->Empfänger auswählen -> Vorhandene Liste).

Als nächstes werden die Seriendruckfelder eingefügt.

Bild einfügen

Die URL zum Bild ist in unserer Quelle (Excel-Liste). Nun wird dieses in den Seriendruck eingefügt.

  1. ALT+F9 drücken um Anzeige umzuschalten.
  2. Cursor an Position wo das Bild erscheinen soll
  3. STRG+F9 drücken um Feld einzufügen und folgendes eingeben:
    INCLUDEPICTURE „
  4. STRG+F9 drücken und danach eingeben:
    HYPERLINK
  5. STRG+F9 drücken und wieder einfügen:
    MERGEFIELD (FELDNAME_EXCEL)
    (FELDNAME_EXCEL) ersetzen durch Spaltenname der URL (im Beispiel Image)
  6. Vor der letzten Klammer noch ein “ einfügen.
    So muss es aussehen:
    mergefield
  7. Etiketten aktualisieren drücken.

Etiketten exportieren

Nun müssen die Etiketten in ein neues Dokument exportiert werden:

Fertig stellen und zusammenführen -> Einzelne Dokumente bearbeiten.

Im neu erstellten dokument STRG+A drücken danach F9

Nun wurden alle Bilder aktualisiert.

fertig

ACHTUNG im Serienbriefmodus NICHT die Datensätze aktualisieren, sonst werden die falschen Bilder gecached!

AvisCam / Mikrokopter / CHDK USB Auslöseskript

Mittels CHDK kann man die Canon Kameras gut programmieren. Für AvisCam und USB Auslösung benutze ich folgendes Script mit automatischer Berechnungsfunktion.
Die Verschlusszeit wird vorgegeben und die ISO-Werte werden entsprechend berechnet.

@title AvisCam USB Shutter
@param q Shutter 1st Shot
 @default q 1
 @values q 1/100 1/200 1/400 1/640 1/800 1/1000

@param p Shutter 2nd Shot
 @default p 1
 @values p 1/100 1/200 1/400 1/640 1/800 1/1000

@param f AutofocusLock
 @default f 1
 @values f off on
 
@param w Pic Delay (sec)
 @default w 2
 
@param r Raw Mode
 @default r 0
 @values r off on

rem Defaults - Kameraabhängig - Tabelle benutzen
rem Minium ISO = 80
n = 371
rem Maximum ISO = 3200
x = 891

print "Set focus unendlich"
set_focus 65535
sleep 500
print "Set AF lock"
set_aflock f
sleep 500

rem ND Filter ausschalten
print "ND Filter ausschalten"
set_nd_filter 2
sleep 200

rem Raw mode setzen
print "Set Raw Mode"
set_raw r

sleep 1000 
w = w * 1000
print "Start!"

:eval
c = 0
while 1
 p = get_usb_power 1
 if p > 0 then
 c = c + 1
 rem Shoot Foto now
 rem 1. Messen
 gosub "meter"
 
 rem 2. Verschlusszeit festlegen
 v = q
 gosub "gettv96"
 rem 3. Belichtung ausrechnen
 gosub "exposure"
 rem 4. Knipsen
 
 click "shoot_full"
 print "Shot TV1 #", c, S , T 
 rem 2. Foto wenn untersch. Bel. Zeiten
 if q <> p then
 sleep 1000
 rem 2. Verschlusszeit festlegen
 v = p
 gosub "gettv96"
 rem 3. Belichtung ausrechnen
 gosub "exposure"
 rem 4. Knipsen
 print "Shot TV2 #", c, S, T
 click "shoot_full"
 endif
 rem Zwangspause lt. Parameter
 sleep w
 endif
wend

rem Bild messen
:meter
 press "shoot_half"
 while get_shooting = false
 sleep 10
 wend
 A = get_av96
 B = get_bv96
 return


rem Belichtung (ISO berechnen) 
:exposure
 S = t + A - B
 rem Limit minimum Sv
 if S<n then let S=n
 rem Limit maximum Sv
 if S>x then let S=x
 
 rem Tv anpassen falls außerhalb der limits
 if S <> A + t - B then let T = B + S - A else let T = t
 set_tv96_direct T
 set_sv96 S
 sleep 200
 return
 
rem Umwandlung verschlusszeit in werte
:gettv96
 if v = 0 then let t=640
 if v = 1 then let t=736
 if v = 2 then let t=832
 if v = 3 then let t=896 
 if v = 4 then let t=928
 if v = 5 then let t=960
return

PHP Secure Connection RSA / AES Javascript

Da SSL nicht immer verfügbar ist, und ggf. doch sicher Daten ausgetauscht werden müssen, habe ich mich dazu entschlossen eine PHP und JavasScript Klasse zu bauen, die genau dies tut:

  • Serverseitig Public und Private key (RSA) generieren.
  • Public Key an den Client schicken
  • Client erstellt einen gemeinsamen Schlüssel und sendet ihn per RSA Verschlüsselung an den Server.
  • Ab jetzt erfolgt die Kommunikation AES verschlüsselt.
  • Der Server kann AES verschlüsselte HTML-Daten (texte, Divs etc.) übermitteln, der Client empfängt diese und entschlüsselt sie.
  • Der Client kann GET Parameter und POST Formulare verschlüsselt and den Server senden
  • Key Renewal und Connection Ping eingebaut.

Auf PHP Server-Seite braucht man fast nichts tun – auf Client Seite ebenfalls nicht.

Beispiel einer PHP Date auf dem Server, die HTML Generiert und ausgibt, sowie Daten empfängt.

<?php
 require_once("secon/secon.cls.inc.php");
 $secon = new secon();
?>
<html>
<!-- BEGIN SECON INCLUDE -->
 <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
 <script type="text/javascript" src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/pbkdf2.js"></script> <!-- Used for Key Generation and Salting --->
 <script type="text/javascript" src="secon/js/jsbn.js"></script> <!-- BigInt etc. Java Helper Tools --->
 <script type="text/javascript" src="secon/js/rsa2.js"></script> <!-- RSA Encryption --->
 <script type="text/javascript" src="secon/js/sha1.js"></script> <!-- Used SHA1 Hashing --->
 <script type="text/javascript" src="secon/js/aes.js"></script> <!-- AES Ecnryption --->
 <script type="text/javascript" src="secon/js/storage.js"></script> <!-- Browser Window Storage of Variables and Content --->
 <script type="text/javascript" src="secon/js/secon.js"></script> <!-- Secure Connection Class --->
 
 <!-- Activate Secon for this page and assign handlers -->
 <script type="text/javascript">
 var secon = new secon(); // Init Secon
 $( document ).ready(function() { // When loading is finished do
 secon.Ping(); // Initiate a Ping to check if Server and Client are OK.
 secon.DeCryptContent('.seconCrypted'); // Run Decrypting HTML Content deliverd by server, specify Jquery selector
 secon.AttachForms(".secon"); // Attach Secon Handler for secure Form - Transfer
 secon.AttachLinks(".secon"); // Attach Secon Handler for secure GET (Link) Transfer
 });
 </script> 
 
<!-- END SECON INCLUDE ---> 
 
 <body>
 <?php
 echo "<div class='seconCrypted'>";
 echo $secon->CryptText('Dies ist ein geheimer Text')
 echo "</div>"; 
 ?>
 </body>
</html>

Im Debug Fenster des Clients (Browser z.B. Chrome oder Firefox, kann man sehen, dass die Daten vershlüsselt werden und dann übertragen werden.form

Formular verschlüsselt senden

Das Formular wird verschlüsselt abgeschickt indem es einfach die Klasse .secon bekommt.

<form method="post" class="secon">

Normalerweise würden bei PHP die Daten in $_POST stehen. Da wir verschlüsselt übertragen stehen diese Daten in gleicher Form jedoch in $secon->DATA zur verfügung.

Parameter von Links verschlüsselt senden

Es ist einfach möglich Links wie zb. http://myserver.de/mydat.php?secret=1234&type=mode Verschlüsselt an den Server zu übertragen. Dazu muss der <a href link einfach die klasse secon bekommen.

<a href="index.php?mode=test&secret=1234&irgendwas=2" class="secon">My Link</a>

Im empfangenden PHP Script auf dem Server stehen die Daten dann ebenfalls in $secon->DATA zur verfügung.

Daten vom Server verschlüsselt an den Client senden:

Auch wird es notwendig sein bestimmte Daten vom Server in verschlüsselter Form an den Client zu transferieren. Die Daten müssen in einen Container z.B. DIV gepackt werden, da der Client per Javascript JQUERY Selector dann die Daten entschlüsselt. In dem Container dürfen dann nur verschlüsselte Daten stehen!

<div class="seconCrypted">
<?php echo $secon->CryptText('<b>My very secure data here...</b>');?>
</div>

 

Auf dem Client kommen die Daten dann wie unten zu sehen an und werden erst dort entschlüsselt und visualisiert:response

Auf diese Art könnte nicht nur ein DIV, sondern sogar der ganze Body verschlüsselt übertragen werden.

Secon Komplettpaket (PHP, JS und DEMO) als ZIP

 

Raspberry GPIOs mit PHP schalten

Die einfachste Methode ist die, dass wir dem www-data User (unter welchem der Apache und auch die PHP Skripte laufen in die GPIO gruppe aufnehmen. Dabei ist zu beachten, dass dies ggf. ein Sicherheitsrisiko darstellt.

[sourcecode language=“plain“]sudo usermod -aG gpio www-data
sudo usermod -aG gpio pi
sudo /etc/init.d/apache2 restart[/sourcecode]

PHP Beispiel für REST mit Json response

[sourcecode language=“php“]<?php
// Valid GPIOS
$C["GPIOS"] = Array(17,22);

$mode = @$_GET["mode"];
switch($mode){
case "SWGPIO": $on = intval(@$_GET["on"]);
$id = intval(@$_GET["id"]);
SwitchGPIO($id, $on);
break;

case "GETGPIO": $id = intval(@$_GET["id"]);
GetGPIO($id);
break;
}

function GetGPIO($id){
global $C;
if(!in_array($id, $C["GPIOS"])){
Status(false, "GPIO $id not allowed.");
}
if(!is_dir("/sys/class/gpio/gpio$id/")){
Status(false,"GPIO $id not active.");
}
$fn = "/sys/class/gpio/gpio$id/value";
if(!file_exists($fn)){
Status(false, "GPIO $id Valuefile does not exist.");
}
$val = trim(file_get_contents($fn));
Status(true,$val);
}
function SwitchGPIO($id, $on){
// Vorher beim Booten Berechtigungen setzen!
// sudo usermod -aG sudo usermod -aG gpio www-data

global $C;
if(!in_array($id, $C["GPIOS"])){
Status(false, "GPIO $id not allowed.");
}
if(!in_array($on, Array(0,1))){
Status(false, "GPIO status $on not allowed.");
}
// check if GPIO is open
if(!is_dir("/sys/class/gpio/gpio$id/")){
file_put_contents("/sys/class/gpio/export",$id);
sleep(1);
file_put_contents("/sys/class/gpio/gpio$id/direction","out");
}
if(!is_dir("/sys/class/gpio/gpio$id/")){
Status(false,"Cannot access GPIO $id");
}

// Set correct status
$fn = ‚/sys/class/gpio/gpio‘.$id.’/value‘;
$val = file_get_contents($fn);
if($val == $on){
Status(true, "Switch status already $on");
}
file_put_contents($fn,$on);
usleep(250000);
// Check status
$val = file_get_contents($fn);
if($val == $on){
Status(true, "Switch status now: $on");
} else {
Status(false, "Switch status wrong");
}
}

Status(false,"Wrong Parameters");

function Status($stat, $txt){
$ret["STATUS"] = ($stat)? "OK" : "ERROR";
$ret["MSG"] = $txt;
echo json_encode ($ret);
die;
}

function warning_handler($errno, $errstr) {
echo $errno;
return false;
}
?>
[/sourcecode]

GPIOs sollten jedoch bereits beim Neustart des Raspberry initialisiert werden
dazu ein Script anlegen und ggf. anpassen: im pi home:

[sourcecode language=“plain“]nano ~/init_gpio[/sourcecode]

Inhalt:

[sourcecode language=“plain“]#!/bin/bash

#Init GPIOS on Startup with Script
#Ohne SUDO gehen die Befehle nur wenn pi Mitglied der Gruppe gpio ist.
echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction
echo "0" > /sys/class/gpio/gpio17/value[/sourcecode]

Script Berechtigungen:

[sourcecode language=“plain“]sudo chmod u+x ~/init_gpio[/sourcecode]

Autostart mit der crontab des pi

[sourcecode language=“plain“]crontab -e -u pi[/sourcecode]

letzte Zeile anfügen:

[sourcecode language=“plain“]@reboot ~/init_gpio[/sourcecode]

Prüfen ob alles OK:

[sourcecode language=“plain“]crontab -l -u pi[/sourcecode]