Розробка

Створення тонкого клієнта на базі дешевих одноплатних ПК

Іноді потрібно зробити хитрий тонкий клієнт — з незвичайною авторизацією і мінімумом витрат.

Простіше всього для цього використовувати Linux.

Для одноплатних пк на базі ARM широко поширений дистрибутив Armbian, який заснований на Debian гілці.

Для експериментів у мене в наявності була плата OrangePi One

і MicroSD карта на 2G — я вирішив зробити з цього тонкий клієнт RDP з вікном блокування і без зайвого програмного забезпечення

Качаємо дистрибутив Armbian для цієї плати  останній з поміткою:

Server or light desktop usage scenarios.

У ньому не буде робочого столу, який не потрібен в тонкому клієнті.

Записуємо образ на MicroSD (я використовував sourceforge.net/projects/win32diskimager).

Після запису: вставляємо MicroSD, підключаємо всі інтерфейси (LAN, Дисплей, клавіатуру, мишу), подаємо живлення, чекаємо поки завантажиться.

Коли система завантажилася з’явиться пропозиція ввести логін і пароль. Також можна зайти по SSH глянувши виданий DHCP адреса (я скористався цією можливістю щоб не вбивати налаштування клавіатури і запустив PuTTY).

Логін: root
Пароль: 1234

Після входу система попросить змінити пароль: вводимо поточний 1234 і два рази новий пароль.

Також пропонується створити користувача — назвемо його user.

Після цього приступимо до налаштування системи під завдання.

Встановимо відсутні пакети:

apt update
apt install xorg lightdm xcursor-themes numix-gtk-theme numix-icon-theme 
apt install python-gtk2 freerdp

Запуск графічного входу для systemd включаєтся так:

systemctl set-default graphical.target

Створимо файли:
(створити їх простіше всього за допомогою редактора vi — після запуску з параметром потрібно натиснути i для вставки — потім просто вставити текст у вікно консолі, а по завершенні натиснути Esc і набрати :w для запису :q для виходу)

Для отримання своїх параметрів через DHCP сервер

vi /etc/dhcp/dhclient-exit-hooks.d/paramscript

 

setup_add() {
 echo $new_host_name > /etc/hostname
 hostname $new_host_name

 if [ -z "$new_nds_servers" ] ; then
 echo $new_routers > /tmp/rdp_server
 echo "testuser" > /tmp/rdp_user
 echo "1234test#" > /tmp/rdp_passwd
else
 echo $new_nds_servers > /tmp/rdp_server
 echo $new_nds_tree_name > /tmp/rdp_user
 echo $new_nds_context > /tmp/rdp_passwd
fi
}

case $reason in
BOUND|RENEW|REBIND|REBOOT)
setup_add
;;
EXPIRE|FAIL|RELEASE|STOP)
return
;;
esac

цей скрипт одержує по DHCP ім’я хоста і nds_servers, nds_tree_name, nds_context і зберігає їх значення у файлах в тимчасовій папці. За замовчуванням буде використаний IP-адресу маршрутизатора.

Параметри GTK і призначення його тим створимо

vi /home/user/.gtkrc-2.0

 

gtk-icon-theme-name = "Numix"
gtk-theme-name = "Numix"
gtk-cursor-theme-name = "whiteglass"

Для налаштування менеджера графічної середовища LightDM:

vi /etc/lightdm/lightdm.conf

 

[LightDM]
greeter-user=user
[Seat:*]
greeter-session=my-greeter

я після експериментів вирішив використовувати greeter (програма входу), таким чином не доведеться налаштовувати автовхід, а програма запуститься від потрібного користувача.

Тепер створимо ярлик програми (і його папку):

mkdir /usr/share/xgreeters/
vi /usr/share/xgreeters/my-greeter.desktop

 

[Desktop Entry]
Name=PyGTK+ Greeter
Comment=This runs the PyGTK+ greeter, it should only be run from LightDM
Exec=python /home/user/greeter.py
Type=Application
X-Ubuntu-Gettext-Domain=lightdm

І сам код входу:

vi /home/user/greeter.py

Код пропонує ввести 4 значний пароль 0811 (місяць день з незначащими нулями без прбелов)
Після успішного введення запускається xfreerdp з отриманими параметрами.

Код

#!/usr/bin/python env
# -*- coding: UTF-8 -*-

from datetime import datetime
from gobject import timeout_add
import os,sys,gtk,pango,subprocess

class GreeterApp:
 def __init__( self ):
 self.builder = gtk.Builder()
#self.builder.add_from_file (ui.glade")
self.builder.add_from_string(ui)
 self.window = self.builder.get_object ("window")
 self.pass_field = self.builder.get_object ("pass_field")
 self.passct = self.builder.get_object ("pass")
 self.contrl = self.builder.get_object ("control")
 self.pass_field.modify_font(pango.FontDescription('Sans Bold 36'))
 self.passwd = datetime.now().strftime("%m - %d")
 self.rdp_server = open("/tmp/rdp_server").read().split(',')[0]
 self.rdp_user = open("/tmp/rdp_user").read()
 self.rdp_passwd = open("/tmp/rdp_passwd").read()
 self.process = None
 timeout_add(5000, self.timeout)
 if self.window:
 self.window.connect("destroy", gtk.main_quit)
self.contrl.set_visible(False)
self.window.set_size_request(gtk.gdk.screen_width(),gtk.gdk.screen_height())
self.builder.connect_signals(self)
 def hide(self, widget):
self.contrl.set_visible(False)
self.passct.set_visible(True)
self.window.move(0,0)
self.window.set_size_request(gtk.gdk.screen_width(),gtk.gdk.screen_height())
 if self.process:
self.process.terminate()
 self.process = None
 def timeout(self):
 if self.window:
self.window.set_keep_above(True)
self.window.set_modal(True)
 return True
 def check_pass(self, widget):
 if self.pass_field.get_text() == self.passwd:
self.pass_field.set_text("")
self.passct.set_visible(False)
self.contrl.set_visible(True)
self.window.set_size_request(50,50)
self.window.move(0,gtk.gdk.screen_height()-50)
#sys.exit()
 self.process = subprocess.Popen(["xfreerdp","/v:"+self.rdp_server,"/f","/cert-ignore","/u:"+self.rdp_user,"/p:"+self.rdp_passwd])
 def clear_pass(self, widget):
self.pass_field.set_text("")
 def num_press(self, widget):
self.pass_field.set_text(self.pass_field.get_text()+widget.get_tooltip_text())
 def on_destroy(self, widget):
sys.exit()
if __name__ == "__main__":
 settings = gtk.settings_get_default()
 #settings.props.gtk_button_images = True
 settings.props.gtk_enable_tooltips = False
 ui = """
<?xml version="1.0" encoding="UTF-8"?>
<interface>
 <requires lib="gtk+" version="2.24"/>
 <object class="GtkWindow" id="window">
 <property name="can_focus">False</property>
 <property name="type">popup</property>
 <property name="title" translatable="yes">MyGreeter</property>
 <property name="resizable">False</property>
 <signal name="destroy" handler="on_destroy" swapped="no"/>
<child>
 <object class="GtkVBox" id="vbox1">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
<child>
 <object class="GtkHBox" id="control">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
<child>
 <object class="GtkButton" id="мощность1">
 <property name="use_action_appearance">False</property>
 <property name="width_request">50</property>
 <property name="height_request">42</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <signal name="clicked" handler="hide" swapped="no"/>
<child>
 <object class="GtkImage" id="image4">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="stock">gtk-no</property>
</object>
</child>
</object>
<packing>
 <property name="expand">False</property>
 <property name="fill">True</property>
 <property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
 <property name="expand">False</property>
 <property name="fill">True</property>
 <property name="position">0</property>
</packing>
</child>
<child>
 <object class="GtkHBox" id="hbox1">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
<child>
 <object class="GtkTable" id="pass">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="n_rows">6</property>
 <property name="n_columns">3</property>
<child>
 <object class="GtkEntry" id="pass_field">
 <property name="width_request">240</property>
 <property name="height_request">60</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="visibility">False</property>
 <property name="invisible_char">●</property>
 <property name="xalign">1</property>
 <property name="invisible_char_set">True</property>
 <property name="primary_icon_activatable">False</property>
 <property name="secondary_icon_activatable">False</property>
 <property name="primary_icon_sensitive">True</property>
 <property name="secondary_icon_sensitive">True</property>
</object>
<packing>
 <property name="right_attach">3</property>
 <property name="bottom_attach">2</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_0">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">0</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_0">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">0</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="top_attach">5</property>
 <property name="bottom_attach">6</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_1">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">1</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_1">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">1</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="top_attach">4</property>
 <property name="bottom_attach">5</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_2">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">2</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_2">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">2</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">1</property>
 <property name="right_attach">2</property>
 <property name="top_attach">4</property>
 <property name="bottom_attach">5</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_3">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">3</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_3">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">3</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">2</property>
 <property name="right_attach">3</property>
 <property name="top_attach">4</property>
 <property name="bottom_attach">5</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_4">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">4</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_4">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">4</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="top_attach">3</property>
 <property name="bottom_attach">4</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_5">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">5</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_5">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">5</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">1</property>
 <property name="right_attach">2</property>
 <property name="top_attach">3</property>
 <property name="bottom_attach">4</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_6">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">6</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_6">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">6</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">2</property>
 <property name="right_attach">3</property>
 <property name="top_attach">3</property>
 <property name="bottom_attach">4</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_7">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">7</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_7">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">7</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="top_attach">2</property>
 <property name="bottom_attach">3</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_8">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">8</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_8">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">8</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">1</property>
 <property name="right_attach">2</property>
 <property name="top_attach">2</property>
 <property name="bottom_attach">3</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_9">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <property name="tooltip_text" translatable="yes">9</property>
 <signal name="clicked" handler="num_press" swapped="no"/>
<child>
 <object class="GtkLabel" id="l_9">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="label" translatable="yes">9</property>
<attributes>
 <attribute name="weight" value="bold"/>
 <attribute name="size" value="40000"/>
</attributes>
</object>
</child>
</object>
<packing>
 <property name="left_attach">2</property>
 <property name="right_attach">3</property>
 <property name="top_attach">2</property>
 <property name="bottom_attach">3</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_ent">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <signal name="clicked" handler="check_pass" swapped="no"/>
<child>
 <object class="GtkImage" id="image2">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="stock">gtk-yes</property>
</object>
</child>
</object>
<packing>
 <property name="left_attach">1</property>
 <property name="right_attach">2</property>
 <property name="top_attach">5</property>
 <property name="bottom_attach">6</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
<child>
 <object class="GtkButton" id="b_del">
 <property name="use_action_appearance">False</property>
 <property name="width_request">80</property>
 <property name="height_request">80</property>
 <property name="visible">True</property>
 <property name="can_focus">True</property>
 <property name="receives_default">True</property>
 <signal name="clicked" handler="clear_pass" swapped="no"/>
<child>
 <object class="GtkImage" id="image3">
 <property name="visible">True</property>
 <property name="can_focus">False</property>
 <property name="stock">gtk-no</property>
</object>
</child>
</object>
<packing>
 <property name="left_attach">2</property>
 <property name="right_attach">3</property>
 <property name="top_attach">5</property>
 <property name="bottom_attach">6</property>
 <property name="x_options">GTK_SHRINK</property>
 <property name="y_options">GTK_SHRINK</property>
</packing>
</child>
</object>
<packing>
 <property name="expand">True</property>
 <property name="fill">False</property>
 <property name="position">0</property>
</packing>
</child>
</object>
<packing>
 <property name="expand">True</property>
 <property name="fill">False</property>
 <property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
"""
 app = GreeterApp()
app.window.show()
 gtk.main()

Код надано як приклад, можна зробити просто введення імені і пароля для RDP підключення, вибір серверів, моніторинг серверів і все на що здатна ваша фантазія — хоч вхід по відбитку пальця.

Related Articles

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Close