Как стать автором
Обновить

Создание тонкого клиента на базе дешевых одноплатных ПК

Время на прочтение19 мин
Количество просмотров48K
Иногда требуется сделать хитрый тонкий клиент — с необычной авторизацией и минимумом затрат.

Проще всего для этого использовать Linux.

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

Для экспериментов у меня в наличии была плата OrangePi One
image
и 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/env python
# -*- 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="power1">
                <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 подключения, выбор серверов, мониторинг серверов и все на что способна ваша фантазия — хоть вход по отпечатку пальца.
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 38: ↑35 и ↓3+32
Комментарии85

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань