Hook-овите за поврзување на VPN демон работат како демонот, и обично тоа значи дека работат како root. Во моментот кога еден од тие hook-ови запишува во домашниот директориум на некој или рестартира помошник на работната површина, вие сте му дале на мрежен настан способност да дејствува како кој било локален корисник. Вообичаениот кратен пат, да прочитате корисничко име од променлива на околината и да го пренесете, не го поправа тоа. Тоа само го преозначува дејството без да смени која сметка го извршува. Она што работи е триделно предавање, во кое привилегираниот чекор кажува што се случило, а посебна единица го извршува дејството под правилниот идентитет.

Корисничкото име во околината не е прекинувач за идентитет

Unix одлучува до што смее да допре процесот според неговиот ефективен кориснички ID, а не според низите во неговата околина. Поставувањето на USER=alice или извозот на LOGNAME менува што невнимателен софтвер печати во својот излез; не менува ништо во проверките за сопственост на датотеки. Hook што работи како root со поставена таа променлива сепак ги отвора датотеките на Алис како root.

Ни имињата не се истото име. Најавата за далечински пристап, локалната Unix сметка и заедничкото име на сертификатот се три идентификатори што често се совпаѓаат, а понекогаш не. Корисникот што се автентификува како a.smith може воопшто да нема локална сметка или да се мапира на asmith преку директориумска служба. Секој дизајн што претпоставува дека една низа служи за трите улоги ќе се скрши кај првиот изведувач.

Затоа првото прашање не е «кој корисник е поврзан» туку «која локална сметка смее да го изврши ова дејство». Одговорете на тоа од сопствената база на сметки, а не од она што го испратил клиентот:

id -u alice
getent passwd alice | cut -d: -f1,3,6
stat -c '%U:%G %a %n' /home/alice/.local/state/vpn-status.json

Третата команда е онаа што луѓето ја прескокнуваат. Ако целната датотека е во сопственост на root зашто претходниот hook ја создал, правилно конфигурирано корисничко дејство ќе пропадне со грешка за дозвола што изгледа како проблем со systemd и не е.

Демонот останува таму каде што е

Менувањето на сопствениот User= на VPN службата на Алис е погрешната лост. Тоа менува како демонот ги врзува интерфејсите, чита клучеви, ја пишува својата датотека за состојба и управува со маршрути, и го пренесува целиот тој ризик во единица што работеше. На демонот му требаат неговите привилегии; на следното дејство не.

Погледнете што единицата всушност прави пред да ја допрете. Две команди ви ја кажуваат идентитетот на извршување и точните командни линии на сила, вклучително сето што додало некое препишување:

systemctl show -p User -p Group -p ExecStart openvpn-server@server
systemctl cat openvpn-server@server

При стандардно инсталирање од пакет, демонот е root и скриптата --up го наследува тоа. Потврдете го тоа наместо да претпоставувате, бидејќи стврдната дистрибуција можеби го преместила на посебна сметка, а рачно напишан drop-in може да го пушта hook-от преку обвивка со други правила.

Три именувани делови, еден тесен тригер

Распоредот што преживува одржување има настан, тригер и дејство. Демонот известува дека именувана сесија се крена. Тригерот го прифаќа тој мал факт и бара едно фиксно дејство. Дејството работи под локалната сметка што ги поседува датотеките што ги допира. Ништо во синџирот не прифаќа произволна командна патека.

Самото дејство е обична сервисна единица, а идентитетот е една директива во неа:

[Service]
Type=oneshot
User=alice
Group=alice
ExecStart=/usr/local/bin/refresh-vpn-status --slot %i

Забележете дека User=alice важи за целата единица, и systemd документира еден исклучок што вреди да се знае: команда со префиксот + работи со целосни привилегии без оглед на тоа. Тој префикс е легитимна алатка за еден привилегиран чекор и удобен начин да го поништите одвојувањето што штотуку го изградивте, затоа третирајте го секој + во таа единица како нешто што треба да се оправда при преглед.

Држете го влезот на тригерот минимален и недоверлив. Идентификаторот на сесијата, мапираната сметка и временската ознака обично тоа е сето што е потребно. Никогаш не предавајте преку границата фрагмент од shell, патека до извршна датотека или блок на околина доставен од клиентот, зашто страната што прима нема начин да разликува легитимна вредност од вметната.

Проверете го идентитетот што навистина работеше

Прегледот на конфигурацијата не е доказ. По одобрено пробно поврзување, потврдете го UID-от што ја допрел датотеката и UID-от што демонот уште го користи:

stat -c '%U %G %y %n' /home/alice/.local/state/vpn-status.json
ps -o user=,pid=,cmd= -p "$(pgrep -o -f 'openvpn --daemon')"
systemd-run --uid=alice --pipe /usr/local/bin/refresh-vpn-status --dry-run

Првата линија треба да ја покаже Алис и временска ознака во вашиот прозорец за тестирање. Втората треба да покажува уште root или сервисната сметка на пакетот, што докажува дека демонот не се поместил. Третата е најевтиниот начин да ги тестирате дозволите на дејството без да чекате вистинско повторно поврзување.

Ако сопственикот на датотеката е Алис, но нејзината група или режим се смениле меѓу извршувања, барајте UMask= или разлика во дозволите на директориумот наместо логичка грешка. Ако временската ознака воопшто не се поместила, тригерот никогаш не се активирал, и корисниот следен чекор е journalctl -u за единицата на дејството, а не уште едно повторно поврзување на клиентот.

Што уште не докажува настанот на поврзување

Воспоставен тунел е еден факт. Тоа не значи дека сесијата на работната површина е отклучена, дека шината на сесијата е достапна или дека приватниот ресурс што го сака дејството е веќе достапен. oneshot единица под UID-от на Алис го има нејзиниот идентитет и ништо од нејзината сесија, затоа сето што бара графичка шина мора да помине низ поддржаниот механизам за корисничка сесија и да определи што се случува кога таа е одјавена.

Повторните поврзувања исто така се повторуваат. Преносно сметач што спие во воз може да создаде десетина настани на поврзување за еден час, а hook што додава линија секој пат ја претвора датотеката за состојба во дневник што никој не може да го чита. Направете го дејството идемпотентно или дозволете му да замени еден запис клучуван по сесија.

На крајот, ограничете го радиусот на штета во самата единица: приказ на датотечен систем само за читање надвор од директориумот за состојба, без нови привилегии и со изречен рок. Одвојувањето на демонот и корисничкото дејство вреди само по себе, но вреди многу повеќе кога дејството не може да направи ништо друго освен тоа едно нешто што сте го одобриле.

Поврзани статии