iBeacons <3 Cisco Meraki

What I have noticed is that when companies buys network devices like firewalls, switches and access points they won’t use all the features what is implemented inside those devices.

I don’t know if the problem is sales or technical peoples, but what I think that companies don’t know how to use their hardware and take all out what they are already bought.

Situation is like this. You buy new car and then you don’t use lights, because you don’t know how to use lights or nobody hasn’t told you that there is lights in your car.

Because of this, I decided to write this little program and give it out for PoC purposes, so companies which have Cisco Meraki APs with bluetooth can get some ideas what they can do it.

First of all, if you don’t know what iBeacon is go to https://en.wikipedia.org/wiki/IBeacon and check it out. For the short, it is protocol developed by Apple and it’s based on bluetooth low energy (BLE) technology.

What is also great that if you don’t have Meraki APs, then you can use Linux hcitool or MAC mbeacon to test it. Do some googling (when I was “young” altavista was search engine back then) and check it out.

Now let’s stop talking and let’s go to the technical stuff.

Note: You need to have xcode installed in your MAC and you also know how to use it. You also need to have iPhone. I tested this code in IOS 14.0.1 and xcode 12.0.1 (12A7300).

First you need to enable iBeacons in your AP.

Go to Wireless > Configure and IoT radio settings.

Then go down to Beaconing and choose Advertising On.

Advertising > On.

Now you need to generate UUID for your BLE iBeacon advertising. This is the UUID what your APP try to find.

Click Generate new UUID.

After this you need to choose are you using unique Major and Minor for whole network or should all AP share same Major and Minor numbers. This depends in your use case.

Unique or Non-unique Major and Minor numbers.

If you have larger area and you want to get different impulse when Major or Minor changes then you can use Unique. If you only need one Major and Minor then you can choose Non-unique.

If you have multiple AP in your test bench, then you can set individual using API or using defaults.

Different Major and Minor numbers based on AP.

I suggest that you copy your UUID in file and upload it in your phone notes.

After this you need install My iBeacons app in your phone, you can download source code here: https://github.com/hrleinonen/iBeacon

After xcode has finished the installation, your phone will ask questions.

Chooce Allow While Using App.
Choose Allow.

After this click Configuration.

Choose Configuration.

Now you need to insert your UUID, Major and Minor number. Name field is not yet implemented.

Give UUID, Major and Minor numbers. After those click Save and Back.

Now you should see your distance to your AP.

iBeacon fully functional.

If you want use MAC and mbeacon application you should check this site https://github.com/watr/mbeacon

And if you want to use your RPI for iBeacon sender, then go here http://www.wadewegner.com/2014/05/create-an-ibeacon-transmitter-with-the-raspberry-pi/

Happy iBeaconing.

Regards,

Ville

Ubuntu (and maybe others) OpenLDAP 2.4+ and schema extension

I needed to integrate Cisco ISE (Identity Service Engine) and OpenLDAP together. It was easy task, but after couple week I realized that I need more fields. I didn’t find good fields in ISE SGT and some other uses, so I decide to create my own LDAP schema.

There was lot’s off discussion about how to generate your own schema, but only couple which worked for me. Now in this blogpost I parse those information together so you don’t need to do so much googling, ducking, bingin etc.

First install Apache Directory Studio. You can find it here https://directory.apache.org/studio/

After Directory Studio is started, open Apache Directory Studio Schema Editor.

Schema Editor Icon.

Choose File > New.

File > New.

Select Schema Editor > New Schema Project and click Next.

New Schema Project.

Give project name and click Next.

Project name.

Choose Server Type OpenLDAP and click Finish. After this you have created new project where you can add your own OpenLDAP schema.

Server Type.

Choose File > New.

File > New.

Select Schema Editor > New Schema and click Next.

Give schema name and click Finish.

Schema name.

Under Cisco > Object Classes, choose New > New Object Class.

New Object Class.

Give new OID, you can find instruction how get new private OID here https://pen.iana.org/pen/PenApplication.page and current OIDs here https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers also give new unique name for attribute in aliases field and description. After this click Next.

New Object Type OID.

Choose Class type Auxiliary and click Finish.

Class Type.

Under Cisco > Attribute Types, choose New > New Attribute Type.

New Attribute Type.

Give new OID, you can find instruction how get new private OID here https://pen.iana.org/pen/PenApplication.page and current OIDs here https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers also give new unique name for attribute and description. After this click Next.

New attribute type.

Choose Syntaxt, lenght and properties.

Attribute Type Content.

Choose matching rules and click Finish.

Matching Rules.

Choose Object class CiscoSchemaExtension and look Optional attributes. Choose Add…

Optional attributes.

Find your attribute and click Choose.

Object attributes.

Choose File > Save.

You are now create your own OpenLDAP schema extension.

Now choose Cisco > Export > Schemas as OpenLDAP files.

Schema Export.

Choose what to export and click Finish.

Schema export.

Login your linux server and create directory.

mkdir /tmp/ldapschema && cd /tmp/ldapschema

Copy your exported ldap file (cisco.schema) to directory /etc/ldap/schema

Create file called ldap.conf

echo “include /etc/ldap/schema/cisco.schema” > ldap.conf

Now generate ldap files what you import to your OpenLDAP server. Give command slaptest -f ldap.conf -F .

slaptest -f ldap.conf -F .

As you can see there is new directory in /tmp/ldapschema

New file and directory.

Go to directory cn=config/cn=schema

Directory cn=config/cn=schema

Edit file called cn={0}cisco.ldif

Remove bottom lines.

Removed lines in bottom.

Edit top lines

Original lines.
Edited lines.

Now you can add your new schema to your OpenLDAP server. Use command ldapadd -Y EXTERNAL -H ldapi:/// -f cn\=\{0\}cisco.ldif

If everything goes fine you get adding new entry….

Now restart your slapd (service slapd restart) and start using your new schema.

Cisco Meraki MX and Graylog3 Part 4

This parser/content pack are used to log Meraki MX security-events.

First download content pack from my github https://github.com/hrleinonen/graylog-meraki

File called “Cisco_Meraki_MX_Appliance_Security.json” is for MX appliance security events. It brings couple new search fields in Graylog3.

New fields are:

  • DISPOSITION = Disposition (eg. malicious)
  • ACTION = Action (eg. block)
  • SHA256 = SHA256 about file (eg. 2546dcffc5ad854d4d…)
  • NAME = Malware name (eg. Win.Ransomware.Eicar::95.sbx.tg)
  • SRCIP = Source IP-address (eg. 10.10.101.101)
  • SRCPORT = Source port (eg. 23434)
  • DSTIP = Destination IP-address (eg. 193.166.3.7)
  • DSTPORT = Destination port (eg. 443)

Upload file to Graylog3 using instruction from my blog https://www.hacknetwork.org/?p=167

Action report example.

Malware name example.

Now open Meraki dashboard and choose correct network.

Choose Network-wide > Configure > General.

Find part called reporting.

Cisco Meraki Syslog-server configuration.

Add your Graylog-server IP-address, port 5556 and choose Appliance event log role. Click save after this. Now your should see traffic in your graylog input.

Cisco Meraki MX and Graylog3 Part 3

This parser/content pack are used to log Meraki MX URL-events.

First download content pack from my github https://github.com/hrleinonen/graylog-meraki

File called “Cisco_Meraki_MX_Appliance_URLs.json” is for MX appliance events. It brings couple new search fields in Graylog3.

New fields are:

  • AGENT = Browser agent (eg. Mozilla Firefox)
  • REQUEST = Http request (eg. POST)
  • SRCIP = Source IP-address (eg. 10.10.101.101)
  • SRCPORT = Source port (eg. 23434)
  • DSTIP = Destination IP-address (eg. 193.166.3.7)
  • DSTPORT = Destination port (eg. 443)
Map based on destination IP-addresses.
Example fields.

Upload file to Graylog3 using instruction from my blog https://www.hacknetwork.org/?p=167

Now open Meraki dashboard and choose correct network.

Choose Network-wide > Configure > General.

Find part called reporting.

Cisco Meraki Syslog-server configuration.

Add your Graylog-server IP-address, port 5555 and choose Appliance event log role. Click save after this. Now your should see traffic in your graylog input.

Cisco Meraki MX and Graylog3 Part 2

First download content pack from my github https://github.com/hrleinonen/graylog-meraki

File called “Cisco_Meraki_MX_Appliance_Events.json” is for MX appliance events. It brings couple new search fields in Graylog3.

New fields are:

  • EVENT_TYPE = Event type (eg. IDS, content_filtering_block or dhcp)
  • SPI = Security Parameter Index  (eg. 53afbb30231007)
  • URL_CATEGORY = Category where blocked site belongs (eg. Malware)
Top 10 values for event type.
Top 5 blocked URL categories

Upload file to Graylog3 using instruction from my blog https://www.hacknetwork.org/?p=167

Now open Meraki dashboard and choose correct network.

Choose Network-wide > Configure > General.

Find part called reporting.

Cisco Meraki Syslog-server configuration.

Add your Graylog-server IP-address, port 5557 and choose Appliance event log role. Click save after this. Now your should see traffic in your graylog input.

Cisco Meraki MX and Graylog3

I really like Meraki cloud concept. It includes complete network stack Firewalls/SD-WAN, WLAN, Switches and Security Cameras (also MDM). Only problem (if we don’t count missing AnyConnect) is logs and query. Now, because I use Graylog for my other projects, I decide to use Graylog3 to my logstorage. There is no ready parsers or dashboards in Graylog marketplace, so I start to make my own.

Here comes parser (GROK based) for MX flow records, enjoy.

First download content pack from my github https://github.com/hrleinonen/graylog-meraki

There is couple roles in Meraki MX which you use to send syslog. I choose to send every role to different port, so I can choose what extractol to use.

File called “Cisco_Meraki_Flow_Content_Pack.json” is for MX flow events. It brings couple new search fields in Graylog3.

New fields are:

  • DSTIP = Destination IP-address (eg. 208.67.222.222)
  • DSTPORT = Destination port (eg. 53)
  • FLOW_TYPE = Flow start or end (eg. ip_flow_end)
  • PROTOCOL = Protocol (eg. tcp)
  • SRCIP = Source IP-address (eg. 172.16.0.10)
  • SRCPORT = Source port (eg. 1007)
  • TRANSLATED_DST_IP = Translated destination IP-address (eg. 1.2.3.4)
  • TRANSLATED_PORT = Translated port (eg. 55)
  • TRANSLATED_SRC_IP = Translated source IP-address (eg. 81.3.4.1)

Example for new search fields (btw. this is demo generated data not my inside net).

Now, when you are downloaded json file you must to upload it to the Graylog3. Choose System > Content Packs, Content Packs site opens. Click Upload (green box) upper left corner. Choose content pack file and upload.

There is now new content pack called Cisco Meraki Flow, now click Install. After this you have new input and dashboard.

Go to the input site System > Inputs. There is input called MERAKI_LOGS_FLOWS. Input listens RAW/plaintext udp port 5558 (there is some problems in standard Syslog udp). If input is not started, you must start it.

Now open Meraki dashboard and choose correct network.

Choose Network-wide > Configure > General.

Find part called reporting.

Cisco Meraki Syslog-server configuration.

Add your Graylog-server IP-address, port 5558 and choose Flows role. Click save after this. Now your should see traffic in your graylog input.

Click System > Inputs find MERAKI_LOGS_FLOWS and choose Show Received Messages.

There is also new dashboard called “Meraki MX Flow Records”. Go Dashboards > Meraki MX Flows Records.

Example flow dashboard.

Meraki Social Login with Linkedin

How to use Linkedin account to login company guest network?

That’s the question.

Most commonly companies use static passwords or pre/self registration in guest networks. Cisco Meraki guest access can give you much more information about your guests (eg. name, email, photo and current position). In this article I tell you how-to use Cisco Meraki’s social login feature. I’d like to thank phpgang for basic idea for oAuth2 authentication.

I did some search in Google and I did not find good examples how-to use Cisco Meraki and php together with social login. Here is one example how-to use those.

What if you guest-network login layout looks something like this instead of username/password fields? What if you can advertise your products in guest access page?

First you need to development account in Linkedin and give some permission to your php-code.

https://www.linkedin.com/developer/apps/

You need to get Client Id, Client Secret and you must to know your server dns name. Remember use SSL secured site, with real certificates.

You must also need to configure Meraki walled garden to allow access to certain sites (Linkedin and your www-server) without authentication.

After this is done you can put your php-code together. This code is still under dev and contains some bugs, idiotism etc, but it works.

Create config.php file. This file include your Linkedin app client id and client secret, so keep it private.

<?php
    $client_id = "client_id_here"; // enter your client id
    $client_secret = "client_secret_here"; // enter your client secret
    $redirect_uri = "https://acme_jetlag_url_here/callback.php"; // enter your redirect url
?>

Next you need to create index.php file. Your client will first see this page.

<!DOCTYPE html>
<html>
<head>
<style>

div.container {
    width: 100%;
    border: 1px solid gray;
}

div {
    background-color: white;
}

header, footer {
    padding: 1em;
    color: white;
    background-color: #3A454A;
    clear: left;
    text-align: center;
}

nav {
    float: left;
    max-width: 160px;
    margin: 0;
    padding: 1em;
}

nav ul {
    list-style-type: none;
    padding: 0;
}

nav ul a {
    text-decoration: none;
}

article {
    margin-left: 270px;
    border-left: 1px solid gray;
    padding: 1em;
    overflow: hidden;
}

body  {
    background-image: url("showroom.jpg");
    background-color: #ffffff;
}

img {
    width:100%;
}

</style>
</head>

<body>
<div class="container">
<header>
   <h1>Social Login</h1>
</header>

<nav>
    <p>Sign in using your Linkedin account.</p>

<?php
   session_start();
   $base_grant_url = urldecode($_GET['base_grant_url']);
   $user_continue_url = urldecode($_GET['user_continue_url']);
   $override_continue_url = 'https://www.hacknetwork.org';
   $override_the_users_request = false;

   echo '<a href="login.php"><img src="Linkedin_login.png" alt="Linkedin login button" style="width:200px;"></a>';
      if ($override_the_users_request) {
        $grant_url = $base_grant_url . "?continue_url=" . urlencode($override_continue_url) ."&duration=28800";
      } else {
        $grant_url = $base_grant_url . "?continue_url=" . urlencode($user_continue_url) ."&duration=28800";
      }

      $_SESSION['grant_url'] = $grant_url;
      $_SESSION['user_continue_url'] = $user_continue_url;
      $_SESSION['base_grant_url'] = $base_grant_url;
?>

</nav>
<article>
  <h1>Guest Network</h1>
  <p>Lorium ipslum, blah, blah. Session timeout is 8 hour.</p>
  <br><p>Guest network is using Meraki techology and custom social login functions.</p>
</article>
<footer>Under GPL. Ville Leinonen/Atea Finland</footer>
</div>
</body>
</html>

When guest clicks Linkedin image, user will redirected to Linkedin authentication page. This page is provided by Linkedin, so we don’t see guest passwords.

After successfully login Linkedin will send data back to callback.php page.

<?php
// Replace this in next version -->
function post_curl($url,$param="")
{
    $ch = curl_init();
    curl_setopt($ch,CURLOPT_URL,$url);
    if($param!="")
        curl_setopt($ch,CURLOPT_POSTFIELDS,$param);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}
// <---

    session_start();
    if( isset($_SESSION['user_info']) or !isset($_SESSION['login']) ){ // if user is logged in
        header("location: index.php"); // redirect user to index page
        return false;
    }
    include 'config.php'; // include app data
if(isset($_GET['code'])) // get code after authorization
{
    $url = 'https://www.linkedin.com/uas/oauth2/accessToken';
    $param = 'grant_type=authorization_code&code='.$_GET['code'].'&redirect_uri='.$redirect_uri.'&client_id='.$client_id.'&client_secret='.$client_secret;
    $return = (json_decode(post_curl($url,$param),true));
       $url = 'https://api.linkedin.com/v1/people/~:(id,firstName,lastName,pictureUrls::(original),headline,publicProfileUrl,location,industry,positions,email-address)?format=json&oauth2_access_token='.$return['access_token'];
       $obj = json_decode(file_get_contents($url), true);
}
    $_SESSION['user_info'] = $obj;
      $grant_url = $_SESSION['grant_url'];
      $user_continue_url = $_SESSION['user_continue_url'];
      $base_grant_url = $_SESSION['base_grant_url'];
      $_SESSION['grant_url'] = $grant_url;
      $_SESSION['user_continue_url'] = $user_continue_url;
      $_SESSION['base_grant_url'] = $base_grant_url;

    header("location: afterlogin.php");
?>

Site callback.php will redirect client to afterlogin.php page. Guest still need to click link to get access.

<!DOCTYPE html>
<html>
<head>
<style>

div.container {
    width: 100%;
    border: 1px solid gray;
}

div {
    background-color: white;
}

header, footer {
    padding: 1em;
    color: white;
    background-color: #3A454A;
    clear: left;
    text-align: center;
}

nav {
    float: left;
    max-width: 260px;
    margin: 0;
    padding: 1em;
}

nav ul {
    list-style-type: none;
    padding: 0;
}

nav ul a {
    text-decoration: none;
}

article {
    margin-left: 270px;
    border-left: 1px solid gray;
    padding: 1em;
    overflow: hidden;
}

body  {
    background-image: url("showroom.jpg");
    background-color: #ffffff;
}

img {
    width:100%;
}

</style>
</head>
<body>
<div class="container">
<header>
   <h1>Login Success</h1>
</header>
<nav>
<?php
    session_start();
    if( isset($_SESSION['user_info']) ){ // if user is logged in
        $user_info = $_SESSION['user_info'];
        $grant_url = $_SESSION['grant_url'];
        $user_continue_url = $_SESSION['user_continue_url'];

        $override_continue_url = 'https://www.hacknetwork.org';
        $base_grant_url = $_SESSION['base_grant_url'];
?>
        <p>Continue to Internet <a href="<?php print $grant_url ?>">Click here</a>.</p>
        <?php
    }

    else{ // if user is not logged in
       header("location: index.php");
    }
?>

</nav>
<article>
    <h1>Welcome <?php echo $user_info["firstName"]; ?> <?php echo $user_info["lastName"]; ?></h1>
    <p>Click left side link continue to the Internet.</p>
    <p>Use your daily ACME coupon code for get free cup of coffee: <b>Free4Cf33</b></p>
    <p>Get our latest offers from Atea eSHOP (eshop.atea.fi)</p>
</article>
<footer>Under GPL. Ville Leinonen/Atea Finland</footer>
</div>
</body>
</html>

If everything goes fine you get information for your guest users.