nova-cert顾名思义,是与证书有关的服务,用于为上传镜像生成X509证书,只在EC2 API中才会被用到。
nova-cert作为rpc server运行,对外提供证书有关的RpcAPI,作用和CA类似。
由于对PKI了解的不是太多,因此下面可能有错漏,还望斧正
1.在服务开始运行之前,需要判断ca证书是否存在,不存在就需要使用openssl创建自己的ca证书和私钥,这些文件通常存放在配置项state_path下的CA目录里
def ensure_ca_filesystem():
# 确认CA目录的路径, 即配置项$state_path/CA
ca_dir = ca_folder()
# 如果CA证书不存在
if not os.path.exists(ca_path()):
# 确认genrootca.sh脚本的所在
genrootca_sh_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), 'CA', 'genrootca.sh'))
start = os.getcwd()
# 创建CA目录
fileutils.ensure_tree(ca_dir)
# 切换工作目录至CA目录
os.chdir(ca_dir)
# 通过sh执行genrootca.sh脚本来生成CA证书和私钥
utils.execute("sh", genrootca_sh_path)
# 恢复原来的工作目录
os.chdir(start)
class CertManager(manager.Manager):
target = messaging.Target(version='2.0')
def __init__(self, *args, **kwargs):
super(CertManager, self).__init__(service_name='cert',
*args, **kwargs)
def init_host(self):
# 服务启动之前, 需要保证CA证书、私钥以及存放它们和用户证书的目录存在
crypto.ensure_ca_filesystem()
2.之后, 就可以通过generate_x509_cert RpcAPI来为自己生成签名证书了
def _sign_csr(csr_text, ca_folder):
# 创建临时目录
with utils.tempdir() as tmpdir:
inbound = os.path.join(tmpdir, 'inbound.csr')
outbound = os.path.join(tmpdir, 'outbound.csr')
try:
with open(inbound, 'w') as csrfile:
# 将证书请求写入inbound.csr临时文件
csrfile.write(csr_text)
except IOError:
with excutils.save_and_reraise_exception():
LOG.exception(_('Failed to write inbound.csr'))
LOG.debug(_('Flags path: %s'), ca_folder)
start = os.getcwd()
# 确保CA目录存在, 然后切换工作目录到CA目录
fileutils.ensure_tree(ca_folder)
os.chdir(ca_folder)
# 通过inbound.csr证书请求文件生成outbound.csr签名证书文件;
# CA目录下的openssl.cnf是openssl的配置文件, 里面配置了新生成证书的存放目录, 默认是CA目录/newcerts;
# 所以该命令在生成outbound.csr证书之余, 还会在CA目录/newcerts存放一个副本, 副本名为"序列号.pem";
utils.execute('openssl', 'ca', '-batch', '-out', outbound, '-config',
'./openssl.cnf', '-infiles', inbound)
# 获取outbound.csr证书的序列号
out, _err = utils.execute('openssl', 'x509', '-in', outbound,
'-serial', '-noout')
serial = string.strip(out.rpartition('=')[2])
os.chdir(start)
with open(outbound, 'r') as crtfile:
# 返回序列号和证书内容
return (serial, crtfile.read())
def sign_csr(csr_text, project_id=None):
# 可以配置为每个project均有一个CA, 默认为False, 也即所有project共用一个CA;
# 这里通过project_id来确定CA目录;
# 最后通过_sign_csr方法生成自签名证书
if not CONF.use_project_ca:
project_id = None
if not project_id:
return _sign_csr(csr_text, ca_folder())
_ensure_project_folder(project_id)
project_folder = ca_folder(project_id)
return _sign_csr(csr_text, ca_folder(project_id))
# 为项目中的用户生成签名证书
def generate_x509_cert(user_id, project_id, bits=1024):
# 通过user_id,project_id来生成证书主题
subject = _user_cert_subject(user_id, project_id)
# 创建临时目录
with utils.tempdir() as tmpdir:
keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key'))
csrfile = os.path.abspath(os.path.join(tmpdir, 'temp.csr'))
# 调用openssl生成RSA私钥文件
utils.execute('openssl', 'genrsa', '-out', keyfile, str(bits))
# 调用openssl生成证书请求
utils.execute('openssl', 'req', '-new', '-key', keyfile, '-out',
csrfile, '-batch', '-subj', subject)
private_key = open(keyfile).read()
csr = open(csrfile).read()
# 通过证书请求来生成签名证书和序列号, project_id用于确认CA目录
(serial, signed_csr) = sign_csr(csr, project_id)
# 用于确定证书的存放路径
fname = os.path.join(ca_folder(project_id), 'newcerts/%s.pem' % serial)
cert = {'user_id': user_id,
'project_id': project_id,
'file_name': fname}
# 将证书的相关信息譬如user_id、project_id和file_name等存入数据库
db.certificate_create(context.get_admin_context(), cert)
# 返回用户私钥和签名证书内容
return (private_key, signed_csr)