Я пытаюсь создать безопасный (например, SSL/HTTPS) сервер XML-RPC-клиента. Часть клиент-сервер работает отлично, когда требуемые сертификаты присутствуют в моей системе; Однако, когда я пытаюсь создать сертификаты во время выполнения, я получаю FileNotFoundError при открытии Ssl обернутого гнезда, даже если сертификаты явно присутствуют (поскольку предшествующая функция создала их.)FileNotFoundError Когда файл существует (при создании в текущем скрипте)
Почему FileNotFoundError, если файлы присутствуют? (Если я просто закрываю и перезапускает скрипт python, при открытии сокета не возникает ошибки, и все работает без каких-либо проблем.)
Я искал в другом месте решения, но самый лучший/ближайший ответ, который я нашел, , возможно, «условия гонки» между созданием сертификатов и их открытием. Тем не менее, я попытался добавить «сон», чтобы облегчить возможность гонки (а также запустить каждую функцию отдельно через меню ввода пользователя) с одинаковой ошибкой каждый раз.
Что мне не хватает?
Вот отрывок из моего кода:
import os
import threading
import ssl
from xmlrpc.server import SimpleXMLRPCServer
import certs.gencert as gencert # <---- My python module for generating certs
...
rootDomain = "mydomain"
CERTFILE = "certs/mydomain.cert"
KEYFILE = "certs/mydomain.key"
...
def listenNow(ipAdd, portNum, serverCert, serverKey):
# Create XMLRPC Server, based on ipAdd/port received
server = SimpleXMLRPCServer((ipAdd, portNum))
# **THIS** is what causes the FileNotFoundError ONLY if
# the certificates are created during THE SAME execution
# of the program.
server.socket = ssl.wrap_socket(server.socket,
certfile=serverCert,
keyfile=serverKey,
do_handshake_on_connect=True,
server_side=True)
...
# Start server listening [forever]
server.serve_forever()
...
# Verify Certificates are present; if not present,
# create new certificates
def verifyCerts():
# If cert or key file not present, create new certs
if not os.path.isfile(CERTFILE) or not os.path.isfile(KEYFILE):
# NOTE: This [genert] will create certificates matching
# the file names listed in CERTFILE and KEYFILE at the top
gencert.gencert(rootDomain)
print("Certfile(s) NOT present; new certs created.")
else:
print("Certfiles Verified Present")
# Start a thread to run server connection as a daemon
def startServer(hostIP, serverPort):
# Verify certificates present prior to starting server
verifyCerts()
# Now, start thread
t = threading.Thread(name="ServerDaemon",
target=listenNow,
args=(hostIP,
serverPort,
CERTFILE,
KEYFILE
)
)
t.daemon = True
t.start()
if __name__ == '__main__':
startServer("127.0.0.1", 12345)
time.sleep(60) # <--To allow me to connect w/client before closing
Когда я бегу выше, с NO сертификатов, присутствующих, это ошибка я получаю:
$ python3 test.py
Certfile(s) NOT present; new certs created.
Exception in thread ServerDaemon:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "test.py", line 41, in listenNow
server_side=True)
File "/usr/lib/python3.5/ssl.py", line 1069, in wrap_socket
ciphers=ciphers)
File "/usr/lib/python3.5/ssl.py", line 691, in __init__
self._context.load_cert_chain(certfile, keyfile)
FileNotFoundError: [Errno 2] No such file or directory
Когда я просто повторно запустить сценарий второй раз (т. е. файлы сертификатов уже присутствуют при запуске, все работает так, как ожидалось, с ошибками NO, и я могу просто подключить моего клиента.
$ python3 test.py
Certfiles Verified Present
Что мешает функции ssl.wrap_socket видеть/получать доступ к только что созданным файлам (и, таким образом, создавать исключение FileNotFoundError)?
EDIT 1: Спасибо за комментарии Джон Гордон. Вот копия gencert.py, любезно Атулу Varm, здесь https://gist.github.com/toolness/3073310
import os
import sys
import hashlib
import subprocess
import datetime
OPENSSL_CONFIG_TEMPLATE = """
prompt = no
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
C = US
ST = IL
L = Chicago
O = Toolness
OU = Experimental Software Authority
CN = %(domain)s
emailAddress = [email protected]
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = %(domain)s
DNS.2 = *.%(domain)s
"""
MYDIR = os.path.abspath(os.path.dirname(__file__))
OPENSSL = '/usr/bin/openssl'
KEY_SIZE = 1024
DAYS = 3650
CA_CERT = 'ca.cert'
CA_KEY = 'ca.key'
# Extra X509 args. Consider using e.g. ('-passin', 'pass:blah') if your
# CA password is 'blah'. For more information, see:
#
# http://www.openssl.org/docs/apps/openssl.html#PASS_PHRASE_ARGUMENTS
X509_EXTRA_ARGS =()
def openssl(*args):
cmdline = [OPENSSL] + list(args)
subprocess.check_call(cmdline)
def gencert(domain, rootdir=MYDIR, keysize=KEY_SIZE, days=DAYS,
ca_cert=CA_CERT, ca_key=CA_KEY):
def dfile(ext):
return os.path.join('domains', '%s.%s' % (domain, ext))
os.chdir(rootdir)
if not os.path.exists('domains'):
os.mkdir('domains')
if not os.path.exists(dfile('key')):
openssl('genrsa', '-out', dfile('key'), str(keysize))
# EDIT 3: mydomain.key gets output here during execution
config = open(dfile('config'), 'w')
config.write(OPENSSL_CONFIG_TEMPLATE % {'domain': domain})
config.close()
# EDIT 3: mydomain.config gets output here during execution
openssl('req', '-new', '-key', dfile('key'), '-out', dfile('request'),
'-config', dfile('config'))
# EDIT 3: mydomain.request gets output here during execution
openssl('x509', '-req', '-days', str(days), '-in', dfile('request'),
'-CA', ca_cert, '-CAkey', ca_key,
'-set_serial',
'0x%s' % hashlib.md5(domain +
str(datetime.datetime.now())).hexdigest(),
'-out', dfile('cert'),
'-extensions', 'v3_req', '-extfile', dfile('config'),
*X509_EXTRA_ARGS)
# EDIT 3: mydomain.cert gets output here during execution
print "Done. The private key is at %s, the cert is at %s, and the " \
"CA cert is at %s." % (dfile('key'), dfile('cert'), ca_cert)
if __name__ == "__main__":
if len(sys.argv) < 2:
print "usage: %s <domain-name>" % sys.argv[0]
sys.exit(1)
gencert(sys.argv[1])
EDIT 2: Что касается комментария Джона, «это может означать, что эти файлы создаются, но не в каталоге [ I] ожидать «:
Когда у меня есть каталог, открытый в другом окне, я вижу, что файлы появляются в правильном месте во время выполнения. Кроме того, при запуске скрипта test.py
второй раз без изменений файлы идентифицируются как присутствующие в правильном (одинаковом) месте. Это заставляет меня думать, что расположение файлов не является проблемой. Спасибо за предложение. Я буду продолжать смотреть.
EDIT 3: Я прошел через программу gencert.py, и каждый из файлов правильно выводится в нужное время во время выполнения. Я указал, когда именно они были выведены в указанном выше файле с меткой «EDIT 3»
Хотя gencert приостановлен в ожидании моего ввода (raw_input), я могу без проблем открывать/просматривать/редактировать указанные файлы в другой программе.
Кроме того, с запущенным экземпляром test.py (приостановлено, ожидая ввода пользователя, сразу после mydomain.cert появляется), я могу запустить второй экземпляр test.py в другом терминале, и он видит/использует файлы просто отлично.
В первом случае, однако, если я продолжу программу, она выведет «FileNotFoundError».
Вы не показали нам нужен код для 'gencert', поэтому возможно, что функция просто не делает то, что она должна. Как очень простая проверка, попробуйте добавить еще один, если не os.path.isfile (...)' call inside' verifyCerts() ', _after_ вызов на 'gencert()'. –
Делает ли 'gencert()' _close_ файлы после их создания? Если нет, это может объяснить поведение, которое вы видите, - файлы, возможно, не будут записаны на диск до тех пор, пока программа не будет существовать. –
Спасибо за комментарии, @John Вот ссылка на функцию gencert, которую я использовал, о которой я расскажу более подробно и посмотрю, решает ли она мою проблему: https://gist.github.com/toolness/3073310 – JLR