Ejemplo n.º 1
0
def device_cert_required() -> bool:
    apidata = get_apidata()
    if 'verify_tls_device' in apidata and type(apidata['verify_tls_device']) == bool and \
            not apidata['verify_tls_device']:
        return False
    else:
        return True
Ejemplo n.º 2
0
def generate_device_cert(hostname: str, ipv4_address: IPv4Address):
    apidata = get_apidata()
    try:
        if not os.path.isfile(apidata['cafile']):
            raise Exception("Specified cafile is not a file: {}".format(
                apidata['cafile']))
    except KeyError:
        raise Exception("No cafile specified in api.yml")

    try:
        if not os.path.isfile(apidata['cakeyfile']):
            raise Exception("Specified cakeyfile is not a file: {}".format(
                apidata['cakeyfile']))
    except KeyError:
        raise Exception("No cakeyfile specified in api.yml")

    try:
        if not os.path.isdir(apidata['certpath']):
            raise Exception("Specified certpath is not a directory")
    except KeyError:
        raise Exception("No certpath found in api.yml settings")

    with open(apidata['cakeyfile'], "rb") as cakeyfile:
        root_key = serialization.load_pem_private_key(
            cakeyfile.read(),
            password=None,
        )

    with open(apidata['cafile'], "rb") as cafile:
        root_cert = x509.load_pem_x509_certificate(cafile.read())

    cert_key = rsa.generate_private_key(public_exponent=65537,
                                        key_size=2048,
                                        backend=default_backend())
    new_subject = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, hostname),
    ])
    cert = (x509.CertificateBuilder().subject_name(new_subject).issuer_name(
        root_cert.issuer).public_key(cert_key.public_key()).serial_number(
            x509.random_serial_number()).not_valid_before(
                datetime.datetime.utcnow()).not_valid_after(
                    datetime.datetime.utcnow() +
                    datetime.timedelta(days=7300)).add_extension(
                        x509.SubjectAlternativeName(
                            [x509.IPAddress(ipv4_address)]),
                        critical=False,
                    ).sign(root_key, hashes.SHA256(), default_backend()))

    with open(os.path.join(apidata['certpath'], "{}.crt".format(hostname)),
              "wb") as f:
        f.write(cert.public_bytes(serialization.Encoding.PEM))

    with open(os.path.join(apidata['certpath'], "{}.key".format(hostname)),
              "wb") as f:
        f.write(
            cert_key.private_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PrivateFormat.TraditionalOpenSSL,
                encryption_algorithm=serialization.NoEncryption(),
            ))
Ejemplo n.º 3
0
 def firmware_url(self) -> str:
     apidata = get_apidata()
     httpd_url = ''
     if isinstance(apidata, dict) and 'firmware_url' in apidata:
         httpd_url = apidata['firmware_url']
     elif 'FIRMWARE_URL' in os.environ:
         httpd_url = os.environ['FIRMWARE_URL']
     return httpd_url
Ejemplo n.º 4
0
def get_ssl_context():
    apidata = get_apidata()
    new_ssl_context = None

    if 'cafile' in apidata:
        if os.path.isfile(apidata['cafile']):
            new_ssl_context = ssl.create_default_context(
                cafile=apidata['cafile'])
        else:
            logger.error("Specified cafile is not a file: {}".format(
                apidata['cafile']))

    if 'verify_tls_device' in apidata and type(apidata['verify_tls_device']) == bool and \
            not apidata['verify_tls_device']:
        logger.warning("Accepting unverified TLS certificates")
        new_ssl_context = ssl._create_unverified_context()

    if not new_ssl_context:
        logger.debug("Using system default CAs")
        new_ssl_context = ssl.create_default_context()

    return new_ssl_context
Ejemplo n.º 5
0
    def post(self, hostname: str):
        """Apply exact specified configuration to device without using templates"""
        json_data = request.get_json()
        apply_kwargs = {'hostname': hostname}
        allow_live_run = get_apidata()['allow_apply_config_liverun']
        if not Device.valid_hostname(hostname):
            return empty_result(
                status='error',
                data=f"Invalid hostname specified"
            ), 400

        if 'full_config' not in json_data:
            return empty_result('error', "full_config must be specified"), 400

        if 'dry_run' in json_data and isinstance(json_data['dry_run'], bool) \
                and not json_data['dry_run']:
            if allow_live_run:
                apply_kwargs['dry_run'] = False
            else:
                return empty_result('error', "Apply config live_run is not allowed"), 400
        else:
            apply_kwargs['dry_run'] = True

        apply_kwargs['config'] = json_data['full_config']

        scheduler = Scheduler()
        job_id = scheduler.add_onetime_job(
            'cnaas_nms.confpush.sync_devices:apply_config',
            when=1,
            scheduled_by=get_jwt_identity(),
            kwargs=apply_kwargs,
        )

        res = empty_result(data=f"Scheduled job to apply config {hostname}")
        res['job_id'] = job_id

        return res, 200
Ejemplo n.º 6
0
            if stop_websocket_threads:
                break


if __name__ == '__main__':
    # Starting via python run.py
    # gevent monkey patching required if you start flask with the auto-reloader (debug mode)
    monkey.patch_all()
    from cnaas_nms.api import app
    from cnaas_nms.db.session import redis_session
    import json

    t_websocket_events = threading.Thread(target=thread_websocket_events)
    t_websocket_events.start()

    apidata = get_apidata()
    if isinstance(apidata, dict) and 'host' in apidata:
        host = apidata['host']
    else:
        host = None

    app.socketio.run(get_app(), debug=True, host=host)
    stop_websocket_threads = True
    t_websocket_events.join()

    if 'COVERAGE' in os.environ:
        save_coverage()
else:
    # Starting via uwsgi
    from cnaas_nms.api import app
    from cnaas_nms.db.session import redis_session
Ejemplo n.º 7
0
def arista_copy_cert(task, job_id: Optional[str] = None) -> str:
    set_thread_data(job_id)
    logger = get_logger()
    apidata = get_apidata()

    try:
        key_path = os.path.join(apidata['certpath'],
                                "{}.key".format(task.host.name))
        crt_path = os.path.join(apidata['certpath'],
                                "{}.crt".format(task.host.name))
    except KeyError:
        raise Exception("No certpath found in api.yml settings")
    except Exception as e:
        raise Exception("Unable to find path to cert {} for device".format(
            e, task.host.name))

    if not os.path.isfile(key_path):
        raise Exception("Key file {} not found".format(key_path))
    if not os.path.isfile(crt_path):
        raise Exception("Cert file {} not found".format(crt_path))

    net_connect = task.host.get_connection("netmiko", task.nornir.config)
    net_connect.fast_cli = False

    res_key = task.run(netmiko_file_transfer,
                       source_file=key_path,
                       dest_file="cnaasnms.key",
                       file_system="/mnt/flash",
                       overwrite_file=True)
    if res_key.failed:
        logger.exception(res_key.exception)

    res_crt = task.run(netmiko_file_transfer,
                       source_file=crt_path,
                       dest_file="cnaasnms.crt",
                       file_system="/mnt/flash",
                       overwrite_file=True)
    if res_crt.failed:
        logger.exception(res_crt.exception)

    if res_key.failed or res_crt.failed:
        raise CopyError("Unable to copy cert file to device: {}".format(
            task.host.name))
    else:
        logger.debug("Certificate successfully copied to device: {}".format(
            task.host.name))

    certstore_commands = [
        "copy flash:cnaasnms.crt certificate:",
        "copy flash:cnaasnms.key sslkey:", "delete flash:cnaasnms.key",
        "delete flash:cnaasnms.crt"
    ]
    for cmd in certstore_commands:
        res_certstore = task.run(netmiko_send_command,
                                 command_string=cmd,
                                 enable=True)
        if res_certstore.failed:
            logger.error(
                "Unable to copy cert into certstore on device: {}, command '{}' failed"
                .format(task.host.name, cmd))
            raise CopyError(
                "Unable to copy cert into certstore on device: {}".format(
                    task.host.name))

    logger.debug(
        "Certificate successfully copied to certstore on device: {}".format(
            task.host.name))
    return "Cert copy successful"
Ejemplo n.º 8
0
def verify_tls() -> bool:
    verify_tls = True
    apidata = get_apidata()
    if isinstance(apidata, dict) and 'verify_tls' in apidata:
        verify_tls = apidata['verify_tls']
    return verify_tls
Ejemplo n.º 9
0
def get_httpd_url() -> str:
    apidata = get_apidata()
    httpd_url = 'https://cnaas_httpd/api/v1.0/firmware'
    if isinstance(apidata, dict) and 'httpd_url' in apidata:
        httpd_url = apidata['httpd_url']
    return httpd_url
Ejemplo n.º 10
0
            data = {'status': 'error', 'data': 'Invalid token signature'}
        elif isinstance(e, IndexError):
            # We might catch IndexErrors which are not cuased by JWT,
            # but this is better than nothing.
            data = {'status': 'error', 'data': 'JWT token missing?'}
        elif isinstance(e, NoAuthorizationError):
            data = {'status': 'error', 'data': 'JWT token missing?'}
        elif isinstance(e, InvalidHeaderError):
            data = {'status': 'error', 'data': 'Invalid header, JWT token missing? {}'.format(e)}
        else:
            return super(CnaasApi, self).handle_error(e)
        return jsonify(data), 401


try:
    jwt_pubkey = open(get_apidata()['jwtcert']).read()
except Exception as e:
    print("Could not load public JWT cert from api.yml config: {}".format(e))
    sys.exit(1)

app = Flask(__name__)
# TODO: make origins configurable
cors = CORS(app,
            resources={r"/api/*": {"origins": "*"}},
            expose_headers=["Content-Type", "Authorization", "X-Total-Count"])
socketio = SocketIO(app, cors_allowed_origins='*')
app.config['SECRET_KEY'] = os.urandom(128)
app.config['JWT_PUBLIC_KEY'] = jwt_pubkey
app.config['JWT_IDENTITY_CLAIM'] = 'sub'
app.config['JWT_ALGORITHM'] = 'ES256'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = False