Package backend :: Package satellite_tools :: Module rhn_satellite_activate
[hide private]
[frames] | no frames]

Source Code for Module backend.satellite_tools.rhn_satellite_activate

  1  # 
  2  # Copyright (c) 2008--2017 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  # language imports 
 17  import os 
 18  import sys 
 19  import time 
 20  import tempfile 
 21  import re 
 22  from optparse import Option, OptionParser 
 23  from M2Crypto import X509 
 24   
 25  from rhn.connections import idn_ascii_to_puny 
 26  # Check if python-rhsm is installed 
 27  try: 
 28      from rhsm.config import RhsmConfigParser 
 29  except ImportError: 
 30      RhsmConfigParser = None 
 31   
 32  # common, server imports 
 33  from spacewalk.common import fileutils, rhnLog 
 34  from spacewalk.common.rhnConfig import CFG, initCFG, PRODUCT_NAME 
 35  from spacewalk.common.rhnTranslate import _ 
 36  from spacewalk.server.rhnServer import satellite_cert 
 37  # Try to import cdn activation module if available 
 38  try: 
 39      from spacewalk.cdn_tools import activation as cdn_activation 
 40      from spacewalk.cdn_tools.manifest import MissingSatelliteCertificateError, ManifestValidationError,\ 
 41          IncorrectEntitlementsFileFormatError 
 42      from spacewalk.cdn_tools.common import CdnMappingsLoadError 
 43  except ImportError: 
 44      cdn_activation = None 
 45      MissingSatelliteCertificateError = None 
 46      ManifestValidationError = None 
 47      CdnMappingsLoadError = None 
 48  from spacewalk.satellite_tools.syncLib import log, log2disk, log2 
 49   
 50   
 51  DEFAULT_RHSM_MANIFEST_LOCATION = '/etc/sysconfig/rhn/rhsm-manifest.zip' 
 52  DEFAULT_WEBAPP_GPG_KEY_RING = "/etc/webapp-keyring.gpg" 
 53  DEFAULT_CONFIG_FILE = "/etc/rhn/rhn.conf" 
 54  DEFAULT_RHSM_CONFIG_FILE = "/etc/rhsm/rhsm.conf" 
 55  SUPPORTED_RHEL_VERSIONS = ['5', '6'] 
 56  LOG_PATH = '/var/log/rhn/activation.log' 
 57   
 58   
59 -def writeError(e):
60 log2(0, 0, '\nERROR: %s\n' % e, stream=sys.stderr, cleanYN=1)
61 62
63 -class CaCertInsertionError(Exception):
64 "raise when fail to insert CA cert into the local database"
65 66
67 -def getRHSMUuid():
68 """ Tries to get UUID of of this system if it's registered into Subscription manager.""" 69 70 if RhsmConfigParser and os.path.isfile(DEFAULT_RHSM_CONFIG_FILE): 71 cfg = RhsmConfigParser(config_file=DEFAULT_RHSM_CONFIG_FILE) 72 cert_dir = cfg.get('rhsm', 'consumerCertDir') 73 cert_path = os.path.join(cert_dir, 'cert.pem') 74 if os.path.isfile(cert_path): 75 f = open(cert_path, 'r') 76 cert = X509.load_cert_string(f.read()) 77 f.close() 78 subject = cert.get_subject() 79 return subject.CN 80 return None
81 82
83 -class RHNCertGeneralSanityException(Exception):
84 "general failure"
85 86
87 -def getCertChecksumString(sat_cert):
88 result = "" 89 tree = {} 90 91 # Scalar attributes of sat_cert 92 for field in sat_cert.fields_scalar: 93 tree[field] = getattr(sat_cert, field) 94 # List attributes of sat_cert 95 for name, value in sat_cert.fields_list.items(): 96 field = value.attribute_name 97 tree[name] = [] 98 for item in getattr(sat_cert, field): 99 attributes = {} 100 for k, v in item.attributes.items(): 101 attr = getattr(item, v) 102 if attr != "": 103 attributes[k] = attr 104 tree[name].append(attributes) 105 106 # Create string from tree 107 for key in sorted(tree): 108 if isinstance(tree[key], list): 109 for item in sorted(tree[key], key=lambda item: "".join(sorted(item.keys() + item.values()))): 110 line = "%s" % key 111 for attribute in sorted(item): 112 line += "-%s-%s" % (attribute, item[attribute]) 113 result += "%s\n" % line 114 else: 115 if tree[key] is not None: 116 result += "%s-%s\n" % (key, tree[key]) 117 118 return result
119 120
121 -def validateSatCert(cert):
122 """ validating (i.e., verifing sanity of) this product. 123 I.e., makes sure the product Certificate is a sane certificate 124 """ 125 126 sat_cert = satellite_cert.SatelliteCert() 127 sat_cert.load(cert) 128 129 for key in ['generation', 'product', 'owner', 'issued', 'expires', 'slots']: 130 if not getattr(sat_cert, key): 131 writeError("Your satellite certificate is not valid. Field %s is not defined.\n" 132 "Please contact your support representative." % key) 133 raise RHNCertGeneralSanityException("RHN Entitlement Certificate failed " 134 "to validate.") 135 136 signature = sat_cert.signature 137 138 # copy cert to temp location (it may be gzipped). 139 fd, certTmpFile = tempfile.mkstemp(prefix="/tmp/cert-") 140 fo = os.fdopen(fd, 'wb') 141 fo.write(getCertChecksumString(sat_cert)) 142 fo.flush() 143 fo.close() 144 145 fd, signatureTmpFile = tempfile.mkstemp(prefix="/tmp/cert-signature-") 146 fo = os.fdopen(fd, 'wb') 147 fo.write(signature) 148 fo.flush() 149 fo.close() 150 151 args = ['gpg', '--verify', '-q', '--keyring', 152 DEFAULT_WEBAPP_GPG_KEY_RING, signatureTmpFile, certTmpFile] 153 154 log(1, "Checking cert XML sanity and GPG signature: %s" % repr(' '.join(args))) 155 156 ret, out, err = fileutils.rhn_popen(args) 157 err = err.read() 158 out = out.read() 159 160 # nuke temp cert 161 os.unlink(certTmpFile) 162 os.unlink(signatureTmpFile) 163 164 if err.find('Ohhhh jeeee: ... this is a bug') != -1 or err.find('verify err') != -1 or ret: 165 msg = "%s Entitlement Certificate failed to validate.\n" % PRODUCT_NAME 166 msg += "MORE INFORMATION:\n" 167 msg = msg + " Return value: %s\n" % ret +\ 168 " Standard-out: %s\n" % out +\ 169 " Standard-error: %s" % err 170 writeError(msg) 171 raise RHNCertGeneralSanityException("RHN Entitlement Certificate failed " 172 "to validate.") 173 return 0
174 175
176 -def writeRhsmManifest(options, manifest):
177 if os.path.exists(DEFAULT_RHSM_MANIFEST_LOCATION): 178 fileutils.rotateFile(DEFAULT_RHSM_MANIFEST_LOCATION, depth=5) 179 fo = open(DEFAULT_RHSM_MANIFEST_LOCATION, 'w+b') 180 fo.write(manifest) 181 fo.close() 182 # Delete from temporary location 183 if options.manifest_refresh: 184 os.unlink(options.manifest) 185 options.manifest = DEFAULT_RHSM_MANIFEST_LOCATION
186 187
188 -def storeRhsmManifest(options):
189 """ storing of the RHSM manifest 190 writing to default storage location 191 """ 192 193 if options.manifest and options.manifest != DEFAULT_RHSM_MANIFEST_LOCATION: 194 try: 195 manifest = open(os.path.abspath(os.path.expanduser(options.manifest)), 'rb').read() 196 except (IOError, OSError), e: 197 msg = _('"%s" (specified in commandline)\n' 198 'could not be opened and read:\n%s') % (options.manifest, str(e)) 199 writeError(msg) 200 raise 201 try: 202 writeRhsmManifest(options, manifest) 203 except (IOError, OSError), e: 204 msg = _('"%s" could not be opened\nand/or written to:\n%s') % ( 205 DEFAULT_RHSM_MANIFEST_LOCATION, str(e)) 206 writeError(msg) 207 raise
208 209
210 -def enableSatelliteRepo(rhn_cert):
211 args = ['rpm', '-q', '--qf', '\'%{version} %{arch}\'', '-f', '/etc/redhat-release'] 212 ret, out, err = fileutils.rhn_popen(args) 213 data = out.read().strip("'") 214 version, arch = data.split() 215 # Read from stdout, strip quotes if any and extract first number 216 version = re.search(r'\d+', version).group() 217 218 if version not in SUPPORTED_RHEL_VERSIONS: 219 log(0, "WARNING: No Satellite repository available for RHEL version: %s." % version) 220 return 221 222 arch_str = "server" 223 if arch == "s390x": 224 arch_str = "system-z" 225 226 sat_cert = satellite_cert.SatelliteCert() 227 sat_cert.load(rhn_cert) 228 sat_version = getattr(sat_cert, 'satellite-version') 229 230 repo = "rhel-%s-%s-satellite-%s-rpms" % (version, arch_str, sat_version) 231 args = ['/usr/bin/subscription-manager', 'repos', '--enable', repo] 232 ret, out, err = fileutils.rhn_popen(args) 233 if ret: 234 msg_ = "Enabling of Satellite repository failed." 235 msg = ("%s\nReturn value: %s\nStandard-out: %s\n\n" 236 "Standard-error: %s\n" 237 % (msg_, ret, out.read(), err.read())) 238 writeError(msg) 239 raise EnableSatelliteRepositoryException("Enabling of Satellite repository failed. Make sure Satellite " 240 "subscription is attached to this system, both versions of RHEL and " 241 "Satellite are supported or run activation with --disconnected " 242 "option.")
243 244
245 -class EnableSatelliteRepositoryException(Exception):
246 "when there is no attached satellite subscription in rhsm or incorrect combination of rhel and sat version"
247 248
249 -def expiredYN(cert):
250 """ dead simple check to see if our RHN cert is not expired 251 returns either "" or the date of expiration. 252 """ 253 254 # parse it and snag "expires" 255 sc = satellite_cert.SatelliteCert() 256 sc.load(cert) 257 # note the correction for timezone 258 # pylint: disable=E1101 259 try: 260 expires = time.mktime(time.strptime(sc.expires, sc.datesFormat_cert))-time.timezone 261 except ValueError: 262 writeError("Can't seem to parse the expires field in the RHN Certificate. " 263 "RHN Certificate's version is incorrect?") 264 # a cop-out FIXME: not elegant 265 sys.exit(11) 266 267 now = time.time() 268 if expires < now: 269 return sc.expires 270 else: 271 return ''
272 273
274 -def processCommandline():
275 options = [ 276 Option('--sanity-only', action='store_true', help="confirm certificate sanity. Does not activate " 277 + "the Red Hat Satellite locally or remotely."), 278 Option('--ignore-expiration', action='store_true', help='execute regardless of the expiration ' 279 + 'of the RHN Certificate (not recommended).'), 280 Option('--ignore-version-mismatch', action='store_true', help='execute regardless of version ' 281 + 'mismatch of existing and new certificate.'), 282 Option('-v', '--verbose', action='count', help='be verbose ' 283 + '(accumulable: -vvv means "be *really* verbose").'), 284 Option('--dump-version', action='store', help="requested version of XML dump"), 285 Option('--manifest', action='store', help='the RHSM manifest path/filename to activate for CDN'), 286 Option('--rhn-cert', action='store', help='this option is deprecated, use --manifest instead'), 287 Option('--deactivate', action='store_true', help='deactivate CDN-activated Satellite'), 288 Option('--disconnected', action='store_true', help="activate locally, not subscribe to remote repository"), 289 Option('--manifest-info', action='store_true', help="show information about currently activated manifest"), 290 Option('--manifest-download', action='store_true', 291 help="download new manifest from RHSM to temporary location"), 292 Option('--manifest-refresh', action='store_true', help="download new manifest from RHSM and activate it"), 293 Option('--manifest-reconcile-request', action='store_true', 294 help="request regeneration of entitlement certificates") 295 ] 296 297 parser = OptionParser(option_list=options) 298 options, args = parser.parse_args() 299 300 initCFG('server.satellite') 301 if options.verbose is None: 302 options.verbose = 0 303 CFG.set('DEBUG', options.verbose) 304 rhnLog.initLOG(LOG_PATH, options.verbose) 305 log2disk(0, "Command: %s" % str(sys.argv)) 306 307 # we take no extra commandline arguments that are not linked to an option 308 if args: 309 writeError("These arguments make no sense in this context (try --help): %s" % repr(args)) 310 sys.exit(1) 311 312 # No need to check further if deactivating 313 if options.deactivate: 314 return options 315 316 if options.sanity_only: 317 options.disconnected = 1 318 319 if options.manifest_refresh: 320 options.manifest_download = 1 321 322 if CFG.DISCONNECTED and not options.disconnected: 323 msg = """Satellite server has been setup to run in disconnected mode. 324 Either correct server configuration in /etc/rhn/rhn.conf 325 or use --disconnected to activate it locally.""" 326 writeError(msg) 327 sys.exit(1) 328 329 options.http_proxy = idn_ascii_to_puny(CFG.HTTP_PROXY) 330 options.http_proxy_username = CFG.HTTP_PROXY_USERNAME 331 options.http_proxy_password = CFG.HTTP_PROXY_PASSWORD 332 log(1, 'HTTP_PROXY: %s' % options.http_proxy) 333 log(1, 'HTTP_PROXY_USERNAME: %s' % options.http_proxy_username) 334 log(1, 'HTTP_PROXY_PASSWORD: <password>') 335 336 return options
337 338 339 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 340
341 -def main():
342 """ main routine 343 1 general failure 344 10 general sanity check failure (to include a remedial cert 345 version check) 346 11 expired! 347 12 certificate version fails remedially 348 13 certificate missing in manifest 349 14 manifest signature incorrect 350 15 cannot load mapping files 351 16 manifest download failed 352 17 manifest refresh failed 353 18 manifest entitlements parse failed 354 30 local activation failure 355 356 90 not registered to rhsm 357 91 enabling sat repo failed 358 359 127 general unknown failure (not really mapped yet) 360 361 FIXME - need to redo how we process error codes - very manual 362 """ 363 # pylint: disable=R0911 364 365 options = processCommandline() 366 367 if not cdn_activation: 368 writeError("Package spacewalk-backend-cdn has to be installed for using this tool.") 369 sys.exit(1) 370 371 # CDN Deactivation 372 if options.deactivate: 373 cdn_activation.Activation.deactivate() 374 # Rotate the manifest to not have any currently used 375 if os.path.exists(DEFAULT_RHSM_MANIFEST_LOCATION): 376 fileutils.rotateFile(DEFAULT_RHSM_MANIFEST_LOCATION, depth=5) 377 os.unlink(DEFAULT_RHSM_MANIFEST_LOCATION) 378 return 0 379 380 if options.rhn_cert: 381 writeError("Activation with RHN Classic Satellite Certificate is deprecated.\nPlease obtain a Manifest for this" 382 " Satellite version via https://access.redhat.com/knowledge/tools/satcert, " 383 "and re-run this activation tool with option --manifest=MANIFEST-FILE.") 384 sys.exit(1) 385 386 if not options.manifest: 387 if os.path.exists(DEFAULT_RHSM_MANIFEST_LOCATION): 388 options.manifest = DEFAULT_RHSM_MANIFEST_LOCATION 389 if options.manifest_info: 390 cdn_activation.Activation.manifest_info(DEFAULT_RHSM_MANIFEST_LOCATION) 391 return 0 392 # Call regeneration API on Candlepin server 393 if options.manifest_reconcile_request: 394 log(0, "Requesting manifest regeneration...") 395 ok = cdn_activation.Activation.refresh_manifest( 396 DEFAULT_RHSM_MANIFEST_LOCATION, 397 http_proxy=options.http_proxy, 398 http_proxy_username=options.http_proxy_username, 399 http_proxy_password=options.http_proxy_password) 400 if not ok: 401 writeError("Manifest regeneration failed!") 402 return 17 403 log(0, "Manifest regeneration requested.") 404 return 0 405 # Get new refreshed manifest from Candlepin server 406 if options.manifest_download: 407 log(0, "Downloading manifest...") 408 path = cdn_activation.Activation.download_manifest( 409 DEFAULT_RHSM_MANIFEST_LOCATION, 410 http_proxy=options.http_proxy, 411 http_proxy_username=options.http_proxy_username, 412 http_proxy_password=options.http_proxy_password) 413 if not path: 414 writeError("Manifest download failed!") 415 return 16 416 if options.manifest_refresh: 417 options.manifest = path 418 else: 419 log(0, "New manifest saved to: '%s'" % path) 420 return 0 421 else: 422 writeError("No currently activated manifest was found. " 423 "Run the activation tool with option --manifest=MANIFEST.") 424 return 1 425 # Handle RHSM manifest 426 try: 427 cdn_activate = cdn_activation.Activation(options.manifest) 428 except CdnMappingsLoadError, e: 429 writeError(e) 430 return 15 431 except MissingSatelliteCertificateError, e: 432 writeError(e) 433 return 13 434 except IncorrectEntitlementsFileFormatError, e: 435 writeError(e) 436 return 18 437 438 # general sanity/GPG check 439 try: 440 validateSatCert(cdn_activate.manifest.get_satellite_certificate()) 441 except RHNCertGeneralSanityException, e: 442 writeError(e) 443 return 10 444 445 # expiration check 446 if not options.ignore_expiration: 447 date = expiredYN(cdn_activate.manifest.get_satellite_certificate()) 448 if date: 449 just_date = date.split(' ')[0] 450 writeError( 451 'Satellite Certificate appears to have expired: %s' % just_date) 452 return 11 453 454 if options.sanity_only: 455 return 0 456 457 if not options.disconnected: 458 rhsm_uuid = getRHSMUuid() 459 if not rhsm_uuid: 460 writeError("System not registered to RHSM? No identity found. Please register system to RHSM" 461 " or run activation with --disconnected option.") 462 return 90 463 try: 464 enableSatelliteRepo(cdn_activate.manifest.get_satellite_certificate()) 465 except EnableSatelliteRepositoryException: 466 e = sys.exc_info()[1] 467 writeError(e) 468 return 91 469 470 try: 471 cdn_activate.activate() 472 except ManifestValidationError: 473 e = sys.exc_info()[1] 474 writeError(e) 475 return 14 476 477 storeRhsmManifest(options) 478 479 return 0
480 481 482 #------------------------------------------------------------------------------- 483 if __name__ == "__main__": 484 sys.stderr.write('\nWARNING: intended to be wrapped by another executable\n' 485 ' calling program.\n') 486 sys.exit(abs(main() or 0)) 487 #=============================================================================== 488