def fetch_device_metadata(dev, logger=logger): with Locker('dev.metadata'): setup_endpoints(dev) logger.info('Fetching node metadata...') can_read_cert() dev_md_req = mtls_request('get', 'device-metadata', dev=dev, requester_name="Fetching node metadata") if dev_md_req is None or not dev_md_req.ok: logger.error('Fetching failed.') return metadata = dev_md_req.json() logger.info('metadata retrieved.') if os.path.exists(SECRET_DEV_METADATA_PATH ) and not os.path.isfile(SECRET_DEV_METADATA_PATH): logger.error( "Error: The filesystem object '{}' is not a file. Looks like a break-in attempt." .format(SECRET_DEV_METADATA_PATH)) exit(1) with open(SECRET_DEV_METADATA_PATH, "w") as outfile: json.dump(metadata, outfile, indent=4) os.chmod(SECRET_DEV_METADATA_PATH, 0o600) logger.info('metadata stored.')
def run(ping=True, dev=False, logger=logger): with Locker('ping'): setup_endpoints(dev) bootstrapping = is_bootstrapping() if bootstrapping: device_id = generate_device_id() logger.info('Got WoTT ID: {}'.format(device_id)) write_metadata({'device_id': device_id}, rewrite_file=True) else: device_id = get_device_id() try_enroll_in_operation_mode(device_id=device_id, dev=dev) write_metadata({'device_id': device_id}, rewrite_file=False) if not time_for_certificate_renewal( ) and not is_certificate_expired(): if ping: send_ping(dev=dev) time_to_cert_expires = get_certificate_expiration_date( ) - datetime.datetime.now(datetime.timezone.utc) logger.info( "Certificate expires in {} days and {} hours. No need for renewal." "Renewal threshold is set to {} days.".format( time_to_cert_expires.days, floor(time_to_cert_expires.seconds / 60 / 60), RENEWAL_THRESHOLD, )) exit(0) else: return logger.info('My WoTT ID is: {}'.format(device_id)) logger.info('Generating certificate...') gen_key = generate_cert(device_id) ca = get_ca_cert() if not ca: logger.error('Unable to retrieve CA cert. Exiting.') exit(1) logger.info('Submitting CSR...') enroll_token = None if bootstrapping: crt = sign_cert(gen_key['csr'], device_id) enroll_token = get_enroll_token() if enroll_token is not None: logger.info('Node enrollment token found...') elif is_certificate_expired(): crt = renew_expired_cert(gen_key['csr'], device_id) else: crt = renew_cert(gen_key['csr'], device_id) if not crt: logger.error('Unable to sign CSR. Exiting.') exit(1) if enroll_token is None: logger.info('Got Claim Token: {}'.format(crt['claim_token'])) logger.info( 'Claim your node: {WOTT_ENDPOINT}/claim-device?device_id={device_id}&claim_token={claim_token}' .format(WOTT_ENDPOINT=DASH_ENDPOINT, device_id=device_id, claim_token=crt['claim_token'])) logger.info('Writing certificate and key to disk...') with open(CLIENT_CERT_PATH, 'w') as f: f.write(crt['crt']) os.chmod(CLIENT_CERT_PATH, 0o644) with open(CA_CERT_PATH, 'w') as f: f.write(ca) os.chmod(CA_CERT_PATH, 0o644) with open(CLIENT_KEY_PATH, 'w') as f: f.write(gen_key['key']) os.chmod(CLIENT_KEY_PATH, 0o600) with open(COMBINED_PEM_PATH, 'w') as f: f.write(gen_key['key']) f.write(crt['crt']) os.chmod(COMBINED_PEM_PATH, 0o600) send_ping(dev=dev) if enroll_token is not None: logger.info('Enroll node by token...') if enroll_device(enroll_token, crt['claim_token'], device_id): enroll_token = None logger.info("Writing config...") config = configparser.ConfigParser() config['DEFAULT'] = {'fallback_token': crt['fallback_token']} if enroll_token is not None: config['DEFAULT'][ 'enroll_token'] = enroll_token # if enroll fails, store enroll token for next run with open(INI_PATH, 'w') as configfile: config.write(configfile) os.chmod(INI_PATH, 0o600)
def fetch_credentials(dev, logger=logger): def clear_credentials(path): files = glob.glob(os.path.join(path, '**/*.json'), recursive=True) for file in files: os.remove(os.path.join(path, file)) logger.debug("remove...{}".format(file)) with Locker('credentials'): setup_endpoints(dev) logger.info('Fetching credentials...') can_read_cert() credentials_req = mtls_request('get', 'credentials', dev=dev, requester_name="Fetch credentials") if credentials_req is None or not credentials_req.ok: logger.error('Fetching failed.') return credentials = credentials_req.json() logger.info('Credentials retrieved.') if not os.path.exists(CREDENTIALS_PATH): os.mkdir(CREDENTIALS_PATH, 0o711) else: os.chmod(CREDENTIALS_PATH, 0o711) if not os.path.isdir(CREDENTIALS_PATH): logger.error( "There is file named as our credentials dir(%s), that's strange...", CREDENTIALS_PATH) exit(1) clear_credentials(CREDENTIALS_PATH) # group received credentials, by linux_user, name credentials_grouped = {} for cred in credentials: name = cred['name'] owner = cred['linux_user'] if 'linux_user' in cred else '' if owner not in credentials_grouped: credentials_grouped[owner] = {} if name not in credentials_grouped[owner]: credentials_grouped[owner][name] = cred['data'] else: logger.error( "Duplicated owner/name combination for credentials ({}/{}). Skipped." .format(owner, name)) root_pw = pwd.getpwnam("root") for owner in credentials_grouped: pw = root_pw # if no owner, use 'root' if owner: try: pw = pwd.getpwnam(owner) except KeyError: logger.warning( "There are credentials with wrong owner ({}). Skipped." .format(owner)) continue uid = pw.pw_uid gid = pw.pw_gid owner_path = CREDENTIALS_PATH if not owner else os.path.join( CREDENTIALS_PATH, owner) if owner and not os.path.isdir(owner_path): if os.path.exists(owner_path): logger.error( "There is a file with name of system user in credentials directory ({})." .format(owner_path)) exit(1) os.mkdir(owner_path, 0o700) os.chown( owner_path, uid, gid) # update ownership if user existence in system changed for name in credentials_grouped[owner]: credential_file_path = os.path.join(owner_path, "{}.json".format(name)) file_credentials = credentials_grouped[owner][name] logger.debug( 'Store credentials to {}'.format(credential_file_path)) with open(credential_file_path, 'w') as outfile: json.dump(file_credentials, outfile, indent=4) os.chmod(credential_file_path, 0o400) os.chown(credential_file_path, uid, gid)
CONFIG_PATH = os.getenv('CONFIG_PATH', '/opt/wott') if CONFINEMENT == Confinement.SNAP: Locker.LOCKDIR = CONFIG_PATH CERT_PATH = os.getenv('CERT_PATH', os.path.join(CONFIG_PATH, 'certs')) CREDENTIALS_PATH = os.getenv('CREDENTIALS_PATH', os.path.join(CONFIG_PATH, 'credentials')) BACKUPS_PATH = os.path.join(CONFIG_PATH, 'backups') CLIENT_CERT_PATH = os.path.join(CERT_PATH, 'client.crt') CLIENT_KEY_PATH = os.path.join(CERT_PATH, 'client.key') CA_CERT_PATH = os.path.join(CERT_PATH, 'ca.crt') COMBINED_PEM_PATH = os.path.join(CERT_PATH, 'combined.pem') INI_PATH = os.path.join(CONFIG_PATH, 'config.ini') SECRET_DEV_METADATA_PATH = os.path.join(CONFIG_PATH, 'device_metadata.json') with Locker('config'): if not os.path.isdir(CONFIG_PATH): os.makedirs(CONFIG_PATH) os.chmod(CONFIG_PATH, 0o711) if not os.path.isdir(BACKUPS_PATH): os.makedirs(BACKUPS_PATH, 0o711) # This needs to be adjusted once we have # changed the certificate life span from 7 days. RENEWAL_THRESHOLD = 3 logger = logging.getLogger('agent') def is_bootstrapping(): # Create path if it doesn't exist