Привет! В Mac OS чаще всего используют программу Tunnelblick для подключения по vpn. Я тоже ей пользовался и это было отвратительно, может у меня кривые руки и я не сумел ее грамотно настроить ( ну те проставить правильно галочки в gui интерефейсе ), но в целом получить от нее стабильности и простоты у меня так и не получилось.
Возможно эта программное обеспечение для подключения к vpn удобно когда тебе нужно подключать по 1 vpn для просмотра ютубчика на обеде ( хотя vpn для этого не нужен, достаточно настроить анонимный proxy ) но когда требуется что-то более сложное Tunnelblick начинает хромать.
Стандартная ситуация, вы работаете в крупной компании и вам требуется использовать корпоративные сервисы, которые развернуты внутри сети. Для этой цели вам выдали vpn.
Так же внутри компании есть разные отделы, например завязанные на какие-то конкретные проекты и внутри корпоративной сети у вас должен так же быть отдельный доступ к внутренним сервисам вашего отдела проекта в котором вы работаете, таким образом вам уже требуется подключаться к 2 vpn сетям одновременно приплюсуем к этому что вы живете в стране в которой часть ресурсов заблокирована из-за непростой геополитической ситуации и вам уже потребуется 3 vpn которые должны работать и не конфликтовать друг с другом.
Tunnelblick с 3 VPN уже работает плохо, давайте еще добавим, что VPN тоже не работает стабильно так как время от времени провайдер тестирует оборудование фильтрации VPN и связь пропадает. Это катастрофа, если вам требуется VPN для работы.
Допустим, еще в вашей компании время от времени обновляются ovpn и его нужно скачивать по специальной ссылке и таким образом, используя гуи приложения для vpn вам придется время от времени заниматься удалением, добавлением и если вы используете еще sock5 для своего подключения — правками ovpn файлов.
Каждый раз после того как Tunnelblick зависал, я грустил и вспоминал времена когда у меня был arch linux и для подключения к vpn я использовал простой скрипт на bash.
Последней каплей стала ситуация когда после обновления ОС, вместе с зависанием Tunnelblick стала пропадать сеть так что требовалась перезагрузка макбука для того чтобы сеть снова появилась ( позже я разобрался с этим и это оказалось, что это из-за того что Tunnelblick не мог правильно возвращал DNS сервера на место ). Это стало последней каплей терпения и я решил окончательно решить этот вопрос и удалив с радостью Tunnelblick написал свой скрипт для подключения к VPN, чем хочу поделится тут.
#!/bin/bash VPN_PID="$HOME/.vpn/vpn.pid" LOG_FILE="$HOME/.vpn/vpn_download.log" # в этом файле нужно прописать ссылки ( каждая на своей строке по которым скрипт будет забирать обновленные ovpn файлы # содержимое файла ~/.vpn/update.txt должно иметь вот такой вид: # https://corporate.site/share/project.ovpn # https://corporate.size/share/corporate.ovpn URL_LIST_FILE="$HOME/.vpn/update.txt" # пути к .ovpn файлам TARGET_DIR="$HOME/.vpn/configs" VPN_CONFIG_1="$TARGET_DIR/corporate.ovpn" VPN_CONFIG_2="$TARGET_DIR/project.ovpn" VPN_CONFIG_3="$TARGET_DIR/private.ovpn" # логин и пароль от корпоративного VPN # содержимое файла ~/.vpn/password.auth # <login_vpn> # <password_vpn> VPN_PASSWORD="$HOME/.vpn/password.auth" # пусть к команде openvpn # в моем случае я ставил openvpn для mac os через homebrew VPN_COMMAND="/opt/homebrew/sbin/openvpn" # замените на ваш DNS DNS_SERVER="8.8.8.8" # замените на вашу сеть NETWORK_SERVICE="Wi-Fi" # таймауты и настройки проверки подключений к vpn CHECK_TIMEOUT=10 MAX_ATTEMPTS=3 CONNECT_TIMEOUT=30 # Shadowsocks параметры, как настраивать я писал тут: # Как настроить Shadowsocks - https://killercoder.ru/maskirovka-trafika-cherez-shadowsocks # Как установить Shadowsocks - https://killercoder.ru/ustanavlivaem-shadowsocks-na-svoj-server SHADOWSOCKS=false SHADOWSOCKS_PROXY="socks5://127.0.0.1:1080" # Парсинг аргументов # Поддерживает 1 аргумент --sock ( подключение через sock proxy ) while [[ $# -gt 0 ]]; do case "$1" in --sock) SHADOWSOCKS=true shift ;; *) break ;; esac done # загружает обновленные файлы ovpn с поддержкой загрузки через sock5 ( если дела совсем плохи.. ) download_file() { local url=$1 local filename=$(basename "$url") local target_path="$TARGET_DIR/$filename" local retry=0 echo "$(date '+%Y-%m-%d %H:%M:%S') - Начало загрузки $filename" >> "$LOG_FILE" while [ $retry -lt $MAX_ATTEMPTS ]; do if [ "$SHADOWSOCKS" = true ]; then if curl -sSL --fail --connect-timeout $CONNECT_TIMEOUT --proxy "$SHADOWSOCKS_PROXY" "$url" -o "$target_path"; then echo "$(date '+%Y-%m-%d %H:%M:%S') - Успешно (через Shadowsocks): $filename сохранен в $target_path" >> "$LOG_FILE" echo "✅ $filename успешно загружен через Shadowsocks" return 0 fi else if curl -sSL --fail --connect-timeout $CONNECT_TIMEOUT "$url" -o "$target_path"; then echo "$(date '+%Y-%m-%d %H:%M:%S') - Успешно: $filename сохранен в $target_path" >> "$LOG_FILE" echo "✅ $filename успешно загружен" return 0 fi fi ((retry++)) echo "$(date '+%Y-%m-%d %H:%M:%S') - Попытка $retry/$MAX_ATTEMPTS: Ошибка загрузки $filename" >> "$LOG_FILE" sleep 2 done echo "$(date '+%Y-%m-%d %H:%M:%S') - Ошибка: не удалось загрузить $filename после $MAX_ATTEMPTS попыток" >> "$LOG_FILE" echo "❌ Не удалось загрузить $filename после $MAX_ATTEMPTS попыток" return 1 } update() { if [ ! -f "$URL_LIST_FILE" ]; then echo "❌ Файл $URL_LIST_FILE не найден!" | tee -a "$LOG_FILE" exit 1 fi stop_vpn sleep 2 echo "=== Начало загрузки VPN-конфигов ===" | tee -a "$LOG_FILE" echo "Чтение URL из файла: $URL_LIST_FILE" | tee -a "$LOG_FILE" success_count=0 fail_count=0 while IFS= read -r url || [ -n "$url" ]; do if [[ -z "$url" || "$url" == \#* ]]; then continue fi download_file "$url" if [ $? -eq 0 ]; then ((success_count++)) else ((fail_count++)) fi done < "$URL_LIST_FILE" echo "=== Загрузка завершена ===" | tee -a "$LOG_FILE" echo "Успешно: $success_count файлов" | tee -a "$LOG_FILE" echo "Не удалось: $fail_count файлов" | tee -a "$LOG_FILE" if [ $fail_count -gt 0 ]; then exit 1 else start_vpn exit 0 fi } # команда запуска личного VPN start_private() { stop_vpn sleep 2 echo "🔒 Запускаю VPN private..." if [ "$SHADOWSOCKS" = true ]; then echo "Использую Shadowsocks proxy для подключения" sudo $VPN_COMMAND \ --config "$VPN_CONFIG_3" \ --socks-proxy 127.0.0.1 1080 \ --log-append /var/log/openvpn.log & ALL_PROXY=$SHADOWSOCKS_PROXY else sudo $VPN_COMMAND \ --config "$VPN_CONFIG_3" \ --log-append /var/log/openvpn.log & fi echo "VPN(private)" > $VPN_PID } # команда запуска корпоративного VPN start_vpn() { stop_vpn echo "🔒 Запускаю VPN corporate..." # Проверка наличия нужных файлов if [[ ! -f "$VPN_CONFIG_1" ]]; then echo "❌ Ошибка: файл конфигурации $VPN_CONFIG_1 не найден!" exit 1 fi if [[ ! -f "$VPN_CONFIG_2" ]]; then echo "❌ Ошибка: файл конфигурации $VPN_CONFIG_2 не найден!" exit 1 fi if [[ ! -f "$VPN_PASSWORD" ]]; then echo "❌ Ошибка: файл аутентификации $VPN_PASSWORD не найден!" exit 1 fi if [ "$SHADOWSOCKS" = true ]; then echo "Использую Shadowsocks proxy для подключения" sudo $VPN_COMMAND \ --config "$VPN_CONFIG_1" \ --auth-user-pass "$VPN_PASSWORD" \ --socks-proxy 127.0.0.1 1080 \ --log-append /var/log/openvpn.log & sudo $VPN_COMMAND \ --config "$VPN_CONFIG_2" \ --auth-user-pass "$VPN_PASSWORD" \ --socks-proxy 127.0.0.1 1080 \ --log-append /var/log/openvpn.log & else sudo $VPN_COMMAND \ --config "$VPN_CONFIG_1" \ --auth-user-pass "$VPN_PASSWORD" \ --log-append /var/log/openvpn.log & sudo $VPN_COMMAND \ --config "$VPN_CONFIG_2" \ --auth-user-pass "$VPN_PASSWORD" \ --log-append /var/log/openvpn.log & fi # тут же проверяем подключение attempt=1 while [ $attempt -le $MAX_ATTEMPTS ]; do echo "Проверка подключения (попытка $attempt/$MAX_ATTEMPTS)..." if check_vpn_connection; then echo "VPN успешно подключен!" if set_dns; then export CURRENT_VPN="work" echo "VPN(corporate)" > $VPN_PID exit 0 else exit 1 fi fi sleep $CHECK_TIMEOUT ((attempt++)) done } stop_vpn() { echo "🛑 Останавливаю VPN..." sudo networksetup -setdnsservers $NETWORK_SERVICE empty sudo pkill -SIGINT openvpn rm -f $VPN_PID } log() { sudo tail -f /var/log/openvpn.log } empty_dns() { sudo networksetup -setdnsservers $NETWORK_SERVICE empty } status_vpn() { if pgrep -x "openvpn" > /dev/null; then echo "✅ VPN работает (PID: $(pgrep -x "openvpn"))." if [ -f "$VPN_PID" ]; then echo "Текущий профиль: $(cat "$VPN_PID")" fi if [ "$SHADOWSOCKS" = true ]; then echo "Используется Shadowsocks proxy" fi else echo "❌ VPN не запущен." fi } clear_utun() { for i in $(ifconfig | grep '^utun' | cut -d: -f1); do if ! lsof -i | grep -q "$i"; then echo "Удаляю $i..." sudo ifconfig "$i" down sudo ifconfig "$i" destroy fi done } # функция остановки DNS set_dns() { echo "Устанавливаю DNS $DNS_SERVER для $NETWORK_SERVICE..." sudo networksetup -setdnsservers "$NETWORK_SERVICE" "$DNS_SERVER" if networksetup -getdnsservers "$NETWORK_SERVICE" | grep -q "$DNS_SERVER"; then echo "DNS успешно изменён" return 0 else echo "Ошибка: не удалось изменить DNS" return 1 fi } # функция проверки соединения check_vpn_connection() { if ifconfig | grep -q '^utun\|^tun'; then if ping -c 1 -t 2 10.34.96.1 >/dev/null 2>&1; then return 0 fi fi return 1 } case "$1" in corp) start_vpn ;; stop) stop_vpn ;; restart) stop_vpn sleep 2 start_vpn ;; status) status_vpn ;; private) start_private ;; empty_dns) empty_dns ;; log) log ;; clear_utun) clear_utun ;; update) update ;; *) echo "Использование: $0 {start|stop|restart|status|update|clear_utun|empty_dns|log} [--sock]" echo "" echo "Опции:" echo " --sock Использовать Shadowsocks proxy для подключения" exit 1 esac
Все подробности как что работает есть в комментариях в коде
Есть еще несколько вещей, которые можно сделать для удобства. Например, чтобы каждый раз не вводить пароль при запросе sudo через visudo добавляете
user ALL=(ALL) NOPASSWD: /opt/homebrew/sbin/openvpn user ALL=(ALL) NOPASSWD: /usr/sbin/networksetup user ALL=(ALL) NOPASSWD: /opt/homebrew/sbin/openvpn user ALL=(ALL) NOPASSWD: /opt/homebrew/sbin/openvpn user ALL=(ALL) NOPASSWD: /usr/sbin/networksetup user ALL=(ALL) NOPASSWD: /usr/bin/pkill -SIGINT openvpn
Вместо user указываете своего пользователя, вместо команд правильные пути в вашей системе. Правильный путь можно узнать командой:
$ which openvpn
И так же по-аналогии с sock5 можно сделать вывод подключения в командной строке, если пользуетесь оболочкой zsh
vpn_status() { if [[ -f ~/.vpn/vpn.pid ]]; then local vpn_name=$(cat ~/.vpn/vpn.pid) echo "🟢 VPN: %F{green}$vpn_name%f" # Иконка + название VPN else echo "🔴 VPN: %F{red}DISCONECT%f" fi } PROMPT=' ... %F{yellow}%f $(vpn_status) ...
На этом все.. Если есть вопросы, пишите в телегу а так же подписывайтесь на обновление сайта в группу телеграм