Руководство разрaботчика интерфейсов доступа


Содержание

Руководство разработчика интерфейсов доступа
License
Интерфейс XML-RPC
Типы сокетов и аутентификация
Untrusted UNIX-socket
Trusted UNIX-socket
TCP-socket
Отладка
Структуры данных
Данные авторизации
Данные, которые возвращает вызов list
Доступные методы
Запуск сессии
Настройка параметров сессии
Получение списка дополнений
Выполнение команды
Получение и передача данных
Import

Список примеров

1. Запуск ядра с созданием безопасного сокета
2. Запуск ядра с созданием доверенного сокета
3. Запуск ядра с созданием TCP-сокета
4. Отладка XML-RPC

Руководство разработчика интерфейсов доступа

License

Copyright (c) 2007 Connexion project, Peter V. Saveliev.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found on the GNU site[1].

Интерфейс XML-RPC

Типы сокетов и аутентификация

Ядро Connexion не несёт в себе механизмов аутентификации (вполне умышленно), поэтому за предоставление авторизационных данных отвечает или клиент, или ядро операционной системы. Для того, чтобы полученным данным можно было верить, канал, по которому они получены, должен быть защищённым. На данный момент сервер Connexion может работать на UNIX-сокетах и на TCP-сокетах. В силу малой возможности защитить TCP-сокет, в текущей реализации его нужно использовать только в отладочных целях.

Untrusted UNIX-socket

Пример 1. Запуск ядра с созданием безопасного сокета

$ connexion -s /path/to/socket
			

По умолчанию, Connexion создаёт untrusted сокет в TMPDIR, но можно также и явно задать путь. Untrusted означает, что клиент не должен передавать авторизационные данные. Вместо этого, Connexion будет их брать через интерфейсы ядра операционной системы, получая EUID, EGID и PID процесса клиента.

Trusted UNIX-socket

Пример 2. Запуск ядра с созданием доверенного сокета

$ connexion -t /path/to/socket
		      

По умолчанию, Connexion не создаёт trusted сокет, но можно явно попросить это сделать. Trusted означает, что авторизационные данные будут переданы клиентом. Для того, чтобы этим данным можно было доверять, программа-клиент должна работать из-под доверенного аккаунта, а сам сокет должен быть защищён правами на файловой системе.

Этот интерфейс полезен, когда одна программа-клиент обслуживает нескольких операторов, скажем, это может быть веб-сервер консоли управления. В этом случае аутентификация проводится на стороне интерфейса.

TCP-socket

Пример 3. Запуск ядра с созданием TCP-сокета

$ connexion -n 127.0.0.1 -p 8090
		      

По умолчанию, Connexion не создаёт TCP сокет. Параметр `-n` указывает, на каком адресе слушать. Если не указано явно, сервер будет слушать на всех доступных адресах. Параметр `-p` указывает порт. Если порт не указан, TCP сокет не будет создан.

TCP сокет по умолчанию является trusted, так как нет простой возможности проверить авторизационные данные отправителя запроса.

Внимание

XML-RPC интерфейс Connexion на TCP сокете в текущей реализации можно использовать только для целей отладки

Отладка

Если вы не увереы в работе реализации XML-RPC или сами пишете свою реализацию для необычного языка (например, для XGawk), то можно запускать Connexion со включённым отладочным выводом протокола. Для этого нужно использовать ключ -d

Пример 4. Отладка XML-RPC

$ ./connexion.py -x -p 8090 -d

Debug shell started. Autocomplete does not work. See 'help' for details
Executable file was started from ./connexion.py

# <?xml version='1.0'?>
<methodCall>
<methodName>start</methodName>
<params>
<param>
<value><struct>
<member>
<name>pid</name>
<value><int>0</int></value>
</member>
<member>
<name>group</name>
<value><string>peet</string></value>
</member>
<member>
<name>user</name>
<value><string>peet</string></value>
</member>
</struct></value>
</param>
</params>
</methodCall>

<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><string>d65e991ade9a3ae966fd0080d50be0f6</string></value>
</param>
</params>
</methodResponse>

		      

Структуры данных

Данные авторизации

creds = {
      "user": string,
      "group": string,
      "pid": int
}
		    

Эту структуру ядро Connexion получает первым аргументом при каждом вызове со стороны интерфейса. Однако, разработчику нужно её использовать только в случае доверенных соединений, как на UNIX-, так и на TCP-сокетах. Именно в этих случаях ядро ожидает, что аутентификацию пользователя будет производить интерфейс, и доверяет переданной от интерфейса информации.

По умолчанию же ядро не доверяет этой информации и не ожидает её получения со стороны интерфейса (untrusted sockets). В случае, если вызов делается через untrusted socket, необходимо передавать ядру на один аргумент меньше, т.к. ядро Connexion получит эту информацию из операционной системы.

В полях нужно передать имена пользователя и группы в виде строк (например, "nobody") и (необязательно пока) process id.

Данные, которые возвращает вызов list

FIXME

Доступные методы

Реализацию доступных интерфейсу методов можно найти в файле gateway/core.py, класс proxy.

Запуск сессии

sid = start(creds)
		    

Возвращает идентификатор сессии (sid) в виде строкового идентификатора (UUID) В дальнейшем, во всех обращениях в рамках этой сессии нужно использовать полученный sid. Можно использовать и уже полученный идентификатор незаконченной когда-то сессии. Использовать чужой session id не получится, т.к. пользователю доступны только открытые им сессии.

Настройка параметров сессии

result = init(creds,opts)

result = {
	"prmopt": string
}

opts = {
	##
	# параметр передавать не обязательно, он зарезервирован
	# для грядущих нужд
	##
}
		    

Этот вызов на данный момент не требует никаких аргументов (аргумент opts является необязательным и в текущей версии не анализируется). Вызов init() возвращает словарь, на данный момент несущий один ключ: prompt, это приглашение командной строки и имя основной иерархии модулей.

Получение списка дополнений

dict = list(creds,sid,text)
		    

Получить список возможных команд или параметров для текущего состояния (если не указан text) или для состояния с префиксом text.

Выполнение команды

result = parse(creds,sid,clist)

###
# clist —  список команд на исполнение
###
clist = [
	cspec,
	cspec,
	...
]

###
# cspec —  описание команды
###
cspec = [
	command line,
	strings
]

###
# strings —  список строк многострочного ввода
###
strings = [
	string,
	string,
	...
]
		    

Команда принимает в качестве аргументов идентификатор сессии и список команд на исполнение. Каждая позиция в списке команд состоит из непосредственно строчки вызова и списка строк многострочного ввода.

После вызова `parse()` всегда надо делать вызов `input()`, чтобы получить как минимум код возврата (в случае синтаксической ошибки он всегда 255).

Получение и передача данных

Import

Дальше идёт необработанный текст из wiki, в процессе импортирования... Be patient.

=== Получение и передача данных === '''dict input(creds,sid,text)''' Переменная text несёт в себе данные, которые нужно передать сервису (нужно для интерактивных команд). Она может быть пустая. Работа с сервисами (потенциально запускающими и интерактивные команды) через синхронный протокол (каким и является XML-RPC) должна осуществляться по следующей схеме: 1. вызов `parse()` 1. если поле `error` присутствует, информируем пользователя 1. вызов `input()` 1. анализ: если queue == 0 и есть поле retval, это была последняя транзация, рвём цикл 1. если нет, продолжаем в цикле опрашивать `input()` с возможным вводом от пользователя либо без такового 1. если есть управляющий ввод, вызываем `interrupt()` См. также пример сессии дальше. === Управляющие команды === '''boolean interrupt(creds,sid,code)''' Послать управляющую команду сервису. Например, при нажатии Ctrl-C можно вызывать `interrupt(creds,sid,"c")`, что в сервисах по умолчанию означает отсылку SIGINT текущему процессу. Метод всегда возвращает True. Если на исполнении нет сервисов, то управляющие команды буферизуются (FIXME: нужно ограничить размер очереди, потенциальная атака) и будут отправлены первому же запущенному сервису. === Завершение сессии === '''boolean terminate(creds,sid)''' Ядро Connexion не удаляет сессии, если об этом не попросить явно. Это пока что :) Соответственно, чтобы не было утечек (а каждая сессия занимает память, пусть и немного), ненужные сессии необходимо терминировать. Возвращает True (если такая сессия была) или False (если не было доступной сессии по переданным данным) == Примеры == === Пример клиентской сессии === Для примера был взят Python. Коментарии записаны с `#`. {{{ [peet@localhost ~]$ python Python 2.4.4 (#1, May 15 2007, 20:42:45) [GCC 4.1.1 20070105 (ALT Linux, build 4.1.1-alt11)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import xmlrpclib >>> session = xmlrpclib.Server("http://192.168.1.248:8100") >>> creds = {"user": "peet", "group": "peet", "pid": 0} # стартуем сессию >>> sid = session.start(creds) # запускаем команду show interfaces address >>> session.parse(creds,sid,[["show interfaces address",[]]]) {'prompt': 'xshell'} # начинаем в цикле забирать вывод >>> session.input(creds,sid,"") {'queue': 1, 'data': 'MIME-Version: 1.0\nContent-Type: text/plain; charset="utf-8"\nContent-Transfer-Encoding: base64\n\nMTogbG86IDxMT09QQkFDSyxVUCxMT1dFUl9VUD4gbXR1I DE2NDM2IHFkaXNjIHNmcSAKICAgIGxpbmsvbG9vcGJhY2sgMDA6MDA6 # # пропущена часть вывода # QLExPV0VSX1VQPiBtdHUgMTI4MCBxZGlzYyBwZmlmb19mYXN0IHFsZW 4gMwogICAgbGluay9wcHAgCiAgICBpbmV0IDEwLjAuMC4yIHBlZXIgM TAuMC4wLjEvMzIgc2NvcGUgZ2xvYmFsIHBwcDAK'} # очередь исчерпана, получаем queue == 0, а заодно exit status >>> session.input(creds,sid,"") {'queue': 0, 'retval': 0} # если при пустой очереди спросить input(), то получаем False после # секундного таймаута >>> session.input(creds,sid,"") {'queue': 0, 'output': False} }}} === Ещё один пример запуска `parse()` === Агрегация нескольких команд в одну используется при буферизации ввода, полезно для ускорения работы скриптов: вместо 150, скажем, вызовов `parse()` можно делать один. Например, см. source:/interfaces/console/branches/0.4.4/shell.py@558#L251 {{{ session.parse(creds,sid,[ [ "show interfaces address",[]], [ "show ip route",[]], [ "!", ["that's a multiline input","for a comment"]] ]) }}} === Пример простейшего клиента === Он ничего не умеет, зато работает :) {{{ #!python #!/usr/bin/python import xmlrpclib creds = { "user": "peet", "group": "peet", "pid": 0 } session = xmlrpclib.Server("http://localhost:8090") sid = session.start(creds) lst = session.list(creds,sid) for i in lst[1].keys(): print "%-16s - %s" % (i,lst[1][i]["description"]) session.terminate(creds,sid) }}} {{{ #!html </div> }}}