@ -22,8 +22,7 @@ short_description: Generate OpenSSL Certificate Signing Request (CSR)
description :
description :
- " This module allows one to (re)generate OpenSSL certificate signing requests.
- " This module allows one to (re)generate OpenSSL certificate signing requests.
It uses the pyOpenSSL python library to interact with openssl . This module supports
It uses the pyOpenSSL python library to interact with openssl . This module supports
the subjectAltName as well as the keyUsage and extendedKeyUsage extensions .
the subjectAltName as well as the keyUsage and extendedKeyUsage extensions . "
Note : At least one of common_name or subject_alt_name must be specified . "
requirements :
requirements :
- " python-pyOpenSSL >= 0.15 "
- " python-pyOpenSSL >= 0.15 "
options :
options :
@ -61,6 +60,12 @@ options:
required : true
required : true
description :
description :
- Name of the folder in which the generated OpenSSL certificate signing request will be written
- Name of the folder in which the generated OpenSSL certificate signing request will be written
subject :
required : false
description :
- Key / value pairs that will be present in the subject name field of the certificate signing request .
- If you need to specify more than one value with the same key , use a list as value .
version_added : ' 2.5 '
country_name :
country_name :
required : false
required : false
aliases : [ ' C ' , ' countryName ' ]
aliases : [ ' C ' , ' countryName ' ]
@ -214,10 +219,10 @@ filename:
type : string
type : string
sample : / etc / ssl / csr / www . ansible . com . csr
sample : / etc / ssl / csr / www . ansible . com . csr
subject :
subject :
description : A dictionnary of the subject attached to the CSR
description : A list of the subject tuples attached to the CSR
returned : changed or success
returned : changed or success
type : list
type : list
sample : { ' CN ' : ' www.ansible.com ' , ' O ' : ' Ansible ' }
sample : " [( ' CN ' , ' www.ansible.com ' ), ( ' O ' , ' Ansible ' )] "
subjectAltName :
subjectAltName :
description : The alternative names this CSR is valid for
description : The alternative names this CSR is valid for
returned : changed or success
returned : changed or success
@ -283,20 +288,25 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
self . request = None
self . request = None
self . privatekey = None
self . privatekey = None
self . subject = {
self . subject = [
' C ' : module . params [ ' countryName ' ] ,
( ' C ' , module . params [ ' countryName ' ] ) ,
' ST ' : module . params [ ' stateOrProvinceName ' ] ,
( ' ST ' , module . params [ ' stateOrProvinceName ' ] ) ,
' L ' : module . params [ ' localityName ' ] ,
( ' L ' , module . params [ ' localityName ' ] ) ,
' O ' : module . params [ ' organizationName ' ] ,
( ' O ' , module . params [ ' organizationName ' ] ) ,
' OU ' : module . params [ ' organizationalUnitName ' ] ,
( ' OU ' , module . params [ ' organizationalUnitName ' ] ) ,
' CN ' : module . params [ ' commonName ' ] ,
( ' CN ' , module . params [ ' commonName ' ] ) ,
' emailAddress ' : module . params [ ' emailAddress ' ] ,
( ' emailAddress ' , module . params [ ' emailAddress ' ] ) ,
}
]
if not self . subjectAltName :
if module . params [ ' subject ' ] :
self . subjectAltName = [ ' DNS: %s ' % self . subject [ ' CN ' ] ]
self . subject = self . subject + crypto_utils . parse_name_field ( module . params [ ' subject ' ] )
self . subject = [ ( entry [ 0 ] , entry [ 1 ] ) for entry in self . subject if entry [ 1 ] ]
self . subject = dict ( ( k , v ) for k , v in self . subject . items ( ) if v )
if not self . subjectAltName :
for sub in self . subject :
if OpenSSL . _util . lib . OBJ_txt2nid ( to_bytes ( sub [ 0 ] ) ) == 13 : # 13 is the NID for "commonName"
self . subjectAltName = [ ' DNS: %s ' % sub [ 1 ] ]
break
def generate ( self , module ) :
def generate ( self , module ) :
''' Generate the certificate signing request. '''
''' Generate the certificate signing request. '''
@ -305,12 +315,16 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
req = crypto . X509Req ( )
req = crypto . X509Req ( )
req . set_version ( self . version - 1 )
req . set_version ( self . version - 1 )
subject = req . get_subject ( )
subject = req . get_subject ( )
for ( key , value ) in self . subject . items ( ) :
for entry in self . subject :
if value is not None :
if entry [ 1 ] is not None :
setattr ( subject , key , value )
# Workaround for https://github.com/pyca/pyopenssl/issues/165
nid = OpenSSL . _util . lib . OBJ_txt2nid ( to_bytes ( entry [ 0 ] ) )
OpenSSL . _util . lib . X509_NAME_add_entry_by_NID ( subject . _name , nid , OpenSSL . _util . lib . MBSTRING_UTF8 , to_bytes ( entry [ 1 ] ) , - 1 , - 1 , 0 )
altnames = ' , ' . join ( self . subjectAltName )
extensions = [ ]
extensions = [ crypto . X509Extension ( b " subjectAltName " , self . subjectAltName_critical , altnames . encode ( ' ascii ' ) ) ]
if self . subjectAltName :
altnames = ' , ' . join ( self . subjectAltName )
extensions . append ( crypto . X509Extension ( b " subjectAltName " , self . subjectAltName_critical , altnames . encode ( ' ascii ' ) ) )
if self . keyUsage :
if self . keyUsage :
usages = ' , ' . join ( self . keyUsage )
usages = ' , ' . join ( self . keyUsage )
@ -324,7 +338,8 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
usages = ' , ' . join ( self . basicConstraints )
usages = ' , ' . join ( self . basicConstraints )
extensions . append ( crypto . X509Extension ( b " basicConstraints " , self . basicConstraints_critical , usages . encode ( ' ascii ' ) ) )
extensions . append ( crypto . X509Extension ( b " basicConstraints " , self . basicConstraints_critical , usages . encode ( ' ascii ' ) ) )
req . add_extensions ( extensions )
if extensions :
req . add_extensions ( extensions )
req . set_pubkey ( self . privatekey )
req . set_pubkey ( self . privatekey )
req . sign ( self . privatekey , self . digest )
req . sign ( self . privatekey , self . digest )
@ -350,10 +365,10 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
self . privatekey = crypto_utils . load_privatekey ( self . privatekey_path , self . privatekey_passphrase )
self . privatekey = crypto_utils . load_privatekey ( self . privatekey_path , self . privatekey_passphrase )
def _check_subject ( csr ) :
def _check_subject ( csr ) :
subject = csr . get_subject ( )
subject = [ ( OpenSSL . _util . lib . OBJ_txt2nid ( to_bytes ( sub [ 0 ] ) ) , to_bytes ( sub [ 1 ] ) ) for sub in self . subject ]
for ( key , value ) in self . subject . items ( ) :
current_subject = [ ( OpenSSL . _util . lib . OBJ_txt2nid ( to_bytes ( sub [ 0 ] ) ) , to_bytes ( sub [ 1 ] ) ) for sub in csr . get_subject ( ) . get_components ( ) ]
if getattr ( subject , key , None ) != value :
if not set ( subject ) == set ( current_subject ) :
return False
return False
return True
return True
@ -437,6 +452,7 @@ def main():
version = dict ( default = ' 1 ' , type = ' int ' ) ,
version = dict ( default = ' 1 ' , type = ' int ' ) ,
force = dict ( default = False , type = ' bool ' ) ,
force = dict ( default = False , type = ' bool ' ) ,
path = dict ( required = True , type = ' path ' ) ,
path = dict ( required = True , type = ' path ' ) ,
subject = dict ( type = ' dict ' ) ,
countryName = dict ( aliases = [ ' C ' , ' country_name ' ] , type = ' str ' ) ,
countryName = dict ( aliases = [ ' C ' , ' country_name ' ] , type = ' str ' ) ,
stateOrProvinceName = dict ( aliases = [ ' ST ' , ' state_or_province_name ' ] , type = ' str ' ) ,
stateOrProvinceName = dict ( aliases = [ ' ST ' , ' state_or_province_name ' ] , type = ' str ' ) ,
localityName = dict ( aliases = [ ' L ' , ' locality_name ' ] , type = ' str ' ) ,
localityName = dict ( aliases = [ ' L ' , ' locality_name ' ] , type = ' str ' ) ,
@ -455,7 +471,6 @@ def main():
) ,
) ,
add_file_common_args = True ,
add_file_common_args = True ,
supports_check_mode = True ,
supports_check_mode = True ,
required_one_of = [ [ ' commonName ' , ' subjectAltName ' ] ] ,
)
)
if not pyopenssl_found :
if not pyopenssl_found :