@ -179,11 +179,13 @@ import sys
import tempfile
import time
from urllib . parse import urlencode
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . common . file import S_IRWU_RG_RO as DEFAULT_SOURCES_PERM
from ansible . module_utils . common . respawn import has_respawned , probe_interpreters_for_module , respawn_module
from ansible . module_utils . common . text . converters import to_native
from ansible . module_utils . urls import fetch_ url
from ansible . module_utils . urls import fetch_ file, fetch_ url
from ansible . module_utils . common . locale import get_best_parsable_locale
@ -472,10 +474,9 @@ class UbuntuSourcesList(SourcesList):
self . codename = module . params [ ' codename ' ] or distro . codename
super ( UbuntuSourcesList , self ) . __init__ ( module )
self . apt_key_bin = self . module . get_bin_path ( ' apt-key ' , required = False )
self . gpg_bin = self . module . get_bin_path ( ' gpg ' , required = False )
if not self . apt_key_bin and not self . gpg_bin:
self . module . fail_json ( msg = ' Either apt-key or gpg binary is required, but neither could be found' )
if not self . gpg_bin:
self . module . fail_json ( msg = ' gpg binary is required, but this could be found' )
def __deepcopy__ ( self , memo = None ) :
return UbuntuSourcesList ( self . module )
@ -497,25 +498,13 @@ class UbuntuSourcesList(SourcesList):
except IndexError :
ppa_name = ' ppa '
line = ' deb %s / %s / %s /ubuntu %s main ' % ( self . PPA_URI , ppa_owner , ppa_name , self . codename )
keypath = self . _get_ppa_keyfile ( " ubuntu- %s -main " % self . codename , ppa_owner , ppa_name )
line = ' deb [signed-by= %s ] %s / %s / %s /ubuntu %s main ' % ( keypath , self . PPA_URI , ppa_owner , ppa_name , self . codename )
return line , ppa_owner , ppa_name
def _key_already_exists ( self , key_fingerprint ) :
if self . apt_key_bin :
locale = get_best_parsable_locale ( self . module )
APT_ENV = dict ( LANG = locale , LC_ALL = locale , LC_MESSAGES = locale , LC_CTYPE = locale , LANGUAGE = locale )
self . module . run_command_environ_update = APT_ENV
rc , out , err = self . module . run_command ( [ self . apt_key_bin , ' export ' , key_fingerprint ] , check_rc = True )
found = bool ( not err or ' nothing exported ' not in err )
else :
found = self . _gpg_key_exists ( key_fingerprint )
return found
def _gpg_key_exists ( self , key_fingerprint ) :
found = False
def _existing_gpg_filepath ( self , key_fingerprint ) :
""" Returns the path to an existing GPG key, or " " if one cannot be found """
found_keyfile = " "
keyfiles = [ ' /etc/apt/trusted.gpg ' ] # main gpg repo for apt
for other_dir in APT_KEY_DIRS :
# add other known sources of gpg sigs for apt, skip hidden files
@ -531,10 +520,37 @@ class UbuntuSourcesList(SourcesList):
continue
if key_fingerprint in out :
found = Tru e
found _keyfile = key_fil e
break
return found
return found_keyfile
def _retreive_gpg_key ( self , keyfile , info ) :
# TODO: report file that would have been added if not check_mode
if not self . module . check_mode :
# use first available key dir, in order of preference
keyserver_url = " https://keyserver.ubuntu.com/pks/lookup? {} " . format (
urlencode ( { " op " : " get " , " search " : " 0x " + info [ ' signing_key_fingerprint ' ] } )
)
armored_keyfile = fetch_file ( self . module , keyserver_url )
command = [ self . gpg_bin , ' --no-tty ' , ' -o ' , keyfile , ' --dearmor ' , armored_keyfile ]
rc , stdout , stderr = self . module . run_command ( command , check_rc = True , encoding = None )
if rc != 0 or len ( stderr ) != 0 :
self . module . fail_json ( msg = ' Unable to get required signing key ' , rc = rc , stderr = stderr , command = command )
self . module . log ( ' Added repo key " %s " for apt to file " %s " ' % ( info [ ' signing_key_fingerprint ' ] , keyfile ) )
def _get_ppa_keyfile ( self , source , ppa_owner , ppa_name ) :
for keydir in APT_KEY_DIRS :
if os . path . exists ( keydir ) :
break
else :
self . module . fail_json ( " Unable to find any existing apt gpgp repo directories, tried the following: %s " % ' , ' . join ( APT_KEY_DIRS ) )
return ' %s / %s - %s - %s .gpg ' % ( keydir , os . path . basename ( source ) . replace ( ' ' , ' - ' ) , ppa_owner , ppa_name )
# https://www.linuxuprising.com/2021/01/apt-key-is-deprecated-how-to-add.html
def add_source ( self , line , comment = ' ' , file = None ) :
@ -548,37 +564,16 @@ class UbuntuSourcesList(SourcesList):
info = self . _get_ppa_info ( ppa_owner , ppa_name )
# add gpg sig if needed
if not self . _key_already_exists ( info [ ' signing_key_fingerprint ' ] ) :
# TODO: report file that would have been added if not check_mode
keyfile = ' '
if not self . module . check_mode :
if self . apt_key_bin :
command = [ self . apt_key_bin , ' adv ' , ' --recv-keys ' , ' --no-tty ' , ' --keyserver ' , ' hkps://keyserver.ubuntu.com:443 ' ,
info [ ' signing_key_fingerprint ' ] ]
else :
# use first available key dir, in order of preference
for keydir in APT_KEY_DIRS :
if os . path . exists ( keydir ) :
break
else :
self . module . fail_json ( " Unable to find any existing apt gpgp repo directories, tried the following: %s " % ' , ' . join ( APT_KEY_DIRS ) )
keyfile = ' %s / %s - %s - %s .gpg ' % ( keydir , os . path . basename ( source ) . replace ( ' ' , ' - ' ) , ppa_owner , ppa_name )
command = [ self . gpg_bin , ' --no-tty ' , ' --keyserver ' , ' hkps://keyserver.ubuntu.com:443 ' , ' --export ' , info [ ' signing_key_fingerprint ' ] ]
rc , stdout , stderr = self . module . run_command ( command , check_rc = True , encoding = None )
if keyfile :
# using gpg we must write keyfile ourselves
if len ( stdout ) == 0 :
self . module . fail_json ( msg = ' Unable to get required signing key ' , rc = rc , stderr = stderr , command = command )
try :
with open ( keyfile , ' wb ' ) as f :
f . write ( stdout )
self . module . log ( ' Added repo key " %s " for apt to file " %s " ' % ( info [ ' signing_key_fingerprint ' ] , keyfile ) )
except ( OSError , IOError ) as e :
self . module . fail_json ( msg = ' Unable to add required signing key for %s ' , rc = rc , stderr = stderr , error = to_native ( e ) )
existing_keyfile = self . _existing_gpg_filepath ( info [ " signing_key_fingerprint " ] )
if not existing_keyfile :
for keydir in APT_KEY_DIRS :
if os . path . exists ( keydir ) :
break
else :
self . module . fail_json ( " Unable to find any existing apt gpgp repo directories, tried the following: %s " % ' , ' . join ( APT_KEY_DIRS ) )
keyfile = self . _get_ppa_keyfile ( source , ppa_owner , ppa_name )
self . _retreive_gpg_key ( keyfile , info )
# apt source file
file = file or self . _suggest_filename ( ' %s _ %s ' % ( line , self . codename ) )
else :