One place for hosting & domains

      для

      Настройка удаленного доступа для MongoDB в Ubuntu 20.04


      Предыдущая версия данного руководства была написана Мелиссой Андерсон.

      Введение

      MongoDB (или Mongo) — это документоориентированная база данных с открытым исходным кодом, используемая во многих современных веб-приложениях. По умолчанию она допускает только те подключения, которые исходят с того же сервера, на котором она установлена. Если вы хотите управлять MongoDB удаленно или подключить ее к отдельному серверу приложения, вам необходимо будет внести несколько изменений в исходную конфигурацию по умолчанию.

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

      Предварительные требования

      Для завершения данного обучающего модуля вам потребуется:

      • Сервер на базе Ubuntu 20.04. На сервере должен быть пользователь без привилегий root с правами администратора и брандмауэр, настроенный с помощью UFW. Вы можете выполнить настройку, следуя указаниям руководства Начальная настройка сервера для Ubuntu 20.04.
      • MongoDB, установленная на сервере. Данное обучающее руководство предполагает, что у вас уже установлена MongoDB версии 4.4 или более новой. Вы можете установить эту версию, следуя указаниям руководства Установка MongoDB в Ubuntu 20.04.
      • Второй компьютер, с которого вы получите доступ к вашему экземпляру MongoDB. Для простоты в данном руководстве мы будем считать, что этот компьютер — это еще один сервер Ubuntu 20.04 с пользователем без привилегий root и правами администратора и брандмауэром UFW, настроенным в соответствии с руководством Начальная настройка сервера Ubuntu 20.04. Однако шаги 1 и 2, которые описывают фактическую процедуру обеспечения удаленного подключения на сервере базы данных, можно выполнять независимо от используемой операционной системы удаленного компьютера.

      Наконец, мы настоятельно рекомендуем вам обеспечить безопасность системы MongoDB путем создания учетной записи пользователя с правами администратора для базы данных и обеспечения аутентификации, хотя это и не требуется для выполнения данного обучающего модуля. Для этого следуйте указаниям руководства Обеспечение безопасности MongoDB в Ubuntu 20.04.

      Шаг 1 — Настройка брандмауэра

      Если вы выполнили начальную инструкцию по настройке сервера и включили брандмауэр UFW на вашем сервере, тогда ваша система MongoDB будет недоступна из Интернета. Если вы собираетесь использовать MongoDB только локально с запуском приложений на том же сервере, эту безопасную настройку рекомендуется сохранить. Но если вы хотите иметь возможность подключаться к серверу MongoDB с удаленной локации, вам нужно разрешить входящие подключения к порту, на котором прослушивается база данных. Для этого нужно добавить новое правило UFW.

      Для начала проверьте порт, на котором прослушивается ваша система MongoDB, с помощью команды lsof. Эта команда обычно возвращает список со всеми открытыми файлами в системе, но при сочетании с опцией -i она указывает только сетевые файлы или потоки данных.

      Следующая команда перенаправляет вывод команды lsof -i на команду grep, которая ищет строку с именем mongo:

      • sudo lsof -i | grep mongo

      Этот пример вывода показывает, что MongoDB прослушивает подключения на порту по умолчанию 27017:

      Output

      mongod 82221 mongodb 11u IPv4 913411 0t0 TCP localhost:27017 (LISTEN)

      В большинстве случаев доступ к MongoDB следует разрешать только из определенных доверенных локаций, таких как другой сервер хостинга приложения. Одним из способов настройки этого ограничения является запуск на сервере MongoDB следующей команды, которая открывает доступ к порту по умолчанию MongoDB только для IP-адреса другого доверенного сервера.

      Запустите следующую команду, убедившись, что вы поменяли trusted_server_ip на IP-адрес доверенного удаленного компьютера, который вы будете использовать для доступа к вашему экземпляру MongoDB:

      Примечание. Если вывод предыдущей команды показал, что ваша установка MongoDB выполняет прослушивание не на порту по умолчанию, используйте номер этого порта вместо 27017 в данной команде.

      • sudo ufw allow from trusted_server_ip to any port 27017

      В дальнейшем, если вы захотите получить доступ к MongoDB с другого компьютера, запустите эту команду снова с IP-адресом нового компьютера вместо trusted_server_ip.

      Вы можете проверить изменение параметров брандмауэра с помощью ufw:

      Вывод покажет, что трафик к порту 27017 с удаленного сервера сейчас разрешен:

      Output

      Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 27017 ALLOW trusted_server_ip OpenSSH (v6) ALLOW Anywhere (v6)

      Дополнительные настройки брандмауэра для ограничения доступа к службам можно найти в разделе Основы UFW: распространенные правила и команды брандмауэра.

      В следующем шаге мы свяжем MongoDB с публичным IP-адресом сервера, чтобы получить доступ с удаленного компьютера.

      Шаг 2 — Настройка публичного bindIP

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

      Чтобы разрешить удаленные подключения, необходимо отредактировать файл конфигурации MongoDB — /etc/mongod.conf — для дополнительной привязки MongoDB к публичному маршрутизированному IP-адресу вашего сервера. Таким образом ваша система MongoDB сможет прослушивать подключения к вашему серверу MongoDB, выполненные с удаленных компьютеров.

      Откройте файл конфигурации MongoDB в предпочитаемом текстовом редакторе. В следующем примере используется nano:

      • sudo nano /etc/mongod.conf

      Найдите раздел network interfaces (сетевые интерфейсы), а затем значение bindIp:

      /etc/mongod.conf

      . . .
      # network interfaces
      net:
        port: 27017
        bindIp: 127.0.0.1
      
      . . .
      

      Добавьте запятую в эту строку, а затем публичный IP-адрес вашего сервера MongoDB:

      /etc/mongod.conf

      . . .
      # network interfaces
      net:
        port: 27017
        bindIp: 127.0.0.1,mongodb_server_ip
      
      . . .
      

      Сохраните и закройте файл. Если вы используете nano, нажмите CTRL+X, Y, затем ENTER.

      Затем перезапустите MongoDB, чтобы изменение вступило в силу:

      • sudo systemctl restart mongod

      После этого ваша система MongoDB сможет принимать удаленные подключения с любых компьютеров, которым вы разрешите доступ к порту 27017. В качестве завершающего шага вы можете проверить, сможет ли доверенный удаленный сервер, которому вы разрешили доступ через брандмауэр на шаге 1, связаться с экземпляром MongoDB, работающем на вашем сервере.

      Шаг 3 — Тестирование удаленного подключения

      Теперь, когда вы настроили систему MongoDB для прослушивания подключений, исходящих с ее публичного маршрутизированного IP-адреса, и предоставили удаленному компьютеру доступ через брандмауэр сервера к порту по умолчанию Mongo, можно проверить, сможет ли удаленный компьютер выполнить подключение.

      Примечание. Как указано в разделе «Предварительные требования», это обучающее руководство предполагает, что ваш удаленный компьютер — это еще один сервер на базе Ubuntu 20.04. Процедура по обеспечению удаленных подключений, описанная в шагах 1 и 2, должна работать независимо от операционной системы вашего удаленного компьютера. Однако методы тестирования, описанные в этом шаге, не являются универсальными для всех операционных систем.

      Один из способов проверки возможности подключения вашего доверенного удаленного сервера к экземпляру MongoDB — это использование команды nc. nc (сокращенно от netcat) — это утилита, используемая для установки сетевых подключений с TCP или UDP. Она используется для тестирования в подобных случаях, поскольку позволяет вам указать как IP-адрес, так и номер порта.

      Вначале войдите на ваш доверенный сервер с помощью SSH:

      • ssh sammy@trusted_server_ip

      Затем запустите команду nc, которая включает опцию -z. Это ограничивает nc только сканированием прослушивающего демона на целевом сервере без отправки каких-либо данных. Напомним из руководства по установке, что MongoDB работает как служебный демон, что делает эту опцию полезной для тестирования подключения. Также она включает опцию v, которая увеличивает детализацию команды, благодаря чему netcat возвращает некоторые выводы, которые иначе не были бы получены.

      Запустите следующую команду nc с вашего доверенного удаленного сервера и не забудьте заменить mongodb_server_ip IP-адресом сервера, на котором вы установили MongoDB:

      • nc -zv mongodb_server_ip 27017

      Если доверенный сервер сможет получить доступ к демону MongoDB, его вывод будет означать, что подключение было успешным:

      Output

      Connection to mongodb_server_ip 27017 port [tcp/*] succeeded!

      Если вы установили совместимую версию оболочки mongo на вашем удаленном сервере, вы можете на данном этапе подключаться напрямую к экземпляру MongoDB, установленному на сервере хоста.

      Одним из способов подключения является URI строки подключения, например:

      • mongo "mongodb://mongo_server_ip:27017"

      Примечание. Если вы выполнили рекомендации из руководства Обеспечение безопасности MongoDB в Ubuntu 20.04, у вас будет закрыт доступ к вашей базе данных от пользователей, которые не прошли аутентификацию. В этом случае вам нужно будет использовать URI с указанием действительного имени пользователя, например:

      • mongo "mongodb://username@mongo_server_ip:27017"

      Оболочка автоматически попросит ввести пароль пользователя.

      Этим вы подтверждаете, что ваш сервер MongoDB может принимать соединения с доверенного сервера.

      Заключение

      Теперь вы можете получить доступ к вашей системе MongoDB с удаленного сервера. На этом этапе вы можете управлять вашей базой данных Mongo удаленно с доверенного сервера. Также вы можете настроить приложение, которое будет работать на доверенном сервере и удаленно использовать базу данных.

      Если вы не настроили пользователя с правами администратора и не активировали аутентификацию, любой, кто имеет доступ к вашему удаленному серверу, также может получить доступ к вашей системе MongoDB. Если вы еще не сделали этого, мы настоятельно рекомендуем вам следовать инструкциям руководства Обеспечение безопасности MongoDB в Ubuntu 20.04, чтобы добавить пользователя с правами администратора и дополнительные ограничения.



      Source link

      Установка и настройка Postfix в качестве сервера SMTP только для отправки в Ubuntu 20.04


      Автор выбрал фонд Free and Open Source Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Postfix — это агент передачи почты (MTA), т. е. приложение для отправки и приема электронной почты. Его можно настроить так, чтобы только локальные приложения могли использовать его для отправки электронной почты. Это может быть полезно, если вам нужно регулярно рассылать по электронной почте уведомления от ваших приложений или у вас много исходящего трафика, а сторонний поставщик услуг электронной почты не разрешает такие объемы. Это облегченная альтернатива развертыванию полноценного сервера SMTP, позволяющая сохранить требуемые функции.

      В этом обучающем модуле мы установим и настроим Postfix как сервер SMTP, доступный только для отправки. Также мы запросим бесплатные сертификаты TLS от Let’s Encrypt для вашего домена и настроим их использование для шифрования исходящей электронной почты.

      Предварительные требования

      • Один сервер Ubuntu 20.04, настроенный в соответствии с инструкциями по начальной настройке сервера с Ubuntu 20.04, а также пользователь без привилегий root с привилегиями sudo.
      • Зарегистрированное полное доменное имя. В этом обучающем руководстве мы будем использовать your_domain. Вы можете купить доменное имя на Namecheap, получить его бесплатно на Freenom или воспользоваться услугами любого предпочитаемого регистратора доменных имен.
      • Запись DNS A с your_domain​​​, указывающая на публичный IP-адрес вашего сервера. В руководстве Введение в DigitalOcean DNS содержится подробная информация по их добавлению.

      Примечание. Имя хоста вашего сервера и имя вашего дроплета должны соответствовать your_domain, т. к. DigitalOcean автоматически задает записи PTR для IP-адреса дроплета по его имени.

      Для проверки имени хоста сервера можно ввести hostname в командной строке. Вывод должен соответствовать имени, которое вы присвоили дроплету при его создании.

      Шаг 1 — Установка Postfix

      На этом шаге мы выполним установку Postfix. Быстрее всего будет установить пакет mailutils, включающий Postfix и несколько дополнительных программ, которые можно использовать для тестирования отправки электронной почты.

      Вначале обновите базу данных пакетов:

      Затем выполните установку Postfix, запустив следующую команду:

      • sudo apt install mailutils

      Перед окончанием установки вы увидите окно настройки конфигурации Postfix:

      Выберите в меню пункт Internet Site (Сайт), нажмите TAB для выбора<Ok>, а затем нажмите ENTER

      По умолчанию используется опция Internet Site (Сайт). Это наиболее подходящая опция для нашего случая, поэтому нажмите TAB, а затем нажмите ENTER. Если вы увидите только текст описания, нажмите TAB для выбора пункта OK, а затем нажмите ENTER.

      Если опция не отображается автоматически, запустите следующую команду:

      • sudo dpkg-reconfigure postfix

      После этого откроется еще один диалог настройки конфигурации System mail name (имя системной почты):

      Введите свое доменное имя, а затем нажмите TAB для выбора<Ok>, ENTER

      Имя системной почты System mail name должно совпадать с именем, которое вы присвоили своему серверу при его создании. После завершения настройки нажмите TAB, а затем нажмите ENTER.

      Мы установили Postfix и готовы приступить к настройке.

      Шаг 2 — Настройка Postfix

      На этом шаге мы настроим Postfix для отправки и приема электронных писем только с сервера, на котором он запущен, т. е. с localhost.

      Для этого нужно настроить Postfix для прослушивания только интерфейса виртуальной сети loopback, который сервер использует для внутренней связи. Для внесения изменений потребуется отредактировать главный файл конфигурации Postfix с именем main.cf, хранящийся в каталоге etc/postfix.

      Откройте его для редактирования в предпочитаемом текстовом редакторе:

      • sudo nano /etc/postfix/main.cf

      Найдите следующие строки:

      /etc/postfix/main.cf

      . . .
      mailbox_size_limit = 0
      recipient_delimiter = +
      inet_interfaces = all
      . . .
      

      Задайте для параметра inet_interfaces значение loopback-only:

      /etc/postfix/main.cf

      . . .
      mailbox_size_limit = 0
      recipient_delimiter = +
      inet_interfaces = loopback-only
      . . .
      

      Также необходимо изменить директиву mydestination, которая указывает список доменов, почта с которых доставляется через транспорт local_transport. По умолчанию значения выглядят примерно так:

      /etc/postfix/main.cf

      . . .
      mydestination = $myhostname, your_domain, localhost.com, , localhost
      . . .
      

      Измените строку, чтобы она выглядела следующим образом:

      /etc/postfix/main.cf

      . . .
      mydestination = localhost.$mydomain, localhost, $myhostname
      . . .
      

      Если ваш домен фактически является субдоменом, и вы хотите, чтобы сообщения электронной почты выглядели отправленными с главного домена, вы можете добавить следующую строку в конец файла main.cf:

      /etc/postfix/main.cf

      ...
      masquerade_domains = your_main_domain
      

      Необязательный параметр masquerade_domains указывает, для каких доменов в адресе электронной почты будут урезаться субдомены.

      После внесения изменений сохраните и закройте файл.

      Примечание. В случае хостинга нескольких доменов на одном сервере другие домены также можно передать Postfix с помощью директивы mydestination.

      Затем перезапустите Postfix, выполнив следующую команду:

      • sudo systemctl restart postfix

      Вы настроили Postfix только для отправки почты с вашего сервера. Теперь протестируем настройку, отправив тестовое сообщение на адрес электронной почты.

      Шаг 3 — Тестирование сервера SMTP

      На этом шаге мы проверим, может ли Postfix отправлять электронные сообщения на внешний адрес электронной почты, используя команду mail, которая входит в пакет mailutils, установленный на первом шаге.

      Выполните следующую команду для отправки тестового письма:

      • echo "This is the body of the email" | mail -s "This is the subject line" your_email_address

      Вы можете изменить тело и тему письма по своему желанию. Обязательно замените your_email_address действующим адресом электронной почты, к которому у вас есть доступ.

      Проверьте почтовый ящик, на адрес которого вы отправили сообщение. Вы должны увидеть это сообщение в папке «Входящие». Если его там нет, проверьте папку «Нежелательная почта». Сейчас все электронные письма отправляются без шифрования, и поэтому провайдеры могут посчитать их спамом. Шифрование мы настроим немного позднее, на шаге 5.

      Если при выполнении команды mail появится сообщение об ошибке, или вы не получите письмо в течение длительного времени, проверьте правильность измененной вами конфигурации Postfix и соответствие имени сервера и имени хоста вашему домену.

      С этой конфигурацией адрес в поле «От» в отправляемых тестовых письмах будет иметь вид your_user_name@your_domain, где your_user_name — имя пользователя сервера, от лица которого вы запустили команду.

      Вы отправили с сервера электронное письмо и убедились, что оно успешно получено. На следующем шаге мы настроим переадресацию электронной почты для пользователя root.

      Шаг 4 — Переадресация системной почты

      На этом шаге мы настроим переадресацию электронной почты для пользователя root, чтобы сгенерированные системой сообщения, отправляемые на этот адрес, пересылались на внешний адрес электронной почты.

      Файл /etc/aliases содержит список альтернативных имен получателей электронных писем. Откройте его для редактирования:

      По умолчанию он выглядит так:

      /etc/aliases

      # See man 5 aliases for format
      postmaster:    root
      

      Единственная содержащаяся в нем директива предписывает пересылать сгенерированные системой электронные сообщения пользователю root.

      Добавьте в конец файла следующую строку:

      /etc/aliases

      ...
      root:          your_email_address
      

      Эта строка предписывает пересылать электронные письма, отправленные пользователю root, на указанный адрес электронной почты. Обязательно замените your_email_address своим личным адресом электронной почты. После внесения изменений сохраните и закройте файл.

      Для вступления изменений в силу выполните следующую команду:

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

      Протестируйте отправку электронных писем пользователю root с помощью следующей команды:

      • echo "This is the body of the email" | mail -s "This is the subject line" root

      Письмо должно прийти на указанный вами почтовый ящик. Если его там нет, проверьте папку «Нежелательная почта».

      На этом шаге мы настроили переадресацию сгенерированных системой сообщений на ваш адрес электронной почты. Теперь мы можем включить шифрование сообщений, чтобы все отправляемые вашим сервером электронные письма были защищены от модификации во время пересылки и считались легитимными.

      Шаг 5 — Включение шифрования SMTP

      Теперь вы можете включить шифрование SMTP, запросив бесплатный сертификат TLS от Let’s Encrypt для вашего домена (с помощью Certbot) и настроив Postfix для использования этого сертификата при отправке сообщений.

      Certbot содержится в репозиториях пакетов Ubuntu по умолчанию, и вы можете установить его с помощью следующей команды:

      В диалоге подтверждения введите Y и нажмите ENTER.

      При начальной настройке сервера вы установили простой брандмауэр ufw. Вам нужно настроить его, чтобы разрешить трафик через порт HTTP 80, и тем самым завершить подтверждение вашего домена. Запустите следующую команду для его активации:

      Итоговый результат будет выглядеть следующим образом:

      Output

      Rule added Rule added (v6)

      Теперь порт открыт, и мы можем запустить Certbot для получения сертификата:

      • sudo certbot certonly --standalone --rsa-key-size 4096 --agree-tos --preferred-challenges http -d your_domain

      Эта команда предписывает Certbot выдавать сертификаты с размером ключа RSA 4096 бит, запустить временный отдельный веб-сервер (--standalone) для проверки и выполнить проверку через порт 80 (--preferred-challenges http). Перед запуском команды обязательно замените your_domain именем вашего домена и введите свой адрес электронной почты, когда система его запросит.

      Результат будет выглядеть примерно следующим образом:

      Output

      Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator standalone, Installer None Obtaining a new certificate Performing the following challenges: http-01 challenge for `your_domain` Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-07-11. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

      Как указано в примечаниях, ваши сертификат и файл закрытого ключа сохранены в каталоге /etc/letsencrypt/live/your_domain.

      Получив сертификат, откройте файл main.cf для редактирования:

      • sudo nano /etc/postfix/main.cf

      Найдите следующий раздел:

      /etc/postfix/main.cf

      # TLS parameters
      smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
      smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
      smtpd_tls_security_level=may
      
      smtp_tls_CApath=/etc/ssl/certs
      smtp_tls_security_level=may
      smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
      

      Замените your_domain именем вашего домена, где это необходимо. Ваши настройки TLS для Postfix будут обновлены:

      /etc/postfix/main.cf

      # TLS parameters
      smtpd_tls_cert_file=/etc/letsencrypt/live/your_domain/fullchain.pem
      smtpd_tls_key_file=/etc/letsencrypt/live/your_domain/privkey.pem
      smtpd_tls_security_level=may
      
      smtp_tls_CApath=/etc/ssl/certs
      smtp_tls_security_level=may
      smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
      

      Закончив, сохраните и закройте файл.

      Перезапустите Postfix, чтобы применить изменения:

      • sudo systemctl restart postfix

      Попробуйте отправить электронное письмо еще раз:

      • echo "This is the body of an encrypted email" | mail -s "This is the subject line" your_email_address

      Проверьте указанный вами адрес электронной почты. Возможно вы сразу же увидите сообщение в папке «Входящие», поскольку провайдеры с меньшей вероятностью помечают шифрованные сообщения как спам.

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

      Заключение

      Теперь у вас имеется сервер электронной почты, предназначенный только для отправки и работающий на базе Postfix. Благодаря шифрованию всех исходящих сообщений мы снизили вероятность того, что провайдеры сразу же пометят ваши сообщения как спам. Если вы делаете это при разработке, данной меры должно быть достаточно.

      Если вы намереваетесь отправлять электронные письма потенциальным пользователям сайта (например, письма для подтверждения регистрации на форуме), попробуйте настроить записи SPF, чтобы электронные письма с вашего сервера выглядели еще более легитимными.



      Source link

      Как использовать субпроцесс для запуска внешних программ в Python 3


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Python 3 включает модуль subprocess для запуска внешних программ и чтения их выводов в коде Python.

      Модуль subprocess может оказаться вам полезен, если вы хотите использовать на своем компьютере другую программу из вашего кода Python. Например, вы можете запустить git из своего кода Python для извлечения в ваш проект, отслеживаемый в системе управления версиями git. Поскольку с помощью модуля subprocess можно контролировать любую программу, доступную на вашем компьютере, приведенные здесь примеры применимы к любым внешним программам, которые вы можете запустить из своего кода Python.

      subprocess содержит несколько классов и функций, но в этом обучающем модуле мы расскажем об одной из самых полезных функций модуля subprocess: subprocess.run. Мы изучим разные схемы его применения и основные аргументы ключевых слов.

      Предварительные требования

      Чтобы получить максимум знаний из этого обучающего модуля, рекомендуется иметь небольшое представление о программировании на Python 3. Дополнительную информацию можно найти в следующих обучающих руководствах:

      Запуск внешней программы

      Теперь вы можете использовать функцию subprocess.run для запуска внешней программы с кода Python. Прежде всего необходимо импортировать в программу модули subprocess и sys:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "print('ocean')"])
      

      Если вы запустите их, вы увидите следующий вывод:

      Output

      ocean

      Рассмотрим этот пример:

      • sys.executable — абсолютный путь к исполняемому файлу Python, из которого изначально была запущена ваша программа. Например, sys.executable может быть путем, таким как /usr/local/bin/python.
      • subprocess.run получает список строк, состоящих из компонентов команды, которую мы пытаемся запустить. Поскольку первой строкой мы передаем sys.executable, мы предписываем subprocess.run запустить новую программу Python.
      • Компонент -c — это опция командной строки python, позволяющая передать строку с целой программой Python для исполнения. В нашем случае мы передаем программу, которая распечатывает строку ocean.

      Вы можете представить каждую запись в списке, отправляемом в subprocess.run, как отделенную пробелом. Например, [sys.executable, "-c", "print('ocean')"] примерно эквивалентно /usr/local/bin/python -c "print('ocean')". Обратите внимание, что subprocess автоматически заключает в кавычки компоненты команды перед их запуском в операционной системе, так что вы можете передать имя файла с пробелами.

      Предупреждение. Никогда не передавайте в subprocess.run строки из ненадежных источников. Поскольку subprocess.run может выполнять на компьютере произвольные команды, злоумышленники могут использовать его для манипуляции компьютером непредвиденным образом.

      Захват вывода внешней программы

      Теперь мы можем вызывать внешнюю программу subprocess.run. Далее мы покажем, как захватывать вывод этой программы. Например, этот процесс может быть полезным, если бы мы хотели использовать файлы git ls для вывода всех файлов, хранящихся в системе контроля версий.

      Примечание. Для примеров, показанных в этом разделе, требуется версия Python 3.7 или выше. В частности, в версию Python 3.7, выпущенную в июне 2018 г. были добавлены аргументы capture_output и ключевое слово text.

      Дополним наш предыдущий пример:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      stdout: ocean stderr:

      Этот пример в целом аналогичен рассмотренному в первом разделе, и мы все еще запускаем субпроцесс для печати ocean. Однако теперь мы передаем аргументы ключевых слов capture_output=True и text=True в subprocess.run.

      subprocess.run возвращает объект subprocess.CompletedProcess, который привязан к результату. Объект subprocess.CompletedProcess содержит детали о коде выхода внешней программы и ее выводе. Благодаря capture_output=True в result.stdout и result.stderr вносится соответствующий вывод внешней программы. По умолчанию result.stdout и result.stderr привязаны как байты, но аргумент ключевого слова text=True предписывает Python декодировать байты в строки.

      В разделе вывода stdout — это ocean (плюс новая строка в конце, где print добавляет явность), и у нас нет stderr.

      Рассмотрим пример, где значение stderr не пустое:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

      Этот код запускает субпроцесс Python, который немедленно выдает ошибку ValueError. При просмотре окончательного результата мы не видим ничего в stdout и видим результат Traceback ошибки ValueError в stderr. Это связано с тем, что по умолчанию Python записывает Traceback необработанного исключения в stderr.

      Создание исключения при коде ошибки выхода

      Иногда бывает полезно создать исключение, если при выходе из работающей программы выдается код ошибки выхода. Программы, выдающие нулевой код ошибки при выходе считаются успешными, однако при ненулевом коде ошибки выхода считается, что в программе произошла ошибка. Например, данная система может быть полезной, если нам нужно выдавать исключение при запуске файлов git ls в каталоге, не являющемся репозиторием git.

      Мы можем использовать аргумент ключевого слова check=True, чтобы subprocess.run выдавал исключение в случае возврата внешней программой ненулевого кода ошибки выхода:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Этот вывод показывает, что мы запустили субпроцесс, выдавший ошибку, которая выводится в stderr на нашем терминале. Затем subprocess.run добросовестно выдал ошибку subprocess.CalledProcessError от нашего имени в нашей основной программе Python.

      Также модуль subprocess включает метод subprocess.CompletedProcess.check_returncode, который можно использовать с похожим результатом:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
      result.check_returncode()
      

      Если мы запустим этот код, мы получим:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Поскольку мы не передали check=True в subprocess.run, мы успешно привязали экземпляр subprocess.CompletedProcess к результату, хотя наша программа выполнила выход с ненулевым кодом ошибки. При этом при вызове result.check_returncode() выдается ошибка subprocess.CalledProcessError, поскольку модуль обнаруживает код ошибки выхода для завершенного процесса.

      Использование timeout для раннего выхода из программ

      subprocess.run имеет аргумент timeout, позволяющий остановить внешнюю программу, если ее выполнение занимает слишком много времени:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

      Субпроцесс, который мы попытались запустить, использовал функцию time.sleep для сна в течение 2 секунд. Однако мы передали аргумент с ключевым словом timeout=1 в subprocess.run, чтобы закрыть наш субпроцесс по таймауту по прошествии 1 секунды. Это объясняет, почему при вызове subprocess.run в конечном итоге было выдано исключение subprocess.TimeoutExpired.

      Обратите внимание, что аргумент ключевого слова timeout для subprocess.run является приблизительным. Python постарается остановить субпроцесс после заданного в аргументе timeout количества секунд, но это не обязательно будет точным.

      Передача ввода в программы

      Иногда программы ожидают передачи ввода через stdin.

      Аргумент ключевого слова input для subprocess.run позволяет передавать данные в stdin субпроцесса. Например:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
      )
      

      Запустив этот код, мы получим примерно следующий вывод:

      Output

      underwater

      В данном случае мы передали байты underwater в input. Наш целевой субпроцесс использовал sys.stdin для чтения переданного в stdin аргумента (underwater) и вывел его в результатах.

      Аргумент ключевого слова input может быть полезен, если вы хотите создать цепочку из нескольких вызовов subprocess.run с передачей вывода одной программы как ввода в другую программу.

      Заключение

      Модуль subprocess — это мощный компонент стандартной библиотеки Python, позволяющий легко запускать внешние программы и просматривать их вывод. В этом обучающем модуле вы научились использовать subprocess.run для управления внешними программами, передачи ввода в эти программы, анализа их вывода и проверки возвращаемых ими кодов.

      Модуль subprocess представляет дополнительные классы и утилиты, которые мы не охватили в этом обучающем модуле. Изучив основы, вы можете использовать документацию модуля subprocess для получения дополнительной информации о других имеющихся классах и утилитах.



      Source link