Содержание статьи
SO_PEERCRED
.В качестве примера мы напишем сервер, который просто хранит какое-то число, при этом пользователи из группы wheel
могут его изменять, а все остальные — только получать значение. Писать будем на Python, но все сказанное применимо к любому языку.
В этой статье речь идет о Linux, если не указано обратное.
Абстрактные сокеты Linux
Для взаимодействия с сервером мы будем использовать вариант UNIX domain sockets — abstract namespace sockets. В отличие от сетевых сокетов, в случае с локальными сокетами UNIX ядро знает, какой процесс подключается к сокету, и знает все о пользователе (создателе) процесса, что и позволяет переиспользовать системную аутентификацию.
В классической реализации сокеты UNIX представляют собой файлы. Это позволяет применить к ним права доступа и запретить пользователям или группам подключаться к ним, но более сложную модель привилегий на этом не построить.
Основной недостаток классической реализации — опция SO_REUSEADDR
для них не работает. Файл сокета должен быть создан процессом, который на нем слушает. Если процесс упал, а файл сокета остался, то новому процессу сначала нужно его удалить. Правильно написанный сервер должен использовать lock-файлы, чтобы предотвратить случайный запуск нескольких экземпляров процесса и обеспечить безопасное удаление старого файла.
Для решения этой проблемы в Linux существуют так называемые abstract namespace sockets. По своей сути они идентичны традиционным сокетам UNIX, но не являются файлами и автоматически исчезают с завершением процесса, который их создал. К ним также неприменимы обычные права доступа, и авторизация остается на совести приложения — но мы ведь к этому и стремимся.
Чтобы создать абстрактный сокет, нужно добавить в начало его «пути к файлу» нулевой байт. В остальном все так же, как с обычными.
Пишем сервер
Для начала мы напишем основу для сервера, пока без авторизации. Чтобы не писать разбор сообщений, мы сделаем всего две команды без аргументов: read
(вернуть значение счетчика) и inc
(увеличить счетчик на единицу).
Сокет мы назовем counter-server
. Соответственно, путь его будет '\0counter-server'
.
#!/usr/bin/env python3
import os
import socket
SOCK_FILE = '\0counter-server'
## Счетчик
counter = 0
## Создаем сокет
sock = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
sock.bind(SOCK_FILE)
sock.listen()
while True:
conn, _ = sock.accept()
command = conn.recv(1024).decode().strip()
if command == 'inc':
print("Received an increment request")
counter += 1
response = "Success"
elif command == 'read':
print("Received a read request")
response = "Counter value: {0}".format(counter)
else:
response = "Invalid command"
conn.send(response.encode())
conn.send(b'\n')
conn.close()
Попробуем запустить его:
$ sudo ./counter-server.py
Counter server started
Перейдем в другую консоль и попробуем подключиться с помощью socat
. В случае с обычным stream-сокетом протокол был бы UNIX-CONNECT
, но поскольку наш — «необычный», нужен ABSTRACT-CONNECT
.
$ socat - ABSTRACT-CONNECT:counter-server
inc
Success
$ socat - ABSTRACT-CONNECT:counter-server
read
Counter value: 1
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»