Primitive password window on gtkdialog

/ / snippets :: , , ,

В альмаматер сисадминам надоело возиться с firewall'ом и открывать пользователям порты вручную на каждый хост по их неисчислимым требованиям, поэтому запилили VPN. Ну, пока не пережили массовой эпидемии червей, как это произошло в CERN в силу того что у нас всего лишь несколько сотен пользователей нуждаются в удалённом доступе, и секьюрность сети в целом это, видимо, не слишком уменьшает.

VPN предлагается использовать через Cisco Any Connect VPN Client. Ясное дело, под Linux клиент, если даже и есть, то не той он сложности, чтобы вот так, с порога, его вкатывать, когда есть openconnect с ебилдом в официальном дереве портажей.

Одна беда. Реквизиты SSO‐учётки хранить в конфигах я не хочу, как не хочу использовать истекающие куки и добывать сертификат тоже не хочу. А хочу вводить пароль каждый раз, когда нужно подключиться.

Я сделал себе небольшой скрипт на gtkdialog (есть почти везде), который спрашивает у меня иксовым диалогом пароль для соединения. Всё остальное вбито в шелл‐скрипт гвоздями. Меня интересовала главным образом возможность сделать лишь средствами shell‐only диалог для ввода некоторой критической информации.

У gtkdialog непростая судьба, и он несколько рудиментарен в функциях. Его модификация XML (который на самом деле не XML) не описана в больших книжках, а постигается листанием примеров в SVN‐репозитории. Может быть на него и есть какой‐то официальный референс, но я за весь вечер ничего так и не нашёл. Правда архивы форума некоторых коммьюнити оказались весьма информативны. Во всём прочем, у gtkdialog большой бардак с документацией, хотя позапускав примерчики из транка всякий может убедиться, что маленький диалект gtkdialog не такой уж и маленький.

Прежде всего, вот такой небольшой пара‐XML файл сделает вам диалог с полем для вводом пароля и двумя кнопочками:

<window title="TPU VPN"
        modal="True"
        resizable="False"
        type="popup"
        type_hint="dialog">
    <vbox>
        <entry visibility="false"
               invisible-char="42">
            <variable>PASSWD</variable>
        </entry>
    <hbox>
        <button cancel></button>
        <button can-default="true" has-default="true">
                <label>OK</label>
                <input file stock="gtk-ok"></input>
        </button>
    </hbox>
    </vbox>
</window>

Кнопочки не так нужны такому диалогу как реакция на клавиши: <esc> — для отмены, <return> — для ввода. Наибеолее прямолинейное решение состояло бы в одновременном использовании атрибутов signal и condition тэга <action>, вроде вот такого:

 <action signal="key-press-event"
         condition="command_is_true([ $KEY_RAW = 0x9 ] && echo false)">
         echo "$KEY_RAW -- exit"</action>

Однако gtkdialog версии 0.8.2 отчего‐то игнорирует condition, кажется, в пользу signal. Пришлось размаскировать ~amd64‐ебилд 0.8.3 и кончить вот таким вот шелл‐скриптом (выкладываю целиком):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/bin/sh
# Requires:
#  - >=gtkdialog-0.8.3
#  - gksudo
#  - awk
#  - sed
#
# Please, note this openconnect parameters:
VPN_AUTH_SERVER="https://ssl.yourserver.com"
VPN_CFG_FILE="/root/access/only/vpn.conf"
#
# Utils
[ -z $GTKDIALOG ]   &&   GTKDIALOG=$(whereis gtkdialog   | awk '{print $2}')
[ -z $GKSUDO ]      &&      GKSUDO=$(whereis gksudo      | awk '{print $2}')
[ -z $OPENCONNECT ] && OPENCONNECT=$(whereis openconnect | awk '{print $2}')
[ -z $XTERM ]       &&       XTERM=$(whereis xterm       | awk '{print $2}')
if     [ -z "$GTKDIALOG" ] \
    || [ -z "$GKSUDO" ] \
    || [ -z "$OPENCONNECT" ] \
    || [ -z "$XTERM" ]
then
    echo "Couldn't find one or more of the dependencies."
    exit 1
fi
#
# gtkdialog stuff:
MAIN_DIALOG='
<window title="My VPN"
        modal="True"
        resizable="False"
        type_hint="dialog"
        center="True">
    <vbox>
        <entry visibility="false"
               invisible-char="42"
               caps-lock-warning="true">
            <variable>PASSWD</variable>
            <action signal="key-press-event"
                    condition="command_is_true([ $KEY_RAW = 0x24 ] && echo true)"
                    type="exit">OK</action>
            <action signal="key-press-event" condition="command_is_true([ $KEY_RAW = 0x9  ] && echo true)"
                    condition="command_is_true([ $KEY_RAW = 0x24 ] && echo true)"
                    type="exit">Cancel</action>
        </entry>
    <hbox>
        <button cancel></button>
        <button can-default="true" has-default="true">
                <label>OK</label>
                <input file stock="gtk-ok"></input>
        </button>
    </hbox>
    </vbox>
</window>
'
export MAIN_DIALOG
#
# Running util:
function open_vpn_tun() {
    vpnPasswd=''
    dialogEStr=''
    while read line; do
        key=$(echo $line | awk -F "=" '{print $1}')
        val=$(echo $line | awk -F "=" '{print $2}' | sed 's/^"\(.*\)".*/\1/')
        case $key in
            PASSWD ) vpnPasswd=$val ;;
            EXIT ) dialogEStr=$val ;;
        esac
    done
    case $dialogEStr in
        OK ) $GKSUDO "xterm -hold -e 'echo \"$vpnPasswd\" | $OPENCONNECT --config=$VPN_CFG_FILE $VPN_AUTH_SERVER'" ;;
        # ... whatever dialog exit results can be treated in special manner?
        * ) >&2 echo "Exit due to password dialog result (action failed or cancelled by user)."; exit 1 ;;
    esac
}
#
# Entry point
case $1 in
    #-d | --dump) echo "$MAIN_DIALOG" ;;
    #-t | --test) open_vpn_tun one ;;
    *) $GTKDIALOG --center --program=MAIN_DIALOG | open_vpn_tun ;;
esac

Скрипт использует с пяток утилит, которые, по идее, должны быть в каждом современном дистрибутиве. openconnect невозможно использовать без root‐привелегий, так что пароля придётся вводить аж два — для учётки на VPN, и для sudo‐сессии. Сама сессия будет инициализированна в xterm, что, вообще, довольно опасно и нужно будет когда‐нибудь исправить.


Comments