<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title><![CDATA[ Blog | Unimus by NetCore j.s.a. ]]></title>
<description><![CDATA[ Latest updates and release overviews. Articles on best practices and real-world use examples. ]]></description>
<link>https://blog.unimus.net</link>
<image>
    <url>https://blog.unimus.net/favicon.png</url>
    <title>Blog | Unimus by NetCore j.s.a.</title>
    <link>https://blog.unimus.net</link>
</image>
<lastBuildDate>Wed, 22 Apr 2026 13:11:15 +0000</lastBuildDate>
<atom:link href="https://blog.unimus.net" rel="self" type="application/rss+xml"/>
<ttl>60</ttl>

    <item>
        <title><![CDATA[ Release Overview - Unimus 2.8.0 ]]></title>
        <description><![CDATA[ Unimus 2.8.0 is the latest major release, introducing the Compliance module for automated configuration and runtime validation, along with UI improvements, bug fixes, and expanded device support. ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-8-0/</link>
        <guid isPermaLink="false">68ff4bc7b5d27f0001a0bf04</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Aleš Deák ]]></dc:creator>
        <pubDate>Thu, 11 Dec 2025 17:31:26 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2025/11/2.8.0.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Unimus 2.8.0 is here, bringing one of the most anticipated features in recent releases — <strong>Compliance reporting</strong>. This is a major addition that opens the door to automated configuration validation, policy enforcement, and continuous oversight of configuration and runtime state across your entire infrastructure.</p><p>Alongside this headline feature, we’re shipping a host of smaller improvements, UI tweaks, fixes, and the usual batch of newly supported devices. Let’s walk through what’s new.</p><hr><h2 id="compliance-reporting"><strong>Compliance Reporting</strong></h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-18.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-18.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-18.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-18.png 1600w, https://blog.unimus.net/content/images/2025/12/image-18.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>Compliance Grid</figcaption></figure><p>This release introduces our brand-new <strong>Compliance</strong> module — a key tool for teams focused on security, governance, and operational stability. The module is available exclusively in the Advanced license tier.</p><p>With the new module, you can now define your golden configuration standards, internal policies, or industry standards, and have Unimus automatically validate your devices against them. Every time Unimus detects a configuration change, or a change in the runtime state of your network, your devices are re-validated, making it much easier to catch runtime surprises and configuration drift, before they escalate into security gaps or operational headaches.</p><p>Compliance rules can be as simple or as complex as you need — from straightforward text matching checks against your golden config, all the way to multi-condition regex logic linked together with logical operators. Results are presented clearly on preset and device levels and are ready for audits.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/unimus-2-8-0-compliance-home.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/unimus-2-8-0-compliance-home.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/unimus-2-8-0-compliance-home.png 1600w, https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>Compliance Home</figcaption></figure><p>For a deeper look — from preset structure to practical examples — explore our full <a href="https://blog.unimus.net/config-compliance-unimus-2-8/">Compliance Overview article</a>, along with our <a href="https://wiki.unimus.net/display/UNPUB/Compliance+Reporting">detailed wiki articles</a>.</p><hr><h2 id="enhancements-bug-fixes-and-new-device-drivers"><strong>Enhancements, bug fixes, and new device drivers</strong></h2><h3 id="device-cli-improvements">Device CLI improvements</h3><p>The Device CLI had a few issues when running "full-screen" programs like vim, mc, etc. We have changed it to use xterm by default, and implemented full handling for full-screen apps, resizing, changing window modes (full screen vs. partial), etc.</p><p>It should now be an even more capable terminal emulator :)</p><h3 id="saved-search-queues"><strong>Saved search queues</strong></h3><p>Saved Searches received a small but handy improvement. When running multiple Saved searches in parallel, queued searches now correctly appear with a QUEUED state in the Saved Searches screen. The Saved Search detail view now also includes a state indicator for queued jobs.</p><p>You can additionally change the maximum number of Saved searches executed in parallel — which defaults to 5 — using the -D<em>unimus.server.config-search.query-page-size</em> configuration property.</p><h3 id="api-v3-tag-expiration"><strong>API v3: tag expiration</strong></h3><p>The API v3 documentation now includes expiration information for temporary tags across the <strong>POST</strong>, <strong>PUT</strong>, and <strong>GET</strong> endpoints, keeping the documentation fully aligned with the temporary tags feature introduced in Unimus 2.7.1.</p><h3 id="expanded-device-support"><strong>Expanded device support</strong></h3><p>This release also adds and improves support for a variety of platforms:</p><ul><li>New driver for <strong>Arrcus ArcOS</strong>, <strong>DNVP </strong>and<strong> Ribbon </strong>devices</li><li>Expanded support for <strong>NetGear GS-series </strong>switches</li><li>Expanded support for <strong>Aruba Mobility Controller</strong></li><li>And more - check the full Changelog...</li></ul><p>As always, these additions make Unimus more capable across the increasingly diverse networks deployed today.</p><hr><p>For the complete list of changes, including all minor features, fixes, and newly supported devices, see the full <strong>2.8.0 Changelog</strong> below:</p><pre><code>= Version 2.8.0 =
Features:
  Improved Config Search / Saved Search queueing system that allows queueing and running many searches in parallel
  When running many Config Searches / Saved Searches in parallel, a proper "Queued" status will be displayed for queued Searches
  Various minor UI / UX improvements (help texts, styling, etc.)
  Added support for more types of login banners during SSH login
  Added support for more keyboard-interactive password auth variations during SSH login
  Added support for more variations of login prompts over Telnet
  On WatchGaurd firewalls, admin sessions will be properly released before a CLI session is closed

  Improvements to live Device CLI (Devices screen):
    - Device CLI could misbehave when opening "full-screen" programs like vim, etc.
    - the Device CLI will now use xterm as the default terminal type
    - the Device CLI will now automatically scale and rescale with CLI window size changes
    - this should fix CLI working correctly with full-screen programs, when being resized, etc.

  Added a new Config Compliance feature:
    - Compliance is the hero feature of the 2.8 release, and the largest new feature we have released in the last 2 years
    - you can now create Compliance presets which will report if you Configuration is compliant with your policies
    - Compliance also supports checking Runtime-state compliance of your devices - check for runtime port security violations, etc.
    - this is a large and complex feature, we look forward to your feedback and improvements will be coming in point releases in 2.8
    - Feature Overview: https://blog.unimus.net/config-compliance-unimus-2-8/
    - documentation: https://wiki.unimus.net/display/UNPUB/Compliance+Reporting

  Added support for:
    - Arrcus ArcOS
    - Aruba Mobility Controller running v8 firmware
    - Aruba AOS-8
    - DNWP Connection Master
    - DNWP Dyna Wiz
    - Netgate TNSR
    - Netgear GS-series switches (GS752TP)
    - Nokia Lightspan MF
    - Ribbon NPT

Fixes:
  Fixed strange characters could be printed in Device CLI if specific locales were used on the server
  Fixed 'Create another device' checkbox in device creation could sometimes not work
  Fixed Discovery failing on specific versions and models of Juniper JunOS devices
  Fixed Discovery failing on RuggedCom switches
  Fixed Discovery failing on Alcatel switches
  Fixed consecutive jobs on WatchGuard firewalls failing due to admin session count limitations
  Fixed jobs failing on SonicWall with enabled login banner
  Fixed jobs could fail on Fiberstore (FS.com) 32xx switch series with specific pagination variations
  Fixed Backup failing on Digi WR44 due to missing prompt after backup output
  Fixed Discovery failing on Nokia Lightspan due to Lightspan's command echo format

Embedded Core version:
  2.8.0</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Configuration Compliance in Unimus 2.8.0 ]]></title>
        <description><![CDATA[ With the Unimus 2.8.0 release, we are introducing the new Compliance module, enabling automated configuration and runtime compliance checks at scale. You define custom rules and policies and track compliance across your entire network with Unimus. ]]></description>
        <link>https://blog.unimus.net/config-compliance-unimus-2-8/</link>
        <guid isPermaLink="false">68ff4b9eb5d27f0001a0befc</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Aleš Deák ]]></dc:creator>
        <pubDate>Mon, 08 Dec 2025 18:03:54 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2025/12/Compliance-reporting.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Like it or not, IT infrastructure and its underlying networks exist in constant flux.</p><p>What starts as a clean, well-planned environment can - if left unchecked - slowly slip into something… less predictable. Device configurations drift, policies go stale, a temporary firewall rule added during troubleshooting might linger long after it’s needed, and the gap between “how things should be” and “how things actually are” quietly widens.</p><p>And that’s exactly where Compliance steps in to restore order.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home-2.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/unimus-2-8-0-compliance-home-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/unimus-2-8-0-compliance-home-2.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/unimus-2-8-0-compliance-home-2.png 1600w, https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home-2.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>Compliance Home</figcaption></figure><h2 id="why-compliance-matters"><strong>Why compliance matters</strong></h2><p>Compliance isn’t an unnecessary administrative burden. It’s about knowing exactly where your network stands.</p><p>A network that is compliant to configuration rules &amp; best practices delivers real, practical benefits:</p><ul><li>Configs stay consistent</li><li>Policies remain enforced</li><li>Troubleshooting is simpler and therefore faster</li><li>Audit requests are manageable and don’t turn into a fire drill</li><li>Attackers have fewer opportunities to exploit</li></ul><p>And with standards like PCI DSS, ISO/IEC 27001 and the NIS2 Directive, having a controlled and monitored configuration baseline is no longer optional - it’s simply expected.</p><p>For all the reasons outlined above, Unimus 2.8.0 introduces the new Compliance engine — a highly requested addition to device configuration management. This module provides a structured way to automatically validate your network’s compliance at scale, whether you’re checking device configurations or monitoring live runtime state. With it, you can keep device configuration drift under control and ensure your network operates as intended. Read on for the full details.</p><h2 id="how-the-new-unimus-compliance-module-works">How the new Unimus Compliance module works</h2><p>Let's dive into the details. The new Compliance engine follows a preset-based structure, similar in spirit to Mass Config Push. Each Compliance preset defines a set of policies (rules and conditions), that Unimus checks against a chosen dataset and on specific devices.</p><p>Each Compliance preset is built from three parts: Source, Targets and Rules. Let's break down each of them below.</p><h3 id="preset-sources">Preset sources</h3><p><strong>Source</strong> is the actual data you want the Compliance engine to validate.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-9.png" class="kg-image" alt loading="lazy" width="1667" height="311" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-9.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-9.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-9.png 1600w, https://blog.unimus.net/content/images/2025/12/image-9.png 1667w" sizes="(min-width: 720px) 720px"><figcaption>Compliance preset source selection</figcaption></figure><p>You can choose from multiple source types:</p><p><strong>Last Backup</strong> - the preset validates the most recent configuration backup (the current config running on the device).</p><p><strong>Config Search result</strong> - the preset validates the latest configs of devices returned by a Saved Search.</p><p><strong>Mass Config Push result</strong> - the preset validates device output produced by running an MCP preset.<br><br>Next, let’s look at how targets are determined.</p><h3 id="preset-targets">Preset targets</h3><p><strong>Compliance targets</strong> are the devices being validated. Your selected data source affects how the targets are determined.</p><p>For presets where the Source type is <strong>Last Backup</strong>, you choose the targets directly - you can pick devices one by one, filter them by vendor or device type, or simply select by Tag.</p><p>When the preset source is set to <strong>Config Search result</strong>, the devices returned by a selected Saved Search become the targets. Only devices whose configurations match the search criteria of the Saved search are included. This is useful when you want to narrow the scope to devices that contain a specific configuration pattern - for example, to run checks only on devices that still use an old SNMP community, contain a deprecated ACL, or have a specific banner present in their backups.</p><p>When the preset source is a  <strong>Mass Config Push result</strong>, the targets are simply the devices targeted in that MCP preset.</p><p>With the sources and targets settled, we can turn to the heart of every Compliance preset - the rules.</p><h3 id="preset-rules"><strong>Preset rules</strong></h3><p>Finally, rules define what the Compliance engine should look for in the chosen dataset (source). This is where the engine becomes really flexible. Each preset can include one or more rules, and each rule can include one or more conditions. You can create your own  compliance policies by defining conditions using:</p><ul><li>simple text-matching</li><li>regular expression matching</li><li>line-anchored checks</li><li>combination of multiple conditions with <em>AND</em>/OR logic</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-8.png" class="kg-image" alt loading="lazy" width="1641" height="541" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-8.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-8.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-8.png 1600w, https://blog.unimus.net/content/images/2025/12/image-8.png 1641w" sizes="(min-width: 720px) 720px"><figcaption>Condition types</figcaption></figure><p>Whether you need to enforce a specific configuration line, detect an insecure service, or confirm that an operational state is correct, these Compliance preset building blocks let you express the logic you need.</p><p>Continue reading to see how the new Compliance feature can fit into your workflow, with examples highlighting its flexibility and capabilities.</p><h2 id="examples-and-use-cases">Examples and use cases</h2><h3 id="1-defining-a-golden-configuration">1. Defining a golden configuration</h3><p>Let’s start with the most straightforward example.</p><p>Many teams maintain an internal “golden config” - a baseline device setup reflecting their operational and security standards. With Unimus, you can now define that baseline and have the system check against it.</p><p>Simply paste your expected configuration snippets into a Compliance condition, set the preset source to Last Backup, and define your target devices. Your condition might check for the correct firmware version, approved AAA servers, proper NTP servers, and logging. It could also enforce security hardening measures such as restricted SNMPv2 use, disabled insecure protocols like Telnet, removal of legacy ciphers, and strict password or authentication policies.</p><p>And with automatic execution enabled, the Compliance engine runs whenever a new configuration backup is generated for a targeted device.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-2.png" class="kg-image" alt loading="lazy" width="1652" height="779" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-2.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-2.png 1600w, https://blog.unimus.net/content/images/2025/12/image-2.png 1652w" sizes="(min-width: 720px) 720px"><figcaption>Golden config compliance preset example&nbsp;</figcaption></figure><p>With the preset in place, Unimus will continually review your latest configuration backups and flag any devices that drift from the baseline. No more combing through massive configs across hundreds of devices — the Compliance engine takes over, quietly keeping everything in check.</p><h3 id="2-beyond-the-golden-config-enforcing-precise-network-policies">2. Beyond the golden config: Enforcing precise network policies</h3><p>In addition to golden configuration presets, the Compliance engine lets you define flexible conditions using regular expressions, giving precise control over which configuration elements are allowed or disallowed on each device.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-7.png" class="kg-image" alt loading="lazy" width="1634" height="784" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-7.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-7.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-7.png 1600w, https://blog.unimus.net/content/images/2025/12/image-7.png 1634w" sizes="(min-width: 720px) 720px"><figcaption>Validation of firewall drop rules in the input chain</figcaption></figure><p>On the access layer of your network, you can enforce protections such as BPDU Guard or Root Guard for STP, making sure edge ports behave exactly as they should.</p><p>Moving up to the distribution and core layers, the engine can verify routing protocol security: OSPF or EIGRP authentication, proper route filtering, or that your IPv4 and IPv6 firewall states are in the expected shape.</p><p>At the edge or peering layer, you can validate BGP hardening — passwords, TTL security, session policies, and other controls that keep your network’s perimeter well-defended.</p><p>In this way, multiple checks can be combined for each device role, ensuring every part of your network aligns precisely with your policies.</p><h3 id="3-runtime-compliance-validating-your-live-network-state">3. Runtime compliance: validating your live network state</h3><p>Compliance goes beyond just validating device configurations!</p><p>A valid configuration doesn’t automatically guarantee smooth operations. Configurations show how things should be — runtime data shows how things actually are. For example, your OSPF configuration may be correct, but how do you know your OSPF peers are actually up? Reviewing your latest configuration backups won’t help you here.<br><br>That's why Compliance Reporting also lets you validate live device output gathered through Mass Config Push / Pull, ensuring you have a precise understanding of your network’s current state.</p><p>Start by creating and scheduling a Mass Config Push preset targeting the devices whose runtime state you want to inspect. Even tho the feature in Unimus is called "Mass Config Push", we will use it to Pull data from devices in this case. The MCP preset should include the appropriate show commands that query the devices’ operating state.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image.psd.png" class="kg-image" alt loading="lazy" width="1655" height="931" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image.psd.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image.psd.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image.psd.png 1600w, https://blog.unimus.net/content/images/2025/12/image.psd.png 1655w" sizes="(min-width: 720px) 720px"><figcaption>MCP preset</figcaption></figure><p>Next, create a Compliance preset, enable Automatic execution, and select “Mass Config Push result” as its source, targeting your preconfigured MCP preset. Then define the rules and conditions that will evaluate the collected device output using text or regex matching. After each MCP run, the engine analyzes the latest runtime output from your devices and checks it against your Compliance rules.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-3.png" class="kg-image" alt loading="lazy" width="1651" height="773" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-3.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-3.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-3.png 1600w, https://blog.unimus.net/content/images/2025/12/image-3.png 1651w" sizes="(min-width: 720px) 720px"><figcaption>Compliance preset validating a Config push output</figcaption></figure><p><br>This gives you visibility into the living, breathing state of your network, and because the system lets you define granular, fine-tuned conditions, the possibilities are remarkably rich — limited mainly by what you choose to monitor and the information your devices can provide through their CLI.</p><p>You can see whether your routing is healthy, BGP neighbors have dropped, OSPF is stuck in INIT, or EIGRP peers have gone missing. You can keep an eye on interface health as well: ports slipping into <code>err-disabled</code>, speed and duplex mismatches, rising error counters, or even devices running hot and experiencing high CPU utilization. The engine can also surface stack member inconsistencies in high-availability setups, failover activity, or sync issues.</p><p>And that’s just scratching the surface — the range of runtime checks you can create is virtually limitless, so explore them freely and tailor them to your specific needs.</p><h2 id="tracking-compliance-at-a-glance">Tracking compliance at a glance</h2><p>Once your Compliance rules are set up and presets executed, how do you track the compliance state across hundreds of devices and multiple policies? You don’t have to dig through menus — a quick glance at the <strong>Device List</strong> shows whether everything is as it should be. For each device, Unimus displays a small compliance indicator in the <strong>Compliance</strong> column:</p><p><strong>Green</strong> – device is fully compliant</p><p><strong>Red</strong> – device is non-compliant</p><p><strong>Yellow</strong> – source dataset is missing or invalid</p><p><strong>Grey</strong> – device is unmanaged or hasn’t been checked yet</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="data:image/png;base64,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gKlzU9YbEoVHx5hSK4nAAyVgqij8QBtFNyMCRIAIEAEiQASIABEgAkSACBABIjCKCHh5eY6i1lJTiYB0Ardv10k/ic4gAkSACBABIkAERozAoERh14mTRqzBdGMiQASGTqCp9uZ9L0Ki8NAZ0xWIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiYCkESBS2lJGgdhCBB0iAROEHCJtuRQSIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEIERJkCi8AgPAN2eCIwEARKFR4I63ZMIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACI0OAROGR4U53JQIjSoBE4RHFTzcnAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIwAMlQKLwA8VNNyMClkGARGHLGAdqBREgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQAQeBAEShR8EZboHEbAwAiQKW9iAUHOIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiMIwESBQeRrh0aSJgqQRIFLbUkaF2EQEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRMD8BEgUNj9TuiIRsHgCJApb/BBRA4kAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJgNgIkCpsNJV2ICIweAiQKj56xopYSASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEYKgESBQeKkE6nwiMQgIkCo/CQaMmEwEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRGCQBEgUHiQ4Oo0IjGYCJAqP5tGjthMBIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkRAGgEShaXxoqOJgFUQMJcoHDDR3Sp4UCeIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiMNoIVNQ2mNxkEoVNRkUHEgHrIWBOUbis6rb1gKGeEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACROABEQjy9cJgdRZ2rsWLwjKZDBM93eHqMh4yma1JWLu7u9FwpxH1d5rQ09Nj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QASIABEgAkSACIw6AiQcjjqXWM0g8q3VUFJHRGBUESBReFS5g4whAhOPAInCE8/nNGMiQASIABEgAkSACBABIkAEiAARIAJEgAgQgbFPuqIQCgAAIABJREFUgITDse9DUzMg345f39LMJjYBEoUntv9p9kRgxAmQKDziLiADiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIwJAJkHA4ZGRjpgH5dsy4igwlAkMiQKLwkHDRxUSACFibAInC1iZK/REBIkAEiAARIAJEgAgQASJABIgAESACRIAIEAHbEyDh0PaMR2oE8u1IkadxiYBtCZAobFu+1DsRIAKDECBRmJYIESACRIAIEAEiQASIABEgAkSACBABIkAEiAARGHsESDgcez4z12Lyrbmk6DoiMLYIkCg8tvxF1hKBcUdgJEThJ74faRWOn3xaa5V+qBMiQASIABEgAkSACBABIkAEiAARIAJEgAgQASIw1giQcDjWPGa+veRb81nRlURgLBEgUXgseYtsJQLjkACJwuPQqTQlIkAEiAARIAJEgAgQASJABIgAESACRIAIEIFxT4CEw/HrYvLt+PUtzWxiEyBReGL7n2ZPBEacAInCI+4CMoAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgMmQAJh0NGNmYakG/HjKvIUCIwJAIkCg8JF11MBIiAtQmQKGxtotQfESACRIAIEAEiQASIABEgAkSACBABIkAEiAARsD0BEg5tz3ikRiDfjhT50TMuJwyF1wx/THH1hGCKJ1zY/7t4grtZj6+rN+CMQjV6jCVLzCZAorDZqOhCIkAEbEFgNIjC1QfrzJpazJIIveuoprBZ2OgiIkAEiAARIAJEgAgQASJABIgAESACRIAI2I6APwe7cAEQyAHTHNXjfHsbaFHhXr0SaCPhwlbwh004JB/byoUm+x023w77zGhAswjYCeEb9UeE+wmAe0rc6LkCZU8nem4Abt6hEN6uR8M+EobNYjnKLiJReJQ5hMwhAhONAInCNvS423wkrE5F5LVSvL25AYq7+mMJQxIh+WEYfBxUUHx3Beebv0DV3jqcVRq3iZsehoQVcQj3EwJ3AM5Bifaj1Sgrq0HLtb427LqktESEuwPXrytw6exRHNhbjcaOQX6EDWKvnlVuIchal45geyVUvfNSQXGsHBs+0p8r5z4fS8Ux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" class="kg-image" alt loading="lazy"><figcaption>Device compliance indicator</figcaption></figure><p>You can see per-preset Compliance state on the Compliance Home page.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home-1.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/unimus-2-8-0-compliance-home-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/unimus-2-8-0-compliance-home-1.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/unimus-2-8-0-compliance-home-1.png 1600w, https://blog.unimus.net/content/images/2025/12/unimus-2-8-0-compliance-home-1.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>Compliance Home</figcaption></figure><p>But if you want to see the entire "battlefield" on one page, Unimus has you covered with the Compliance Matrix: every device laid out against every preset, making patterns and outliers jump out instantly. It’s especially useful when validating large-scale changes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-10.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-10.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-10.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-10.png 1600w, https://blog.unimus.net/content/images/2025/12/image-10.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>Compliance Matrix</figcaption></figure><p>If you need more than a bird’s-eye view, you can drill down into the evaluation details. The Compliance engine validates compliance on multiple levels, letting you see exactly which condition caused a device to fail. For a deeper dive, check out our <a href="https://wiki.unimus.net/display/UNPUB/Compliance+Evaluation+Logic">detailed wiki article</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2025/12/image-5.png" class="kg-image" alt loading="lazy" width="1644" height="903" srcset="https://blog.unimus.net/content/images/size/w600/2025/12/image-5.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/12/image-5.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/12/image-5.png 1600w, https://blog.unimus.net/content/images/2025/12/image-5.png 1644w" sizes="(min-width: 720px) 720px"><figcaption>In-depth view of a preset result</figcaption></figure><h2 id="what%E2%80%99s-next"><strong>What’s next?</strong></h2><p>Compliance Reporting in 2.8.0 lays the groundwork for broader compliance automation. In upcoming updates, you can expect Compliance notifications via email, Pushover, and Slack, as well as export options for HTML and CSV formats to support reporting and audits. We’re also improving the ability to search through rules and conditions, making it easier to manage complex presets.</p><p>As these features roll out, Compliance will continue to evolve, becoming an integral part of your everyday operational workflow.</p><h2 id="final-words"><strong>Final words</strong></h2><p>Thanks for reading all the way through!</p><p>We hope this article has given you a clear overview of the robust and flexible Compliance Reporting module in Unimus 2.8.0, and shown how it helps keep your network aligned with both industry standards and your own company policies.</p><p>For a full, wiki-style documentation of the feature, check out our <a href="https://wiki.unimus.net/display/UNPUB/Compliance+Reporting">wiki article</a>.</p><p>The Compliance Module is available in the Advanced Unimus tier as part of our <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=2221">new licensing model</a>.</p><p>Lastly, if you encounter anything unexpected or have feedback, feel free to reach out via our forums or the usual support channels.</p><p>Until next time — stay on top of your networks, and keep your devices compliant.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.7.0 ]]></title>
        <description><![CDATA[ Unimus 2.7.0 is the latest major release, delivering new features for Config Search and Mass Config Push, improved performance, bug fixes, and the usual round of newly supported devices. ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-7-0/</link>
        <guid isPermaLink="false">688765f8adced600017b2d73</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Aleš Deák ]]></dc:creator>
        <pubDate>Tue, 19 Aug 2025 03:51:06 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2025/08/2.7.0.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Unimus 2.7.0 is here, and it's another major step forward. This release introduces three highly requested features – <strong>Saved Config Searches, Scheduled Config Searches </strong>and output filtering for<strong> Mass Config Push</strong>.</p><p>Alongside these, we’ve added a range of minor features, significant performance optimizations, various smaller enhancements, fixes, UI improvements, as well as the usual batch of newly supported devices. Let’s dive into what’s new in this release.</p><hr><h2 id="saved-config-searches">Saved Config searches</h2><p>With this release, you no longer need to retype your frequent Config Search queries every time you want to run them. You can now save your frequently used <strong>Config Searches</strong> into a "library", and access them at any time. Just perform a search in <strong>Config search</strong>, click "Actions", then "Save search", and give the preset a name.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2025/08/image-4.png" class="kg-image" alt loading="lazy" width="1920" height="960" srcset="https://blog.unimus.net/content/images/size/w600/2025/08/image-4.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/08/image-4.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/08/image-4.png 1600w, https://blog.unimus.net/content/images/2025/08/image-4.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>This addition makes <strong>Config search</strong> a significantly more powerful tool for recurring config lookups, audit reporting, and forensic or post-mortem analysis.<br><br>More info on <strong>Saved searches</strong> is available <a href="https://wiki.unimus.net/display/UNPUB/Config+search">here</a>.</p><hr><h2 id="scheduled-config-searches">Scheduled Config searches</h2><p>Scheduled searches are now here!</p><p>When you save a Search, the results you see may be outdated the next time you visit it — the results are simply from the last time the search was ran. Now, you can apply a schedule to your searches so their results are always up to date and ready when you need them. No need for manual re-runs.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2025/08/image-5.png" class="kg-image" alt loading="lazy" width="1656" height="962" srcset="https://blog.unimus.net/content/images/size/w600/2025/08/image-5.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/08/image-5.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/08/image-5.png 1600w, https://blog.unimus.net/content/images/2025/08/image-5.png 1656w" sizes="(min-width: 720px) 720px"></figure><p>And there’s more coming: in <strong>Release 2.8.0</strong>, our brand-new <strong>Network Compliance</strong> module will arrive. The results of your scheduled searches will feed directly into it, enabling automatic validation of your network configuration and your configuration policies.</p><p>Stay tuned for more good stuff! ;-)</p><hr><h2 id="filtering-dynamic-data-in-config-push-outputs">Filtering dynamic data in Config Push outputs</h2><p><strong>Mass Config Push</strong> just got a lot smarter. Before 2.7.0, even small dynamic variations - like timestamps or session counters - could cause Unimus to treat each <strong>Config Push</strong> output as a unique result. To address this, Unimus now supports applying <strong>Backup Filters</strong> to <strong>Mass Config Push</strong> outputs.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2025/08/image-1.png" class="kg-image" alt loading="lazy" width="394" height="634"></figure><p>These filters clean up the noise by removing or ignoring dynamic data before results are grouped into unique output groups.</p><p>This means cleaner outputs, less visual clutter, and more accurate comparisons across your devices. Prefer raw, unfiltered output? Simply uncheck the “Apply backup filters to output” option in your <strong>Config Push</strong> preset <strong>Advanced settings</strong>.</p><p>More info on <strong>Config Push</strong> output filtering can be found <a href="https://wiki.unimus.net/display/UNPUB/Filtering+dynamic+data+in+Config+Push+outputs">here</a>.</p><hr><h2 id="performance-optimizations">Performance optimizations</h2><p>This release also brings major performance improvements across the board.</p><p><strong>Config search</strong> has been completely re-engineered on the backend to use significantly less memory and deliver faster results - even in environments where you are searching in over 100,000 backups.</p><p>Downloading and exporting huge (1+ GB) search results is now memory-efficient, even when running with Unimus' default 768 MB memory limit, thanks to a new paging-based algorithm.</p><p>UI responsiveness has also been greatly improved, particularly for large device inventories. Previously, screens like the <strong>Devices</strong> table could lag when dealing with tens of thousands of devices, tags, and user access rules. With 2.7.0, we’ve reworked how access permissions are handled behind the scenes - resulting in page load times dropping from over a second to just a few milliseconds, even with huge inventories.</p><hr><h2 id="bug-fixes-and-security-fixes">Bug fixes and security fixes</h2><p>A long list of minor bug fixes accompanies this release, covering UI inconsistencies, rare discovery failures, and various edge-case issues.</p><p>We also addressed a few security issues, including cases where <em>Zone IDs</em> could appear in <strong>Config search</strong> results and where full API tokens could be exposed in debug logs.</p><p>As always, the goal is to ensure Unimus remains reliable in environments of any scale - from a few dozen devices to tens of thousands.</p><hr><p>For a complete list of all changes, including 50+ minor features, bug fixes and improvements; 20+ newly supported devices; see the full 2.7.0 Changelog below:</p><pre><code>= Version 2.7.0 =
Features:
  Added a counter for the number of backups of each devices in the Backups screen
  Device deletion logs now include the device IP address and Zone ID of the deleted device
  Added a Zone ID column to the tables in the Tag Devices and Un-tag Devices windows in the Tags screen
  Added device descriptions and Zone ID information to the Device Backup View window
  Added the Zone ID of a device in multiple notification and export messages where it was missing
  Improved the password reset process to properly indicate why a password can't be changed and if / when it was changed successfully
  Various minor UI / UX improvements and tweaks
  Added checking of Core configuration parameters - if you try to provide invalid Core config, a proper error message will be logged
  Added support for Config Sessions on Arista switches - Unimus will now properly close its Config Session on logout if they are enabled
  Added support for persistent management session slots on RAD Carrier Switches
  RAD Carrier Switch driver will now fallback to "info" if "info running" is not available
  Added support for newer firmware versions of Aruba APs (AOS-8)
  On Enterasys / Extreme switches session-based paging will now automatically be enabled on Unimus CLI sessions
  On Calix EXA session-based alarms will now automatically be disabled on Unimus CLI sessions
  Added support for partition in Config Push and Backup Flows for A10 Thunder Series
  Improved built-in dynamic filters for FortiOS devices

  Added a new Saved Config Searches feature:
    - you can save Config Searches you use often into the Saved Searches library
    - you can build your search library and quickly return to important or frequently used searches
    - Saved Searches can either be run manually, or they can run on schedule so you always have up-to-date results ready

  Config Searches can now be scheduled:
    - with addition of Saved Searches, searches can now also be scheduled to automatically run in the background
    - this means you can always have up-to-date results available in your Saved Searches without needing to run them manually
    - scheduled Saved Searches will also serve as one of the inputs into the Config Compliance feature coming in 2.8

  Backup Filters can now be applied to outputs of Config Push / Automations:
    - the Output Grouping feature of Config Push is super useful, but any dynamic data in device output (like timestamps) would cause it to not work
    - to solve this, you now have the option to apply Backup Filters to device outputs in Config Push before grouping takes place
    - more info: https://wiki.unimus.net/display/UNPUB/Filtering+dynamic+data+in+Config+Push+outputs

  Significant performance improvements:
    - large improvements were made in both application speed and memory usage - especially in large environments
    - the Dashboard will now load properly even with millions of job history records
    - login to Unimus will no longer be slow due to slow Dashboard load even in large environments
    - load speed of data in all tables and forms was also massively improved
    - improved Config search export to handle very large datasets reliably, preventing out-of-memory failures during export
    - rewrote the Config search engine to reduce memory usage and improve execution time

  Added support for devices which require a specific command to be sent before disconnection:
    - this is required on devices which keep persistent management sessions, and require a command to close them
    - this fixes an issue where running many jobs from Unimus in rapid succession could exhause the management session pool on these devices
    - in this release we added support for Arista switches with enabled Config Sessions and RAD Carries Switches with session persistency

  Added support for:
    - Alstom (KB) ElectroBlox
    - Alstom (KB) ElectroLogIXS EC4
    - Alstom (KB) ElectroLogIXS EC5
    - Alstom (KB) ElectroLogIXS VHLC
    - Alstom (KB) microWIU
    - Ansaldo STS MicroLok II VitalNet WIU
    - Cisco Secure Firewall for VMware
    - Digi TransPort WR44 v2
    - DZS (Zhone) MXK-F108
    - DZS (Zhone) V1-16XC
    - F5 F5OS based devices
    - Fiberstore (FS.com) switches running NetworkOS firmware
    - Fiberstore (FS.com) S3100 Series
    - HP(E) ProCurve Switch 4100GL series
    - Meteorcomm ARedge
    - Meteorcomm PTC 220 Base Station Radio
    - Nokia OLTs (MF-2)
    - Radware ADC
    - Radware LB
    - RUGGEDCOM RS900G Ethernet Switches
    - Siemens CNA-2000 Protocol Converter
    - Siemens Trainguard PTC Console
    - Ubiquiti UFiber OLTs with firmware version 4.9.0

Fixes:
  Fixed an issue where Dashboard load (and therefore also login) would be slow with very large job histories (millions of records)
  Fixed an issue where the Backup Retention Policy could remove even the current backup of a device
  Fixed an issue where successive opens of the Device Variable window would contain data for wrong devices
  Fixed an issue where diffs would not display lines ignored by Custom Backup Filters at all instead of showing the "&lt;-filtered-&gt;" fragment
  Fixed an issue where empty lines used for spacing could be wrongly removed from Backups when using Custom Backup Filters
  Fixed an issue where the horizontal scrollbar was missing in the Credentials sections window during Credential Binding
  Fixed unnecessary errors in logs in some cases when a user logged into Unimus from the same browser as a different user
  Fixed ZoneID of a Device on the Device screen becoming N/A after adding a comment (visual issue only)
  Fixed an issue where running a Discovery job caused the Credentials table to refresh repeatedly
  Fixed multiple small UI / UX issues, inconsistencies, element misalignments, etc.
  Fixed Discovery would not work on some Cisco Catalyst switches due to a legacy IOS bug causing the configure prompt to end with "(confi)" instead of "(config)"
  Fixed Discovery would not work on Cambium ePMP devices (broken in 2.6.3)
  Fixed Discovery would not work on Fiberstore / FS.com S2805 series devices
  Fixed Discovery would wrongly identify Fiberstore / FS.com IES3110 series devices as Moxa devices
  Fixed Backup would fail on Fiberstore / FS.com IES 31xx series when pagination was enabled
  Fixed Discovery would fail on Cisco Firepower devices rebranded as Secure Firewall
  Fixed Discovery would fail on specific versions of H3C-based devices (specifically reported on Huawei NE8000)
  Fixed Discovery would fail on older UniFi switch models, including US-8, US-24, and USW-24
  Fixed Discovery would fail on Adit 600 series devices over Telnet
  Fixed Discovery would fail on Aruba 7205 controllers with an asterisk (crash dump indicator) in the prompt
  Fixed Discovery would fail on Aruba APs with latest firmware (AOS-8)
  Fixed issue with Arista switches with enabled Config Sessions where Unimus would eventually exhaust the device's available Config Session limit
  Fixed issue with RAD Carrier Switches with persistent management slots where rapid successive jobs would lead to management session exhaustion
  Fixed Backup would fail on Calix E7-2 EXA devices caused by session alarms / events interfering with the backup
  Fixed Backup would fail with INTERACTION_ERROR on F5 devices using the "imish" shell
  Fixed Backup would fail on Ubiquiti UFiber OLTs after upgrade to latest firmware
  Fixed Discovery would fail on Radware Alteon appliances running the latest firmware versions
  Fixed Discovery could miss-identify specific Cisco IOS XR devices incorrectly as Cisco IOS routers
  Fixed Discovery could fail on HP(E) ProCurve / ProVision switches in specific circumstances
  Fixed Discovery could fail on Aruba AOS and AOS-S switches in specific circumstances
  Fixed Discovery could fail on T-MARC devices if the "show system" command prompted for a password
  Fixed Backup of multi-context ASA firewalls could not back up some contexts with specific CLI settings
  Fixed Custom Backup Flows on A10 Thunder Series would fail if switching partitions
  Fixed Backup would fail on Enterasys / Extreme switches with large configs where the device could take 90+ seconds to even start outputting its backup
  Fixed Discovery of Calix BLC / OccamOS could fail in specific cases
  Fixed Config Push issue where very long commands executed on JunOS devices could result in an INTERACTION_ERROR
  Fixed Config Push issue that could cause invalid commands detection to fail in rare cases
  Fixed incorrect model identification for Fiberstore / FS.COM S3400 and S3900 (non-R) switches during discovery
  Fixed incorrect config change notifications for scep password and tertiary key hashes on FortiOS

Security fixes:
  Fixed an issue where the Zone ID of a device was visible in Config Search results for users without access to the corresponding Zone
  Fixed an issue where using invalid search criteria in Backup Filters would expose Filters which a user should not have had access to
  Fixed multiple cases where the full API tokens were displayed in debug logs

Embedded Core version:
  2.7.0</code></pre><p><br></p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Relaying Unimus logs to a Syslog server ]]></title>
        <description><![CDATA[ This guide walks you through configuring Unimus to forward its internal logs to a remote Syslog server. Centralized logging streamlines log management and troubleshooting across your network environment. ]]></description>
        <link>https://blog.unimus.net/sending-unimus-logs-to-syslog/</link>
        <guid isPermaLink="false">680112b02ab3bd0001f8680c</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Aleš Deák ]]></dc:creator>
        <pubDate>Thu, 10 Jul 2025 02:34:31 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2025/07/Cover_Syslog-Server-@2x---3-.png" medium="image"/>
        <content:encoded><![CDATA[ <h3 id="introduction">Introduction</h3><p>"Logs don't lie – they just patiently wait for you to finally listen."</p><p>Logs are the diary of any system, revealing what’s truly happening behind the scenes amid the chaos of day-to-day operations. They can – and often do – provide invaluable insights during debugging or troubleshooting sessions.</p><p>Today, we’ll look at where Unimus stores its logs and walk through how you can configure Unimus to relay its logs to a Syslog server of your choice. Whether you're troubleshooting, monitoring, or just want to keep all your logs in one place, setting up external log forwarding is a smart move. Let's get started!</p><h3 id="why-centralized-logging-matters">Why centralized logging matters</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2025/07/Syslog-Server-@2x--1-.png" class="kg-image" alt loading="lazy" width="2000" height="898" srcset="https://blog.unimus.net/content/images/size/w600/2025/07/Syslog-Server-@2x--1-.png 600w, https://blog.unimus.net/content/images/size/w1000/2025/07/Syslog-Server-@2x--1-.png 1000w, https://blog.unimus.net/content/images/size/w1600/2025/07/Syslog-Server-@2x--1-.png 1600w, https://blog.unimus.net/content/images/2025/07/Syslog-Server-@2x--1-.png 2160w" sizes="(min-width: 720px) 720px"></figure><p>Unimus logs contain valuable clues about your system's activity: including backup jobs, device discoveries, <strong>Mass Config Pushes</strong>, and more. Integrating these logs into your logging system can simplify troubleshooting by making it easier to correlate Unimus logs with logs from your network devices and other systems. It also streamlines log management, and improves visibility.</p><p>Not to forget, storing Unimus logs in both your local file system and an external Syslog server provides a valuable layer of redundancy.</p><hr><h3 id="finding-your-unimus-logs">Finding your Unimus logs</h3><p>The <strong>Unimus Server</strong> writes log files to the following directories:</p><ul><li>Linux<strong>:</strong> <em>/var/log/unimus</em></li><li>Windows<strong>:</strong> <em>C:\ProgramData\Unimus\log</em><br></li></ul><blockquote>Note: <em>C:\ProgramData\</em> is usually a hidden folder.</blockquote><p>That is pretty much all the essential information you need to get started. However, if you’re curious about the Unimus Logging Subsystem, you can check out <a href="https://wiki.unimus.net/display/UNPUB/Unimus+Logging">our wiki page</a> for more details about the content of Unimus logs, how to customize the logging level, or modify the log rotation settings.</p><h3 id="before-you-configure-unimus">Before you configure Unimus</h3><p>Before configuring Unimus to send logs to your Syslog server, there are a few essential prerequisites to ensure a working setup.</p><p><strong>1. Syslog server setup</strong><br>Before proceeding, make sure that your Syslog server is:</p><ul><li>Enabled and listening on UDP port 514.</li><li>Configured to accept and properly process Syslog messages from your <strong>Unimus server</strong>.</li></ul><p>When it comes to choosing a Syslog server solution, you have plenty of options. Popular choices include the Linux-based <strong>rsyslog </strong>and<strong> Syslog-ng</strong>, or a platform like <strong>Graylog</strong>, a powerful log analysis solution. Alternatively, many Network Monitoring Systems (NMSes) also come with built-in Syslog servers or receivers, such as <strong>Zabbix</strong>, <strong>LibreNMS</strong>, <strong>PRTG</strong>, or <strong>NetXMS</strong>. If you’re already using one of these for collecting your logs or for even inventory synchronization with Unimus, it might make sense to forward your Unimus logs to the same destination for centralized log management.</p><p><strong>2. Ensuring network accessibility</strong><br>Next, verify that the Unimus instance can reach the Syslog server over the network. Make sure that any firewalls between your Unimus instance and the Syslog server are configured to allow traffic on the Syslog port (UDP 514). If this port is blocked by a firewall, the logs will never reach the Syslog server.</p><p>On Linux, the <code>logger</code> command is ideal for testing Syslog. Tools like <strong>Netcat</strong> or <strong>Nmap</strong> can help verify network reachability and whether UDP port 514 is open. Keep in mind that UDP is connectionless, so even if the port is open, you might not get a definitive success/failure unless something responds.</p><!--kg-card-begin: markdown--><pre><code>nmap -sU -p 514 &lt;your_syslog_server_IP&gt;
logger -n &lt;your_syslog_server_IP&gt; -P 514 -d &quot;Test syslog message&quot;
</code></pre>
<!--kg-card-end: markdown--><p>On Windows, tools such as <strong>Packet Sender</strong>, or <strong>Netcat</strong> can help you send test messages to a remote server over UDP 514. You can also use <strong>PowerShell</strong> with .NET classes to craft and send UDP packets manually.</p><p>Once you’ve completed the above checks, you can proceed with the steps below.</p><hr><h3 id="configuring-unimus-to-relay-its-logs-to-a-syslog-receiver">Configuring Unimus to relay its logs to a Syslog receiver</h3><p><strong>1.</strong> Stop the Unimus service.</p><p><strong>2.</strong> Create a file named <code>syslog-logging.xml</code> in <em>/etc/unimus/</em> (or <em>C:\Program Files\Unimus\</em>) with the following contents:</p><!--kg-card-begin: markdown--><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;

&lt;configuration&gt;
    &lt;include resource=&quot;org/springframework/boot/logging/logback/base.xml&quot;/&gt;
    
  &lt;!-- https://logback.qos.ch/manual/appenders.html#SyslogAppender --&gt;
  &lt;appender name=&quot;SYSLOG&quot; class=&quot;ch.qos.logback.classic.net.SyslogAppender&quot;&gt;
    &lt;syslogHost&gt;10.0.0.0&lt;/syslogHost&gt;
    &lt;facility&gt;USER&lt;/facility&gt;
    &lt;suffixPattern&gt;[%thread] %logger %msg&lt;/suffixPattern&gt;
  &lt;/appender&gt;

  &lt;root level=&quot;INFO&quot;&gt;
    &lt;appender-ref ref=&quot;SYSLOG&quot; /&gt;
  &lt;/root&gt;
    
&lt;/configuration&gt;
</code></pre>
<!--kg-card-end: markdown--><p>This is a custom <strong>Logback</strong> configuration file that tells Unimus how to send logs to an external Syslog server — including the destination IP log format, and Syslog facility.</p><p><strong>3.</strong> Replace the <code>syslogHost</code> value with the IP address of your Syslog server. You can adjust other values, such as the suffix pattern or facility, as documented here:<br><a href=" https://logback.qos.ch/manual/appenders.html#SyslogAppender"> <u>https://logback.qos.ch/manual/appenders.html#SyslogAppender</u></a></p><p><strong>4.</strong> For Linux deployments, add the following JVM options to the end of <em>/etc/default/unimus:</em></p><!--kg-card-begin: markdown--><pre><code>-Dlogging.config=/etc/unimus/syslog-logging.xml -Dlogging.file.name=/var/log/unimus/unimus.log -Dlogging.logback.rollingpolicy.max-file-size=50MB -Dlogging.logback.rollingpolicy.max-history=9
</code></pre>
<!--kg-card-end: markdown--><p>For Windows deployments, add the following to<em> C:\Program Files\Unimus\Unimus.l4j.ini:</em></p><!--kg-card-begin: markdown--><pre><code>-Dlogging.config=&quot;C:\Program Files\Unimus\syslog-logging.xml&quot;
-Dlogging.file.name=&quot;C:\ProgramData\Unimus\log\unimus.log&quot;
-Dlogging.logback.rollingpolicy.max-file-size=50MB
-Dlogging.logback.rollingpolicy.max-history=9
</code></pre>
<!--kg-card-end: markdown--><p>If you're running Unimus in a container, specify the above JVM options in the Docker Compose file instead, like this:</p><!--kg-card-begin: markdown--><pre><code>unimus:
    image: croc/unimus
    environment:
      - JAVA_OPTS=-Xms256M -Xmx1024M -Dlogging.config=/etc/unimus/syslog-logging.xml -Dlogging.file.name=/var/log/unimus/unimus.log -Dlogging.logback.rollingpolicy.max-file-size=50MB -Dlogging.logback.rollingpolicy.max-history=9
      - TZ=Europe/Budapest
    container_name: unimus
    ports:
      - &quot;8085:8085&quot;
      - &quot;5509:5509/tcp&quot;
</code></pre>
<!--kg-card-end: markdown--><p>Note that the above setups will relay the contents of the <code>unimus.log</code> file which can be found on a <strong>Unimus server</strong>.</p><p>If you wish to relay the logs from a <strong>Remote Core</strong>, edit the Core config file (<em>/etc/default/unimus-core</em> or <em>C:\Program Files\Unimus\Unimus-Core.l4j.ini</em>) and set the logging file parameter<em> </em>and specify <em>/var/log/unimus-core/unimus-core.log</em> <em>or C:\ProgramData\Unimus-Core\log\unimus-core.log</em>.</p><p><strong>5. </strong>Restart the Unimus (or Unimus Core) service.</p><p>All done!</p><p>After the restart, Unimus will log both to <em>/var/log/unimus/</em> (or <em>C:\ProgramData\Unimus\log</em>) and to the Syslog server specified in the <code>syslog-logging.xml</code> file. From this point on, every log entry written to the file system should also appear on your Syslog server.</p><h3 id="unimus-not-starting">Unimus not starting?</h3><p>If Unimus fails to start, it's likely a syntax issue in your modified Unimus defaults file. Double-check for typos, unintentional or missing line breaks in the JVM options.</p><p>On Linux, make sure to place all the JVM options on a single line. On Windows deployments, each argument should be placed on its own line, and if the path argument contains spaces, wrap it in double quotes.</p><hr><h3 id="whats-next">What's next?</h3><p>Once the Unimus logs are flowing to your syslog server, the fun is not over. Modern Syslog collectors with log analysis capabilities let you go far beyond basic log collection.</p><p>You can correlate Unimus logs with those from routers, firewalls, and other systems to gain deeper insights. Want to detect when a <strong>Unimus server</strong> loses connection to a <strong>Remote Core</strong>? Create an event policy to trigger alerts or auto-generate tickets in your monitoring tools via API. Need to pinpoint the root cause of a BGP flap? Cross-reference <strong>Mass Config Push</strong> activity with network device logs. Focus on logs with severity levels 4 (WARN) and 3 (ERROR), as those specifically report problems that need attention.</p><p>With the right setup, your Unimus logs can become a rich source of operational intelligence, supporting you in troubleshooting when things go south.</p><h3 id="final-words">Final words</h3><p>Hopefully, this article has been helpful for those of you who’ve been looking for a way to set up log relaying in Unimus. If you have any questions or run into any issues that we didn’t foresee in this article, please feel free to post in the <a href="https://forum.unimus.net/viewforum.php?f=9">support section of our forums</a> or reach out through our usual support channels.</p><p>Until next time, take care!</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.6.0 ]]></title>
        <description><![CDATA[ Unimus 2.6.0 is out! This release contains 3 major new features, 20+ minor features and enhancements, support for 19 new device types, and fixes for various bugs and issues. Check out what's new in this article... ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-6-0/</link>
        <guid isPermaLink="false">6762d63a67609f0001939778</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 18 Dec 2024 16:07:25 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2024/12/260.png" medium="image"/>
        <content:encoded><![CDATA[ <p>A new major feature release - Unimus 2.6.0 was released today! This release contains 3 major new features, 20+ minor features and enhancements, support for 19 new device types, and a healthy dose of bug and security fixes.</p><p>This article is an overview of the biggest changes and features in 2.6.0, but if you want to read up on everything that is new, you can find the full Changelog at the bottom of this article.</p><hr><h2 id="device-cli">Device CLI</h2><p>The biggest feature of 2.6 is a new terminal emulator directly in your Unimus web UI.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/12/image-2.png" class="kg-image" alt loading="lazy" width="1516" height="897" srcset="https://blog.unimus.net/content/images/size/w600/2024/12/image-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/12/image-2.png 1000w, https://blog.unimus.net/content/images/2024/12/image-2.png 1516w" sizes="(min-width: 720px) 720px"></figure><p>This gives you the ability to connect to the CLI of your devices directly from Unimus. The connection to Device CLI works seamlessly with our <a href="https://wiki.unimus.net/display/UNPUB/Architecture+overview">distributed deploy using Remote Cores</a>, so you can easily connect to CLI of devices in remote networks with just a few clicks.</p><p>With this new feature, Unimus (on top of all the other features) also becomes a remote access gateway / jump host to all your device CLIs!</p><hr><h2 id="mfa-totp-support-for-unimus-login">MFA (TOTP) support for Unimus login</h2><p>Another new feature in 2.6 is support for TOTP-based MFA when logging in to Unimus.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/12/image-1.png" class="kg-image" alt loading="lazy" width="877" height="374" srcset="https://blog.unimus.net/content/images/size/w600/2024/12/image-1.png 600w, https://blog.unimus.net/content/images/2024/12/image-1.png 877w" sizes="(min-width: 720px) 720px"></figure><p>For improved security, you can enable MFA, and it will be required when logging in to Unimus. This will work with all your usual MFA apps (or hardware TOTP MFA tokens).</p><p>In 2.6 we are adding support for TOTP-based MFA, but going forward we will be expanding this to support FIDO2-compatible hardware keys and passkeys in the future.</p><hr><h2 id="new-my-account-screen">New "My account" screen</h2><p>With the inclusion of MFA support, we also added a new "My account" screen.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/12/image-3.png" class="kg-image" alt loading="lazy" width="1652" height="919" srcset="https://blog.unimus.net/content/images/size/w600/2024/12/image-3.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/12/image-3.png 1000w, https://blog.unimus.net/content/images/size/w1600/2024/12/image-3.png 1600w, https://blog.unimus.net/content/images/2024/12/image-3.png 1652w" sizes="(min-width: 720px) 720px"></figure><p>Non-admin accounts (that don't have access to the <code>User management</code> screen) can change their own password here, assuming local auth (not LDAP/Radius) is used. Users can also set up their MFA here.</p><hr><h2 id="database-improvements-and-postgresql-v12">Database improvements and PostgreSQL v12+</h2><p>Support for modern PostgreSQL versions is also finally here! We now support Postgre 12 through 17, while still keeping the existing support for 9 through 11.</p><p>We have also introduced separation for MariaDB vs. MySQL database drivers. During the Deploy Wizard, you will be able to select the exact version of your SQL database now. This will allow us to introduce flavor-specific optimizations into the DB layer of Unimus going forward.</p><p>We also published a full list of officially supported and tested DB versions on our Wiki: <a href="https://wiki.unimus.net/display/UNPUB/Database+requirements">Database requirements</a></p><hr><h2 id="20-minor-enhancements-and-improvements">20+ minor enhancements and improvements</h2><p>In addition to the features above, 2.6 also brings a few other minor new features and 20+ enhancements and improvements to existing features. A few of the notable ones are:</p><ul><li>Unified usage of server time (instead of browser local time) for the Backups screen, Diff screen and Config Change notifications</li><li>Improved filtering of dynamic backup data when using Custom Backup Flows. Filters are now applied to outputs of each command, so you don't need to setup Backup Filters manually.</li><li>You can now have multiple backup windows open in "Backups &gt; Show" at the same time. No need to close and reopen windows anymore.</li><li>Added a "Create another step" option in Custom Backup Flows creation for better UX.</li><li>Support for post-login log messages on Lenovo switches, improvements to multi-context ASA handling, improved built-in backup filters.</li><li>And much more...</li></ul><hr><h2 id="device-support-bug-fixes-and-security-improvements">Device support, bug fixes and security improvements</h2><p>In 2.6, we are adding support for 19 new device types across 14 different vendors. Our full supported device list is now over 350+ various device types. Check the full Changelog below for the exact additions in 2.6.</p><p>As always, we are also fixing bugs, solving issues, and improving security. In this release over 25 issues got fixed, from annoying to trivial. More in the full Changelog below...</p><hr><p>Finally, here is the full 2.6.0 Changelog:</p><pre><code>= Version 2.6.0 =
Features:
  Added native support for MFA / TOTP for login to Unimus
  Added support for PostgreSQL v12 and newer (12-17)
  Users using local auth can now change their own passwords
  Unified usage of server time (instead of browser local time) for the Backups screen, Diff screen and Config Change notifications
  Multiple Network Scans when using the Embedded Core are now properly queued and executed in sequence
  Added a "Copy key" button to copy a Zone Access Key to clipboard
  Improved filtering of dynamic backup data in Custom Backup Flows, filters are now applied to outputs of each command
  Added separation between MySQL and MariaDB drivers in Wizard and config, and optimization for each
  Export windows will now automatically close after a successful export (Backup export, Config Search export, etc.)
  Configured context size for Config Search is also applied to Config Search Export
  You can now have multiple backup windows open in "Backups &gt; Show" at the same time
  Added a "Create another step" option in Custom Backup Flows creation for better UX
  Improved handling of object ownership changes across the entire application
  Various minor UI / UX improvements and tweaks
  Added support for post-login log messages on Lenovo switches
  Added handling for multi-context Cisco ASA, if unable to switch to system context, just backup a single context instead of failing
  Improved log messages on failed device logins for easier visibility into failed jobs
  Improved device authentication algorithm when using SSH
  Improved sorting for random ordering of dynamic rules on Palo Alto PanOS when managed by Panorama
  Improved handling of failed mode switches in the Discovery algorithm
  Improved handling of password change requests during device logins (Unimus will detect this, cancel login, and show proper error)
  Added builtin support for the "Power Off the system ? [yes,no]" prompt (JunOS) in Config Push
  Added builtin support for the "Are you sure you want to continue connecting?" prompt in Config Push
  Improved builtin backup filters for Ericsson IPOS
  Improved builtin backup filters for IOS XR

  Added a new "My account" section:
    - users can change their own password if not using external auth
    - users can enable / manage MFA for their account
    - more per-account settings to be added to this screen soon
    - more info: https://wiki.unimus.net/display/UNPUB/User+accounts

  Added a new "Device CLI" feature:
    - you can now open a CLI session to your device directly in Unimus
    - this is a full web-based terminal emulator, making Unimus into a remote access gateway to all your devices
    - this can be disabled if desired: https://wiki.unimus.net/display/UNPUB/Disabling+specific+Unimus+features
    - full documentation: https://wiki.unimus.net/display/UNPUB/Device+CLI

  Added support for:
    - ArubaOS-CX Virtual
    - Broadcom Trident / Trident2 based devices
    - Cisco Catalyst 1300 series
    - Cisco IOS XRv / vIOS XR
    - Edgewater EdgeMarc
    - Fiberstore (FS.com) AC Wireless Controller
    - Fiberstore (FS.com) S3250
    - Fiberstore (FS.com) Wireless Switch
    - FreeWave radios (based on 900 series)
    - LDA Tech (LDAtech) MUX
    - Nokia WaveLite (based on Metro 200)
    - OcNOS-SP
    - OcNOS VM
    - Ribbon EdgeMarc
    - Supermicro SMIS modules and switches
    - Supermicro GEM (MBM-GEM, SMB-GEM) switches
    - Supermicro XEM (MBM-XEM, SMB-XEM) switches
    - Supermicro SSE switches
    - Telrad BreezeCompact (based on 1000e)
    - Versa SDWAN (VOS)
    - Wi-Tek (WiTek) switches

Fixes:
  Fixed notifications not working with private Slack channels
  Fixed issues where different time (browser vs. local) would be used for the Backup timeline vs. Diffs and Config Change notifications
  Fixed issue that could cause NMS Sync Rule Group IDs to be deleted when migrating from 2.5.0 to 2.5.1 (no other migration paths were affected)
  Fixed device presence in Zone in Network Scan would always be compared to the Default Zone, instead of the Zone selected in the Scan
  Fixed devices with "Planned" status being imported as Managed from Netbox / Nautobot
  Fixed a possible issue with migration of NMS Sync rules introduced in 2.5.1
  Fixed wrong examples in APIv3 documentation for a few endpoints
  Fixed built-in dynamic data backup filters would not be properly applied to outputs of Custom Backup Flows in some cases
  Fixed devices would take extremely long to delete if deleted during an ongoing job
  Fixed stopping a running Network Scan could take a very long time
  Fixed multiple Network Scans using the Embedded Core would not work properly
  Fixed Ownership updates not being propagated between concurrent users (live updates were missing)
  Fixed "Schedules &gt; Show scheduled tasks" not being updated correctly on changes (live updates were missing)
  Fixed issues when trying to download an export of Config Search multiple times
  Fixed issue where Config Search export configuration could be ignored, and defaults were used
  Fixed config change notifications even when nothing changed on PA PanOS when managed by Panorama
  Fixed an issue when trying to sort by "Present in Zone" in Network Scan
  Fixed multiple small UI / UX issues, inconsistencies, element misalignments, etc.
  Fixed backups on Palo Alto devices could sometimes be empty, or only contain the backup command echo
  Fixed discovery failing on newer versions of ArubaOS-CX
  Fixed jobs failing on specific versions of IOS XR
  Fixed parts of device output missing in Config Push results in very rare cases
  Fixed HP(E) ProCurve / ProVision / ArubaOS failing discovery in specific cases
  Fixed Juniper JunOS devices could fail discovery in specific cases
  Fixed backups could fail on Ericsson SSR / IPOS in certain multi-context configurations
  Fixed backups could fail on Adva XG devices with specific firmware versions
  Fixed discovery failing on Quanta running on Broadcom Trident2
  Fixed Config Push could fail on Palo Alto devices in some cases

Security fixes:
  Fixed a rare case when switching a user to a restricted Role (like Read Only) would only be applied after the user logged out
  Fixed an already opened Device Info window would not be closed if user lost access to the device
  Fixed Operator-level users seeing the instance license key in Other settings
  Fixed users which did not have access to all devices in Zone being able to download all Zone logs

Embedded Core version:
  2.6.0
</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.5.0 ]]></title>
        <description><![CDATA[ Unimus 2.5.0 is out! This is a major release with many new features and improvements. In this article we review what's new - from new functionality to support for new devices, fixes for various bugs and issues, and security improvements. ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-5-0/</link>
        <guid isPermaLink="false">6698493ad32df10001158991</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Thu, 18 Jul 2024 12:03:10 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2024/07/opengraph-web-1.png" medium="image"/>
        <content:encoded><![CDATA[ <p>A new major feature Unimus release - 2.5.0 is finally here! This is a large release with lots of new features, enhancements, new device support, and a healthy dose of bug and security fixes.</p><p>This article is an overview of the biggest changes and features in 2.5.0, but you can also find the full Changelog at the bottom of this article.</p><hr><h2 id="custom-backup-flows">Custom Backup Flows</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-15.png" class="kg-image" alt loading="lazy" width="992" height="736" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-15.png 600w, https://blog.unimus.net/content/images/2024/07/image-15.png 992w" sizes="(min-width: 720px) 720px"></figure><p>One of the biggest features in 2.5 is the ability to create Custom Backup Flows. These Flows will allow you to specify your own sequences of commands to be executed during backup jobs on devices.</p><p>If you want to do backups on a device differently from our built-in flow (for example, add outputs of a few commands to be a part of the backup), up until now you didn't have a way to. Custom Backup Flows allow you to create your own backup procedures, so if you want to do a backup differently than our built-in flow, you now can.</p><p>You can find more info on Custom Backup flows, with more details and examples in this <a href="https://blog.unimus.net/custom-backup-flows-in-unimus-2-5/">article on the Blog</a>, or on <a href="https://wiki.unimus.net/display/UNPUB/Backup+Flows">our Wiki</a>.</p><hr><h2 id="new-object-access-policies-and-improvements-to-user-management">New Object Access Policies and improvements to user management</h2><p>2.5 also brings a lot of improvements and changes to User Management and introduces new Object Access Policies.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-16.png" class="kg-image" alt loading="lazy" width="1202" height="519" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-16.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-16.png 1000w, https://blog.unimus.net/content/images/2024/07/image-16.png 1202w" sizes="(min-width: 720px) 720px"></figure><p>Object Access policies replace Device Access rules (which existed before 2.5), and offer much better flexibility to define exact object access rules you need.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-17.png" class="kg-image" alt loading="lazy" width="1234" height="699" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-17.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-17.png 1000w, https://blog.unimus.net/content/images/2024/07/image-17.png 1234w" sizes="(min-width: 720px) 720px"></figure><p>Together with new features like the Ownership system improvement and User provisioning (more on this below), the User Management system in Unimus receives a nice boost in 2.5.</p><p>For full info on what is new and what has checked, please check our <a href="https://blog.unimus.net/user-management-provisioning-and-access-policies-in-unimus-2-5/">Improvements to user management, provisioning and access policies in Unimus 2.5</a> on the Blog.</p><hr><h2 id="user-provisioning-automatic-new-user-creation">User provisioning (automatic new user creation)</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-18.png" class="kg-image" alt loading="lazy" width="659" height="215" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-18.png 600w, https://blog.unimus.net/content/images/2024/07/image-18.png 659w"></figure><p>Like we mentioned above, we have added an option to automatically create new users in Unimus when they successfully authenticate against an external AAA system for the first time. This allows for automatic user provisioning in Unimus.</p><p>After enabling this function, new users you create in LDAP / Radius can now login to Unimus without any manual user creation.</p><hr><h2 id="ownership-for-tags-and-zones">Ownership for Tags and Zones</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-19.png" class="kg-image" alt loading="lazy" width="1150" height="503" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-19.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-19.png 1000w, https://blog.unimus.net/content/images/2024/07/image-19.png 1150w" sizes="(min-width: 720px) 720px"></figure><p>We have extended the <a href="https://wiki.unimus.net/display/UNPUB/Object+Ownership" rel="noopener">Ownership system</a> to include Tags and Zones. We also added an option to show all objects owned by a particular user in the User Management screen.</p><p>These changes improve the overall access and security model in Unimus, and solve a few edge-cases where users could create new objects and immediately lose access to them due to access restrictions.</p><hr><h2 id="netbox-support-in-nms-sync">NetBox support in NMS Sync</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-20.png" class="kg-image" alt loading="lazy" width="1656" height="636" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-20.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-20.png 1000w, https://blog.unimus.net/content/images/size/w1600/2024/07/image-20.png 1600w, https://blog.unimus.net/content/images/2024/07/image-20.png 1656w" sizes="(min-width: 720px) 720px"></figure><p>Due to popular demand from the community (you!), we have added support for NetBox in NMS Sync.</p><p>Together with <a href="https://blog.unimus.net/new-nms-sync-logic-2-4-0/">improvements to NMS Sync</a> we introduced in 2.4, you can now sync your NetBox inventory to Unimus, sync object state and use NetBox as the source of truth for Unimus.</p><hr><h2 id="tons-of-minor-features-and-improvements">Tons of minor features and improvements</h2><p>On top of the major features mentioned above, this release also contains over 20 minor features and improvements.</p><p>A few notable changes:</p><ul><li>Added option to create a new Credential directly in the Credential Binding window</li><li>Tags can now be edited (you can change the Name or Owner)</li><li>Added additional "Used by..." columns to the Tags table showing usage of Tags across Unimus</li><li>Added a link to open the last failed job details to the "Device &gt; Info" window</li><li>Added an option to not show Unmanaged devices in results of Config Search</li><li>Added an icon for credentials in High Security Mode to all relevant tables</li><li>Added an option to specify your own Pushover API Key in Pushover settings</li><li>Added an option to select the color scheme of diffs sent by notifications</li><li>etc.</li></ul><p>As always, we are also expanding the list of devices supported by Unimus. In this release, we added support for 12 new device types. Check the Changelog below for full info!</p><hr><h2 id="bug-fixes-and-security-improvements">Bug fixes and security improvements</h2><p>As always, we are also fixing bugs, solving issues, and improving security. In this release, we fixed 20+ bugs - from minor annoying issues all the way to jobs failing on various device types.</p><p>We have also focused quite a bit of attention on security. We have tightened the access restrictions for non-admin roles, but also fixed 10+ various security-related issues.</p><p>Please see the Changelog below for full info.</p><hr><p>Finally, here is the full 2.5.0 Changelog:</p><pre><code>= Version 2.5.0 =
Features:
  Device Tags have been renamed to just Tags, since they can be used on many more objects than just Devices now
  Tags can now be edited, allowing for change of Name or Owner (more on Ownership later)
  Changed default job concurrency (max number of parallel jobs) to 50
  When deleting a Zone, you can now choose to move devices to any other Zone you have access to before deleting the Zone
  Added an option to create a new Credential directly in the Credential Binding window
  Updated NetXMS client library to latest version (5.0.3)
  Added a Zone ID column to "Backups &gt; Devices" table
  Added a link to open the last failed job details to the "Device &gt; Info" window
  Added a notification banner to "Backup Filters" when user doesn't see all filters due to Access Policy restrictions
  Added a notification banner when trying to edit a Backup Filter when you don't have access to all devices covered by that filter
  Added a better message when a user with the "None" role attempts to log in
  Added additional "Used by..." columns to the Tags table showing usage of Tags across Unimus
  Added an option to not show Unmanaged devices in results of Config Search
  Added an icon for credentials in High Security Mode to all relevant tables
  Added an option to specify your own Pushover API Key in Pushover settings
  Added an option to select the color scheme of diffs sent by notifications
  Added a help popup to "Notifications &gt; Show FQDN"
  Fixed various small UI / UX issues and UI element misalignment and sizing issues
  Changed Cisco ASA multi-context driver to only attempt backing up contexts when switching to the "system" context is possible
  Added support for offer prompts when a device offers multiple corrective options for invalid commands
  Improved handling of Nokia SROS / TimOS devices, fixing multiple issues in the process
  Improved support for Raisecom RAX / ISCOM devices (more device types now supported)

  Added new "Custom Backup Flows" feature:
    - you can now create presets that specify what commands are sent to Devices during Backup
    - you can also specify pre-backup commands, post-backup commands, and what is consumed as the backup content
    - if a Custom Flow exists for a Device, it will be used instead of the built-in flow in the Device Driver
    - you can target devices by Tag, Vendor, Type, etc.
    - more info at: https://blog.unimus.net/custom-backup-flows-in-unimus-2-5/

  Added support for NetBox in NMS Sync:
    - you can now sync your NetBox inventory into Unimus
    - import filtering based on "role", "tag", "location" and "field" (Custom Fields) is available
    - the "status" field in NetBox is used to set the Managed flag in Unimus
    - more info at: https://wiki.unimus.net/display/UNPUB/NetBox+importer

  Prefixes for filters in NMS Sync were replaced by a key-value system
    - until this release, entries in Sync Rules needed prefixes, with each prefix meaning something different
    - this was inconsistent across different Sync Connectors, and also quite confusing (you had to read docs every time on what prefix does what)
    - we replaced prefixes with a Key=Value system (for example "id=123", "group=routers", etc.)
    - existing Sync Rule configuration will be automatically migrated to the new system

  Device Access was reworked into Object Access Policies:
    - you can now create complex Object Access policies which specify where a user should have access to
    - Object Access Policies can then be assigned to users to limit object access across Unimus
    - existing Device Access rules will be migrated to new Object Access Policies automatically
    - more info at: https://blog.unimus.net/user-management-provisioning-and-access-policies-in-unimus-2-5/

  Added an option to create user accounts for users successfully authenticated by an external auth system:
    - this allows provisioning of users on first successful login to Unimus when using Radius / LDAP auth
    - using this system, you no longer need to create user accounts in Unimus for external AAA users before they can log in
    - both Role and Object Access Policy for automatically created accounts are configurable
    - more info at: https://blog.unimus.net/user-management-provisioning-and-access-policies-in-unimus-2-5/

  Object Ownership system has been extended to Tags and Zones:
    - Tags and Zones now have an "Owner" attribute, same as Devices
    - access to these objects can now be gained by being their Owner, separately from Object Access Policies
    - ownership has precedent over Object Access Policies - owners always have access to objects owned by them

  You can now see all Objects owned by a User in User Management:
    - new "Show object ownership" button was added in User management
    - this will show all Objects, as well as their types owned by this User
    - you can also remove ownership of Objects from this User in this window

  Improvements to APIv2 / APIv3:
    - added the zoneId attribute to all Devices and Diff APIv2 endpoints
    - added the zoneId attribute to multiple response objects in APIv3 where it was missing

  Added support for:
    - Cisco IOL (IOS on Linux) switches
    - Cisco IOL (IOS on Linux) routers
    - CheckPoint Gaia running on bare metal
    - CheckPoint TE series
    - CheckPoint QLS (Quantum Light Speed)
    - iS5 IMX devices
    - iS5 iES devices
    - Netonix WS3 switches
    - Racom RAy
    - Raisecom REAP OS devices
    - Ruckus vSZ-H
    - SONiC OS

Fixes:
  Fixed Config Change notifications would not apply Backup Filters in the notification diff when a new changepoint was generated
  Fixed importing valid .csv files with formatting errors could result in a stuck Import job
  Fixed "Export Diff" ignored the "Only changed lines" checkbox, and always sent only changes
  Fixed selection model breaking in the Credentials table after editing a Credential
  Fixed issues when changing large amount of objects (2000+) in a single operation when using MSSQL
  Fixed multiple other object manipulation failures when using MSSQL (Device Zone change, etc.)
  Fixed selected Zone disappearing from the Zone selection dropdown in "Basic import" after a successful import
  Fixed config change notifications even when nothing changed on PA PanOS when managed by Panorama
  Fixed issue in API with Zones which had a NetXMS Agent selected as their Connection method
  Fixed Mass Config Push &gt; Advanced Settings allowed setting an empty value for Override Timeouts
  Fixed live updates there were missing in multiple screens, tables, and "used by" counters
  Fixed many various minor UI and UX issues and inconsistencies
  Fixed wrong / extraneous logging during the database upgrade stage when updating
  Fixed OPNSense jobs failing when device presented a menu after switching to root
  Fixed discovery failing on Ericsson SGSN in specific cases
  Fixed multi-context backup failing on Ericsson IPOS in specific cases
  Fixed a few specific Cisco router models being identified as switches
  Fixed backup failing on Cisco routers that were incorrectly identified as switches
  Fixed discovery could fail on Nokia SROS / TimOS devices on specific version
  Fixed backup and Config Push could fail on Nokia SROS / TimOS devices on specific version
  Fixed backup and Config Push could on specific version of NetElastic vBGN
  Fixed Cisco ASA backup failing when logging into a context without the ability to switch into the "system" context
  Fixed more cases when jobs could fail on Checkpoint Gaia devices

Security fixes:
  Only Administrator-level users can now change Notification settings
  Only Administrator-level users can now change Retention settings
  Only Administrator-level users can now change Advanced System Settings
  NMS Sync Presets where "Device Action policy" is set to "Move from All Zones" are now read-only for Users who do not have access to all Zones
  Users who do not have access to all Zones can not select the "Move from All Zones" in "Device Action policy" when creating a new NMS Sync Preset
  Users will not see an NMS Sync Rule if they don't have access to the Zone selected for that Rule
  For Presets using Tags (Config Push, Backup Filters, etc.), only Users with access to all Devices under that Tag can manage the Preset
  Users can no longer edit Credentials that are used on Devices they don't have access to
  Users can no longer edit CLI Mode Change Passwords used on Devices they don't have access to
  If a User doesn't have access to all Devices in Unimus, they can no longer change the Default Schedule
  Users can no longer delete a Schedule if they don't have access to all Devices that use that Schedule
  Fixed cases where Users could see Backup Filters even for Devices they did not have access to
  Fixed Users could still see and modify Targets in Config Push if Object ownership was modified concurrently

Embedded Core version:
  2.5.0</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Improvements to user management, provisioning and access policies in Unimus 2.5 ]]></title>
        <description><![CDATA[ In Unimus 2.5, we are bringing changes to user management, introducing new user provisioning and new Object Access Policies. Check out what's new in this article. ]]></description>
        <link>https://blog.unimus.net/user-management-provisioning-and-access-policies-in-unimus-2-5/</link>
        <guid isPermaLink="false">669650bfd32df10001158868</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 17 Jul 2024 13:34:48 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2024/07/opengraph-1-.png" medium="image"/>
        <content:encoded><![CDATA[ <p>With the 2.5.0 release, we are bringing a lot of improvements and changes to User Management and Access Policies in Unimus. We are also adding the ability for User Provisioning from external AAA sources (LDAP or Radius).</p><p>In this post, we would like to walk you through what is new, and what has changed.</p><h1 id="how-user-management-and-security-worked-before-25">How user management and security worked before 2.5</h1><p>We've had full role-based access controls (RBAC), as well as per-object access policies in Unimus for a long while now. We also have support for external auth (AAA) from LDAP and/or Radius.</p><p>How this worked is that you would first setup your external AAA connector - either LDAP or Radius (assuming you wanted to use external AAA). </p><p>Then you would create a local Unimus user. You would set this user to use your preferred auth method, either local, or external. You could also assign a Role for your user, limiting them to specific actions (more info in our <a href="https://wiki.unimus.net/display/UNPUB/Account+security+roles">Role documentation</a>).</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-7.png" class="kg-image" alt loading="lazy" width="1102" height="717" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-7.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-7.png 1000w, https://blog.unimus.net/content/images/2024/07/image-7.png 1102w" sizes="(min-width: 720px) 720px"></figure><p>After the user was created, you could then limit which objects they have access to in Unimus using the Device Access rules. By default, users would have access to all devices, and you could restrict that access to only devices with particular Tags.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-6.png" class="kg-image" alt loading="lazy" width="785" height="606" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-6.png 600w, https://blog.unimus.net/content/images/2024/07/image-6.png 785w" sizes="(min-width: 720px) 720px"></figure><p>Using the combination of roles and Device Access rules, you could achieve control of both what users can do (limit their actions), as well as which objects they can see and perform actions on.</p><h1 id="why-are-we-changing-this">Why are we changing this</h1><p>The existing system had a few limitations. In particular:</p><ul><li>A local Unimus user had to exist before they could auth from an external auth source. This means that even if you were using LDAP as your auth source, when adding a new user, you would still have to log in to Unimus and create a local user and set it to auth from LDAP. This was a little tedious.</li><li>During a new deployment in large networks, even if you had a single central auth source, you still needed to create all users in Unimus locally (related to previous point).</li><li>Due to how Device Access rules worked, you could not, for example, create a rule which gave access to all devices other than a particular device set.</li><li>The <a href="https://wiki.unimus.net/display/UNPUB/Object+Ownership">Ownership system</a> was only applied to Devices, so users could create other objects and immediately lose access to them due to their access rules.</li><li>You had no way to see all objects owned by a User.</li></ul><p>In 2.5.0, we hope to solve all these issues.</p><h1 id="what-is-new-in-unimus-25">What is new in Unimus 2.5</h1><p>To solve the issues described above, we have made 3 major changes to the user management system in Unimus:</p><ul><li>We have reworked Device Access rules into Object Access Policies.</li><li>We have extended the <a href="https://wiki.unimus.net/display/UNPUB/Object+Ownership">Ownership system</a> to now cover not just Devices, but also Tags and Zones.</li><li>We have created an Automatic User Creation system, which can be used for easy User provisioning.</li></ul><p>Let's look at each of these in detail:</p><h2 id="object-access-policies">Object Access Policies</h2><p>Device Access rules have been replaced by Object Access Policies. A Policy has 2 components, a "base access" component and optional exceptions.</p><p>There are 2 base access policies:</p><ul><li>All objects</li><li>No objects</li></ul><p>You can combine these base policies with Tag-based exceptions, to create a custom access policy that covers exactly the objects you need.</p><p>First you need to create a policy:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-8.png" class="kg-image" alt loading="lazy" width="1202" height="519" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-8.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-8.png 1000w, https://blog.unimus.net/content/images/2024/07/image-8.png 1202w" sizes="(min-width: 720px) 720px"></figure><p>After creating a policy, if it has exceptions, you can select which Tags are applied as the exception. In the example below, the user to which this policy would be applied would only have access to objects tagged by the "Routers" Tag.</p><p>This is because the base policy is "No objects", but there is an exception for the Routers Tag, allowing access to those objects.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-9.png" class="kg-image" alt loading="lazy" width="1234" height="699" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-9.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-9.png 1000w, https://blog.unimus.net/content/images/2024/07/image-9.png 1234w" sizes="(min-width: 720px) 720px"></figure><p>All of your existing Device Access rules will be converted to the new Object Access Policies automatically after you upgrade to 2.5.0. You can find more info on Object Access Policies in our <a href="https://wiki.unimus.net/display/UNPUB/Object+Access+Policies">documentation on our Wiki</a>.</p><h2 id="ownership-for-tags-and-zones">Ownership for Tags and Zones</h2><p>We have extended the <a href="https://wiki.unimus.net/display/UNPUB/Object+Ownership">Ownership system</a> to include Tags and Zones:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-12.png" class="kg-image" alt loading="lazy" width="1150" height="503" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-12.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-12.png 1000w, https://blog.unimus.net/content/images/2024/07/image-12.png 1150w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-11.png" class="kg-image" alt loading="lazy" width="1649" height="277" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-11.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-11.png 1000w, https://blog.unimus.net/content/images/size/w1600/2024/07/image-11.png 1600w, https://blog.unimus.net/content/images/2024/07/image-11.png 1649w" sizes="(min-width: 720px) 720px"></figure><p>We also added an option to show all objects owned by a particular user:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-14.png" class="kg-image" alt loading="lazy" width="1629" height="514" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-14.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-14.png 1000w, https://blog.unimus.net/content/images/size/w1600/2024/07/image-14.png 1600w, https://blog.unimus.net/content/images/2024/07/image-14.png 1629w" sizes="(min-width: 720px) 720px"></figure><h2 id="automatic-user-creation-user-provisioning">Automatic User Creation (User provisioning)</h2><p>Finally, we have also added an option to automatically create new users in Unimus when they successfully auth from an external AAA system for the first time. This allows for automatic user provisioning in Unimus.</p><p>You can select both the Role, as well as the Object Access Policy you want the new users created with, so you can easily secure these new accounts, and elevate their permissions if needed.</p><p>The configuration is very simple:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-13.png" class="kg-image" alt loading="lazy" width="659" height="215" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-13.png 600w, https://blog.unimus.net/content/images/2024/07/image-13.png 659w"></figure><p>After enabling this function, new users you create in LDAP / Radius can now login to Unimus without any manual user creation.</p><h1 id="final-words">Final words</h1><p>We hope these new features and options add more flexibility and options to our (and your) security model, and simplify user creation and management for you. If you run into any issues, or want to let us know if anything is missing, please head to our forums, either the <a href="https://forum.unimus.net/viewforum.php?f=9" rel="noopener">support section</a> or the <a href="https://forum.unimus.net/viewforum.php?f=10" rel="noopener">feature request section</a>, and let us know!</p><p>The new user management functions are available starting with Unimus 2.5.0. Please head over to the <a href="https://unimus.net/download.html" rel="noopener">Download</a> section to download the latest Unimus release.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Custom Backup Flows in Unimus 2.5 ]]></title>
        <description><![CDATA[ Custom Backup Flows allow you to specify your own sequences of commands to be executed during backup jobs on devices. ]]></description>
        <link>https://blog.unimus.net/custom-backup-flows-in-unimus-2-5/</link>
        <guid isPermaLink="false">669043a95df8f90001d1e169</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Tue, 16 Jul 2024 14:07:38 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2024/07/opengraph-web.png" medium="image"/>
        <content:encoded><![CDATA[ <p>We are adding the ability to create your own Custom Backup Flows in Unimus 2.5. These Flows will allow you to specify your own sequences of commands to be executed during backup jobs on devices.</p><h2 id="when-will-backup-flows-be-useful">When will Backup Flows be useful?</h2><p>Up until 2.5, how a Device Backup is performed is hard-coded in Device Drivers built into Unimus. When we add support for new devices, we implement a Backup Flow inside the Device Driver. This assures that all devices on our <a href="https://wiki.unimus.net/pages/viewpage.action?pageId=10092755">list of supported devices</a> behave consistently and work out of the box.</p><p>However, if you would like to do backup on a device differently from our built-in flow (for example, add outputs of a few commands to be a part of the backup), up until now you didn't have a way to. Custom Backup Flows allow you to create your own backup procedures, so if you want to do a backup differently than our built-in flow, you now can.</p><h2 id="custom-flows-vs-built-in-flows">Custom Flows vs. Built-in Flows</h2><p>As we mentioned before, Custom Flows will override built-in Flows for devices which have a Custom Flow assigned. You can target devices either by their Vendor, Device Type, or by Tags.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-1.png" class="kg-image" alt loading="lazy" width="599" height="155"></figure><p>Since there are multiple ways to target devices with Custom Flows, it is possible to create "conflicts", where more than one Custom Flow exists for a device. If a conflict like this exists, Unimus will execute the default Flow built in the Device Driver.</p><p>To check if there is a Flow conflict on a device, you can navigate to the <code>Devices</code> screen, select the device, click <code>Info &gt; Backup Flows</code>. A table will show all Flows assigned to this Device. If there is more than one, you have a conflict on your hands.</p><h2 id="how-do-you-define-a-custom-flow">How do you define a Custom Flow?</h2><p>A Backup Flow is a sequence of steps performed on a device. As such, for a Custom Flow you need to define these steps yourself. In the Flow creation window, you can click <code>Add step</code>, and select the step type:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-2.png" class="kg-image" alt loading="lazy" width="1001" height="488" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2024/07/image-2.png 1000w, https://blog.unimus.net/content/images/2024/07/image-2.png 1001w" sizes="(min-width: 720px) 720px"></figure><p>There are options to switch the device into Enable (privileged-exec) or Configure mode. This works in a very similar way to <a href="https://wiki.unimus.net/display/UNPUB/Mass+Config+Push">Config Push</a>. You can see more info on modes here: <a href="https://wiki.unimus.net/display/UNPUB/Device+mode+table">https://wiki.unimus.net/display/UNPUB/Device+mode+table</a>.</p><p>The most used step type is the <code>Send command</code>. This step will send the command you provide to the device. You can then choose if the output of this command should be consumed into the final Device Backup. You can also choose whether this command failing should fail the entire Backup job, or if the failure should be ignored, and next step should be executed.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-3.png" class="kg-image" alt loading="lazy" width="587" height="476"></figure><p>If you just want to run some pre and post-backup commands, you can simply set them to be excluded from the final backup.</p><h2 id="example">Example</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image.png" class="kg-image" alt loading="lazy" width="992" height="736" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image.png 600w, https://blog.unimus.net/content/images/2024/07/image.png 992w" sizes="(min-width: 720px) 720px"></figure><p>Here is an example of how a complete Custom Backup Flow might look like:</p><ul><li>First we will log in to the device and make sure it is in Enable (Privileged Exec) mode.</li><li>Then we send <code>terminal length 1000</code>, just so there is less pagination in output of future commands. Altho Unimus has no issues with pagination (and will remove it from output properly) on some devices it may be useful to turn off pagination completely. You can notice we don't include the output of this device in our backup (<code>Exclude output from Backup</code> is set to <code>true</code>).</li><li>Then we send our main <code>show running-config</code> backup command.</li><li>After getting the config, we do mutliple additional <code>show ...</code> commands, and include those in the device backup. You can notice we set <code>Ignore failure</code> to <code>true</code>, so if any of these additional commands fail, we won't fail the entire backup, and just ignore the failure.</li></ul><p>The result of this Backup Flow will be a single device backup, containing outputs from <code>show running-config</code>, and all the additional <code>show ...</code> commands we listed. Since we target all <code>EdgeSwitch</code>es in our network, when Backup will be run on any <code>EdgeSwitch</code>, our Custom Backup Flow will be used instead of the built-in driver flow.</p><h2 id="how-to-debug-failing-backups">How to debug failing Backups</h2><p>Finally, we want to show you how you can debug failing backups when using Custom Flows. You can easily see what's happening during a Device Backup using our <code>Debug Mode</code> options. Please head to <code>Zones</code> and enable <code>Device Output Logging</code> for the Zone your devices are in:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-4.png" class="kg-image" alt loading="lazy" width="628" height="353" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-4.png 600w, https://blog.unimus.net/content/images/2024/07/image-4.png 628w"></figure><p>After the debug is enabled, you can run a backup on the device which fails. After the backup job fails, you can download the device output log:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/07/image-5.png" class="kg-image" alt loading="lazy" width="605" height="126" srcset="https://blog.unimus.net/content/images/size/w600/2024/07/image-5.png 600w, https://blog.unimus.net/content/images/2024/07/image-5.png 605w"></figure><p>In the log, you will see full CLI communication with the device, and should be able to spot why the backup fails.</p><h2 id="final-words">Final words</h2><p>We are introducing Custom Backup Flows with the hope that this will give you flexibility and customizability to backup your devices in any way you need. If you run into any issues, or want to let us know if anything is missing, please head to our forum, either the <a href="https://forum.unimus.net/viewforum.php?f=9">support section</a> or the <a href="https://forum.unimus.net/viewforum.php?f=10">feature request section</a>, and let us know!</p><p>Custom Backup Flows are available starting with Unimus 2.5.0. Please head over to the <a href="https://unimus.net/download.html" rel="noopener">Download</a> section to download the latest Unimus release.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.4.0 ]]></title>
        <description><![CDATA[ Unimus 2.4.0 is here! Check out our overview of the new features, reworks and improvements present in this release. With support for 20+ new device types, a load of fixes for various bugs and issue, this is a significant release. What joy! ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-4-0/</link>
        <guid isPermaLink="false">65003a75f6bf640001854730</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Viliam Tkacik ]]></dc:creator>
        <pubDate>Tue, 12 Dec 2023 18:14:02 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/12/unimus240.png" medium="image"/>
        <content:encoded><![CDATA[ <p>A new major Unimus release - 2.4.0 is finally here! With it come new features, reworks and improvements, new device support, and as always, bug fixes. This article is dedicated to the highlights of the 2.4.0 release, and the full Changelog is available at the bottom.</p><p>We have also published a video overview on our YouTube channel:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/1iJKY9AuIWg?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Unimus 2.4 release overview"></iframe></figure><hr><h2 id="mass-config-push-macros-overhaul">Mass Config Push macros overhaul</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/12/Screenshot_20231205_104317-1.png" class="kg-image" alt="MCP macros screencap" loading="lazy" width="1383" height="635" srcset="https://blog.unimus.net/content/images/size/w600/2023/12/Screenshot_20231205_104317-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/12/Screenshot_20231205_104317-1.png 1000w, https://blog.unimus.net/content/images/2023/12/Screenshot_20231205_104317-1.png 1383w" sizes="(min-width: 720px) 720px"></figure><p>Mass Config Push is a powerful Unimus feature for automation made even more versatile when used with macros. Macros (modifiers, actions and user variables) allow you to build complex configuration deployments, firmware upgrade procedures or maintenance tasks.</p><p>In 2.4.0 the modifier syntax was changed to a "<strong>$(modifier-name yes/no)</strong>" format in order to minimize overall ambiguity and new modifiers "<code>fail-on-error</code>" and "<code>wait-echo</code>" were added. Format used for all actions is now "<strong>$(action-name value)</strong>". A new delay "<code>action</code>" was also introduced which simply waits for a specified time. Positions within a line for modifiers and actions are now enforced. Check out the <a href="https://wiki.unimus.net/display/UNPUB/Mass+Config+Push#MassConfigPush-Commandsendingmodifiersandcontrolsequences">wiki</a> for more info.</p><hr><h2 id="nms-sync-enhancements">NMS Sync enhancements</h2><figure class="kg-card kg-video-card"><div class="kg-video-container"><video src="https://blog.unimus.net/content/media/2023/12/NMS-Sync-UI.webm" poster="https://img.spacergif.org/v1/1920x970/0a/spacer.png" width="1920" height="970" playsinline preload="metadata" style="background: transparent url('https://blog.unimus.net/content/images/2023/12/media-thumbnail-ember1184.jpg') 50% 50% / cover no-repeat;" /></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#215;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div></figure><p>The old NMS Sync logic served well for three long years, though it wasn't without its shortcomings. The robust rework introduced in 2.4.0-Beta5 handles syncing from any combination of different systems or different instances of the same system.</p><p>We've introduced the concept of device <strong><code>orphaning</code></strong> in order to track changes on the NMS. A device becomes orphaned when it is no longer being provided by the NMS. This is useful in reflecting on Unimus the current state of the remote NMS that is being synced.</p><p>Two new settings were added to the NMS Sync Preset configuration to accommodate the flexibility of the Sync logic. <code><strong>Device Action policy</strong></code> controls creating new devices versus moving existing ones to a target Zone described by a Sync Preset Rule. With <code><strong>Orphaning policy</strong></code> Unimus can keep, unmanage or delete orphaned devices at the end of a Sync.</p><p>Tl;dr: NMS Sync now handles added, changed, removed and disabled devices in virtually any complex setup and will create / move / delete or disable local devices to reflect the state on the NMS. The full NMS Sync rework rundown can be found in this <a href="https://blog.unimus.net/new-nms-sync-logic-2-4-0/">blog article</a> and on our <a href="https://www.youtube.com/watch?v=oCp1qCkwXgg">YouTube</a> channel.</p><hr><h2 id="ssh-handling-support">SSH handling support</h2><p>Multiple improvements were made on the SSH connection establishment and session stability. Among few examples are:</p><ul><li>Devices which don't play well when "none" SSH auth method is offered are now supported.</li><li>Login banner recognition has been adjusted to handle the quirkiest of banner types.</li><li>SSH version validation timeout can now be overridden to accommodate device types that need a little extra time to respond. Details in configuration documentation on our <a href="https://wiki.unimus.net/display/UNPUB/Changing+default+timeouts">wiki</a>.</li></ul><hr><h2 id="zabbix-and-librenms-connectors">Zabbix and LibreNMS connectors</h2><p>By popular demand we've added an option to import Zabbix hosts by their assigned Templates and Tags, using '%' and '@' prefixes respectively. Existing Sync Presets will continue working as expected. More info in the Functionality &gt; Import &gt; Zabbix section on the <a href="https://wiki.unimus.net/display/UNPUB/Zabbix+importer">wiki</a>.</p><p>We have also added new "Address field priority" and "Description field priority" selectors for LibreNMS. These allow you to configure which fields from Libre are pulled into device information in Unimus.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/12/image.png" class="kg-image" alt loading="lazy" width="712" height="115" srcset="https://blog.unimus.net/content/images/size/w600/2023/12/image.png 600w, https://blog.unimus.net/content/images/2023/12/image.png 712w"></figure><hr><h2 id="other-features-and-new-device-type-support">Other features and new device type support</h2><p>Minor features such as the obligatory NetXMS client library update, improved built-in backup filters, UI/UX touch-ups and Zone support hot-fix for APIv2 come included in the 2.4.0 package. </p><p>23 new device types joined the list of devices supported by Unimus in this release. Full Changelog below for more info.</p><hr><h2 id="fixes-of-various-shape-and-form">Fixes of various shape and form</h2><p>Wouldn't be a major release without a healthy dose of bug fixes. One to mention is Remote Core connections could be seen as up, even after they were closed, which prevented the same Remote Core to reconnect.</p><p>One security fix applied involved read-only users being able to add a new Zone.</p><hr><p>Finally, here is the full 2.4.0 Changelog:</p><pre><code>= Version 2.4.0 =
Features:
  Updated NetXMS client library to latest version (4.4.4)
  Added filtering of log messages inside Cisco SMB switch backups
  Added a built-in backup filter for new timestamp format in MikroTik RouterOS v7.10
  Improved built-in backup filters for newer versions of Ubiquiti EdgeSwitch X
  Improved handling of errors for Zones which use a NetXMS Agent as the Zone's proxy
  Added possibility to search by Credential Type in "Credentials &gt; Device credentials" table
  Various minor UI and UX fixes and improvements
  Added support for devices which don't respond to the "none" SSH auth method
  Improved login banner recognition logic, more banner types are now supported
  If a DNS lookup for a device hostname fails, this will now be reported as an exact job failure reason
  Added support for session restoration prompts after login (for example on Cisco ISE)
  Added support for CLIs which don't echo the "?" when receiving commands like "show ?"
  Added the option to override the SSH version validation timeout (new "unimus.core.ssh-version-validation-timeout" setting)
  Added support for multi-partition backup on F5 devices
  Added support for all possible formats of user and root prompts in OPNsense
  Added support for output termination in newer versions of VyOS
  Added support for Cisco SMB switches which don't report their model on the CLI
  Added support for Linux shell login on netElastic vBGN
  Added support for output termination in paged output on netElastic vBGN
  Improved support for Adtran NetVanta devices
  Improved support for logins to JunOS in BSD mode
  Improved handling of quoted strings on MikroTik RouterOS v7
  Added support for paginated output on RAD devices
  Added support for backup multipliers in the Cisco WLC driver
  Improvements to the CLI mode change algorithm (better handling of specific edge cases)
  Improved handling of error messages when Unimus config file is missing

  Config Push modifiers were improved and reworked:
    - modifier syntax was changed to a "$(modifier-name yes/no)" format
    - enforced modifier and action positions within a line
    - added support for new modifiers "fail-on-error", "wait-echo" and their opposites (yes/no)
    - added support for a "delay" action, which simply waits for a specified time
    - all existing Config Push presets should be migrated to the new syntax automatically
    - full documentation: https://wiki.unimus.net/display/UNPUB/Mass+Config+Push

  Major improvements to NMS Sync:
    - devices no longer present in NMS can now be automatically Unmanaged / Deleted in Unimus
    - improved tracking of which local device corresponds to which NMS device, allowing to move devices locally when they are moved in the NMS
    - if a device is not found locally in the target Zone, you can now specify if Unimus looks for a candidate to move into the Zone, or creates a new device
    - allow specifying what scope Unimus searches in for move candidates when trying to move devices across Zones
    - fixed multiple issues that arose in setups where multiple NMSes were being imported from into the same Zone
    - more info at https://blog.unimus.net/new-nms-sync-logic-2-4-0/

  Improvements to the Zabbix NMS Sync connector:
    - added support for importing from Templates and Tags on top of existing options
    - introduced new prefixes for various import sources
    - existing Sync Presets should be migrated automatically, and continue working as expected
    - full documentation: https://wiki.unimus.net/display/UNPUB/Zabbix+importer

  Improvements to the LibreNMS NMS Sync connector:
    - added "Address field priority" selector, allowing to specify how Unimus pulls device addresses from Libre
    - added "Description field priority" selectors, allowing to specify how Unimus pulls device descriptions from Libre

  APIv2 improvements:
    - add optional query param to select zone for the "findByAddress" endpoint at "api/v2/devices/"
    - add option to specify Zone for the "createDevice" endpoint at "api/v2/devices"
    - add option to specify Managed State for the "createDevice" endpoint at "api/v2/devices"
    - add option to specify Managed State for multiple GET and UPDATE endpoints at "api/v2/devices"

  APIv3 improvements:
    - added possibility to search by "usedByDevices", "boundToDevices" and "credentialsTypes" in "api/v3/credentials" endpoint

  Added support for:
    - Adtran NetVanta chassis
    - ADVA FSP 1xx series
    - AricentOS devices
    - more variants of the Aruba Mobility Controller
    - Calix AXOS
    - Calix E7-2
    - Cambium cnPilot
    - Casa vCCAP
    - Cisco Catalyst 1200 series switches
    - Cisco ISE
    - ComNet Switches (based on CNGE11FX3TX8MS)
    - EdgeCore 7316
    - EdgeCore CSR320
    - Ericsson IPOS (SSR series)
    - Ericsson SGSN
    - F5 multi-partition
    - Grandstream GWN7800 series switches
    - improved netElastic vBGN support
    - Opengear Operations Manager
    - Radware Alteon
    - Ruckus vSZ-D
    - Ruckus vSZ-E
    - TRENDnet TI switches
    - Westermo L110
    - Westermo Lynx-5512
    - Westermo RedFox-5728
    - Westermo WeOS

Fixes:
  Fixed inter-connection delay was not applied for Telnet service availability check
  Fixed logs present in backups on Cisco SMB switches (would trigger new change-points and change notifications on every backup)
  Fixed NMS Sync from Zabbix versions 6.2.1 and newer within 6.2 was not working (6.4 and older than 6.2 worked properly)
  Fixed for Zones which use a NetXMS Agent as a proxy all tasks within a job would fail if a single tasks failed
  Fixed inter-connection delay was not applied for NetXMS TCP proxy connections
  Fixed elements in combo box sometimes appearing multiple times in multiple screens across the application
  Fixed elements in combo box sometimes missing in multiple screens across the application
  Fixed beginning of lines could be truncated in diff the view on specific browser configurations
  Fixed reporting wrong Last Job Status for unmanaged devices over API (multiple APIv2 "/devices" endpoints)
  Fixed attempting to input a very long FQDN into the DB address during the Deploy Wizard was not possible
  Fixed Credential usage could be counted twice if a credential was used for both for SSH and Telnet (Credential &gt; Usage screen)
  Fixed CLI Mode Change password usage could be counted twice if a credential was used for both for SSH and Telnet (Credential &gt; Usage screen)
  Fixed an error that could occur if you switched screens while multiple popup windows were opened
  Fixed the possibility to input extremely long strings into dropdowns, which would eventually trigger an error
  Fixed Config Push triggered via API with an empty device ID string would create a wrong entry in Push results
  Fixed Delete Push Job History retention job was not re-scheduled when default schedule is changed
  Fixed issues with Config Push presets being deleted while they were opened in another browser window
  Fixed various minor UI and UX issues and inconsistencies
  Fixed jobs using Telnet could randomly fail
  Fixed login to devices could fail if certain login banners were used
  Fixed Remote Core would not be able to reconnect to the Server in specific cases
  Fixed Remote Core connections could be considered still alive even after the connection was closed
  Fixed jobs on Cambium 450i would always fail
  Fixed jobs on newer versions of VyOS failing
  Fixed login failing on specific Palo Alto devices
  Fixed specific commands on Aruba Mobility Controller (ArubaMM) could cause a Config Push to fail
  Fixed backups could fail on Cisco WLC under heavy load, or with very large configs
  Fixed jobs on specific Moxa switch types could randomly fail
  Fixed jobs on specific RAD devices would fail
  Fixed jobs on specific Adtran NetVanta devices would fail
  Fixed discovery failing on OPNsense with specific account and shell type combinations
  Fixed discovery would fail on JunOS devices with specific BSD prompt format
  Fixed discovery would fail for specific versions of the Aruba Mobility Controller
  Fixed sporadic config change notifications on MikroTik RouterOS v7

Security fixes:
  Fixed read-only users could add a new Zone
  Fixed Credentials and CLI Mode change passwords could be printed to the log file in cleartext on specific API calls

Embedded Core version:
  2.4.0

Migration warnings:
  On MikroTik RouterOS v7, you can get a single config change notification due to changes in how quoted strings
  are handled in our ROSv7 driver. This config change should only happen on the first backup job after upgrade
  and can be ignored.
</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ NMS Sync improvements in Unimus 2.4.0 ]]></title>
        <description><![CDATA[ Unimus 2.4.0 brings a complete rewrite of the NMS Sync logic, adding support for many new use-cases. In this article we showcase new Sync Preset settings that give you more control over Sync behavior. ]]></description>
        <link>https://blog.unimus.net/new-nms-sync-logic-2-4-0/</link>
        <guid isPermaLink="false">6538dc51d84df10001511f2a</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Viliam Tkacik ]]></dc:creator>
        <pubDate>Tue, 12 Dec 2023 02:41:58 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/12/unimus-nms-sync-logic-improvements.png" medium="image"/>
        <content:encoded><![CDATA[ <p>The Unimus 2.4.0 release brings a complete rewrite of the NMS Sync feature logic. It gives you more options and control over Sync behavior, with minimal complexity added to the user experience.</p><p>The key addition to the functionality is <code><strong>device orphaning</strong></code>. Its role is to keep track of changes in device presence and state provided by the import source (NMS). Device additions, changes or removals are now automatically reflected in local inventory (Unimus).</p><p>There are two new settings to notice in the GUI of NMS Sync Preset configuration. These control <code><strong>Device action policy</strong></code> and <code><strong>Orphaning policy</strong></code> described in detail in sections below.</p><p>Lastly, we have also added two new group identifiers for Zabbix. We can now select devices to import that are using specific templates and/or tags by prefixing their names by '%' and '@', respectively. Host groups are used as before without any prefix.</p><h2 id="showcase-of-new-features">Showcase of new features</h2><p>In case you prefer a video format, you can watch an overview of the changes here:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/oCp1qCkwXgg?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="New NMS Sync Logic in 2.4.0"></iframe></figure><h2 id="how-it-works">How it works</h2><p>The NMS Sync is a feature for importing devices into Unimus. Synchronization of devices from NMS systems automates the adoption of new networks into Unimus and simplifies device management by delegating the task entirely to the NMS (no need to manage devices across both Unimus and the NMS separately).</p><p>Since 2.1.0 the NMS Sync configuration is Preset-based, where each Sync Preset defines from which NMS to sync which devices into which Zone(s). The rework in 2.4.0 expands the Preset-based mechanics by Preset adoption. Each device imported by an NMS Sync Preset is now adopted by that given Sync Preset, allowing consistent identification of synced devices.</p><p>NMS Sync takes a device set provided by import source (NMS) and mirrors it in the local inventory (Unimus). Let's take a look at steps needed for a run-of-the-mill setup of an NMS Sync Preset:</p><ol><li>Create NMS Sync Preset of your NMS type specifying URL and credentials</li><li>Create a Sync Rule specifying which groups of devices in the NMS should be imported</li><li>Specify this Rule's target Zone to import devices into</li><li>Go to step 2 if you want to define another Rule</li><li>Leave other settings at default values</li><li>Run and/or schedule the NMS Sync</li></ol><p>After an NMS Sync runs its course it is considered successful if there were no errors or failures, otherwise it goes into the 'Failed jobs' bucket. Detailed report of either can be viewed in 'Dashboard &gt; Import job history'. There we can find all the information about the import source, sync errors, processed, imported, updated devices and failed operations counts.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/11/Screenshot_20231114_183511.png" class="kg-image" alt="Screencap of successful NMS sync result" loading="lazy" width="354" height="512"><figcaption>Successful NMS sync result</figcaption></figure><h3 id="the-nature-of-the-beast">The nature of the beast</h3><p>Unimus currently supports seven different NMSes. You may create Sync Presets with different systems and sync to the same Zone or you may have different instances of the same NMS and sync to different Zones. These systems may or may not support universally unique identifiers (UUIDs) for devices. You may even want to move devices between Zones manually and still be sure of what happens to them following an NMS Sync.</p><p>The logic needed to behave consistently during any changes, be it on the NMS, in Sync Preset Rules configuration or other settings in Unimus. It also needs to account for devices with matching address within and outside of a Sync Rule target Zone. Suffice to say the logic rework turned out to be quite the undertaking.</p><p>The following sections take a closer look at concepts, behaviors and policies introduced with the NMS Sync rework.</p><h3 id="remote-uuids">Remote UUIDs</h3><p>As mentioned before, some of the NMSes Unimus supports provide UUIDs for devices during a Sync operation. These uniquely identify previously imported devices, enabling more consistent device tracking between Unimus and the remote systems. As an example, <u>when UUIDs are used, changing device IP address of a host on the NMS would also result in local device IP address update</u>. This would not be possible without UUIDs, since IP address, that has been changed, is used as the identifying parameter if a UUID is not present.</p><p>NMSes that support UUIDs: <strong>LibreNMS</strong>, <strong>NetXMS</strong>, <strong>Observium</strong> and <strong>Panopta</strong></p><h3 id="device-action-policy">Device Action policy</h3><p>This policy controls the behavior when syncing a device from the NMS. It can be either to always try to <code><strong>Create</strong></code> a new device for the one provided by import source, or try to <code><strong>Move</strong></code> an existing one within a scope, if it is a match.</p><p>The default setting is <code><strong>Move within Preset Zones</strong></code>. The "Preset Zones" scope consist of Zones used by Sync Preset's Rules. Before creating a new device (in the target Zone a Rule specifies) the Sync logic will <u>look for an existing device</u> with matching UUID or address within the scope <u>and moves it to the target Zone</u> if one is found. The benefit is keeping track and moving of devices in response to changes in the NMS, which ensures duplicates are not created.</p><p>The <code><strong>Move from All Zones</strong></code> option is similar to the previous one, but a <u>system wide scope</u> is used when looking for matching devices to move to the Rule target Zone.</p><p>Policy setting <code><strong>No Move/Always Create</strong></code> is used to turn off the moving of devices during the NMS Sync. Only target Zone is looked at when matching a device provided by import source. If a device with the same UUID or address is not present in this Zone, it is created. If it is found, it has its attributes updated. This is handy for environments using same addressing for multiple device in multiple Zones, such as an MSP managing external networks.</p><blockquote>Note: moving of existing devices fails if two or more devices with the same identifier are found in the scope, as the algorithm has no way of determining which device the user intended to "move" and counts it as 'failed to update'.</blockquote><h3 id="device-adoption">Device Adoption</h3><p>An adopted device is simply a device that was imported from an NMS by a Sync Preset. Device adoption designates its parent Sync Preset as the only one that manages any changes on it. An existing device in local inventory is eligible for adoption by a Sync Preset when it is <code><strong>not adopted</strong></code> (manually created device) or when it is <code><strong>orphaned</strong></code>. Devices can become orphaned in multiple cases:</p><ul><li><u>device is no longer present</u> in the device set provided to Unimus by the NMS (was deleted on the NMS, or moved outside the devices Unimus imports)</li><li>when the <u>Zone was changed</u> in a Sync Rule, all devices adopted by this Sync Rule are orphaned</li><li>when the <u>Sync Rule is deleted</u>, all devices adopted by this Sync Rule are orphaned</li><li>when a <u>device is moved</u> to a different Zone manually by the user (this includes the result of Zone deletion with moving of devices to the default Zone)</li></ul><p>Orphaning status can be checked via <strong>device info</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/11/Screenshot_20231114_143457.png" class="kg-image" alt="Screencap of Device info" loading="lazy" width="1040" height="547" srcset="https://blog.unimus.net/content/images/size/w600/2023/11/Screenshot_20231114_143457.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/11/Screenshot_20231114_143457.png 1000w, https://blog.unimus.net/content/images/2023/11/Screenshot_20231114_143457.png 1040w" sizes="(min-width: 720px) 720px"><figcaption>Device info showing 'Orphaning reason: Device not present on NMS'</figcaption></figure><p>Adoption is useful for associating not already adopted devices on Unimus with those on the NMS.</p><h3 id="orphaning-policy">Orphaning policy</h3><p>Orphaning policy determines what happens to orphaned devices during an NMS Sync. User has three options that determine what action to take on orphaned devices during an NMS Sync:</p><ol><li><u>No action</u> - device is kept as is, though it is now eligible for adoption by any NMS Sync Preset</li><li><u>Unmanage</u> - device becomes unmanaged in Unimus, no further jobs will run on it. It will be kept in Unimus, along with any existing backups; it is also eligible for another adoption</li><li><u>Delete</u> - device will be deleted from Unimus, alongside its backups, so this option should be used with a clear intent and a dose of awareness of possible results</li></ol><h3 id="example-scenario">Example scenario</h3><p>Let's simulate a scenario where we want to defer ongoing device management to NMS in a way that any change to device set on NMS is reflected in local inventory on Unimus.</p><p>The starting point on Unimus is a set of devices not adopted by any Sync Preset. The first thing that will happen is pairing the devices between the systems and adopting them by our Sync Preset. In addition the devices will also be moved to Zones corresponding with organizational structure present on the NMS. Next, any other devices not present on Unimus will be imported.</p><p>This is easily achieved by setting up a Sync Preset with Preset rules specifying organizational groups on the NMS and pointing to specific Zones. Since we want the existing devices moved to specific Zones and we want the algorithm to look for them throughout entire Unimus we will use the 'Move from All Zones' Device Action policy.</p><figure class="kg-card kg-video-card"><div class="kg-video-container"><video src="https://blog.unimus.net/content/media/2023/11/Kooha-2023-11-14-16-16-03.webm" poster="https://img.spacergif.org/v1/1920x966/0a/spacer.png" width="1920" height="966" playsinline preload="metadata" style="background: transparent url('https://blog.unimus.net/content/images/2023/11/media-thumbnail-ember263.jpg') 50% 50% / cover no-repeat;" /></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#215;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div></figure><p>After running the NMS Sync we see a successful import notification. Devices are now properly adopted and organized among Zones. Automatic device synchronization is achieved by setting the Sync preset as a 'Scheduled sync'. Any changes, additions or removals of devices in organizational groups on the NMS are reflected in local inventory after the next NMS Sync.</p><h2 id="final-words">Final words</h2><p>Congratulations, dear reader, on making it this far! You are now acquainted with the updates to the NMS Sync feature in Unimus. Feel free to experiment with it, and let us know what works and if something doesn't on <a href="https://forum.unimus.net/viewforum.php?f=9&amp;sid=3621e384c2af151492b0cecaab29d2ed">our Forum</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Generating reports from Unimus job failures ]]></title>
        <description><![CDATA[ How to build failure reports from Unimus device jobs. Review large scale job reports for better understanding of why jobs fail on your devices. ]]></description>
        <link>https://blog.unimus.net/generating-reports-from-unimus-job-failures/</link>
        <guid isPermaLink="false">65098185f6bf640001854a03</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Viliam Tkacik ]]></dc:creator>
        <pubDate>Tue, 07 Nov 2023 16:07:23 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/11/failed_job_report_blog.png" medium="image"/>
        <content:encoded><![CDATA[ <blockquote>“Why do our backups fail, Bruce? So that we can learn to fix them.”</blockquote><h2 id="intro">Intro</h2><p>With hundreds or even many thousands of jobs daily for some Unimus users, there are bound to be a few failed ones. Jobs such as <code>Discovery</code> or <code>Backup</code> fail for various reasons related to connection errors, refused credentials or unsupported devices. The results of failed jobs are all captured and error logs are viewable in the web GUI. From error logs one can learn the details of why a discovery or a backup for a device failed and use the information to remedy the situation.</p><p>In this brief article we will go over generating job report exports. These export files can then be processed externally by a monitoring system, reporting system, by an external parser, or by hand. But first, the error log data needs to be retrieved. The data is stored in the <code>database</code>, so to access it we need the credentials to the database and a few well constructed queries. We will be showing how to generate reports of failed jobs and review them by hand in a spreadsheets editor (Excel) for its advanced data processing functions.</p><h2 id="getting-to-the-data">Getting to the data</h2><p>Unimus supports <code>MySQL</code>, <code>PostgreSQL</code> and <code>MSSQL</code> relational databases in addition to file based <code>HSQL</code>. The former ones store data in tables with columns and rows. Databases use structured query language (SQL) for data manipulation and querying. We'll use such queries to extract the data about failed jobs and export it to a text file.</p><h4 id="connecting-to-the-db">Connecting to the DB</h4><p>In our test scenario an arbitrary Unimus server is using MariaDB, a database system, which is a fork of MySQL. For accessing MariaDB we are going to simply run the mariadb client from shell. Need to specify the host IP, database name and credentials:</p><pre><code>mariadb --host=127.0.0.1 --database=unimusmdb --user=will -password</code></pre><h4 id="constructing-the-query">Constructing the query</h4><p>Let's say we are interested in failed Discovery jobs. So for each device we want to get some info about the device itself and error logs for last job that was a failed <code>Discovery</code>. </p><p>We will be using data from tables <code>device</code>, <code>device_history_job</code> and <code>zone</code>. The <code>device</code> table contains useful columns like <strong>id</strong>, <strong>address</strong>, <strong>description</strong>, <strong>model</strong>, <strong>type</strong> and <strong>vendor</strong>. The <code>device_history_job</code> table is populated by useful data in the <strong>create_time</strong>, <strong>error_log</strong>, <strong>info</strong>, <strong>job_type</strong>, <strong>device_id</strong> and <strong>successful</strong> columns and the <code>zone</code> table is used to describe device zone membership.</p><p>We <code>SELECT</code> columns we want displayed <code>FROM</code> the tables and <code>LEFT JOIN</code> table device_history_job <code>ON</code> id of 'last device job that was a Discovery' via a subquery and table zone <code>ON</code> id of the zone. Then we filter the results with <code>WHERE</code> by 'failed jobs'. And let's say we want to limit the results to recent ones, e.g. ones that took place in the last week. Our query then might look something like this:</p><figure class="kg-card kg-code-card"><pre><code>SELECT
  d.id,
  dhj.info,
  DATE_FORMAT(FROM_UNIXTIME(dhj.create_time), '%H:%i:%s %d.%m.%Y'),
  z.name,
  dhj.job_type,
  REPLACE(dhj.error_log, '\r\n', ' ') AS error_log
FROM device d
LEFT JOIN device_history_job dhj ON dhj.id = (
  select id
  from device_history_job
  where d.id = device_id
    and job_type = 'DISCOVERY'
  order by create_time
  desc limit 1)
LEFT JOIN zone z ON z.id = d.zone_id
WHERE dhj.successful = 0
  AND dhj.create_time &gt; UNIX_TIMESTAMP(DATE_ADD(CURDATE(), INTERVAL -7 DAY));</code></pre><figcaption>MySQL query for fetching last Discovery jobs within last week if they failed</figcaption></figure><p>We have used <code>REPLACE</code> to output the error log on a single line for a more comprehensible way to display results.</p><p>Let's name the columns properly and put the query in quotation marks. Now we can feed the query into the mariadb command via <code>--execute</code> option and write the output into a local file:</p><figure class="kg-card kg-code-card"><pre><code>mariadb --host=127.0.0.1 --user=will --database=unimusmdb --password --execute="SELECT \
  d.id AS \`ID\`, \
  dhj.info AS \`Device info\`, \
  DATE_FORMAT(FROM_UNIXTIME(dhj.create_time), '%H:%i:%s %d.%m.%Y') AS \`Time\`, \
  z.name AS Zone, \
  dhj.job_type AS \`Job type\`, \
  REPLACE(dhj.error_log, '\r\n', ' ') AS \`Error log\` \
FROM device d \
LEFT JOIN device_history_job dhj ON dhj.id = (\
  select id \
  from device_history_job \
  where d.id = device_id \
    and job_type = 'DISCOVERY' \
  order by create_time \
  desc limit 1) \
LEFT JOIN zone z ON z.id = d.zone_id \
WHERE dhj.successful = 0 \
  AND dhj.create_time &gt; UNIX_TIMESTAMP(DATE_ADD(CURDATE(),INTERVAL -7 DAY))" &gt; disco_local.csv</code></pre><figcaption>Shell command to query failed Discovery jobs</figcaption></figure><p>For failed <code>Backup</code> jobs we would just change the <code>job_type</code> in the WHERE section of the query to <code>'BACKUP'</code>. Also, since a successful Discovery is a prerequisite for a Backup job, we can select additional columns, like vendor, type and model, to describe the discovered device in more detail. Queries and shell commands for failed backups, along with Postgres and MSSQL versions can be found on our <a href="https://github.com/netcore-jsa/unimus-useful-scripts/tree/main/job_report_SQL">GitHub</a>.</p><h3 id="reviewing-the-data">Reviewing the data</h3><p>We are using OnlyOffice Excel in our scenario because it is free and gets the job done. To view the data simply open the file in your favorite spreadsheets tool and choose 'Tab' as the delimiter:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230919_140851.png" class="kg-image" alt="Tab as delimiter" loading="lazy" width="380" height="225"></figure><p>Alternatively you can import the data using the Data Import feature:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/11/Screenshot_20231103_104515.png" class="kg-image" alt="Get data from csv file" loading="lazy" width="666" height="560" srcset="https://blog.unimus.net/content/images/size/w600/2023/11/Screenshot_20231103_104515.png 600w, https://blog.unimus.net/content/images/2023/11/Screenshot_20231103_104515.png 666w"></figure><p>Now select the data and format it as a table for advanced filtering capabilities. You can then filter the results by devices, zones or specific keywords in the error log messages. Here is an example of how to filter the logs based on multiple criteria:</p><figure class="kg-card kg-video-card"><div class="kg-video-container"><video src="https://blog.unimus.net/content/media/2023/11/Kooha-2023-11-03-11-07-34.webm" poster="https://img.spacergif.org/v1/1122x722/0a/spacer.png" width="1122" height="722" playsinline preload="metadata" style="background: transparent url('https://blog.unimus.net/content/images/2023/11/media-thumbnail-ember462.jpg') 50% 50% / cover no-repeat;" /></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#215;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div></figure><h3 id="conclusion">Conclusion</h3><p>And that's basically it! This short guide should provide a starting point for generating and viewing failed job reports from Unimus. There is also a thread going on our <a href="https://forum.unimus.net/viewtopic.php?f=7&amp;t=1687">Forum</a> for any feedback, questions or possible improvements.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Running Unimus Core in a container on MikroTik RouterOS ]]></title>
        <description><![CDATA[ A short guide describing how to deploy Unimus Core in a Docker container on MikroTik's RouterOS. ]]></description>
        <link>https://blog.unimus.net/running-unimus-core-in-a-container-on-mikrotik-routeros/</link>
        <guid isPermaLink="false">64a7f6573f52d200013e410b</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Viliam Tkacik ]]></dc:creator>
        <pubDate>Thu, 21 Sep 2023 15:55:44 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/09/Cover_-backup-of-device-without-CLI-with-Unimus-6-.png" medium="image"/>
        <content:encoded><![CDATA[ <p>The purpose of this article is to guide you through configuration for running Unimus remote Core container image on MikroTik's RouterOS.</p><h2 id="introduction">Introduction</h2><p>Today we will be discussing an exciting feature of Unimus - support for remote networks and distributed polling. Managed devices do not always have to be directly reachable by the Unimus Server. In a scenario where our devices and Unimus are separated by a WAN it would make sense to utilize a remote agent. All client devices would be polled locally which eliminates the need for individual direct server-device connections. This saves resources such as bandwidth and processing power and simplifies administration as you only need to maintain connectivity to a single host in each remote location. We would also get vastly improved scalability, enhanced security and fault isolation.</p><h2 id="getting-to-the-core%E2%84%A2-of-the-matter">Getting to the Core™ of the matter</h2><p>To extend Unimus functionality to a remote network we would use Unimus Core. A Core is the brains of Unimus. Same as the embedded Core on any Unimus Server it performs network automation, backups, change management and <a href="https://unimus.net/features.html"><u>more</u></a> on managed network devices. Acting as a remote poller, Unimus Core communicates with Unimus Server over a secure TCP connection conforming to modern industry standards. We can install Unimus Core on any supported OS, run a portable version or run a container image. Find out more on our <a href="https://wiki.unimus.net/display/UNPUB/Architecture+overview">wiki</a>.</p><p>Fairly recently (August 2022) MikroTik added container support on their RouterOS. This introduces a nifty new way of deploying Unimus Core directly on an edge router, thus reducing the number of devices required in the network. Let's have a look at how to set this up.</p><h2 id="setup">Setup</h2><p>Behold, the system we will be testing our remote Core deployment on:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Blog-image-backup-of-device-without-CLI-with-Unimus-5-.png" class="kg-image" alt="Network diagram with Unimus Remote Core container deployed on Mikrotik router" loading="lazy" width="1080" height="420" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Blog-image-backup-of-device-without-CLI-with-Unimus-5-.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/09/Blog-image-backup-of-device-without-CLI-with-Unimus-5-.png 1000w, https://blog.unimus.net/content/images/2023/09/Blog-image-backup-of-device-without-CLI-with-Unimus-5-.png 1080w" sizes="(min-width: 720px) 720px"></figure><p>Starting from the right, the <code>Unimus Server</code> is installed on Ubuntu server (22.04) running on Raspberry Pi 2. It is connected via static IP to the <code>HQ</code> router – a MikroTik RouterBOARD. The HQ router is doing source NAT for Unimus Server, translating the private source IP to WAN interface public IP. This allows Unimus Server to reach resources outside the LAN.</p><p>HQ router is also configured for destination NAT, a.k.a port triggering, directing incoming TCP 5509 and TCP 8085 traffic to Unimus Server. TCP 5509 allows inbound remote Core connection. TCP 8085 is not strictly required for our demonstration, we open it simply for remote access to the HTTP GUI. </p><hr><p>The left side represents a remote network. <code>Branch router</code>, a<strong> </strong>MikroTik RB5009UG+S+, is our edge router capable of running containers. Connected on LAN side is the device we want to manage, another MikroTik RouterBOARD - <code>Branch switch</code>. Branch router<strong> </strong>supports containers and will run <code>Unimus Core</code> in one.</p><p>Our Unimus Core container will have its own virtual ethernet interface assigned to use for communication outside. Although this 'veth' could be added to the local bridge connecting to Branch switch, it makes more sense security-wise to add it to a separate 'containers' bridge. This way any container traffic goes through the routing engine and firewall, where it can be subject to policies.</p><p>Branch router, which is likely source NATting traffic for the whole branch network, needs to also SNAT the container subnet to allow outbound communication to the Unimus Server.</p><hr><p>Edge router WAN ports are reachable via internet simulated by a local network. This is sufficient for our testing purposes as it simulates indirect connectivity between the Unimus Server and the remote Core.</p><p>With all the moving (really just sitting and humming softly) parts introduced, let’s go through the steps needed to achieve our desired result in detail.</p><h2 id="configuration">Configuration</h2><p>The key parts we will be focusing on are <code>Unimus Server</code> and MikroTik RouterBOARD <code>(Branch router)</code> running <code>Unimus Core</code> in a container.</p><h3 id="unimus-server">Unimus Server</h3><p>We assume we already have Unimus Server up and running. If not feel free to check out our <a href="https://wiki.unimus.net/"><u>wiki</u></a> to get you started.</p><p>Once we are all set, we can begin by navigating to Zones and 'Add new Zone'. This Zone will represent our remote location. Enter Zone name, ID and Remote core connection method and hit 'Confirm' to proceed.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_141740.png" class="kg-image" alt="Screenshot for adding a Zone to Unimus" loading="lazy" width="613" height="363" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Screenshot_20230914_141740.png 600w, https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_141740.png 613w"></figure><p>Next, we retrieve the remote core access key by hitting 'Show' and save it for later. It will be used to establish connection to the Unimus Server.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_141054.png" class="kg-image" alt="Screenshot of displaying access key for a Remote Core Zone in Unimus" loading="lazy" width="640" height="343" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Screenshot_20230914_141054.png 600w, https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_141054.png 640w"></figure><h3 id="branch-router-and-unimus-core">Branch router and Unimus Core</h3><p>Before attempting to<strong> </strong>run any containers let's take care of the prerequisites:</p><p>RouterOS<strong> </strong>version 7.5 or higher is needed to run containers so update as necessary. Container package is compatible with arm, arm64 and x86 architectures.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_143533.png" class="kg-image" alt="System resource printout from RouterOS CLI" loading="lazy" width="363" height="285"></figure><p>Requirements above are met. We have that going for us which is nice. The following steps will get us through the process of configuring the Branch router:</p><h4 id="secure-the-router-as-it-is-dangerous-on-the-internet">secure the router as it is dangerous on the internet</h4><p>Take care of basic security. Configure a strong password and restrict management access using a firewall policy.</p><h4 id="get-the-container-package-and-install-it">get the container package and install it</h4><p>Visit <em><a href="https://mikrotik.com/download">mikrotik.com/download</a></em> and get the 'extra packages' for your architecture. Extract the contents, upload the containers package to your router and reboot.</p><p>After reboot we can verify the currently installed packages:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_143403.png" class="kg-image" alt="System package printout from RouterOS CLI" loading="lazy" width="320" height="100"></figure><h4 id="enable-device-mode-containers">enable device-mode containers</h4><p>We need to have physical access to the router for this part due to security implications. We'll be prompted to press the reset button<strong> </strong>to apply the setting.</p><pre><code>/system/device-mode/update container=yes</code></pre><h4 id="configure-container-interface">configure container interface</h4><p>To allow our container access to the network it needs a virtual interface. First we will create a <code>bridge</code> for the container:</p><pre><code>/interface/bridge/add name=containers
/ip/address/add address=10.1.1.1/24 interface=containers</code></pre><p>Then we'll create a <code>veth1</code> interface and assign an IP address that Unimus Core will use for communication to Unimus Server. And we add the interface to the newly created bridge:</p><pre><code>/interface/veth/add name=veth1 address=10.1.1.2/24 gateway=10.1.1.1
/interface/bridge/port add bridge=containers interface=veth1</code></pre><h4 id="configure-nat">configure NAT</h4><p>Source NAT is needed for communication outside. We want the connection originated from the container subnet translated to an IP address reachable from the outside:</p><pre><code>/ip/firewall/nat/
add action=masquerade chain=srcnat src-address=10.1.1.0/24 out-interface=ether1</code></pre><h4 id="use-an-external-drive-optional">use an external drive (optional)</h4><p>To avoid cluttering your platform storage we recommend using a usb stick or an external hard drive for container images and volumes. They need to be formatted to ext3 or ext4 filesystem:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/10/external.png" class="kg-image" alt="Winbox screenshot of Disks menu" loading="lazy" width="1049" height="329" srcset="https://blog.unimus.net/content/images/size/w600/2023/10/external.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/10/external.png 1000w, https://blog.unimus.net/content/images/2023/10/external.png 1049w" sizes="(min-width: 720px) 720px"><figcaption>This author alternates between using the CLI and GUI. Use whichever is more comfortable for you, we neither condone nor discourage from using either one.</figcaption></figure><hr><p>Onto the configuration of the container for Unimus Core. We need to specify where to reach the Unimus Server via container environment variables, pull the Unimus Core container image and run it.</p><h4 id="define-environment-variables">define environment variables</h4><p>Variables are defined in key-value pairs. These are needed to point Unimus Core to the Unimus Server and input the Access Token we got earlier. Additionally, we can set the timezone and memory constraints for Java and there is an option to define volumes' mount points for data persistence. Details on <a href="https://github.com/crocandr/docker-unimus-core"><u>GitHub</u></a><u>.</u></p><p><em>/container/envs/</em></p><pre><code>add key=UNIMUS_SERVER_ADDRESS name=unimuscore_envs value=10.2.3.4
add key=UNIMUS_SERVER_PORT name=unimuscore_envs value=5509
add key=UNIMUS_SERVER_ACCESS_KEY name=unimuscore_envs value=\
    "v3ry_crypto;much_s3cr3t;W0w.."
add key=TZ name=unimuscore_envs value=Europe/Budapest
add key=XMX name=unimuscore_envs value=256M
add key=XMS name=unimuscore_envs value=128M</code></pre><p><em>/container/mounts/</em></p><pre><code>add dst=/etc/unimus-core name=unimuscore_config src=/usb1-part1/config</code></pre><h4 id="adding-container-image">adding container image</h4><p>We will be pulling our Unimus Core container latest image straight from Docker hub at <em><u>https://registry-1.docker.io</u></em>. You could also import one from a PC (via docker pull/save) or build your own. The remote Core needs to be the same version as the embedded core on Unimus Server to avoid any compatibility issues between versions. So just make sure you grab a suitable version.</p><pre><code>/container/config/set
registry-url=https://registry-1.docker.io tmpdir=usb1-part1/pull
/container/add
remote-image=croc/unimus-core-arm64:latest interface=veth1 root-dir=usb1-part1/unimuscore mounts=unimuscore_config envlist=unimuscore_envs logging=yes</code></pre><figure class="kg-card kg-code-card"><pre><code>- tmpdir specifies where to save the image
- root-dir specifies where to extract the image
- mounts specify mount points for volumes to ensure data persistence if container is removed or replaced
- envlist specifies environment variables defined above
- logging is enabled for troubleshooting</code></pre><figcaption>parameters' function explained</figcaption></figure><p>After extraction it should go to "stopped" status. Check via:</p><pre><code>/container/print</code></pre><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_143143.png" class="kg-image" alt="Printout of container section in RouterOS CLI" loading="lazy" width="600" height="71" srcset="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_143143.png 600w"></figure><h4 id="run-it">run it!</h4><p>All is set to start our remote Unimus Core.</p><pre><code>/container/start 0</code></pre><h4 id="run-on-boot-optional">run on boot (optional)</h4><p>It might come in handy configuring our container to start with RouterOS boot to add some automation in case the Branch router gets rebooted for any reason.</p><pre><code>/container/set start-on-boot=yes 0</code></pre><h2 id="all-is-well-that-ends-well">All is well that ends well</h2><p>Assuming we have set it all up and everything went as planned we should see our remote Core’s status as online:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_153904-1.png" class="kg-image" alt="Unimus screenshot of Zone online" loading="lazy" width="629" height="321" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Screenshot_20230914_153904-1.png 600w, https://blog.unimus.net/content/images/2023/09/Screenshot_20230914_153904-1.png 629w"></figure><p>Adding our test device (the <code>Branch Switch</code>) under remote Core zone (BO1) prompts a discovery which results in success:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_134202.png" class="kg-image" alt="Unimus screenshot of successful job" loading="lazy" width="1239" height="204" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Screenshot_20230918_134202.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/09/Screenshot_20230918_134202.png 1000w, https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_134202.png 1239w" sizes="(min-width: 720px) 720px"></figure><h3 id="troubleshooting">Troubleshooting</h3><p>Most common issues relate to Unimus Server connectivity. Here’s a checklist of items we can try in case of need:</p><ul><li>Unimus Server is UP</li></ul><p>Double-check your Unimus Server is up and running. Access it via browser at <em>http(s)://&lt;YourServerIP:8085/</em></p><ul><li>Firewall policy</li></ul><p>Verify whether there’s a security rule allowing connection from outside your network.</p><ul><li>Check NAT</li></ul><p>Destination NAT rule is necessary for core connection traffic. We need to translate destination address of incoming remote Core traffic to the Unimus Server IP address. TCP port 5509 is used by default.</p><ul><li>Check variables</li></ul><p>Our Unimus Core container uses environment variables to establish connection to the server. Make sure the values in key-value pairs reflect your setup:</p><p><strong>UNIMUS_SERVER_ADDRESS</strong> is the IP address where Unimus Server is reachable (before NAT)</p><p><strong>UNIMUS_SERVER_PORT</strong> is the TCP port number (default 5509) on which Unimus listens for remote core messages</p><p><strong>UNIMUS_SERVER_ACCESS_KEY</strong> is the long string generated when you create a new Remote Core Zone</p><p>Enabled container logs make troubleshooting easier:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/10/download2.png" class="kg-image" alt="Log of connection error due to misconfigured port" loading="lazy" width="1250" height="91" srcset="https://blog.unimus.net/content/images/size/w600/2023/10/download2.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/10/download2.png 1000w, https://blog.unimus.net/content/images/2023/10/download2.png 1250w" sizes="(min-width: 720px) 720px"><figcaption>Log of misconfigured Core connection port</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/10/download3.png" class="kg-image" alt="Log of malformed access key" loading="lazy" width="983" height="215" srcset="https://blog.unimus.net/content/images/size/w600/2023/10/download3.png 600w, https://blog.unimus.net/content/images/2023/10/download3.png 983w" sizes="(min-width: 720px) 720px"><figcaption>Log of wrong access key</figcaption></figure><ul><li>Check versions</li></ul><p>For Unimus Server to accept the remote Core connection they both need to run on the same version. Unimus Server log file would reveal this issue:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_131450-1.png" class="kg-image" alt="Log of mismatched Core versions" loading="lazy" width="1292" height="70" srcset="https://blog.unimus.net/content/images/size/w600/2023/09/Screenshot_20230918_131450-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/09/Screenshot_20230918_131450-1.png 1000w, https://blog.unimus.net/content/images/2023/09/Screenshot_20230918_131450-1.png 1292w" sizes="(min-width: 720px) 720px"></figure><hr><p>Attached below are the config exports used in our test setup:</p><figure class="kg-card kg-code-card"><pre><code># HQ router
/interface bridge
add name=local
/interface bridge port
add bridge=local interface=ether2
/ip address
add address=172.31.254.1/24 interface=local network=172.31.254.0
add address=10.2.3.4/24 comment=internet interface=ether1 network=10.2.3.0
/ip dhcp-server network
add address=172.31.254.0/24 dns-server=172.31.254.1 gateway=172.31.254.1
/ip dns
set servers=10.2.3.254 allow-remote-requests=yes
/ip firewall nat
add action=masquerade chain=srcnat out-interface=ether1
add action=dst-nat chain=dstnat dst-address=10.2.3.4 dst-port=5509,8085 protocol=tcp to-addresses=172.31.254.2
/ip route
add distance=1 gateway=10.2.3.254
/system clock
set time-zone-name=Europe/Bratislava
/system identity
set name=HQ</code></pre><figcaption>HQ config</figcaption></figure><figure class="kg-card kg-code-card"><pre><code># Branch router
/interface bridge
add name=containers
add name=local
/interface veth
add address=10.1.1.2/24 gateway=10.1.1.1 name=veth1
/container mounts
add dst=/etc/unimus-core name=unimuscore_config src=/usb1-part1/config
/container
add envlist=unimuscore_envs interface=veth1 logging=yes
/container config
set registry-url=https://registry-1.docker.io tmpdir=usb1-part1/pull
/container envs
add key=UNIMUS_SERVER_ADDRESS name=unimuscore_envs value=10.2.3.4
add key=UNIMUS_SERVER_PORT name=unimuscore_envs value=5509
add key=UNIMUS_SERVER_ACCESS_KEY name=unimuscore_envs value="secret key"
add key=TZ name=unimuscore_envs value=Europe/Budapest
add key=XMX name=unimuscore_envs value=256M
add key=XMS name=unimuscore_envs value=128M
/interface bridge port
add bridge=local interface=ether2
add bridge=containers interface=veth1
/ip address
add address=10.8.9.10/24 interface=local network=10.8.9.0
add address=10.5.6.7/24 comment="internet" interface=ether1 network=10.5.6.0
add address=10.1.1.1/24 interface=containers network=10.1.1.0
/ip dns
set servers=10.5.6.254  allow-remote-requests=yes
/ip firewall nat
add action=masquerade chain=srcnat src-address=10.1.1.0/24 out-interface=ether1
/ip route
add disabled=no dst-address=0.0.0.0/0 gateway=10.5.6.254 routing-table=main
/system clock
set time-zone-name=Europe/Bratislava
/system identity
set name="Branch router"</code></pre><figcaption>Branch router config</figcaption></figure><h3 id="final-words">Final words</h3><p>We hope this guide can serve as a template for deploying Unimus Core container on MikroTik’s RouterOS. If you encounter any difficulties or have additional questions please reach out on the <a href="https://forum.unimus.net/viewtopic.php?f=7&amp;t=1676">forum</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Backup of devices without CLI with Unimus ]]></title>
        <description><![CDATA[ A guide how to backup devices which don't expose their configs over CLI using Unimus' API and a bit of scripting. ]]></description>
        <link>https://blog.unimus.net/backing-up-the-unbackupable/</link>
        <guid isPermaLink="false">64ac2ae13f52d200013e422f</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Viliam Tkacik ]]></dc:creator>
        <pubDate>Fri, 08 Sep 2023 12:03:19 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/09/unimus-backup-of-devices-without-cli.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Today we will have a look at how you can handle devices which don't output their configs over CLI, which is not supported by Unimus out-of-the-box. However, a bit of scripting and Unimus' API enables you to navigate around this adversity.</p><h2 id="intro">Intro</h2><p>Configuration backup is an essential practice in any network environment. Possessing recent configuration files safeguards against data loss or system failure and lets you restore your network to a working state much faster than redeploying everything manually from scratch. Believe me this has saved money (and people's jobs) in the past. </p><p>Back on a serious note, as a full-featured Network Configuration Management solution Unimus performs these backup duties for you. Over time, Unimus also builds a versioned configuration history of your network from each device backup and notifies you of any and all changes in your network. With this superior visibility Unimus gives you a whole new level of change management adding on to other user favorite features like configuration auditing and change automation.</p><h2 id="how-unimus-backs-up-your-devices-and-when-devices-make-it-complicated">How Unimus backs up your devices and when devices make it complicated</h2><p>Unimus gathers backups from managed devices via CLI as any user would. It logs into the device using Telnet or SSH (please don't use Telnet however) and then retrieves the configuration of the device. And therein lies a potential issue. Not all systems have a CLI, such as <a href="https://blog.unimus.net/automating-mikrotik-swos-with-unimus-a-how-to-guide/">MikroTik's SwOS</a> (learn how you can get around it in this article on our blog), and some that have a CLI do not support CLI-based configuration backup. We'll call these the Unbackupables.</p><p>To illustrate an example, let's have a look at FortiAuthenticator by FortiNet. As a network element which provides centralized authentication services its configuration consists of both a textual configuration, available over the CLI, and a configuration database, which is managed through a web GUI. The config DB includes users, groups, the FortiToken device list, certificates, and many other config elements. All of them can be neatly packed into a <strong>binary</strong> backup file. FortiAuthenticator allows you to set up auto-backup. This is done by specifying an FTP server address, FTP directory, backup frequency and backup time.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/superrezFAcrop.png" class="kg-image" alt="FortiAuthenticator screenshot of Auto-backup menu" loading="lazy" width="1268" height="280" srcset="https://blog.unimus.net/content/images/size/w600/2023/07/superrezFAcrop.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/07/superrezFAcrop.png 1000w, https://blog.unimus.net/content/images/2023/07/superrezFAcrop.png 1268w" sizes="(min-width: 720px) 720px"><figcaption>FortiAuthenticator auto-backup config GUI</figcaption></figure><p>Another example where CLI doesn't support a config dump is PMP 450 access points by Cambium. Complete configuration can be downloaded via web interface and it will be stored in a <strong>text</strong> file (.cfg). The backup file can then be passed along to an FTP server using device specific CLI commands.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/image-2.png" class="kg-image" alt="Cambium PMP web GUI for downloading config file" loading="lazy" width="621" height="262" srcset="https://blog.unimus.net/content/images/size/w600/2023/07/image-2.png 600w, https://blog.unimus.net/content/images/2023/07/image-2.png 621w"><figcaption>Cambium PMP 450 configuration file download via GUI</figcaption></figure><p>We now have knowledge about particularities of backups in some devices and, as was cleverly foreshadowed above, we can have <strong>binary/text</strong> backup files pushed to an FTP server (TFTP, SCP or SFTP also works). We can also push backups into Unimus using its API. Let's use all this and a few lines of code to backup even the Unbackupable.</p><h2 id="setup">Setup</h2><!--kg-card-begin: markdown--><p>In real-world deployments, topologies can and will be complex. However with our setup it all boils down to three discrete components - the network device, an FTP server and Unimus, plus a script to tie it all together. Then we automate the process via <a href="#cron">scheduling</a>. The following section captures our test environment in more detail:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-concept-1.png" class="kg-image" alt="Simple diagram of config push to Unimus" loading="lazy" width="1440" height="528" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/backup-of-device-without-CLI-with-Unimus-concept-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/08/backup-of-device-without-CLI-with-Unimus-concept-1.png 1000w, https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-concept-1.png 1440w" sizes="(min-width: 720px) 720px"></figure><p>1) <u>A cisco router</u> on the left, will represent our Unbackupable - a network device Unimus cannot directly pull a config backup from. It needs to have the backup delivered to an FTP server. Just a note - we are using a Cisco device just for illustration as Unimus supports all Cisco devices natively, without needing to use an FTP server. </p><p>2) <u>An FTP (or TFTP, SCP, SFTP...) server</u> runs on either Linux or Windows Server. The only difference being what script version we use - Shell or Powershell. The script will push backups to Unimus (using the API) from files on the FTP server.</p><p>3) <u>Unimus</u>. We assume you already have your Unimus server up and running. If not, we have a guide for how to deploy Unimus on our <a href="https://wiki.unimus.net/display/UNPUB/Installing+Unimus">wiki</a>.</p><!--kg-card-begin: markdown--><p>The <a href="#shell"><strong>Script</strong></a>. Simply put, it creates backups on Unimus via API. It is described in detail later.</p>
<!--kg-card-end: markdown--><hr><p>For our setup to work, device backups need to somehow find themselves on the FTP server. There are two scenarios how this can be achieved:</p><p><u><strong>1) Device itself is capable of a scheduled backup push to an FTP server</strong></u></p><p>Similar to the FortiAuthenticator example, mentioned in the Intro, a device can be set up to push its config backup to an FTP server. Likewise, on Cisco IOS, Embedded Event Manager (EEM) feature enables you to create EEM Applet to execute action "copy config to FTP" when event "scheduled time" is triggered. The following image illustrates this logic:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-event-manager-1.png" class="kg-image" alt="Diagram 1 of config push to Unimus" loading="lazy" width="1440" height="560" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-event-manager-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-event-manager-1.png 1000w, https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-event-manager-1.png 1440w" sizes="(min-width: 720px) 720px"><figcaption>(1) Cisco EEM prompts backup push to FTP (2) Server scheduler executes the script (3) The script grabs backups from FTP and (4) pushes them to Unimus.</figcaption></figure><!--kg-card-begin: markdown--><p>This is the simpler scenario. Only <strong>two</strong> jobs need to be scheduled. First one on Cisco router via EEM applet that pushes running configuration to an FTP server at a scheduled time. Example below:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-code-card"><pre><code>event manager applet backup-config-daily
 event timer cron name daily-time cron-entry "30 3 * * *"
 action 1.0 cli command "enable"
 action 2.0 cli command "copy running-config ftp://username:password@ftp-server-ip/router.ip.address/config-$(date \"+%Y%m%d%H%M%S\").cfg"</code></pre><figcaption>Cisco IOS EEM applet for 'config backup to FTP' everyday at 3:30am</figcaption></figure><!--kg-card-begin: markdown--><p>Second job is scheduled on the host running FTP server via <a href="#cron">Cron</a> or <a href="#taskscheduler">Task Scheduler</a>, depending on the environment used.</p>
<!--kg-card-end: markdown--><p><u><strong>2) The other option - device has to be periodically instructed to push its backup to an FTP server</strong></u></p><p>Since the device itself is incapable of automatically backing itself up to the FTP server, an outside agent (Unimus) will send commands to the device instructing it to create a config backup and copy it to an FTP server according to a schedule.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-unimus-schedule-1.png" class="kg-image" alt="Diagram 2 of config push to Unimus" loading="lazy" width="1440" height="692" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-unimus-schedule-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-unimus-schedule-1.png 1000w, https://blog.unimus.net/content/images/2023/08/backup-of-device-without-CLI-with-Unimus-triggered-by-unimus-schedule-1.png 1440w" sizes="(min-width: 720px) 720px"><figcaption>(1) Unimus prompts device to push backup to FTP and (2) device pushes backup to FTP. (3) Server scheduler executes the script (4) Script grabs backups from FTP and (5) pushes them to Unimus.</figcaption></figure><!--kg-card-begin: markdown--><p>Although this diagram looks more complicated, again, we only need to schedule <strong>two</strong> jobs. First one runs on Unimus. It is a Mass Config Push preset following a daily schedule:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/08/Screenshot_20230823_161229.png" class="kg-image" alt="Unimus job preset GUI" loading="lazy" width="1241" height="628" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/Screenshot_20230823_161229.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/08/Screenshot_20230823_161229.png 1000w, https://blog.unimus.net/content/images/2023/08/Screenshot_20230823_161229.png 1241w" sizes="(min-width: 720px) 720px"><figcaption>Unimus Mass Config Push preset for 'config backup to FTP' everyday at 3:30am</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>tclsh
regexp {\S+ +\S+ +(\w+) +(\d+) +(\d+)} [exec show clock] match month day year
exec copy run ftp://my.ftp.server/router.ip.address/config-$day-$month-$year</code></pre><figcaption>Command set from the push job preset</figcaption></figure><!--kg-card-begin: markdown--><p>In both scenarios the second job is the execution of script on host running FTP server that pushes backups to Unimus. We will schedule it in <a href="#cron">Cron</a> or <a href="#taskscheduler">Task Scheduler</a>, depending on the environment.</p>
<!--kg-card-end: markdown--><h2 id="pushing-the-backups-from-the-ftp-server-to-unimus">Pushing the backups from the FTP server to Unimus</h2><p>Once config backups are copied to the FTP server, be it in <strong>binary</strong> or <strong>text</strong> form, our script can push them to Unimus. Backups of the devices will be stored on the FTP server following this specific structure for convenience:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/image-15.png" class="kg-image" alt="Example directory structure" loading="lazy" width="226" height="233"><figcaption>/ftp_rootdir/&lt;device_ip_address&gt;/config_backup</figcaption></figure><p>Each device has a separate directory using its management IP address as name, where all configuration backup versions are stored:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/image-16.png" class="kg-image" alt="Example config backup files" loading="lazy" width="394" height="234"><figcaption>e.g: <em>/home/ftp_data/10.31.8.3/Backup_10.31.8.3_2023-07-14_10-04.txt</em></figcaption></figure><hr><p>On the host with the FTP server we will be running a script (Linux shell or Powershell) that uploads the config backups to Unimus. Here are the steps it takes:</p><ol><li>Comb through each subdirectory under defined <strong>root directory</strong> extracting the directory name (in form of IP address)</li><li>Look up device on Unimus; (optional) work on specified Zone; (also optional) create a new device if none is found </li><li>Encode each backup file found in a subdirectory to <strong>base64</strong> string digestible by Unimus</li><li>Create device backup on Unimus via API from <strong>oldest to newest</strong> and delete it from ftp directory</li></ol><!--kg-card-begin: markdown--><h3 id="span-idshellshell-scriptspan"><span id="shell">Shell script</span></h3>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>The full ready-to-use script can be found on our <a href="https://github.com/netcore-jsa/unimus-useful-scripts/tree/main/unbackupables">GitHub</a>. If you are using the Powershell script, just skip to the <a href="#powershell">Powershell</a> section. We will focus on the Bash script in this section.</p>
<!--kg-card-end: markdown--><p>You can just download and use it. If you are curious in its internal functioning, feel free to read on!</p><p>Working mainly with APIs we will be using curl and jq packages. Install as necessary.</p><p>To start let's define <strong>mandatory variables</strong> for convenient parametric approach. This is the only part of the script we need to adjust for our environment to make it work. Apart from Unimus hostname/IP address, generated API token and http headers the script only needs to know the FTP root directory where the configs are saved.</p><figure class="kg-card kg-code-card"><pre><code># Mandatory parameters
UNIMUS_ADDRESS="&lt;http(s)://unimus.server.address(:port)&gt;"
TOKEN="&lt;api token&gt;"
# FTP root directory
FTP_FOLDER="/home/user/ftp_data/"</code></pre><figcaption>Mandatory variables</figcaption></figure><p>Next there are some <strong>optional parameters</strong> that are enabled by uncommenting the variables. From Unimus version 2.4.0-Beta3 we can specify a Zone for the script to work on. We can enable curl insecure option if self-signed certificate is used. Also we have an option to create new devices in Unimus if specific ones have not yet been added.</p><figure class="kg-card kg-code-card"><pre><code># Optional parameters
# Specifies the Zone where devices will be searched for by address/hostname
# CASE SENSITIVE; leave commented to use the Default (0) zone
#ZONE="0"
# Insecure mode
# If you are using self-signed certificates you might want to set this to true
SELF_SIGNED_CERT=false
# Variable for enabling creation of new devices in Unimus; set to true to enable
CREATE_DEVICES=false
# Specify description of new devices created in Unimus by the script
CREATED_DESC="The Unbackupable"</code></pre><figcaption>Optional variables</figcaption></figure><hr><p>Our backup solution is based on API calls to Unimus. <em>Create new device </em>and <em>Get device by address </em>are<em> </em>APIs from APIv2. Both functions <strong>createNewDevice</strong> and <strong>getDeviceId</strong> require device IP address as input (<code>$1</code>). GetDeviceId returns device ID value from the response using <code>| jq .data.id</code>. When working with a specific Zone, createNewDevice sends an additional key-value pair in the body and getDeviceId appends zoneId parameter to the URI. Check out the full API documentation on <a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation">wiki</a>.</p><figure class="kg-card kg-code-card"><pre><code>function createNewDevice() {
    if [ -z "$ZONE" ]; then
        curl $insecure -X POST -sSL -H "$HEADERS_ACCEPT" -H "$HEADERS_CONTENT_TYPE" -H "$HEADERS_AUTHORIZATION" -d '{"address": "'"$1"'","description":"'"$CREATED_DESC"'"}'\
        "$UNIMUS_ADDRESS/api/v2/devices" &gt; /dev/null
    else
        curl $insecure -X POST -sSL -H "$HEADERS_ACCEPT" -H "$HEADERS_CONTENT_TYPE" -H "$HEADERS_AUTHORIZATION" -d '{"address": "'"$1"'","description":"'"$CREATED_DESC"'", "zoneId": "'"$ZONE"'"}'\
        "$UNIMUS_ADDRESS/api/v2/devices" &gt; /dev/null
    fi
}</code></pre><figcaption>Shell function for 'Create new device' API</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>function getDeviceId() {
    if [ -z "$ZONE" ]; then
        echo "$(curl $insecure -X GET -sSL -H "$HEADERS_ACCEPT" -H "$HEADERS_AUTHORIZATION" "$UNIMUS_ADDRESS/api/v2/devices/findByAddress/$1" | jq .data.id)"
    else
        echo "$(curl $insecure -X GET -sSL -H "$HEADERS_ACCEPT" -H "$HEADERS_AUTHORIZATION" "$UNIMUS_ADDRESS/api/v2/devices/findByAddress/$1?zoneId=$ZONE" | jq .data.id)"
    fi
}</code></pre><figcaption>Shell function for 'Get device by address' API</figcaption></figure><p><em>Create new backup</em> API call is done by the <strong>createBackup </strong>function. It needs device ID (<code>$1</code>) and a JSON (<code>$2</code>) with base64 string of backup file and TEXT/BINARY string as input arguments.</p><figure class="kg-card kg-code-card"><pre><code>function createBackup() {
    curl $insecure -X POST -sSL -H "$HEADERS_ACCEPT" -H "$HEADERS_CONTENT_TYPE" -H "$HEADERS_AUTHORIZATION" -d "@$2" "$UNIMUS_ADDRESS/api/v2/devices/$1/backups" &gt; /dev/null
}</code></pre><figcaption>Shell function for Create new backup API</figcaption></figure><p>The functions above will be used in the main function -<strong> processFiles</strong>.</p><p><strong>processFiles</strong> function does the following:</p><ul><li>finds sub-directories inside provided ftp folder</li><li>retrieves device ID, creates new device in Unimus if none is found</li><li>sorts files inside each sub-directory by modification date from oldest to newest</li><li>converts each file to base64 string</li><li>checks if file is binary or text, creates binary/text backup accordingly and deletes the file</li></ul><figure class="kg-card kg-code-card"><pre><code>function processFiles() {
    # Set script directory for the script
    script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &amp;&gt;/dev/null &amp;&amp; pwd -P)
    cd $script_dir

    # Creating a log file
    log="$script_dir/unbackupables.log"
    printf 'Log File - ' &gt;&gt; $log
    date +"%F %H:%M:%S" &gt;&gt; $log

    # Insecure curl switch
    if $SELF_SIGNED_CERT; then
        insecure="-k"
    fi

    # Perform Unimus health check
    status=$(healthCheck)
    errorCheck "$?" 'Status check failed'

    if [ $status == 'OK' ]; then
        [ -n "$ZONE" ] &amp;&amp; zoneCheck
        echoGreen 'Checks OK. Script starting...'
        ftp_directory="$1"
        # Begin sweeping through the specified FTP directory
        for subdir in "$ftp_directory"/*; do
            if [ -d "$subdir" ]; then
                # Interpret directory names as device addresses/host names in Unimus
                address=$(basename "$subdir")
                # Check if device already exists in Unimus
                id=$(getDeviceId "$address")
                if [ $id = "null" ]; then
                    if $CREATE_DEVICES; then
                        createNewDevice "$address" &amp;&amp; id=$(getDeviceId "$address") &amp;&amp; echoGreen "New device added. Address: $address, id: $id"
                    fi
                fi
                if [ $id = "null" ] || [ -z "$id" ]; then
                    echoYellow "Device $address not found on Unimus. Consider enabling creating devices. Continuing with next device."
                else
                    for file in $(ls -t "$subdir"); do
                        if [ -f "$subdir/$file" ]; then
                            isTextFile=$(file -b "$subdir/$file")
                            if [[ $isTextFile == *"text"* ]]; then
                                bkp_type="TEXT"
                            else
                                bkp_type="BINARY"
                            fi
                            encoded_backup=$(base64 -w 0 "$subdir/$file")
                            temp_json_file=$(mktemp)

                            cat &lt;&lt;-EOF &gt; "$temp_json_file"
                            {
                            "backup": "$encoded_backup",
                            "type": "$bkp_type"
                            }
EOF
                            # Use jq to process the JSON from the temporary file
                            jq '.' "$temp_json_file" &gt; output.json
                            createBackup "$id" "output.json" &amp;&amp; echoGreen "Pushed $bkp_type backup for device $address from file $file"
                            # Clean up the temporary files &amp; backup file
                            rm "$temp_json_file" output.json "$subdir/$file"
                        fi
                    done
                fi
            fi
        done
    else
        if [ -z $status ]; then
            echoRed 'Unable to connect to unimus server'
            exit 2
        else
            echoRed "Unimus server status: $status"
        fi
    fi
    echoGreen 'Script finished'
}</code></pre><figcaption>Main shell function</figcaption></figure><p>Function is called supplying ftp root folder defined in the beginning:</p><pre><code>processFiles $FTP_FOLDER</code></pre><p>As we mentioned earlier, you can get the script in its wholesomeness on <a href="https://github.com/netcore-jsa/unimus-useful-scripts/tree/main/unbackupables">GitHub</a>.</p><hr><!--kg-card-begin: markdown--><h3 id="span-idpowershellpowershell-scriptspan"><span id="powershell">Powershell script</span></h3>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>Corresponding code walkthrough for <strong>Windows</strong> environments using Powershell can be found in this section. Grab the complete script from <a href="https://github.com/netcore-jsa/unimus-useful-scripts/tree/main/unbackupables">GitHub</a> or continue perusing if you're interested in how it works under the hood!</p>
<!--kg-card-end: markdown--><p>The only part that we need to update for a specific setup is the <strong>mandatory variables</strong>. Required are Unimus hostname/IP address, API token generated in Unimus and the location of FTP root directory. </p><figure class="kg-card kg-code-card"><pre><code># Mandatory parameters
$UNIMUS_ADDRESS = "&lt;http(s)://unimus.server.address(:port)&gt;"
$TOKEN = "&lt;api token&gt;"
# FTP root directory
$FTP_FOLDER = "/ftp_data"</code></pre><figcaption>Mandatory variables</figcaption></figure><p>Optionally we can specify the Zone we want to work on. This works from 2.4.0-Beta3. Next we can enable skipping certification check for when you are using self-signed certificate on Unimus. The script handles this in a separate fashion based on the version of Powershell present on the system. Also, variable <code>$CREATE_DEVICES</code> controls adding devices to Unimus if they're not present already.</p><figure class="kg-card kg-code-card"><pre><code># Optional parameters
# Specifies the Zone where devices will be searched for by address/hostname
# CASE SENSITIVE; leave commented to use the Default (0) zone
#$ZONE="0"
# Insecure mode
# If you are using self-signed certificates you might want to set this to true
$INSECURE = $false
# Variable for enabling creation of new devices in Unimus; set to true to enable
$CREATE_DEVICES = $false
# Specify description of new devices created in Unimus by the script
$CREATED_DESC = "Unbackupable"</code></pre><figcaption>Optional variables</figcaption></figure><hr><p><em>Create new device </em>and <em>Get device by address</em> API endpoints are accessed using functions <strong>Create-NewDevice</strong> and <strong>Get-DeviceId</strong>. Device IP address (<code>$address</code>) is used as a parameter for both functions. Get-DeviceId returns .data.id value (device ID) from API response. Get-DeviceId<em> </em>is also handling response exceptions by returning <code>null</code> when a device doesn't exist on Unimus. When working on non-default Zone Create-NewDevice adds <strong>zoneId</strong> key-value pair to its data payload and Get-DeviceId appends zoneId to the URI. Full API documentation can be found on <a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation">wiki</a>.</p><figure class="kg-card kg-code-card"><pre><code>function Create-NewDevice {
    param(
        [string]$address
    )

    $body = @{
        address = $address
        description = $CREATED_DESC
    }

    if ($ZONE) {
        $body["zoneId"] = $ZONE
    }

    $body = $body | ConvertTo-Json

    $headers = @{
        "Accept" = "application/json"
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $TOKEN"
    }

    if ($INSECURE -and $psMajorVersion -ge 6) {
        Invoke-RestMethod -SkipCertificateCheck -Uri "$UNIMUS_ADDRESS/api/v2/devices" -Method POST -Headers $headers -Body $body | Out-Null
    } else {
        Invoke-RestMethod -Uri "$UNIMUS_ADDRESS/api/v2/devices" -Method POST -Headers $headers -Body $body | Out-Null
    }
}</code></pre><figcaption>Powershell function for Create new device API</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>function Get-DeviceId {
    param(
        [string]$address
    )

    $headers = @{
        "Accept" = "application/json"
        "Authorization" = "Bearer $TOKEN"
    }

    if ($ZONE) {
        $uri="api/v2/devices/findByAddress/" + $address + "?zoneId=" + $ZONE
    } else {
        $uri="api/v2/devices/findByAddress/" + $address
    }

    try {
        if ($INSECURE -and $psMajorVersion -ge 6) {
            $response = Invoke-RestMethod -SkipCertificateCheck -Uri "$UNIMUS_ADDRESS/$uri" -Method GET -Headers $headers
        } else {
            $response = Invoke-RestMethod -Uri "$UNIMUS_ADDRESS/$uri" -Method GET -Headers $headers
        }
        return $response.data.id
    }
    catch {
        if ($_.Exception.Response.StatusCode -eq 404) {
            return "null"
        }
    }
}</code></pre><figcaption>Powershell function for Get device by address API</figcaption></figure><p><em>Create new backup </em>API is called through <strong>Create-Backup</strong> function. It is using device ID(<code>$id</code>), base64 string of the backup file (<code>$encodedBackup</code>) and TEXT/BINARY (<code>$type</code>) as input.</p><figure class="kg-card kg-code-card"><pre><code>function Create-Backup {
    param(
        [string]$id,
        [string]$encodedBackup,
        [string]$type
    )

    $body = @{
        backup = $encodedBackup
        type = $type
    } | ConvertTo-Json

    $headers = @{
        "Accept" = "application/json"
        "Content-Type" = "application/json"
        "Authorization" = "Bearer $TOKEN"
    }
    if ($INSECURE -and $psMajorVersion -ge 6) {
        Invoke-RestMethod -SkipCertificateCheck -Uri "$UNIMUS_ADDRESS/api/v2/devices/$id/backups" -Method POST -Headers $headers -Body $body | Out-Null
    } else {
        Invoke-RestMethod -Uri "$UNIMUS_ADDRESS/api/v2/devices/$id/backups" -Method POST -Headers $headers -Body $body | Out-Null
    }
}</code></pre><figcaption>Powershell function for Create new backup API</figcaption></figure><hr><p>The main function <strong>Process-Files</strong> executes the following logic:</p><ul><li>finds subdirectories inside provided ftp folder</li><li>retrieves device ID, creates new device in Unimus if none is found</li><li>sorts files inside each subdirectory by modification date from oldest to newest</li><li>converts each file to base64 string</li><li>checks if file is binary or text, creates binary/text backup accordingly and deletes the file</li></ul><figure class="kg-card kg-code-card"><pre><code>function Process-Files {
    param(
        [string]$directory
    )

    $log = Join-Path $PSScriptRoot "unbackupablesPS.log"
    $logMessage = "Log File - " + (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
    Add-Content -Path $log -Value $logMessage
    #Health check
    $status = Health-Check

    if ($status -eq 'OK') {
        if ($ZONE) {
            Zone-Check
        }

        Print-Green "Checks OK. Script starting..."
        $ftpSubdirs = Get-ChildItem -Path $directory -Directory

        foreach ($subdir in $ftpSubdirs) {
            $address = $subdir.Name
            # Check if device already exists in Unimus
            $id = "null"; $id = Get-DeviceId $address

            if ($id -eq "null" -and $CREATE_DEVICES) {
                Create-NewDevice $address
                $id = Get-DeviceId $address
                Print-Green ("New device added. Address: " + $address + ", id: " + $id)
            }

            if ($id -eq "null" -or $id -eq $null) {
                Print-Yellow ("Device " + $address + " not found on Unimus. Consider enabling creating devices. Continuing with next device.")
            } else {
                $files = Get-ChildItem -Path $subdir.FullName | Sort-Object -Property LastWriteTime -Descending

                foreach ($file in $files) {
                    if ($file.GetType() -eq [System.IO.FileInfo]) {
                        $content = [System.IO.File]::ReadAllBytes($file.Fullname)
                        $encodedBackup = [System.Convert]::ToBase64String($content)

                        if ($content -contains 0) {
                            $bkp_type = "BINARY"
                        } else {
                            $bkp_type = "TEXT"
                        }
                        Create-Backup $id $encodedBackup $bkp_type
                        Print-Green ("Pushed " + $bkp_type + " backup for device " + $address + " from file " + $($file.Name))
                        Remove-Item $file.FullName
                    }
                }
            }
        }
    } else {
        Print-Red "Unimus server status: $status"
    }
    Print-Green "Script finished."
}</code></pre><figcaption>Main powershell function</figcaption></figure><p>To run, call the main function via:</p><pre><code>Process-Files -directory $FTPFOLDER</code></pre><p>That's all she wrote! Here's a <a href="https://github.com/netcore-jsa/unimus-useful-scripts/tree/main/unbackupables">GitHub</a> link with the full version of the script.</p><!--kg-card-begin: markdown--><h3 id="scheduling-with-span-idcroncronspan">Scheduling with <span id="cron">Cron</span></h3>
<!--kg-card-end: markdown--><p>This section takes care of the automation part of the whole config push process. On Linux we can run our script at a predefined time using <strong>Cron</strong>. Add user-specific job via <code>crontab -e</code> and insert the following line:</p><figure class="kg-card kg-code-card"><pre><code>0 4 * * * /path/to/your_Unbackupables_script.sh</code></pre><figcaption>Crontab entry to run the script at 4:00am every day</figcaption></figure><!--kg-card-begin: markdown--><h3 id="span-idtaskschedulertask-schedulerspan"><span id="taskscheduler">Task Scheduler</span></h3>
<!--kg-card-end: markdown--><p>All you gamer folk working in Windows Server environment can use <strong>Task Scheduler</strong> to run the Powershell version of the script. These are the steps needed:</p><p>1) Under Actions hit 'Create task' and provide a name and description.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/08/TS_general.png" class="kg-image" alt="Task scheduler menu for creating a task" loading="lazy" width="784" height="559" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/TS_general.png 600w, https://blog.unimus.net/content/images/2023/08/TS_general.png 784w" sizes="(min-width: 720px) 720px"></figure><p>2) Switch to Triggers tab, create a 'New' trigger and specify the start time and frequency of your choice.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/08/TS_trigger.png" class="kg-image" alt="Task scheduler menu for adding new trigger" loading="lazy" width="589" height="515"></figure><p>3) In Actions tab hit 'New'. Select 'Start a program' as action, browse for PowerShell executable and add an argument by supplying the path to the Powershell script.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/08/TS_action.png" class="kg-image" alt="Task scheduler menu for creating an action" loading="lazy" width="453" height="498"></figure><p>After confirming we can see our new task 'push backups to Unimus' in Task Scheduler Library. It is now ready to automatically run according to our specified schedule.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/08/TS_result.png" class="kg-image" alt="Task scheduler menu for running tasks" loading="lazy" width="977" height="582" srcset="https://blog.unimus.net/content/images/size/w600/2023/08/TS_result.png 600w, https://blog.unimus.net/content/images/2023/08/TS_result.png 977w" sizes="(min-width: 720px) 720px"></figure><h2 id="backup-push-aftermath-in-unimus">Backup push aftermath in Unimus</h2><p>After the script runs we should see new backups created in Unimus:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/flash.png" class="kg-image" alt="Unimus GUI displaying text backups" loading="lazy" width="1377" height="457" srcset="https://blog.unimus.net/content/images/size/w600/2023/07/flash.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/07/flash.png 1000w, https://blog.unimus.net/content/images/2023/07/flash.png 1377w" sizes="(min-width: 720px) 720px"><figcaption>API created TEXT backups</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2023/07/image-13.png" class="kg-image" alt="Unimus GUI displaying binary backups" loading="lazy" width="1389" height="380" srcset="https://blog.unimus.net/content/images/size/w600/2023/07/image-13.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/07/image-13.png 1000w, https://blog.unimus.net/content/images/2023/07/image-13.png 1389w" sizes="(min-width: 720px) 720px"><figcaption>API created BINARY backups</figcaption></figure><p>Note that new device backups may show almost identical timestamps. This could happen when the script makes multiple API calls due to a collection of config files accumulated inside a single device folder on the FTP server. Likely scenario is an initial script execution when we start with a considerable backup history that we want pushed to Unimus from FTP. Or when the script is not executed periodically and configs on FTP stack up. If your backup push script runs with (slightly after) your 'copy backup to FTP' schedule, this should not happen. Even if it does the backups on Unimus would be created with preserved chronology thanks to the script's internal logic.</p><h2 id="t-shooting">T-shooting</h2><p>Things sometimes work out on the first try. More often tho, they don't. If your case is "they don't", keep on reading.</p><p>There are a few possible sources of headache - FTP directory structure, Unimus API, working Zone selection and using self-signed certificate being the likely candidates. Let's have a closer look at each.</p><h3 id="directory-structure-nerve-wreckers">Directory structure nerve wreckers</h3><p>Successful implementation of the backup handling in our scripts heavily relies on directory structure of your FTP server. Or on how robust the script is (we did our best). In our setup each managed device has its own folder named after device IP address or host name. Each folder in turn contains backup file versions for a given device. Make sure your setup keeps this structure.</p><h3 id="api-mishaps">API mishaps</h3><p>There should not be any but if issues with API calls arise one can check the following.</p><p>Every API request includes <strong>Authorization</strong> header that supplies 'API token', in order to authorize the request, following this scheme:</p><pre><code>Authorization: Bearer &lt;token&gt;</code></pre><p>To create a token log in to your Unimus instance and navigate to User management &gt; API tokens. Make sure you copy the whole string to the mandatory variable 'TOKEN' value. Otherwise you might get the following error:</p><pre><code>ERROR: Unimus server status: null</code></pre><h3 id="zone-related-blunders">Zone related blunders</h3><p>The script is designed to push backups to a specific Zone in Unimus, default one if no Zone is specified. If the specified Zone does not exist you will get an error:</p><pre><code>ERROR: Error. Zone &lt;ZoneID&gt; not found!</code></pre><p>Just make sure to specify an existing Zone ID. Zone ID is case sensitive.</p><h3 id="self-signed-cert-predicaments">Self-signed cert predicaments</h3><p>If you encounter errors with the script it pays to double-check optional parameters. <strong>Self-signed certificate</strong> on Unimus might give you SSL related errors if issued by a certificate authority unknown/untrusted to the system you're executing the script from. Enabling 'certification check skip' in the script is an option to consider.</p><h2 id="final-words">Final words</h2><p>We hope you will find this tutorial helpful for integrating configuration backups to Unimus from devices that do not support backups from CLI. We appreciate any feedback, questions or possible improvements and have a thread on our <a href="https://forum.unimus.net/viewtopic.php?f=11&amp;t=1674">Forum</a> for just that purpose.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Using Active Directory and LDAP for AAA in Unimus ]]></title>
        <description><![CDATA[ In this guide we look at how to pair your Unimus deploy with Active Directory and how to use LDAP for auth in Unimus. ]]></description>
        <link>https://blog.unimus.net/using-active-directory-and-ldap-for-aaa-in-unimus/</link>
        <guid isPermaLink="false">640257128618b40001ed59e1</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Mon, 03 Apr 2023 19:26:31 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/04/unimus-active-directory-and-ldap.png" medium="image"/>
        <content:encoded><![CDATA[ <p>In this guide, we would like to show you how to pair your Active Directory with Unimus and use LDAP for the AAA.</p><p>We will be focusing on AD running on Windows Server, and we will assume you have your server already set up with Active Directory.</p><h2 id="preparing-for-ldaporganizational-structure-in-active-directory">Preparing for LDAP - Organizational structure in Active Directory</h2><p>As LDAP is integrated and available in Active Directory by default, let's start by briefly showcasing the simple, exemplary organizational structure we created in <strong>Active Directory Users and Computers</strong>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/ldap_ad_structure.png" class="kg-image" alt loading="lazy" width="819" height="577" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/ldap_ad_structure.png 600w, https://blog.unimus.net/content/images/2023/03/ldap_ad_structure.png 819w" sizes="(min-width: 720px) 720px"></figure><p>We created a simple structure of a couple of child <strong>Organizational Units (OUs) </strong>under the <code>unimus.local</code> domain, more specifically <code>unimus.local &gt; Unimus &gt; Unimus Admins</code>.</p><p>Let's take another look at them in the <strong>ADSI Edit</strong> utility, where we can have a better view of their <strong>Distinguished Names (DNs):</strong></p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/ldap_adsiedit_structure.png" class="kg-image" alt loading="lazy" width="818" height="573" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/ldap_adsiedit_structure.png 600w, https://blog.unimus.net/content/images/2023/03/ldap_adsiedit_structure.png 818w" sizes="(min-width: 720px) 720px"></figure><p>ADSI Edit gives us a more useful (for us in this context) view of our organizational structure. We can see how each object chains and contributes to the DN of our target OU <code>Unimus Admins</code>.</p><p><code>OU=Unimus Admins,OU=Unimus,DC=unimus,DC=local</code></p><p>This DN will serve as a <strong>Base DN</strong> in Unimus for the lookup of the objects (users) based on an User Identifier attribute we will choose. As per Unimus' LDAP <a href="https://wiki.unimus.net/display/UNPUB/LDAP+Auth">documentation</a> (more on this later), we need to specify a Base DN which will serve as the root point for Unimus to search users from.</p><p>The last thing to look at is one of our users. Let's pick Jane Doe as an example and see how this user's DN looks:</p><p><code>CN=Jane Doe,OU=Unimus Admins,OU=Unimus,DC=unimus,DC=local</code></p><p>This is Jane Doe's DN; however, we don't have to use this full DN to identify Jane Doe. We can choose whichever of the available attributes of this account to use - assuming these attributes exist and do not have blank values. By right-clicking Jane Doe's record and choosing Properties, we can see the <strong>Attribute Editor</strong>, where we can examine and edit existing attributes or add new values to other unused attributes:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/ldap_adsiedit_userattributes.png" class="kg-image" alt loading="lazy" width="397" height="452"></figure><p>In this view, we can sort values so that we see all non-blank attributes together. In typical Windows AD deployments, you usually use the <code>sAMAccountName</code> attribute to lookup AD users instead of the DN. This is typically the username for Windows AD logins, so we will use this for the configuration in Unimus as well.</p><h2 id="ldap-integration-in-unimus">LDAP integration in Unimus</h2><p>Before we jump into the configuration section, let's start with some details on how LDAP auth works in Unimus. The process of authenticating a local user in Unimus against an external LDAP is done in two stages.</p><p>1) In the first stage, the Unimus LDAP client logs into the LDAP directory using the provided server details and accesses user credentials (DN and password). If the login is successful, Unimus will search for the provided user (the user attempting to log into Unimus) in the target directory tree defined by a base DN. If the user is matched using any configurable attribute (the User Identifier), Unimus proceeds to the second stage.</p><p>2) In the second stage, Unimus uses the previously matched provided username to retrieve the full user DN and attempts to authenticate the user with the provided password. If the authentication succeeds, the user is logged into Unimus.</p><p>For more details about authentication, security options, and OpenLDAP / AD configuration examples, we recommend taking a minute to check out our Wiki article, which goes over all of the above and more: <a href="https://wiki.unimus.net/display/UNPUB/LDAP+Auth">https://wiki.unimus.net/display/UNPUB/LDAP+Auth</a></p><h2 id="the-ldap-access-user">The LDAP Access User</h2><p>As mentioned above, we need an Access User which will be used to find the exact account trying to log in to Unimus. The Access User can be any LDAP account that has access (read-only access is sufficient) to the LDAP tree under which your login users exist. More info in the <a href="https://wiki.unimus.net/display/UNPUB/LDAP+Auth">Wiki articled</a> mentioned above.</p><p>The Access User of course also needs to be able to "see" the user account objects as well, not just the OUs in your directory.</p><h2 id="unimus-configurationconfiguring-ldap">Unimus configuration - Configuring LDAP</h2><p>You can configure LDAP in <code>User management &gt; LDAP configuration</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/ldap_unimus_ldapconfiguration-2.png" class="kg-image" alt loading="lazy" width="1098" height="711" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/ldap_unimus_ldapconfiguration-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/ldap_unimus_ldapconfiguration-2.png 1000w, https://blog.unimus.net/content/images/2023/03/ldap_unimus_ldapconfiguration-2.png 1098w" sizes="(min-width: 720px) 720px"></figure><p>Where:</p><p><strong>LDAP server address</strong> - the address of your domain controller.</p><p><strong>LDAP port</strong> - the default port is 389.</p><p><strong>LDAP access user DN</strong> - full DN of the user able to access AD records.</p><p><strong>LDAP access password</strong> - a password for the access user.</p><p><strong>Security</strong> - an optional security measure for LDAP transmission. Both <strong>LDAPS</strong> and <strong>StartTLS</strong> require respective server-side (the LDAP server) configurations.</p><p>Note, if you opt to use LDAPS or StartTLS including certificate validation, you can follow <a href="https://wiki.unimus.net/display/UNPUB/Importing+certificates+for+TLS+mail+support">our guide on importing CA certificates</a> in Unimus; both require importing your CA to the system's <strong>Java KeyStore</strong>. You don't need to do this if you enable <code>Do not check certificate</code> to skip LDAP cert validation.</p><p><strong>LDAP base DN</strong> - the DN of the root of the tree storing user records, which Unimus will be authenticating login attempts against.</p><p><strong>User identifier </strong>- an identifier that Unimus uses to match users in LDAP to retrieve user's full DN for auth purposes. You can use any attribute that suits your needs. For example, you can use <code>CN</code>, <code>sAMAccountName</code>, <code>uid</code>, etc.</p><p><strong>LDAP filter</strong> - an optional LDAP filter to further filter matches in the given base DN. Standard LDAP filter syntax is supported.</p><h2 id="unimus-configurationadding-ldap-accounts-to-unimus">Unimus configuration - Adding LDAP accounts to Unimus</h2><p>Unimus currently requires every external user account to have a matching account in Unimus. You can create a user in <code>User management &gt; Users</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/ldap_unimus_extusercreation.png" class="kg-image" alt loading="lazy" width="230" height="455"></figure><p>Where:</p><p><strong>Username</strong> - the matching username for the external account in Active Directory.</p><p><strong>Authentication method</strong> - LDAP.</p><p><strong>Select access role</strong> - select which role the user will represent.</p><p>At this point, users should be able to log into Unimus using LDAP and their external account credentials.</p><h2 id="troubleshooting">Troubleshooting</h2><p>There are generally two troubleshooting sources for any issue with LDAP AAA. Unimus log and AD debug logging on Windows Server. We actually recommend focusing on Unimus log, as it features full LDAP error codes without any additional configuration, which is not the case with AD debug logging (which requires manually specifying logging levels for all AD components).</p><p>Let's take a closer look at a couple of exemplary error messages you could encounter and their likely cause:</p><pre><code>Ldap authentication failed: '10.20.30.40:389; nested exception is javax.naming.CommunicationException: 10.20.30.40:389 [Root exception is java.net.ConnectException: Connection refused (Connection refused)]'</code></pre><p>This error indicates a wrong configuration (wrongly specified IP and / or port) or a network issue (likely a firewall).</p><pre><code>Ldap authentication failed: '10.20.30.40:636; nested exception is javax.naming.CommunicationException: 10.20.30.40:636 [Root exception is javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake]'</code></pre><p>This error indicates an issue with LDAPS or StartTLS configuration, e.g. Unimus has LDAPS or StartTLS checked, but the server doesn't use either or is not set up for either, or the server is missing an SSL certificate for the LDAP server. In the latter case, refer to the section above where we mention what is required for a certificate to be recognized.</p><pre><code>Ldap authentication failed: '[LDAP: error code 49 - 80090308: LdapErr: DSID-0C09041C, comment: AcceptSecurityContext error, data 52e, v4563]</code></pre><p>This error indicates incorrect credentials. These can indicate any combination of wrong credentials (DN or password) for the access user or a wrong password provided for the Unimus user to be authenticated.</p><pre><code>Ldap authentication failed: 'Incorrect result size: expected 1, actual 0'</code></pre><p>This error indicates the access to LDAP was successful, but given the base DN and input in the username field, no match for the given user was found. Possible causes include:</p><ul><li>Wrong username input and / or the user was not found when searching under the Base DN. Check the username and / or the Base DN.</li><li>The User Identifier is incorrectly configured. Check if the identifier is correct for your LDAP objects.</li><li>The provided base DN does not contain a user identified by the configured User Identifier (similar to the 2 points above). Check if the user you want to auth has the identifier you are using.</li><li>An LDAP filter specifying conditions the user does not comply with. Check if your filter properly returns the user you want to auth.</li></ul><h2 id="final-words">Final words</h2><p>Hopefully this article can guide you through connecting Unimus with Active Directory via LDAP. If you have any questions, or you run into any issues, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=9" rel="noopener">the Support section of our forums</a>, or contact us through our usual support channels.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Unimus &amp; NetXMS - how to monitor and trigger Unimus jobs in NetXMS ]]></title>
        <description><![CDATA[ A how-to guide on monitoring Unimus device job results in NetXMS and creating NetXMS alarms if a device doesn't have a valid backup. We will also explore how to run Unimus jobs directly from NetXMS. ]]></description>
        <link>https://blog.unimus.net/unimus-netxms-how-to-monitor-and-trigger-unimus-jobs-in-netxms/</link>
        <guid isPermaLink="false">6425c8cf14e70400018249f6</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Thu, 30 Mar 2023 20:07:37 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/03/unimus-netxms-1-.png" medium="image"/>
        <content:encoded><![CDATA[ <p>In this guide we will look at how to monitor Unimus device job results in NetXMS, and how to create NetXMS alarms if a device doesn't have a valid backup within a specified timeframe. We will also explore how to run Unimus jobs directly from NetXMS.</p><h2 id="unimus-netxms-integration">Unimus &amp; NetXMS integration</h2><p>Running Unimus together with NetXMS creates a powerful ecosystem for both monitoring (NMS) and config management (NCM) of your infrastructure. You can integrate both solutions together - use data from NetXMS in Unimus, and vice-versa.</p><p>Unimus has a built-in NetXMS importer which allows Unimus to adopt nodes from NetXMS. This allows you to automate device ingestion in your network - both when you are first deploying the systems, as well when you add devices into your network. There is no need to add a device to Unimus and NetXMS separately, just add your device to NetXMS, and Unimus will automatically adopt it. You can read more about how to configure this <a href="https://wiki.unimus.net/display/UNPUB/NetXMS+importer">on our Wiki</a>.</p><p>In this article however, we want to look at how to integrate data from Unimus into NetXMS - how to see Unimus job statuses and device data in NetXMS, and how to trigger Unimus jobs directly from NetXMS.</p><h2 id="preparations">Preparations</h2><p>There are a few steps to do before we get started:</p><h3 id="1-enable-web-service-proxy-in-the-netxms-agent-of-the-node-polling-your-network">1) Enable Web Service Proxy in the NetXMS Agent of the node polling your network.</h3><p>If you are not using Zones in NetXMS, this will be your NetXMS server. If you are using Zones, you will need to enable this on the Zone's proxy.</p><p>To enable the Web Services Proxy, we edit the Agent's config:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image.png" class="kg-image" alt loading="lazy" width="755" height="477" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image.png 600w, https://blog.unimus.net/content/images/2023/03/image.png 755w" sizes="(min-width: 720px) 720px"></figure><p>And add the appropriate setting <code>EnableWebServiceProxy = yes</code> in the config:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-3.png" class="kg-image" alt loading="lazy" width="993" height="293" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-3.png 600w, https://blog.unimus.net/content/images/2023/03/image-3.png 993w" sizes="(min-width: 720px) 720px"></figure><p>You need to restart the NetXMS Agent for this to take effect.</p><h3 id="2-create-scripts-required-for-web-service-definitions-dcis-and-object-tools">2) Create scripts required for Web Service Definitions, DCIs and Object Tools.</h3><p>After the Web Service Proxy is enabled, we need to create a few scripts that other components in NetXMS will use. These scripts provide your Unimus server address the Unimus API token to DCIs and Object Tools which will later use them.</p><p>We have prepared an export of these scripts for you, available in the <a href="https://github.com/netcore-jsa/netxms-configuration-exports">NetXMS Configuration Exports</a> GitHub repo. You can simply download the export and import it in your NetXMS using <code>Tools &gt; Import Configuration...</code></p><p>We will import 4 scripts:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-4.png" class="kg-image" alt loading="lazy" width="961" height="319" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-4.png 600w, https://blog.unimus.net/content/images/2023/03/image-4.png 961w" sizes="(min-width: 720px) 720px"></figure><p>You will need to open the <code>Unimus::getServerAddress</code> script, and change the returned value to the URL of your Unimus Server.</p><p>You will also need to change the API Token in the <code>Unimus::getApiToken</code> script. You can generate an API token in Unimus in the <code>User management &gt; API tokens</code>. Click the clipboard button to copy then token, and paste it as the return string in the NetXMS script.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-5.png" class="kg-image" alt loading="lazy" width="1918" height="487" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-5.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/image-5.png 1000w, https://blog.unimus.net/content/images/size/w1600/2023/03/image-5.png 1600w, https://blog.unimus.net/content/images/2023/03/image-5.png 1918w" sizes="(min-width: 720px) 720px"></figure><h3 id="3-create-web-service-definitions">3) Create Web Service Definitions.</h3><p>Web Service Definitions are also available in the <a href="https://github.com/netcore-jsa/netxms-configuration-exports">NetXMS Configuration Exports</a> GitHub repo. After you import them, you should be able to see them in <code>Configuration &gt; Web Service Definitions</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-6.png" class="kg-image" alt loading="lazy" width="1409" height="288" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-6.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/image-6.png 1000w, https://blog.unimus.net/content/images/2023/03/image-6.png 1409w" sizes="(min-width: 720px) 720px"></figure><h2 id="monitoring-unimus-jobs-in-netxms">Monitoring Unimus jobs in NetXMS</h2><p>After the preparations above are finished, we can now import templates that use the Web Service Definitions to pull data from Unimus into NetXMS.</p><p>The <a href="https://github.com/netcore-jsa/netxms-configuration-exports">NetXMS Configuration Exports</a> GitHub repo contains a template export, which when exported creates 2 templates:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-7.png" class="kg-image" alt loading="lazy" width="833" height="376" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-7.png 600w, https://blog.unimus.net/content/images/2023/03/image-7.png 833w" sizes="(min-width: 720px) 720px"></figure><p>We have not included any Auto-Apply Rules in these templates, as each NetXMS deploy is usually quite unique in how nodes are structured. You can apply your own Auto-Apply Rules if you wish. For this article, we will just bind nodes to this template manually.</p><p>You should bind your networking devices to the <code>Unimus device data</code> template, and the Unimus Server node to the <code>Unimus server status</code> template. For the devices, you should see multiple DCIs created:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-13.png" class="kg-image" alt loading="lazy" width="1264" height="289" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-13.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/image-13.png 1000w, https://blog.unimus.net/content/images/2023/03/image-13.png 1264w" sizes="(min-width: 720px) 720px"></figure><p>There is a threshold on the <code>Unimus device last backup time</code> DCI, which creates an alarm if a backup was not successful for the device in the last 3 days. You can change this threshold in the DCI config if you wish:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-9.png" class="kg-image" alt loading="lazy" width="1427" height="833" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-9.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/image-9.png 1000w, https://blog.unimus.net/content/images/2023/03/image-9.png 1427w" sizes="(min-width: 720px) 720px"></figure><h2 id="triggering-unimus-jobs-from-netxms">Triggering Unimus jobs from NetXMS</h2><p>Next step is to create Object Tools which allow you to trigger Unimus jobs directly from NetXMS. The <a href="https://github.com/netcore-jsa/netxms-configuration-exports">NetXMS Configuration Exports</a> GitHub repo contains Object Tools, which should look like this when imported:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-10.png" class="kg-image" alt loading="lazy" width="890" height="224" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-10.png 600w, https://blog.unimus.net/content/images/2023/03/image-10.png 890w" sizes="(min-width: 720px) 720px"></figure><p>You should see the tools on your nodes under <code>Commands</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/03/image-12.png" class="kg-image" alt loading="lazy" width="1195" height="544" srcset="https://blog.unimus.net/content/images/size/w600/2023/03/image-12.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/03/image-12.png 1000w, https://blog.unimus.net/content/images/2023/03/image-12.png 1195w" sizes="(min-width: 720px) 720px"></figure><p>If you wish, you can create <code>Filters</code> to make sure these tools are only available on Unimus-managed nodes. We have not included any due to the same reason as the Auto-Apply Rules on the templates.</p><h2 id="final-words">Final words</h2><p>Hopefully this article can serve as an example on how to integrate Unimus and NetXMS together. If you have any questions, or you run into any issues, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=9" rel="noopener">the Support section of our forums</a>, or contact us through our usual support channels.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.3.0 ]]></title>
        <description><![CDATA[ 2.3.0 is the latest major Unimus release. With 120+ lines in the Changelog, many new major features, minor features and stability improvements are present. Read on to find out more... ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-3-0/</link>
        <guid isPermaLink="false">63e1b7e98618b40001ed588c</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 15 Feb 2023 18:54:52 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2023/02/unimus-2.3.0.png" medium="image"/>
        <content:encoded><![CDATA[ <p>2.3.0 is the latest major Unimus release. With 120+ lines in the Changelog, this article hopes to provide a short overview of the major features and other new additions in this release.</p><p>The full Changelog is also present at the bottom of this article - if you would like to see everything that this release contains.</p><hr><h2 id="ldap-authentication-support">LDAP authentication support</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/02/ldap-preview.png" class="kg-image" alt loading="lazy" width="1294" height="734" srcset="https://blog.unimus.net/content/images/size/w600/2023/02/ldap-preview.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/02/ldap-preview.png 1000w, https://blog.unimus.net/content/images/2023/02/ldap-preview.png 1294w" sizes="(min-width: 720px) 720px"></figure><p>The most expected feature in this release is support for native LDAP authentication. LDAP has been requested by many users from the community and we are happy to report it's now here!</p><p>The LDAP connector was designed to be fully configurable and to support both OpenLDAP and Microsoft Active Directory. Examples on how to configure both are available on our Wiki. Please check the full documentation <a href="https://wiki.unimus.net/display/UNPUB/LDAP+Auth">on our Wiki</a> for more info.</p><hr><h2 id="ms-sql-database-support">MS SQL database support</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/02/mssql-support-preview.png" class="kg-image" alt loading="lazy" width="1281" height="684" srcset="https://blog.unimus.net/content/images/size/w600/2023/02/mssql-support-preview.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/02/mssql-support-preview.png 1000w, https://blog.unimus.net/content/images/2023/02/mssql-support-preview.png 1281w" sizes="(min-width: 720px) 720px"></figure><p>Another often requested feature implemented in this release is support for using a Microsoft SQL Server database. During the Deploy Wizard, you can now select MSSQL as your database. After you finish the Wizard, everything should work as expected.</p><p>Support for MSSQL brings the total database support in Unimus up to 5 different DB engines (HSQL, MySQL, MariaDB, PostgreSQL and MSSQL). We hope this offers enough flexibility to deploy Unimus in just about any environment. </p><hr><h2 id="offline-mode-support-for-air-gapped-networks">"Offline Mode" (support for air-gapped networks)</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/02/unimus-offline-mode.png" class="kg-image" alt="Unimus offline mode" loading="lazy" width="1476" height="518" srcset="https://blog.unimus.net/content/images/size/w600/2023/02/unimus-offline-mode.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/02/unimus-offline-mode.png 1000w, https://blog.unimus.net/content/images/2023/02/unimus-offline-mode.png 1476w" sizes="(min-width: 720px) 720px"></figure><p>Last year, we announced that we will be bringing support for Offline Mode to Unimus. Until today, Unimus required a check with our Licensing Server to function. Starting with 2.3.0, full air-gapped deployment of Unimus is possible.</p><p>With Offline Mode, Unimus can now be deployed in highly-secured environments where complete outside connectivity blocking is required. </p><p>Please note the Offline Mode is only available to customers with the Unlimited License (more info <a href="https://forum.unimus.net/viewtopic.php?p=3869#p3869">here</a>). If you are interested in using Offline Mode, just contact our Support.</p><hr><h2 id="config-search-export-and-send-functions">Config Search Export and Send functions</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2023/02/unimus-conf-search-export-preview.png" class="kg-image" alt loading="lazy" width="1346" height="700" srcset="https://blog.unimus.net/content/images/size/w600/2023/02/unimus-conf-search-export-preview.png 600w, https://blog.unimus.net/content/images/size/w1000/2023/02/unimus-conf-search-export-preview.png 1000w, https://blog.unimus.net/content/images/2023/02/unimus-conf-search-export-preview.png 1346w" sizes="(min-width: 720px) 720px"></figure><p>Results of Config Search can now be exported! This is very useful when you need to present a report for a security audit, to management, or use the search results for processing in a different system.</p><p>The export format, as well as the contents are fully configurable. You can export the search results in a nice looking HTML document with full search information, or only export the search results themselves in YAML for further machine processing.</p><p>We hope this feature makes your reporting duties a bit easier :)</p><hr><h2 id="other-minor-new-features">Other minor new features</h2><p>On top of the major features shown above, there are many other minor features, improvements, and UI / UX updates. As with every release, we also added support for many new devices types. This time around, drivers for <strong>28 new device types</strong> were added.</p><p>For the full list of new features (and supported devices), please see the Changelog below.</p><hr><h2 id="bug-fixes-and-security-fixes">Bug fixes and security fixes</h2><p>As with every release, a sizable list of fixes for various bugs and issues is present. One of the things of note are the fixes for many edge-cases where jobs (Discovery / Backups / Push) could fail on various older networking devices.</p><p>There are also a few security issues fixed in this release. In particular, our MySQL DB driver library was updated due to multiple fixed vulnerabilities reported in its older versions.</p><hr><p>Finally, here is the full Changelog for 2.3.0. As this is a major release, the Changelog is quite long. But if you want to see all the changes in this release, please read on:</p><pre><code>= Version 2.3.0 =
Features:
  Added device UUIDs in APIv2 (all "/devices" endpoints)
  The "Default" Zone will now be marked as "Default" when renamed
  Added support for recognizing Observium devices IDs in Observium NMS Sync
  Improved built-in backup filters for Siklu devices
  Incremental performance improvements across many parts of the system
  Added support for acknowledging login prompts in keyboard-interactive mode during SSH login
  Added retrieval of backup from Fiberhome devices in configure mode if not available in enable mode
  Improved device CLI mode switching and mode detection during discovery
  Added support for prompt format changing when switching contexts on Cisco ASA (multi-context)
  Added support for Configure Mode on Sonicwall NSA
  Added handling which improves backup formatting on Cambium cnMatrix switches (removes double lining)

  Added "Offline Mode" (support for air-gapped networks):
    - Unimus can be now switched to full offline mode, which removes the necessity to contact our Licensing Server
    - Offline Mode licenses are only available to users with an Unlimited License subscription
    - please contact us to request an Offline Mode license

  Added support for LDAP authentication:
    - LDAP can now be used as an external authentication provider
    - full support for configuring custom user search DN and specifying username LDAP attributes
    - tested on both OpenLDAP as well as Microsoft Active Directory
    - full documentation: https://wiki.unimus.net/display/UNPUB/LDAP+Auth

  Added support for MS SQL:
    - we have added support for Microsoft SQL Server as an officially supported DB engine
    - the Deploy Wizard will allow you to select MSSQL during deployment
    - to migrate to MSSQL, you will need to setup a new Unimus deploy, data migration is currently not supported

  Added Config Search Export and Send functionality:
    - you can now export (download) or directly send Config Search results
    - support for exporting in both HTML and YAML format
    - configurable export formatting (header, search criteria, etc.) or just results

  Added options to specify which SSH cryptography options Unimus supports:
    - in some environments, it may be desired to disable support for weaker SSH crypto
    - full documentation: https://wiki.unimus.net/display/UNPUB/Supported+SSH+cryptography

  Added support for:
    - Accedian AMO series
    - ADVA LX series console servers
    - Arris C4 series chassis
    - BDCOM OLTs
    - Additional Brocade NOS device models
    - CheckPoint Gaia devices
    - CheckPoint Security Gateway
    - CheckPoint Security Management Server
    - CheckPoint SMB Gateway
    - CheckPoint VSX
    - Additional Ciena SAOS device models
    - Dasan OLTs
    - Entrasys switches (A4 / B2 series)
    - Extreme Wing APs in cluster mode / virtual controller mode
    - Extreme WLC
    - Fortinet FortiAuthenticator
    - Metaswitch Perimeta SBCs
    - NetApp switches
    - Nokia OLTs (FX-8)
    - MRV LX series console servers
    - Opengear Infrastructure Manager devices
    - Opengear Resilience Gateway (ACM)
    - Pulse Secure Virtual Traffic Manager
    - Ribbon (ECI) Apollo
    - Securepoint UTM
    - SNR (NAG) Switches
    - YunKe switches
    - Zyxel GS19xx series switches
    - Zyxel ATP

Fixes:
  Fixed backup retention would not work on specific MySQL Server versions
  Fixed Inverted Config Search would not work on specific PostgreSQL versions
  Fixed diff visualization would incorrectly show new empty lines when large delete sections were followed by a new addition
  Fixed first failed job on a newly added device would not set its Last Job Status to failed
  Fixed disabled retention jobs would still show up in "Schedules &gt; Show scheduled jobs" window
  Fixed API v2 get backups by device id and latest backups by device id not working
  Fixed API (of the local instance) denying all requests when connection to Licensing Server was down
  Fixed API v3 Push Jobs search not working on PostgreSQL
  Fixed possible deletion attempt on an already deleted object comment which would result in errors
  Fixed Per-Tag Connector config updates not being propagated between concurrent users (live updates were missing)
  Fixed "Schedules" table updates not being propagated between concurrent users (live updates were missing)
  Fixed "Config Search &gt; Show all lines" does not work if Context lines is set to a negative value
  Fixed moving devices between Zones would not trigger needed rediscovery in specific cases
  Fixed moving devices between Zones would trigger unneeded rediscovery in specific cases
  Fixed incorrect "Currently running Scans" count if a Network Scan preset was deleted while it was running
  Fixed "Devices &gt; Last Job Status" could be incorrect if running a job with all Connectors disabled
  Fixed multiple minor UI / UX issues and UI element state and alignment issues
  Fixed SSH connections failing to PanOS devices when login acknowledgement prompts were enabled
  Fixed backup not working on specific Fiberhome devices
  Fixed backup and Config Push could fail on some Positron GAM devices
  Fixed backup not working on Cisco FXOS devices in cluster mode
  Fixed Cisco SX devices could contain backup command echo as part of the backup
  Fixed Exablaze Fusion devices could contain backup command echo as part of the backup
  Fixed discovery failing on specific Aruba ArubaOS / HP(E) ProCurve devices
  Fixed discovery failing on specific Brocade NOS devices
  Fixed discovery failing on specific Ciena SAOS devices
  Fixed discovery failing on DCN devices with newer firmwares (after rebranding to YunKe)
  Fixed discovery failing on netElastic vBNG
  Fixed discovery failing on Dell OS10 switches if they output a Bell before the prompt
  Fixed discovery failing on Extreme VX devices (VX9000)
  Fixed discovery failing on Opengear devices when using the "root" user
  Fixed discovery failing on newer versions of OPNsense
  Fixed discovery failing on Fiberstore S5850 (and related devices) with newer firmwares
  Fixed discovery failing on specific Nokia / Vecima OLT devices
  Fixed discovery failing on multi-context Cisco ASA with different prompt in different contexts
  Fixed discovery could fail on devices which use pagination in very specific cases
  Fixed discovery not falling back to Telnet after IO errors occurred on the SSH connection

  Fixed SSH connections failing to servers which did not support higher MAC segment size:
     - affected devices usually had very old firmwares with weak SSH MAC support
     - example of affected devices: Dell PowerConnect 55xx, some versions of Cisco SF/SG switches, etc.

Security fixes:
  Updated MySQL Connector due to multiple published vulnerabilities in older versions
  Fixed currently opened "Devices &gt; Tags" window still working if user lost access to the device
  Fixed currently opened "Devices &gt; Comments" window still working if user lost access to the device
  Users which did not have full access to a Config Push preset could still delete the preset in its context menu

Embedded Core version:
  2.3.0

Known issues:
  ISSUE: "Re-discover affected devices when Ports or Connectors change" Advanced Settings option does not work
  WORKAROUND: none
  STATUS: issue scheduled for fixing

  ISSUE: Some screens in Unimus show time in server's time zone, others in client's (browser's) time zone
  WORKAROUND: none, issue only relevant if client has different time zone than server
  STATUS: we are debating on how to fix this - will likely create a setting to select which TZ should be used</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Update on Unimus security - 2022 edition ]]></title>
        <description><![CDATA[ A "state of Unimus security" update for 2022, including the introduction of a new Security Hub and support for full offline mode / support for deploying Unimus in air-gapped networks. ]]></description>
        <link>https://blog.unimus.net/update-on-unimus-security-2022-edition/</link>
        <guid isPermaLink="false">62335abde683410001ceabcb</guid>
        <category><![CDATA[ Other ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Tue, 28 Jun 2022 12:55:44 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/06/unimus-security-update-2022.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Early last year we wrote our first report on the security of Unimus releases and Unimus' code-base (available <a href="https://blog.unimus.net/update-on-unimus-codebase-and-release-security/">here</a>). The report was prompted by the "SolarWinds incident", and the following questions from our user-base (you) on what the state of Unimus security was. Since last year however, many things have happened in the security industry as a whole (log4j anyone?), and we have also been working hard to improve the security of Unimus specifically as well.</p><p>We think now (since we just released <a href="https://blog.unimus.net/unimus-security-penetration-test-report-2022/">the results of Unimus pentests</a>) is the right time to do a "state of Unimus security" update for 2022.</p><h2 id="new-security-hub">New Security Hub</h2><p>We have created a new <a href="https://security.netcore.software/">Security Hub</a>.</p><p>This is hosted on a completely separate server without any links to our other infrastructure to avoid the possibility of tampering with the data in case our other infrastructure components were compromised.</p><p>For now, you can find the hashes of all current production binaries, as well as instructions on how to check the hashes and code signatures of all our binaries. Security-related documents are also hosted there - you can find the results of the mentioned pentests there as well. We will be adding more to the Security Hub in the coming months.</p><h2 id="full-offline-airgapped-mode-for-unimus">Full offline / airgapped mode for Unimus</h2><p>We are officially announcing support for full offline mode - support for running Unimus in air-gapped networks.</p><p>Even tho we have always tried to be as transparent with <a href="https://forum.unimus.net/viewtopic.php?f=7&amp;t=1232">what Unimus sends to our licensing server</a> and we support <a href="https://wiki.unimus.net/display/UNPUB/Running+Unimus+behind+a+HTTP%28S%29+proxy">proxying the licensing communication</a>, we understand that having a hard requirement on outbound connectivity from Unimus to our licensing server can be a security issue in sensitive environments.</p><p>Implementing support for full offline is a large amount of work, but we want to bring this into Unimus before the end of this year (2022). The offline mode will be available to any customer on the Unlimited License tier once ready.</p><h2 id="penetration-testing">Penetration testing</h2><p>Like we mentioned at the start of the article - we published the results of penetration tests of the Unimus API and the web GUI earlier this week. This was the culmination of our security focus over the last year. In the lead-up to the pentests, we did multiple internal rounds of reviews and improvements to security of both our infrastructure, the build pipeline (CI/CD) and the codebase itself.</p><p>You can check out the <a href="https://blog.unimus.net/unimus-security-penetration-test-report-2022/">full pentest results here</a>. As a short summary - we are very happy with the results. A single major issue has been identified which was fixed in <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=1508">the 2.2.3 release</a>, with the rest being only lower severity findings.</p><h2 id="code-signing">Code signing</h2><p>In last year's report we stated that we wanted to introduce code-signing across the entire Unimus binary ecosystem. I am glad to report that this has been done, and since release 2.1.0 (August 2021), all our release binaries are fully code-signed. The <a href="https://security.netcore.software/">Security Hub</a> shows the commands you can use to validate the signatures.</p><p>As a side-note - on Windows you can just right-click the .exe files, and you will find the full signing chain in the "Properties" of the .exes.</p><h2 id="bug-bounty-security-bounty-program">Bug bounty / Security bounty program</h2><p>Another area we pointed to in last year's report was the establishment of an official Security Bounty program. We worked on this over the last year, but sadly due to circumstances outside of our control, an official bounty program is not yet ready. So while we don't have any news on this front for now, we are still committed to find a way to make this happen. As soon as any progress is made, we will keep you informed.</p><h2 id="our-infrastructure-cicd-systems-and-the-unimus-build-pipeline">Our infrastructure, CI/CD systems and the Unimus build pipeline</h2><p>Other than the directly visible public efforts mentioned above, we have also been putting a lot of time into our internal infrastructure, our CI/CD systems and our software build pipelines.</p><p>There has been progress on many fronts in these categories, from technology (work on internal SSO systems to make sure all access controls and account management is in a single place); to monitoring, reviews and audits of our systems (periodic reviews of our infrastructure, monitoring for IoC (indicators of compromise), work on SIEM, etc.); to process improvements (our onboarding and offboarding processes to assure no accounts are left open that should be closed) - all the way to the build process of Unimus itself (we have for example implemented policies which assure that vulnerable software components (like log4j) can not be a part of our software).</p><p>The above is not a complete list at all, so if you are interested in any particular area from this section, let us know and we will gladly provide more details.</p><h2 id="the-outro">The outro</h2><p>With the pentests (and all the work preceding them) now behind us, the largest time investments into security that were needed are now done. Going forward, we will however be keeping increased attention on security, and continue to promote security internally as one of the most important aspects of our software.</p><p>If you have any questions and / or comments, please post in the <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=1518">topic corresponding to this article on our forums</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Unimus security - penetration test report 2022 ]]></title>
        <description><![CDATA[ Over the last year we have been putting increasing focus on security in Unimus, culminating with a full pentest of the Unimus API and the Unimus web GUI. In this post, per our full transparency policy, we want to share the pentest results with you. ]]></description>
        <link>https://blog.unimus.net/unimus-security-penetration-test-report-2022/</link>
        <guid isPermaLink="false">62335ad0e683410001ceabcf</guid>
        <category><![CDATA[ Other ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 22 Jun 2022 14:41:28 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/06/unimus-penetration-test-report-2022.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Over the last year we have been putting increasing focus and dev time on security in Unimus. This culminated with a full pentest of the Unimus API and the Unimus web GUI a couple of months ago. In this post, we want to share the pentest results with you - as per our full transparency policy.</p><p>Unimus API pentest result summary: <a href="https://security.netcore.software/reports/Unimus.API-comprehensive-pentest.Summary.2022.pdf">download here</a><br>Unimus web GUI pentest result summary: <a href="https://security.netcore.software/reports/Unimus.Web-comprehensive-pentest.Summary.2022.pdf">download here</a></p><p>A single high severity issue has been identified during the pentest, and has already been fixed in <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=1508">the 2.2.3 release</a>, available for download <a href="https://unimus.net/download">here</a>.</p><h2 id="overview">Overview</h2><p>In summary, we are quite happy with the outcome, and the state of security in Unimus. With only a single high severity issue discovered (which has already been fixed), and a few other lower severity issues present, this is a very positive result.</p><p>With Unimus being an on-premise application - where each customer's (your) instance and data is completely separate from others and you are in complete control of access to your instance; any issues and their potential impact are greatly mitigated as well.</p><p>Let's look at the discovered results in more details...</p><h2 id="high-severity-issues">High severity issues</h2><h3 id="a1-post-auth-stored-xsshtml-injection-allows-javascript-code-execution-in-the-unimus-web-gui">A1) Post-auth stored XSS/HTML injection allows JavaScript code execution in the Unimus web GUI</h3><p>Starting with the biggest issue - post-auth XSS injection in the Unimus GUI. An authenticated attacker could inject XSS code in multiple places in Unimus. This is definitely a major issue, however Unimus being on-premise, and injection being possible only from a properly authenticated account are mitigating factors.</p><p>As we mentioned above, this has already been fixed in <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=1508">the 2.2.3 release</a>, and we highly recommend all customers upgrade to this release.</p><h2 id="medium-severity-issues">Medium severity issues</h2><p>There are a few medium severity issues in both the API and the web GUI. We plan to tackle these issues one-by-one over the rest of the year. Here are our notes on the individual issues.</p><h3 id="b1-insecure-direct-object-reference-idor-in-the-unimus-api">B1) Insecure Direct Object Reference (IDOR) in the Unimus API</h3><p>This simply means that the API (APIv2 specifically) uses database IDs as object identifiers in API calls. We have already migrated to using UUIDs in APIv3, so this issue will be completely gone once APIv2 is fully replaced by APIv3.</p><h3 id="b2-no-expiration-on-jwt-tokens-for-the-unimus-api">B2) No expiration on JWT tokens for the Unimus API</h3><p>All API tokens currently have infinite lifetime. We will introduce a new option to limit lifetime of API tokens (you will still have the option to create "infinite" tokens if you wish).</p><p>This will be a new optional checkbox during API token creation, which will allow you to set a "Lifetime" for a token.</p><h3 id="b3-session-fixation-allows-theft-of-session-cookie-in-special-cases-in-the-unimus-web-gui">B3) Session fixation allows theft of session cookie in special cases in the Unimus web GUI</h3><p>In special cases, the session auth cookie could be stolen - and allow the attacker to hijack the authenticated user's session. This is difficult to pull off, and requires access to the user's local PC / cookie storage. As such, this is not high severity - since if the attacker has full access to the user's PC, all bets are off anyway.</p><p>There are some technical challenges in our web GUI framework which make this a bit difficult to fix - but we will be put time into research later in the year to see if we can improve our session handling.</p><h3 id="b4-response-time-based-account-enumeration-allows-to-find-valid-application-login-names-in-the-unimus-web-gui">B4) Response time based account enumeration allows to find valid application login names in the Unimus web GUI</h3><p>This is a fun one - an attacker can differentiate between valid and invalid usernames based on the time it takes Unimus to return a failed auth response. This is because for invalid usernames, cryptography (hashing) on the provided password is not performed, and as such invalid username handling is faster.</p><p>In theory, an attacker can fire tons of auth requests with random usernames, and the "slow" (in relative terms) auth failures are likely to be valid usernames.</p><p>Here are examples of how this looks:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Login</th>
<th>Is valid?</th>
<th>Response time (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Unimus</td>
<td>Yes</td>
<td>60</td>
</tr>
<tr>
<td>Aa</td>
<td>Yes</td>
<td>57</td>
</tr>
<tr>
<td>Bb</td>
<td>Yes</td>
<td>55</td>
</tr>
<tr>
<td>Cc</td>
<td>Yes</td>
<td>55</td>
</tr>
<tr>
<td>WrongUser</td>
<td>No</td>
<td>26</td>
</tr>
<tr>
<td>BadUser</td>
<td>No</td>
<td>31</td>
</tr>
<tr>
<td>Invalid</td>
<td>No</td>
<td>31</td>
</tr>
<tr>
<td>Attacker</td>
<td>No</td>
<td>30</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>As you can see, there is still jitter, but a pattern is definitely distinguishable. Unimus being on-premise is again a huge mitigation factor here however - as you are in full control over who can access your instance. Using external auth (like Radius or LDAP) will also make this attack unfeasible.</p><p>We are currently debating on how to approach this, as solutions for this are not as straight-forward as they might seem.</p><h2 id="low-severity-issues">Low severity issues</h2><h3 id="c1-no-function-limiting-in-the-unimus-api">C1) No Function Limiting in the Unimus API</h3><p>This simply means that there is no request rate limiting in the Unimus API. If you want to implement request limits, we would highly recommend doing so on a front-end proxy server (such as an NGINX server acting as a reverse proxy).</p><h3 id="c2-invalid-credential-uuid-accepted-for-delete-in-the-unimus-api">C2) Invalid Credential UUID Accepted For Delete in the Unimus API</h3><p>The APIv3 allows you to call delete methods with any strings in the "uuid" field and will return a HTTP 200 response. If the submitted value is not a valid and existing UUID, nothing will however happen. We will implement input validation and return a proper error HTTP response code if the submitted value is not in the UUID format.</p><h3 id="c3-missing-lock-out-in-the-unimus-api">C3) Missing Lock Out in the Unimus API</h3><p>This is related to C1 - in that the Unimus API will allow any number of requests without throttling, whether the submitted request does or does not have a valid token.</p><p>As with C1, this can be taken care of on the front-end proxy server (such as an NGINX server acting as a reverse proxy).</p><h3 id="c4-no-account-lock-out-policy-allows-password-guessing-attacks-in-the-unimus-web-gui">C4) No account lock out policy allows password guessing attacks in the Unimus web GUI</h3><p>The Unimus web GUI allows an unlimited number of auth attempts. We are conflicted on whether we should add a lockout mechanism - since Unimus is on-premise and you have control over who can access your Unimus instance. We know login lockouts can cause a lot of frustration...</p><p>Either way, Unimus does already have notifications and logging on invalid login attempts. We <strong>highly</strong> recommend either setting up alarms on logs, or enabling our built-in failed login notifications, so you are informed if someone is trying to break in to your Unimus instance.</p><h2 id="the-outro">The outro</h2><p>Like we mentioned in the Overview, we are happy that our focus on security over the last year lead to good pentest results. Going forward, we plan to resolve the outstanding issues as described above for each, and we are going to keep treating security as one of the fundamental priorities in Unimus.</p><p>If you are interested in full pentest reports rather than the summaries (or you need them for compliance), please feel free to reach out to us.</p><p>We hope this was an interesting read, should you have any questions, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=9">the Support section of our forums</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Using Windows Server NPS for AAA in Unimus ]]></title>
        <description><![CDATA[ In this guide, we show you how Microsoft's Network Policy Server, or NPS for short, can be configured to act as a RADIUS server for AAA in Unimus. By using NPS, you can use your domain (AD) credentials to login to Unimus. ]]></description>
        <link>https://blog.unimus.net/using-nps-for-aaa-in-unimus/</link>
        <guid isPermaLink="false">6228bfafe683410001cea9db</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Tue, 24 May 2022 14:58:49 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/03/unimus-windows-nps.png" medium="image"/>
        <content:encoded><![CDATA[ <p>In this guide, we would like to show how Microsoft's Network Policy Server, or NPS for short, can be configured to act as a RADIUS server to handle AAA for Unimus. By using NPS, you can use your Windows domain (Active Directory) credentials to login to Unimus.</p><p>In this guide we assume you are already running <strong>Windows Server</strong>, <strong>Active Directory</strong>, and have installed the<strong> NPS server</strong>. If you don't have an NPS server installed yet, you can do so by navigating to <code>Add roles and features &gt; Role-based or feature-based installation &gt; Select your machine &gt; Network Policy and Access Services</code>. Follow the wizard and confirm the dependencies it will list.</p><h2 id="preparing-for-npswindows-server-2019-users">Preparing for NPS - Windows Server 2019 users</h2><p>If you are running Windows Server 2019 you will need to look at one current bug, which directly touches NPS, and causes the traffic to be dropped at the firewall level despite the default port rules set up by NPS. You can fix the issue by opening the Command Prompt and running this command:</p><pre><code>sc sidtype IAS unrestricted</code></pre><p>Please restart the server after you make the change.</p><p>You can read more about this issue <a href="https://directaccess.richardhicks.com/2018/11/27/always-on-vpn-and-windows-server-2019-nps-bug/">here</a> or <a href="https://www.risual.com/2019/03/windows-2019-server-nps-bug/">here</a>.</p><h2 id="preparing-for-npsusers-and-groups-in-active-directory">Preparing for NPS - Users and Groups in Active Directory</h2><p>To start, create a user group. This user group will be used as a condition for a network policy in NPS to authenticate users later:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/ad_add_group.gif" class="kg-image" alt loading="lazy" width="813" height="577"></figure><p>Next, create a user. This will be a user we want to grant access to Unimus:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/ad_add_user-6.gif" class="kg-image" alt loading="lazy" width="813" height="577"></figure><p>Then add the user(s) to the group you created earlier:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/ad_user_add_group.gif" class="kg-image" alt loading="lazy" width="813" height="577"></figure><h2 id="preparing-for-npsauthentication-methods">Preparing for NPS - Authentication methods</h2><p>Before we start configuring NPS, we need to decide on the auth protocol we will use. Unimus currently supports two authentication methods, PAP and CHAP. </p><h3 id="pap-password-authentication-protocol">PAP (Password Authentication Protocol)</h3><p>PAP is, by all means, an insecure protocol. When using PAP, the password is sent hashed using the shared secret between the RADIUS client (Unimus) and RADIUS server (NPS). This exposes the passwords to a risk, since anyone with the secret could reverse the password hashing and access passwords in plaintext.</p><p>Usually, we don't recommend using PAP. However, PAP has an advantage when compared to CHAP - the password on each end can be stored encrypted using any method. Since it can be encrypted in storage, the password is much more immune to leakage when then password storage is compromised.</p><p>In short, PAP encrypts passwords in storage, but transfers them as a cleartext hash over the network.</p><h3 id="chap-challenge-handshake-authentication-protocol">CHAP (Challenge-Handshake Authentication Protocol)</h3><p>CHAP is a more secure method, which does not transfer passwords via the network at all. Instead, when a link is established between the RADIUS client (Unimus) and the RADIUS server (NPS), the server responds with a challenge (a salt - a random string). The client uses hashing for the password and the challenge and sends the hash via network to the server. This is then used for authentication on that end.</p><p>One disadvantage of CHAP is that both the password(s) must be stored in an unencrypted or reversibly encrypted format. This exposes password storage to a potential risk if the database (AD) is compromised. Healthy Active Directory security practices are recommended.</p><p>In short, CHAP transfers passwords over the network securely, however passwords must be stored in cleartext (or using reversible encryption) in storage.</p><h2 id="what-to-do-if-you-wish-to-use-chap">What to do if you wish to use CHAP</h2><p>If you choose to use CHAP, then before proceeding we need to prepare accounts to be usable with CHAP. As in the section above, we will be making changes to <strong>Active Directory - User and Groups</strong>. You will need to enable reversible encryption for the user's password and reset the password afterwards:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/ad_user_chap.gif" class="kg-image" alt loading="lazy" width="813" height="577"></figure><h2 id="nps-configuration">NPS Configuration</h2><h3 id="register-the-nps-server-in-active-directory">Register the NPS server in Active Directory</h3><p>To use NPS, it needs to be registered as an Active Directory service .To do so, right-click the NPS (Local) entry in the top-left corner of the NPS window and click on <code>Register server in Active Directory</code>.</p><h3 id="add-unimus-as-a-radius-client">Add Unimus as a RADIUS client</h3><p>Next we need to create Unimus as an NPS RADIUS Client:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/nps_add_client.gif" class="kg-image" alt loading="lazy" width="872" height="619"></figure><p>Don't forget to copy the generated secret and paste it somewhere. This secret will be used in the RADIUS configuration in Unimus.</p><h3 id="configure-nps-accounting">Configure NPS Accounting</h3><p>Configuring accounting on NPS is also extremely helpful:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/nps_accounting.gif" class="kg-image" alt loading="lazy" width="872" height="619"></figure><p>You can have NPS log into a database or a text file. If you prefer the database, you can connect it with your MSSQL database. Otherwise, logging to a file is the easier choice. You can also choose which type of records you want to log.</p><h3 id="create-network-policy-for-unimus">Create Network Policy for Unimus</h3><p>To use Unimus with NPS, we need to create a policy:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/nps_network_policy.gif" class="kg-image" alt loading="lazy" width="872" height="619"></figure><p>Let's run down through some of the sections when setting up a Network Policy:</p><!--kg-card-begin: markdown--><p><strong>Conditions</strong> - in this guide, we are using the membership in a user group as a condition for granting access. However, it is up to you if you require or wish to create more conditions or use different conditions to authenticate accounts.</p>
<p><strong>Access Permission</strong> - unless you are setting up some specific network policies, we recommend creating a single network policy with Grant access permission.</p>
<p><strong>Authentication Methods</strong> - here, we want to disable <strong>MS-CHAP v1</strong> and <strong>v2</strong> methods, which are not currently supported, and choose the <strong>PAP</strong> or <strong>CHAP</strong> method. Refer to the section above to decide which method is ideal or preferred for your use case.</p>
<p><strong>Settings</strong> - this section defines settings which can be used upon access being granted. There is no requirement for Unimus to be defined here.</p>
<!--kg-card-end: markdown--><p>This is all that is needed to configure the NPS server to authenticate logins from Unimus. No extra configuration should be required and NPS should be ready at this point.</p><h2 id="unimus-configurationconfiguring-radius">Unimus configuration - Configuring RADIUS</h2><p>You can configure RADIUS in <code>User management &gt; Radius configuration</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/unimus_radius_configuration.png" class="kg-image" alt loading="lazy" width="650" height="415" srcset="https://blog.unimus.net/content/images/size/w600/2022/03/unimus_radius_configuration.png 600w, https://blog.unimus.net/content/images/2022/03/unimus_radius_configuration.png 650w"></figure><p>Where:</p><!--kg-card-begin: markdown--><p><strong>Radius server address</strong> - the address of the Windows Server machine running the NPS server.</p>
<p><strong>Authentication port</strong> - the default port, unless changed, is 1812.</p>
<p><strong>Authentication protocol</strong> - choose one you chose when creating the Network Policy in NPS.</p>
<p><strong>Accounting port</strong> - the default port, unless changed, is 1813.</p>
<p><strong>Radius access secret</strong> - the secret you generated when adding Unimus as the RADIUS client in the NPS.</p>
<!--kg-card-end: markdown--><p>You can also test if your configuration works by clicking <strong>Show test</strong>. You don't need to have a user account added to Unimus to test this. This test should pass if you follow this guide. If it doesn't, we have some troubleshooting steps for you at the end of this guide.</p><h2 id="unimus-configurationadding-radius-accounts-to-unimus">Unimus configuration - Adding RADIUS accounts to Unimus</h2><p>Unimus currently requires every external user account to have a matching account in Unimus. You can create a user in <code>User management &gt; Users</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/unimus_add_user.png" class="kg-image" alt loading="lazy" width="255" height="460"></figure><p>Where:</p><!--kg-card-begin: markdown--><p><strong>Username</strong> - the matching username as the external account in Active Directory.</p>
<p><strong>Authentication method</strong> - Radius.</p>
<p><strong>Select access role</strong> - select which role the user will represent.</p>
<!--kg-card-end: markdown--><p>At this point, users should be able to log into Unimus using RADIUS and their external account credentials.</p><h2 id="troubleshooting">Troubleshooting</h2><p>In our experience, the most useful information when troubleshooting is directly on the Windows Server. Whether checking the <strong>Event Viewer</strong> or the <strong>NPS accounting log</strong>. Here's how.</p><h3 id="event-viewer">Event Viewer</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/windows_event_viewer.png" class="kg-image" alt loading="lazy" width="902" height="673" srcset="https://blog.unimus.net/content/images/size/w600/2022/03/windows_event_viewer.png 600w, https://blog.unimus.net/content/images/2022/03/windows_event_viewer.png 902w" sizes="(min-width: 720px) 720px"></figure><p>The Event Viewer provides a lot of clarity for possible events and errors. Every entry includes a message with a clear description of the problem and an Event ID, which can be used to search for possible solutions to Windows generic issues. Here are some examples of messages you may encounter:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Log Level</th>
<th>Event ID</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr>
<td>Info</td>
<td>6273</td>
<td>Network Policy Server denied access to a user.</td>
</tr>
<tr>
<td>Info</td>
<td>6272</td>
<td>Network Policy Server granted access to a user.</td>
</tr>
<tr>
<td>Error</td>
<td>13</td>
<td>A RADIUS message was received from the invalid RADIUS client IP address 10.1.100.240.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Fortunately, these messages are specific, and in the Details tab you can see even more details, including NPS's internal error interpretation. For example, this way you can see more details on events when users weren't granted access, like in the case of the first one above. The fields you are interested in are <strong>ReasonCode</strong> and <strong>Reason</strong>, and you can see information like this:</p><!--kg-card-begin: markdown--><p><em>ReasonCode: 19</em><br>
<em>Reason: The user could not be authenticated using Challenge Handshake Authentication Protocol (CHAP). A reversibly encrypted password does not exist for this user account. To ensure that reversibly encrypted passwords are enabled, check either the domain password policy or the password settings on the user account.</em></p>
<!--kg-card-end: markdown--><p>This error for example, tells us the user's password is not stored in a reversibly encrypted format, which we can then check in the user's account. If the user already has the support checked, it is very likely the password wasn't reset after the change.</p><h3 id="nps-accounting-log">NPS Accounting log</h3><p>While most of the information about a potential problem can be gathered and interpreted by the Even Viewer in a better human-readable form, the most important information is also included in the accounting log:</p><pre><code>&lt;Event&gt;
    &lt;Timestamp data_type="4"&gt;03/11/2022 11:06:45.206&lt;/Timestamp&gt;
    &lt;Computer-Name data_type="1"&gt;WIN-P3URXOXOR1T&lt;/Computer-Name&gt;
    &lt;Event-Source data_type="1"&gt;IAS&lt;/Event-Source&gt;
    &lt;Class data_type="1"&gt;311 1 10.100.1.111 03/03/2022 16:57:07 31&lt;/Class&gt;
    &lt;Authentication-Type data_type="0"&gt;2&lt;/Authentication-Type&gt;
    &lt;Fully-Qualifed-User-Name data_type="1"&gt;UNIMUS\unimusadmin&lt;/Fully-Qualifed-User-Name&gt;
    &lt;Client-IP-Address data_type="3"&gt;10.9.21.123&lt;/Client-IP-Address&gt;
    &lt;Client-Vendor data_type="0"&gt;0&lt;/Client-Vendor&gt;
    &lt;Client-Friendly-Name data_type="1"&gt;Unimus&lt;/Client-Friendly-Name&gt;
    &lt;Proxy-Policy-Name data_type="1"&gt;Use Windows authentication for all users&lt;/Proxy-Policy-Name&gt;
    &lt;Provider-Type data_type="0"&gt;1&lt;/Provider-Type&gt;
    &lt;SAM-Account-Name data_type="1"&gt;UNIMUS\unimusadmin&lt;/SAM-Account-Name&gt;
    &lt;Packet-Type data_type="0"&gt;3&lt;/Packet-Type&gt;
    &lt;Reason-Code data_type="0"&gt;19&lt;/Reason-Code&gt;
&lt;/Event&gt;
</code></pre><h2 id="final-words">Final words</h2><p>Hopefully this article can guide you through connecting Unimus with NPS. If you have any questions, or you run into any issues, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=9" rel="noopener">the Support section of our forums</a>, or contact us through our usual support channels.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Automating MikroTik SwOS backups with Unimus ]]></title>
        <description><![CDATA[ MikroTik SwitchOS, unlike its RouterOS sibling, doesn't have a CLI interface over SSH nor Telnet. In this article, we look at how to pull backups from SwOS through its HTTP(S) interface into Unimus. ]]></description>
        <link>https://blog.unimus.net/automating-mikrotik-swos-with-unimus-a-how-to-guide/</link>
        <guid isPermaLink="false">62027d54e683410001cea097</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Tue, 03 May 2022 19:39:22 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/mikrotik-switchos.png" medium="image"/>
        <content:encoded><![CDATA[ <p>MikroTik SwitchOS, unlike its RouterOS sibling, doesn't have a CLI interface over SSH nor Telnet. In this article, we look at how to pull backups from SwOS through its HTTP(S) interface into Unimus.</p><p>While we are planning to add native support for HTTP(S) connectors into Unimus, we would like to showcase that even though native support for HTTP(S)-only devices is still not ready, Unimus is equipped with tools to allow users to push backups from such devices into Unimus. </p><p>A few months ago we published a guide on <a href="https://blog.unimus.net/automating-frr-backups-with-unimus-a-how-to-guide/">how to push backups from FRR</a> (FRRouting) into Unimus. In this article, we will look into MikroTik SwOS, downloading a backup via its HTTP(S) interface, and getting these backups to Unimus. Let us show you how below.</p><h3 id="step-1adding-mikrotik-swos-devices-to-unimus-and-generating-an-api-token">STEP 1 - Adding MikroTik SwOS devices to Unimus and generating an API token</h3><p>As a first step, we want to prepare things in Unimus for our new devices and also generate an API token to be able to submit API calls and upload our backups. Let's start with the API token:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_swos_apitoken.gif" class="kg-image" alt loading="lazy" width="1560" height="867"></figure><p>The next step is to add our SwOS devices into Unimus and give them an identical description so that the script is able to fetch all the devices by the Description field. Lastly, we want to set them as Unmanaged devices, so Unimus will not try to run Discovery (nor Backups) on these devices, as that would fail.</p><p>To do so, you can use any of the methods you already know, whether by adding them manually to the list of devices or by importing them in bulk. However, we recommend the latter. In the example below I imported an CSV file with five devices with a predefined comment <code>SwOS</code><em> </em>which will be imported as a device's description.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_swos_unmanaged_csvimport-1.gif" class="kg-image" alt loading="lazy" width="1560" height="867"></figure><p>After importing the devices, we need to set them as Unmanaged. Note that you cannot edit the Description field in bulk, so you can see why the CSV import is the recommended method. It will save you a lot of time.</p><h3 id="step-2preparing-a-backup-and-uploading-it-into-unimus">STEP 2 - preparing a backup and uploading it into Unimus</h3><p>If you haven't had a chance to familiarize yourself with the Unimus API, we recommend checking out our introduction to the Unimus API in our FRRouting article <a href="https://blog.unimus.net/automating-frr-backups-with-unimus-a-how-to-guide/">here</a>, which includes more details on what we will be doing here as well.</p><p>When we looked into options to upload a backup of FRR, we showed you both ways of uploading a text or a binary file. In this article we will focus only on the latter option as the SwOS backup is not human-readable and, as such, it doesn't benefit us to upload it as a text.</p><p>Binary backup means that we will be uploading a file, instead of just text. In this case, it will be SwOS's backup file backup - <code>.swb</code>.</p><p>The script we use below also requires information which you will need to fill in before running it. These information include:</p><ul><li><strong>Unimus address</strong></li><li><strong>Unimus port</strong></li><li><strong>API token</strong> you created in the first step</li><li><strong>Device description</strong> string used to filter devices in Unimus - this string should be unique to just SwOS devices</li><li><strong>Device username</strong></li><li><strong>Device password</strong></li></ul><pre><code>#!/bin/bash

#Basic settings
unimusaddress=
unimusport=
apitoken=
devicedescription=
deviceuser=
devicepass=

#Set a working directory in the current directory
cd "${0%/*}"

#Get devices with a given description and filter out a list of IPs
getdevices=$(curl -s -H "Accept: application/json" -H "Authorization: Bearer $apitoken" "http://$unimusaddress:$unimusport/api/v2/devices/findByDescription/$devicedescription")
deviceiplist=$(echo $getdevices | grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")

for i in $deviceiplist; do
    echo $i
    #Download the backup from the device
    wget -t 1 -T 10 -q --user $deviceuser --password $devicepass http://$i/backup.swb &amp;&amp; echo "Download finished."

    #If the downloaded backup is not empty, upload it to Unimus. Otherwise, skip it.
    if [ -s backup.swb ]; then
        #Get the current device's ID necessary for the upload
        getdeviceiddirty=$(curl -s -H "Accept: application/json" -H "Authorization: Bearer $apitoken" "http://$unimusaddress:$unimusport/api/v2/devices/findByAddress/$i")
        if [[ $getdeviceiddirty =~ \"id\":([[:digit:]]+?), ]]; then
            deviceid=${BASH_REMATCH[1]}
        fi

        #Encode the backup for transport using BASE64
        encodedbackup=$(base64 -w 0 backup.swb)

        #Upload the backup to Unimus
        curl -s -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer $apitoken" -d '{"backup":"'"$encodedbackup"'","type":"BINARY"}' "http://$unimusaddress:$unimusport/api/v2/devices/$deviceid/backups" -o /dev/null &amp;&amp; echo -e "Upload finished.\n"
    else
        if [[ -f backup.swb ]]; then
            echo -e "Downloaded backup from device $i is empty. Skipping the file...\n"
        else
            echo -e "Backup from $i could not be retrieved. Skipping the file...\n"
            continue
        fi
    fi
    #Cleanup
    rm backup.swb
done</code></pre><p>Here is a breakdown of the workflow of this script:</p><ul><li>First, the script will use the Unimus API to fetch a list of devices (searching by the device description) and prepare a list of IPs.</li><li>Then, the script works through each IP within a loop.</li><li>In the loop, the script will download a backup and check if the backup file was successfully downloaded and if it contains data.</li><li>If it does, the script will use the Unimus API again to fetch the device ID for the particular IP, encode the backup, and upload it to Unimus.</li><li>If the file is not downloaded (e.g., the device is offline) or the downloaded file is empty, it will handle both cases separately and return the appropriate message to inform the user if such a case pops up.</li><li>Lastly, the script cleans up the backup file and repeats with the next IP.</li></ul><p>This is an example of what your output may look like after the script is finished:</p><pre><code>10.10.10.1
Download finished. 
Upload finished.

10.10.10.2
Unable to retrieve backup from 10.10.10.2. Skipping the file...

10.10.10.3
Download finished. 
Upload finished.

10.10.10.4
Download finished. 
Upload finished.

10.10.10.5
Downloaded backup from device 10.10.10.5 is empty. Skipping the file...</code></pre><p>Now, let's check Unimus and see if we got our backup and what we can do with it:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_swos_backup.gif" class="kg-image" alt loading="lazy" width="1560" height="867"></figure><p>As you can see, Unimus received our backup, but compared to a text backup, we cannot see contents of this file. This is because it is a binary file and it could be in any format - <code>.tar.gz</code>, <code>.bin</code>, <code>.zip</code>, etc. We can still download or send it.</p><p>If there is a change to this binary file we will see it correctly reflected in Unimus creating a new change point and if we were to check Diff, we would see the changed SHA1 sum.</p><p>Note, when downloading a binary backup, Unimus will not append any extension to this file, we recommend renaming it right away.</p><h2 id="step-3job-scheduling">STEP 3 - job scheduling</h2><p>One-time execution of scripts is nice, however just as with any backup job, we want it to be run automatically and periodically.</p><p>The last part of this article will be adding a scheduled job to Cron. Depending on your software and/or user privileges you have, you might need to change the way to set up a cron job, e.g. via user-specific jobs using <code>crontab -e</code>. Since with our setup we need root privileges for accessing cron configuration files, we will add a job to <code>/etc/crontab</code> directly, and set our script to run every night at 3AM (just like our default schedule in Unimus).</p><pre><code>0 3 * * *    root    /root/swos_backup.sh</code></pre><p>And that's it! We have created an automated way to generate and upload backups into Unimus for MikroTik SwOS devices.</p><h2 id="final-words">Final words</h2><p>We hope this article can serve as a template that can be used to upload any files / backups into Unimus. If you have any questions, or run into any issues using the examples in this article, please feel free to post in <a href="https://forum.unimus.net/viewtopic.php?f=11&amp;t=1478">this topic in the Automation section of our Forums</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Unimus Core HA deploy - a how-to guide ]]></title>
        <description><![CDATA[ In this article we setup a highly-available Unimus Core pair in a 2 node cluster using Corosync and Pacemaker. ]]></description>
        <link>https://blog.unimus.net/unimus-core-ha-deploy/</link>
        <guid isPermaLink="false">622680dde683410001cea8b3</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Tue, 08 Mar 2022 01:58:37 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/03/unimus-core-ha-deploy.png" medium="image"/>
        <content:encoded><![CDATA[ <p>We often get asked how to deploy the Unimus Core in a high availability scenario. While Unimus can natively handle multiple Cores attempting to connect and become the active poller for a single Zone by dropping an incoming Core connection if another Core is already active, this is not an ideal solution in large-scale deploys. In this article we will explore setting up a clustered Unimus Core deploy using Corosync and Pacemaker.</p><p>Here is a high-level diagram of how our example setup looks like:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/03/image.png" class="kg-image" alt loading="lazy" width="1060" height="378" srcset="https://blog.unimus.net/content/images/size/w600/2022/03/image.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/03/image.png 1000w, https://blog.unimus.net/content/images/2022/03/image.png 1060w" sizes="(min-width: 720px) 720px"></figure><p>Using clustering, only one of the Cores will ever be active - this is an active / passive HA scenario. If the active Core fails for any reasons, Pacemaker will failover the service to another available cluster member.</p><p>For the sake of simplicity we will be deploying a 2-node cluster in this example.</p><h2 id="components-of-the-cluster">Components of the cluster</h2><p>Components of our clustering solution:</p><ul><li>Linux - our base operating system that our cluster nodes run.</li><li>Corosync - Provides cluster node membership and status information. Notifies of nodes joining/leaving cluster and provides quorum.</li><li>Pacemaker - Cluster resource manager (CRM). Uses the information from Corosync to manage cluster resources and their availability.</li><li><code>pcs</code> - A helper utility that interfaces with Corosync (<code>corosync.conf</code>) and Pacemaker (<code>cib.xml</code>) to manage a cluster.</li><li>Unimus Core - Our service we want to have highly available.</li></ul><p>We will use <code>pcs</code> to manage the cluster. <code>pcs</code> is a cluster manager helper that we can use as a single frontend for the setup and management of our cluster. Without <code>pcs</code> you would need to setup Corosync manually through the <code>corosync.conf</code> config file, and manage Pacemaker configuration through its <code>crm</code> utility.</p><p>Deploying Corosync / Pacemaker without <code>pcs</code> is absolutely possible, but for the sake of simplicity we will rely on <code>pcs</code> to setup Corosyn and Pacemaker for us.</p><h2 id="preparations">Preparations</h2><p>The example commands below were tested on Ubuntu 18, but the setup should be very similar on any other Linux distro. We assume you are starting with a clean Linux system. As such, we need to prepare both our cluster members by running these commands:</p><pre><code># run everything as root
sudo su

# update
apt-get update &amp;&amp; apt-get upgrade -y

# install dependencies
apt-get install -y \
  wget \
  curl \
  corosync \
  pacemaker \
  pcs

# install Unimus Core in unattended mode
wget https://unimus.net/install-unimus-core.sh &amp;&amp; \
  chmod +x install-unimus-core.sh &amp;&amp; \
  ./install-unimus-core.sh -u

# setup Unimus Core config file
cat &lt;&lt;- "EOF" &gt; /etc/unimus-core/unimus-core.properties
  unimus.address = your_server_address_here
  unimus.port = 5509
  unimus.access.key = your_access_key
  logging.file.count = 9
  logging.file.size = 50
EOF</code></pre><p>Next up, we need to setup a single user that will be the same across all cluster nodes. This user will be used by <code>pcs</code> to kickstart our cluster setup. <code>pcs</code> already creates a <code>hacluster</code> user during its installation, so we will just change that user's credentials:</p><pre><code>CLUSTER_PWD="please_insert_strong_password_here"
echo "hacluster:$CLUSTER_PWD" | chpasswd</code></pre><p>After we have a common user across our cluster nodes, pick <strong>one node</strong> from which we will control the cluster. We can run these commands to setup the cluster:</p><pre><code>CLUSTER_PWD="please_insert_strong_password_here"

# setup cluster
pcs cluster auth test-core1.net.internal test-core2.net.internal -u hacluster -p "$CLUSTER_PWD" --force
pcs cluster setup --name unimus_core_cluster test-core1.net.internal test-core2.net.internal --force

# start cluster
pcs cluster enable --all
pcs cluster start --all</code></pre><p>Since we are using a 2-node cluster in this example, we need to set a few other specific properties. First, we disable quorum, as achieving a quorum with 2 nodes is impossible. We also disable fencing.</p><pre><code>pcs property set no-quorum-policy=ignore
pcs property set stonith-enabled=false</code></pre><p>Our cluster setup should now be done, so lets check our cluster status:</p><pre><code>pcs property list
pcs status</code></pre><p>You should see both your cluster nodes online, like this:</p><pre><code>root@test-core1:~# pcs status
Cluster name: unimus_core_cluster
Stack: corosync
Current DC: test-core1 (version 1.1.18-2b07d5c5a9) - partition with quorum
Last updated: Tue Mar  4 01:08:51 2022
Last change: Tue Mar  4 01:04:49 2022 by hacluster via crmd on test-core1

2 nodes configured
0 resources configured

Online: [ test-core1 test-core2 ]

No resources


Daemon Status:
  corosync: active/enabled
  pacemaker: active/enabled
  pcsd: active/enabled
root@test-core1:~# </code></pre><h2 id="troubleshooting">Troubleshooting</h2><p>If you don't see your cluster members online, or <code>pcs status</code> complains about some issues, here are a few common pitfalls:</p><ul><li>Your cluster nodes should NOT be behind NAT (this is possible, but requires more config not covered in this guide).</li><li>You must use hostnames / FQDNs for cluster nodes. Using IPs is a no-go. If needed, create hostnames for cluster nodes in <code>/etc/hosts</code>.</li><li>The hostname / FQDN you used resolves to <code>127.0.0.1</code>, or a different loopback. This is also a no-go as Corosync / Pacemaker require that the hostnames / FQDNs used for clustering resolve to actual cluster member IPs.</li></ul><p>In general, most of these issues can be resolved by proper DNS setup, or by creating proper records in <code>/etc/hosts</code>.</p><h2 id="creating-a-cluster-resource">Creating a cluster resource</h2><p>Now that our cluster is up, we can tell Pacemaker to start managing the Unimus Core service as a clustered service.</p><p>First we however need to disable Unimus Core from starting automatically at system startup on each node:</p><pre><code># disable Core autostart, Pacemaker will control this
systemctl stop unimus-core
systemctl disable unimus-core</code></pre><p>Then we can create our cluster resource through <code>pcs</code> on one of our cluster nodes:</p><pre><code># we might want to set node as ineligible to run the service if it fails to start
pcs resource defaults migration-threshold=1

# setup our cluster resource
pcs resource create unimus_core systemd:unimus-core op start timeout="30s" op monitor interval="10s"</code></pre><p>You will notice we use <code>systemctl</code>, and also declared the cluster resource using the <code>systemd</code> resource agent. We do this because Ubuntu 18 (which we are showcasing this setup on) uses <code>systemd</code>. If you are running a distro which doesn't use <code>systemd</code> as its init system, you will need to do things differently.</p><p>We recommend checking out <a href="https://clusterlabs.org/pacemaker/doc/deprecated/en-US/Pacemaker/1.1/html/Pacemaker_Explained/ch05.html#s-resource-primitive">Pacemaker documentation</a> on available resource agents and how to use them.</p><h2 id="monitoring-cluster-resources">Monitoring cluster resources</h2><p>Now that our cluster resource is created, lets check if it works:</p><pre><code>pcs status resources</code></pre><p>You should see that the Core is running on one of the nodes. Here is how our output looks:</p><pre><code>root@test-core1:~# pcs status resources
 unimus_core	(systemd:unimus-core):	Started test-core1
root@test-core1:~# </code></pre><p>You can also check the status of the <code>unimus-core</code> service on both of your cluster nodes:</p><pre><code># on core1
root@test-core1:~# systemctl status unimus-core
● unimus-core.service - Cluster Controlled unimus-core
   Loaded: loaded (/etc/systemd/system/unimus-core.service; disabled; vendor preset: enabled)
  Drop-In: /run/systemd/system/unimus-core.service.d
           └─50-pacemaker.conf
   Active: active (running)
...
root@test-core1:~# 

# on core2
root@test-core2:~# systemctl status unimus-core
● unimus-core.service - Unimus Remote Core
   Loaded: loaded (/etc/systemd/system/unimus-core.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
root@test-core2:~# </code></pre><p>You should also see the Core connect to your Unimus server, and the Zone should be <code>ONLINE</code>.</p><h2 id="live-monitoring-of-cluster-status">Live monitoring of cluster status</h2><p>To monitor the cluster status live, you can run the <code>crm_mon</code> in the live / interactive mode (just run the <code>crm_mon</code> command) and see the Core service fail over to a 2nd node on failure.</p><h2 id="simulating-a-failure">Simulating a failure</h2><p>You can easily simulate a failure in many ways. You can reboot one of your cluster members, and you should see the failover occur. You should also see the Zone briefly go <code>OFFLINE</code> in Unimus and then back <code>ONLINE</code>. You can also simulate a failure on one of the cluster nodes by running:</p><pre><code>crm_resource --resource unimus_core --force-stop</code></pre><p>You should see that Core started on the other node:</p><pre><code>root@test-core1:~# pcs status resources
 unimus_core	(systemd:unimus-core):	Started test-core2
root@test-core1:~# </code></pre><p>For the original node (<code>test-core1</code> in our case) to be considered as a viable node to run our resource, we need to run:</p><pre><code>pcs resource cleanup unimus-core</code></pre><p>If you want to migrate the service back to the first node, you can run:</p><pre><code># force a move to another cluster member
crm_resource --resource unimus_core --move

# clear any resource constraints we created
crm_resource --resource unimus_core --clear</code></pre><p>A move may create a constraint to not place the service on the previous node in the future. This is why we clear all constraints after a mode. A useful command to check existing constraints on our cluster resource is:</p><pre><code>crm_resource --resource unimus_core --constraints</code></pre><h2 id="final-words">Final words</h2><p>Hopefully this article can guide you in creating a HA setup for your Unimus Cores. If you have any questions, or you run into any issues, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=9">the Support section of our forums</a>, or contact us through our usual support channels.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.2.0 ]]></title>
        <description><![CDATA[ 2.2.0 is the latest major Unimus release, bringing new major features as well as heavy focus on security, performance and stability. This release is a significant milestone for Unimus - read on to find out more... ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-2-0/</link>
        <guid isPermaLink="false">621543b2e683410001cea727</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 25 Feb 2022 18:57:06 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/unimus-2.2.0.png" medium="image"/>
        <content:encoded><![CDATA[ <p>With each new release, we also upload a release overview video, so if you prefer a video format, you can find it here: <br><a href="https://youtu.be/dOpucg89DHk">Youtube - 2.2.0 Release Overview video</a></p><p>For those who prefer readable content, read on!</p><hr><h2 id="device-variables-for-config-push">Device Variables for Config Push</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-device-variables.gif" class="kg-image" alt loading="lazy" width="1919" height="971"></figure><p>The biggest new feature of this release are the Device Variables. These allow you to inject per-device unique values into generic Config Push presets. This can be used to create Push presets which are generalized and pushed to a large set of devices, but by using variable substitution the data pushed to each device will be tailored to that device.</p><p>Basically, Push presets can now behave more like templates, which was previously not as easily possible. More info on variables <a href="https://wiki.unimus.net/display/UNPUB/Device+Variables">on our Wiki</a>.</p><hr><h2 id="new-apiv3">New APIv3</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-api-credentials.png" class="kg-image" alt loading="lazy" width="1067" height="413" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-api-credentials.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus-api-credentials.png 1000w, https://blog.unimus.net/content/images/2022/02/unimus-api-credentials.png 1067w" sizes="(min-width: 720px) 720px"></figure><p>We are releasing the first wave of APIv3 endpoint groups in 2.2.0. In this release, you can now use APIv3 to manage:</p><ul><li>CLI mode change passwords</li><li>Credentials</li><li>Jobs</li><li>Tags</li><li>Zones</li></ul><p>Plans are to continue implementing the remaining endpoint groups in point releases after 2.2.0. We are focusing on covering what isn't available in APIv2 first, and after we have API feature parity with the GUI, we will start adding endpoints into APIv3 which are currently covered in v2.</p><hr><h2 id="mass-config-push-available-over-the-api">Mass Config Push available over the API</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-api-jobs-config-push.png" class="kg-image" alt loading="lazy" width="926" height="243" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-api-jobs-config-push.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-api-jobs-config-push.png 926w" sizes="(min-width: 720px) 720px"></figure><p>The addition of "Jobs" API endpoints is of significant note, as these allow you to use Config Push over the API.</p><p>If you want to build your own custom network automation front-end, Unimus can now be used as its back-end. This means you don't need to deal with all the intricacies of device communication which Unimus already handles, and allows you to leverage our existing 240+ network device drivers from your own applications.</p><hr><h2 id="improvements-to-api-token-management">Improvements to API Token management</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/image-6.png" class="kg-image" alt loading="lazy" width="1045" height="355" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/image-6.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/image-6.png 1000w, https://blog.unimus.net/content/images/2022/02/image-6.png 1045w" sizes="(min-width: 720px) 720px"></figure><p>As a part of API work, we have also substantionally improved API Token management. Tokens can have descriptions and comments now, and new security controls were introduced to specify if tokens should have access to device Credentials and CLI mode change passwords.</p><hr><h2 id="performance-improvements">Performance improvements</h2><p>We also put a lot of effort into performance improvements in this release, with a target to support 120.000 devices inside a single Unimus instance. This required significant work across all subsystems in Unimus.</p><p>Some highlights of performance differences between 2.1.4 vs. 2.2.0:</p><ul><li>Job initialization time on 120k devices down from 9 minutes to 1.5 minutes</li><li>Average MikroTik RouterOS job duration down from 21 seconds to 9 seconds</li><li>UI component responsiveness massively improved - for example, select all on 120k entities in the UI down from previously 3 minutes to 8 seconds now</li><li>All UI components, screens and tables load data in under 10 seconds (average UI load at ~2 seconds, with 10s being the worst result) with 120k devices in the system</li><li>Full discovery + backup on 120k devices in 2 hours 45 minutes (using 600 concurrent jobs)</li></ul><p>We will be publishing a technical blog article with details on performance improvements, as well as another blog article on large-scale Unimus deploy performance tuning in the near future.</p><hr><h2 id="discovery-algorithm-improvements">Discovery algorithm improvements</h2><p>During Discovery, Unimus tests and validates which Credentials are available for your devices. Our "Credential Binding" feature serves as a method to prevent Unimus from testing all available credentials on devices, allowing you to set specific credentials that should be used for device communication.</p><p>As a part of our work on performance we also optimized device connections during Discovery. If only a single credential is available for a device (either due to Credential Binding, or simply just having one credential in the system), Unimus will now only perform a single connection to the device during Discovery. Previously multiple connection attempts would be performed in this scenario, as the Discovery flow for single and multi-credential discoveries was the same. This has now been optimized.</p><p>In case of using SSH, this can result in significant CPU utilization savings during device jobs, as SSH session establishment is computationally expensive. More info on the new behavior <a href="https://wiki.unimus.net/display/UNPUB/Discovery#Discovery-Deviceconnectionsduringdiscovery">on our Wiki</a>.</p><hr><h2 id="security-improvements">Security improvements</h2><p>Another part of Unimus that received heavy focus durign 2.2.0 development was security. We performed an internal security audit of Unimus in advance of full Penetration Testing.</p><p>We have found and fixed multiple security-related issues of various severity in this release - please check the full Changelog for more info.</p><p>Unimus will undergo a full Penetration Testing cycle during March 2022. We will publish the pentest report publicaly on our Blog - stay tuned.</p><hr><h2 id="other-minor-new-features">Other minor new features</h2><p>In addition to the major features and changes above, this release also brings a bunch of smaller changes and improvements. We added an option to set the UI session timeout, added support for NetXMS v4, RouterOS v7, and many other minor improvements.</p><p>Please check the full Changelog below for more information.</p><hr><h2 id="bug-fixes-and-security-fixes">Bug fixes and security fixes</h2><p>As mentioned in a previous section, security and stability were a large time-investment on our end during the development cycle of this release. In addition to security, we have also fixed a slew of bugs, issues and UI inconsistencies of various severity.</p><p>All together 33 various bugs, and 20 various security-related issues were fixed. Please check the full Changelog below for full details.</p><hr><p>As with each new release, we also added support for a bunch of new networking vendors and devices. In 2.2.0 we are adding support for 12 new device types, from 9 separate networking vendors.</p><p>The Changelog for 2.2.0 is quite long, as this is one of our largest releases to date. If you want to see all the changes in this release, please check the full Changelog below:</p><pre><code>= Version 2.2.0 =
Features:
  Added option to set UI session timeout (example "-Dserver.servlet.session.timeout=1h")
  Updated NetXMS client library to latest version (4.0.2156)
  Added additional built-in Backup Filters for FortiOS devices
  Added missing search in Config Mode Password binding window (Devices &gt; Edit)
  Unmanaged devices are now displayed with Italic font in "Backups" screen (same as in "Devices")
  Added support for device selection menus on Cisco IOS
  Added support for CLI sections in FortiOS
  Improved Huawei VRP driver compatibility
  Improved detection and grouping of invalid commands in Config Push
  Reordered buttons on the Devices screen into logical groups (better UX)

  New Device Variables feature for Config Push
    - Variables can be defined for devices in the Device screen
    - both single and multi device variables edit are supported
    - Variables can be used in Config Push in the "${variable_name}" format
    - more info: https://wiki.unimus.net/display/UNPUB/Device+Variables

  Added new APIv3:
    - implemented new v3 API, exposing functionality currently missing in APIv2
    - currently available endpoints: "Jobs", "Zones", "Tags", "Credentials", "CliModeChangePasswords"
    - API tokens now have a new "Allow access to credentials" checkbox
    - please check http(s)://your_unimus_address/api/v3/ui for new built-in API docs
    - APIv2 will remain functional for the foreseeable future

  Improvements to API Token management:
    - added "Description" to API tokens
    - API tokens now have a new "Allow access to credentials" checkbox
    - added an "Edit" button for API tokens

  Mass Config Push is now available over APIv3:
    - added an "API Jobs" tab to Config Push if any API jobs exist
    - new retention settings for API Push Job history
    - see above section for details on APIv3

  Performance improvements:
    - general improvements across the application due to DB structure and data access improvements
    - substantial performance improvements in high-concurrency environments due to JDBC datasource change
    - Config Search has been offloaded to the database (as required per DB engine), bringing much better performance
    - optimized job initialization time (10x faster when running jobs on 5.000 devices)
    - a single Unimus instance can now handle 120.000 devices with full discovery + backup on 120k devices in 2 hours 45 minutes
    - UI component responsiveness massively improved (for example, select all on 120k objects in the UI now takes 8 seconds, from 3 minutes previously)
    - with 120.000 devices in Unimus, all screens now load in under 10 seconds max (average screen load at 2 seconds)

  Security improvements:
    - performed an internal security audit of Unimus in advance of full Penetration Testing
    - more info on found and fixed issues in the "Security fixes" section
    - updated user password hashing algorithm to Argon2 (previously Bcrypt2 was used)
    - existing user passwords will be migrated on first successful login
    - Unimus 2.2.0 will undergo a full pentest cycle, results will be published publicly on our Blog

  Optimization of device connection count during Discovery:
    - only open a single CLI session when only a single credential is available for a device
    - applies when credential discovery is not needed due to Credential Binding
    - more info: https://wiki.unimus.net/display/UNPUB/Discovery

  Rewrite of MikroTik RouterOS driver:
    - performance increases, average discovery on ROS down to ~9 seconds (from 21 seconds)
    - added handling for new CLI behaviors introduced in latest ROSv6 versions
    - added support for ROSv7

  Added support for:
    - ArubaOS v6
    - DrayTek VigorSwitch
    - Engage IPTube
    - FiberStore Campus switches
    - Hatteras / Overture Networks
    - Huawei USG
    - JunOS EVO
    - MikroTik RouterOS 7
    - Planet XGS switches
    - other various Planet switches
    - Ubiquiti Dream Machine (UDM)
    - Ubiquiti LTU / LTU-Pro

Fixes:
  Fixed a memory leak if a Core connection connected and disconnected frequently
  Fixed wrong Running Job state could be set on devices during heavy concurrency operations
  Fixed job history records would not be created for devices with extremely long addresses
  Fixed a running Network Scan not being stopped if it's Preset was deleted
  Fixed description missing in Mode Change Password binding (Devices &gt; Edit)
  Fixed running job state could be reverted to a wrong state when Managing / Unmanaging devices while a job was running
  Fixed select all / deselect all and the selection model in general could break in the "Device credentials" table
  Fixed moving devices between Zones could cause the Zone Number to update even if device was not moved due to address conflict
  Fixed changing a user's role to visually break the Backups screen if the affected user was already on it
  Fixed possibility to add Comments to deleted objects if the Comment window was opened while object was deleted
  Fixed actions buttons not working properly in "Backups &gt; Configuration" in specific cases
  Fixed wrong time formatting in "Use management &gt; System access history &gt; Session end" (values were correct in DB)
  Fixed "Other settings &gt; Per-Tag connectors" would not properly show all configured ports for a connector
  Fixed attempting to remove all Users would throw an exception (will now properly remove all users other than yours)
  Fixed the Zones screen not properly refreshing when specific changes were done to Zones by another user
  Fixed select all on tables with extremely large amounts of objects could causing loading for a very long time
  Fixed enabling "Show all passwords" in the "CLI mode change passwords" table could cause bad behavior in the "Device credentials" table
  Fixed search in "Import history jobs" did not work
  Fixed the "port" field being formatted wrongly in the "Notifications &gt; Email" screen
  Fixed changing a user's role to duplicate the Theme selector on the Dashboard if the affected user was already on it
  Fixed Credentials screen did not live-update changes to counters when credentials were Bound / Unbound by another user
  Fixed "Basic import &gt; CSV file import" could throw exceptions to the UI when an invalid CSV file was provided
  Fixed possibility to add Device Access restriction without selecting and account, which resulted in an exception
  Fixed Comment icon column in the Schedules screen was not properly sized
  Fixed rare scenarios where upgrade from 2.0 or 2.1 to latest versions could fail
  Fixed possible invalid input in "Notification settings &gt; Diff before and after lines"
  Fixed multiple rare errors on concurrent operation attempts on already deleted objects during multi-user workflows
  Fixed multiple other minor UI and UX issues and missing live value changes during multi-user workflows
  Fixed discovery failing on some models of Adtran TA
  Fixed discovery failing on JunOS-EVO devices
  Fixed discovery failing to recognize newer Planet switch types
  Fixed Config Push on MikroTik RouterOS could fail on specific commands with long output
  Fixed output formatting in Config Push on some MikroTik RouterOS versions could be broken
  Fixed backup could contain some extra unwanted data on some MikroTik RouterOS versions

Security fixes:
  Completely removed log4j library due to multiple exploits that were identified in this library
  Log out all other user's sessions if a user changes their password (other than the session changing the password)
  Log out all sessions of a user if their password is changed by another Administrator user
  Users logged out due to session timeout are redirected to the Login screen instead of just an overlay on their last screen
  Fixed user could remove Backup Filters applied to Tags the user didn't have access to
  Fixed users could re-run Push presents from output group context menu even if they didn't have access to do this
  Close currently opened "Show password" popups in the Credentials and "Device &gt; Info" screens when a password is set to "High security mode"
  Close currently opened "Show password" popups in the Credentials and "Device &gt; Info" screens when a user's role is changed to READ-ONLY
  Fixed Backups screen would not remove access to already opened device backups if access to a device was lost
  Fixed users without access to the Default Zone could still add devices through "Network Scan"
  Changed APIv2 to no longer expose credential passwords through Device endpoints (there was no way to control this), use APIv3 for credential access

  Fixed multiple instances of "live" access changes not working (screen change / reload was required to apply new access restrictions):
   - for all affected screens affected data will be added / removed immediately after accessibility is changed now
   - fixed Dashboard not listening to live device access changes
   - fixed Zones not listening to live access changes
   - fixed "Mass Config Push &gt; Targets" not listening to live device access changes
   - fixed "Mass Config Push &gt; Output groups" not listening to live device access changes
   - fixed "Other settings &gt; Per-Tag connectors" not listening to live access changes
   - fixed Devices screen not listening to Zone-based device Tag live access changes (Tag propagations to Devices from Zones)
   - fixed "Basic import" not listening to live Zone access changes

Embedded Core version:
  2.2.0

Known issues:
  ISSUE: "Re-discover affected devices when Ports or Connectors change" Advanced Settings option does not work
  WORKAROUND: none
  STATUS: issue scheduled for fixing

  ISSUE: "Stop" in Config Push does not work
  WORKAROUND: none
  STATUS: issue scheduled for fixing

  ISSUE: Some screens in Unimus show time in server's time zone, others in client's (browser's) time zone
  WORKAROUND: none, issue only relevant if client has different time zone than server
  STATUS: we are debating on how to fix this - will likely create a setting to select which TZ should be used
</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Automating Cisco IOS updates with Unimus - Part 2 ]]></title>
        <description><![CDATA[ Part 2 of our Cisco IOS update automation series where we look at how to automate large scale IOS upgrades with Unimus. ]]></description>
        <link>https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-2/</link>
        <guid isPermaLink="false">61fb33dae683410001ce9cd0</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Tue, 22 Feb 2022 17:09:31 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/unimus---cisco-1.png" medium="image"/>
        <content:encoded><![CDATA[ <h2 id="intro">Intro</h2><p>In <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a> of our Cisco IOS upgrade automation series, we focused on a simple and quick solution to upgrade (or downgrade) Cisco IOS devices with just a couple of commands in Unimus, deployed through our Mass Config Push functionality.</p><p>Today we will continue this endeavor and show you a more detailed and advanced solution. This article attempts to create a one-stop solution by leveraging TCL scripting to make updating all your IOS devices easy. All IOS-powered devices should be able to update using this guide and script, regardless of whether they are a router or a switch, and also regardless of the product series. All at the same time.</p><p>Let's start with what we need. In <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a> we already described that we need a server/device to source upgrade FW images from, Cisco IOS devices and Unimus. Here is an abstract component diagram:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/schema_koncept-1.png" class="kg-image" alt loading="lazy" width="1360" height="809" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/schema_koncept-1.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/schema_koncept-1.png 1000w, https://blog.unimus.net/content/images/2022/02/schema_koncept-1.png 1360w" sizes="(min-width: 720px) 720px"></figure><p>We will be using the same "topology" as in the previous article, but we will build upon it with some additions:</p><ul><li>FW image source - this time it will store not just the IOS FW images, but also two scripts. One will be a bash script which we will use to generate a list of FW images for devices to find an upgrade candidate in, and the other one will be a dedicated upgrade TCL script.</li><li>Cisco IOS devices. These will be downloading the TCL upgrade script, list of available images, and lastly the FW image file to update to (assuming the devices find an update candidate in the image list).</li><li>Unimus and our Mass Config Push feature (version 2.1.0 and newer) with a slightly different set of commands to push compared to <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a>'s preset.</li></ul><p>Before we start, let us add a disclaimer. Large scale infrastructure automation is almost never easy. While working on this script, I encountered a number of quirks I had to deal with that were unique to one device but not to another. Keep in mind that it is possible your Mass Config Push preset will finish with errors - this is possible and I expect that to happen.</p><p>If errors happen, don't hesitate to check out the troubleshooting FAQ at the end or contact us. It will take a number of picky devices to iron out all weird behaviors and inconsistencies across all the various IOS versions. At least that was definitely my own experience.</p><p>And lastly - don't forget to test everything in a lab environment before deploying to your production network.</p><h2 id="preparing-the-image-fw-source">Preparing the Image / FW source</h2><p>Contrary to <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a>, we will have higher requirements for the image source server. Once again, we will use a VM running Linux for our showcase. As outlined above, we will be generating a list of available FW images with their MD5 sums for the upgrade TCL script to verify the downloaded file's MD5 sums against. Checking image validity before deploying it is one of the first big features of this upgrade process.</p><p>Let's reiterate. The server VM will now "serve" the following purposes:</p><ul><li>We will serve our IOS images from this machine. Note that we are focusing only on IOS images in <code>.bin</code> format, not archived images in <code>.tar</code> format. This is done so that we can can avoid as many issues as possible with devices with insufficient free space, or ones with smaller flash capacity. These wouldn't be able to fit multiple IOS image even if their flash was empty.</li><li>We will generate a sorted list (supporting IOS versioning) of FW images with their respective MD5 sums and make this available for our devices to download.</li><li>We will serve our upgrade TCL script. Our IOS devices will download and execute this to find an update image candidate, download it, verify it, and set it as the boot image.</li></ul><h3 id="fw-transfer-methods">FW Transfer methods</h3><p>Just like last time, we are focusing on SCP and HTTP protocols. Usually we see networkers using TFTP or FTP, but we seldom see anyone choosing SCP or HTTP. SCP and/or HTTP are much more robust protocols and we wanted to showcase these, albeit less popular options.</p><p>If you are interested in more information on both, including some probably necessary steps to make SCP work with all your devices, please refer to the <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a> article. Alternatively jump to the third point of the troubleshooting FAQ at the end of the article, where we describe possible issue with SCP and how to deal with them.</p><p>With that said, let's prepare our FW image source server. First, where to put the files:</p><h3 id="scp">SCP</h3><p>If you opt for SCP you will be placing your files into the home folder of a user you choose. Let's say we create a special user called <code>unimus</code> for this. In this case, you are going to place your files into that user's home folder, which will be, by default, <code>/home/unimus</code>.</p><h3 id="http">HTTP</h3><p>If you opt for HTTP you will be using your site's root folder. In our case, we use NGINX and the root folder for a web location we will use is in <code>var/www/ciscoiosupgrade.netcore.internal</code>.</p><p>Choose the directory according to the method you chose for the IOS image hosting. Note that in both case the script only supports files placed in the main (root) directory from where they are being served, not sub-directories.</p><p>In these directories you also need to place both scripts, which you can find below. And with that being said, let's introduce both scripts and look at the whys and whats of each of them.</p><h3 id="overview-of-the-fw-image-parsing-bash-script">Overview of the FW image parsing bash script</h3><p>This is an absolutely basic script, but still neat. This script simply takes all files with a <code>.bin</code> extension in the same directory, runs them through Linux <code>md5sum</code> command, sorts the output intelligently using <code>sort</code> with the <code>-V</code> argument to take file versioning into the account, and outputs a finished file called <code>fwlist</code>.</p><pre><code>#!/bin/bash

#Set a working directory in the current directory
cd "${0%/*}"

#Extract file names and MD5 sums, sort them and output into a file
md5sum *.bin | sort -V -k2 &gt; fwlist</code></pre><p>Here's the example how the finished list can look like</p><pre><code>a63c90cc3684ad8b0a2176a6a8fe9005  c180x-advipservicesk9-mz.151-4.M12a.bin
6d0bb00954ceb7fbee436bb55a8397a9  c1900-universalk9_npe-mz.SPA.158-3.M7.bin
28518159ba5f75ef0eeb9617fd35e2ba  c2800nm-advipservicesk9-mz.124-24.T4.bin
441018525208457705bf09a8ee3c1093  c3750e-ipbasek9-mz.122-55.SE5.bin
862dec5c27142824a394bc6464928f48  c3750e-universalk9-mz.122-55.SE5.bin
fd4b38e94292e00251b9f39c47ee5710  c3750e-universalk9-mz.152-4.E10.bin
1f94dacb4faf2829b0ffbb25ebd62e2e  c3750-ipbasek9-mz.150-2.SE5.bin
b28cf0ed5cc0d1928ea4f6656e1c8dde  c3750-ipservicesk9-mz.122-55.SE12.bin
871bdd96b159c14d15c8d97d9111e9c8  cat4500-ipbasek9-mz.150-2.SG11.bin
3287282fa1a1523a294fb018e3679872  s72033-adventerprise_wan-vz.122-33.SXI14.bin
a302a771ee0e3127b8950f0a67d17e49  s72033-ipbase-mz.151-2.SY16.bin
bbf7c6077962a7c28114dbd10be947cd  s72033-ipservicesk9-mz.151-2.SY16.bin</code></pre><h3 id="overview-of-the-upgrade-tcl-script">Overview of the upgrade TCL script</h3><pre><code>tclsh

#Load and store list of available FW images
set fwrawlist [read [open fwlist]]

#Retrieve FW type of the current device for further processing
set devicefwdirty [exec show version | include System image file is]
#Get a full name of the current FW
regexp -all {:(.*?)\"} $devicefwdirty junk devicefwfull
#Get a FW type with a major release version, this prevents issues with some devices which might not be compatible (mainly storage constrains) with the next major release version
regexp -all {:(.*?-.*?-.*?\.\d\d).*?.bin} $devicefwdirty junk devicefwrelease

#Find FW update candidate
puts "Finding a viable FW update candidate..."
#Process the list, return the latest match (it will be the latest FW image)
set fwlistparsed [regexp -all -line "\\s{2}($devicefwrelease.+?.bin)$" $fwrawlist junk down_file]
#If no match is found, abort the script
if {$fwlistparsed == 0} {
    puts "List of available FWs does not contain any update candidate. Aborting..."
    return
}
#Run fullname string comparison, if matched, current and matched FW are identical, abort the script
if {[string compare $devicefwfull $down_file] == 0} {
    puts "Current and matched FW image are identical. Aborting..."
    return
}
#If the current and matched FW are not identical, start comparing them on each level
#Compare major release version
regexp -all {\.([0-9]{1,4})\-} $devicefwfull junk curfwmatch1
regexp -all {\.([0-9]{1,4})\-} $down_file junk newfwmatch1
if {$curfwmatch1 == $newfwmatch1} {
    #Major release version is identical, compare minor release version
    regexp -all {\-([0-9]{1,3})\.[a-zA-Z]*?[0-9]*?[a-zA-Z]*?\.bin} $devicefwfull junk curfwmatch2
    regexp -all {\-([0-9]{1,3})\.[a-zA-Z]*?[0-9]*?[a-zA-Z]*?\.bin} $down_file junk newfwmatch2
    if {$curfwmatch2 == $newfwmatch2} {
        #Minor release version is also identical, compare revision
        regexp -all {\-[0-9]{1,3}\.[a-zA-Z]*?([0-9]*?)[a-zA-Z]*?\.bin} $devicefwfull junk curfwmatch3
        regexp -all {\-[0-9]{1,3}\.[a-zA-Z]*?([0-9]*?)[a-zA-Z]*?\.bin} $down_file junk newfwmatch3
        if {$curfwmatch3 == $newfwmatch3} {
            #Revision is also identical, in this case it suggest some other problem in versioning or FW naming
            puts "Current and matched FW image and their (numeric) version seem identical, but full names are not. Aborting..."
            return
        } elseif {$curfwmatch3 &gt; $newfwmatch3} {
            puts "Current FW image is newer than the matched one from the list of available FWs. No update candidates were found. Aborting..."
            return
        } elseif {$curfwmatch3 &lt; $newfwmatch3} {
            puts "Update candidate found."
        } else {
            puts "Unknown error occurred during FW matching. Aborting..."
            return
        }
    } elseif {$curfwmatch2 &gt; $newfwmatch2} {
        puts "Current and matched FW image and their (numeric) version seem identical, but full names are not. Aborting..."
        return
    } elseif {$curfwmatch2 &lt; $newfwmatch2} {
        puts "Update candidate found."
    } else {
        puts "Unknown error occurred during FW matching. Aborting..."
        return
    }
} elseif {$curfwmatch1 &gt; $newfwmatch1} {
    puts "Current and matched FW image and their (numeric) version seem identical, but full names are not. Aborting..."
    return
} elseif {$curfwmatch1 &lt; $newfwmatch1} {
    puts "Update candidate found."
} else {
    puts "Unknown error occurred during FW matching. Aborting..."
    return
}

#Download FW update
#Read common arguments, abort if mandatory arguments are missing, and decide which protocol will be used
set down_prot [lindex $argv 0]
if {[string length $down_prot] == 0} {
    puts "No argument was defined, please add arguments to your MCP in Unimus where you execute this command. Aborting..."
    return
}
set down_addr [lindex $argv 1]
if {[string length $down_addr] == 0} {
    puts "Second argument (address) is missing. Aborting..."
    return
}
#Read HTTP specific arguments and build download URL
if {[string compare http $down_prot] == 0} {
    set down_port [lindex $argv 2]
    if {[string length $down_port] == 0} {
        #Use port 80 if no custom port is defined
        set down_port "80"
    }
    set down_url "http://$down_addr:$down_port/$down_file"
#Read SCP specific arguments and build download URL
} elseif {[string compare scp $down_prot] == 0} {
    set down_user [lindex $argv 2]
    if {[string length $down_user] == 0} {
        puts "Third argument (user) is missing. Aborting..."
        return
    }
    set down_pass [lindex $argv 3]
    if {[string length $down_pass] == 0} {
        puts "Fourth argument (password) is missing. Aborting..."
        return
    }
    set down_url "scp://$down_user:$down_pass@$down_addr/$down_file"
} else {
    puts "Unrecognized protocol. Aborting..."
    return
}
puts "Downloading firmware..."
set down_result [exec copy $down_url flash:]
#Evaluate download result
if {[regexp {bytes copied} $down_result]} {
    puts "Update FW image was downloaded successfully."
} elseif {[regexp {Not enough space} $down_result]} {
    if {[regexp {Not enough space} $down_result]} {
        puts "Error occurred during download - insufficient space left on device. Aborting..."
        return
    }
} elseif {[regexp {Protocol error} $down_result]} {
    puts "Error occurred during download - protocol error. Aborting..."
} elseif {[regexp {busy} $down_result]} {
    puts "Error occurred during download - device is busy. Aborting..."
} else {
    puts "Unknown error occurred during download. Aborting..."
    return
}

#Validate MD5 of the downloaded FW image
puts "Validating integrity..."
#Run validation for the downloaded FW image
set down_file_md5check [exec verify /md5 $down_file]
regexp -all -line "=\\s{1}(.+?)$" $down_file_md5check junk down_file_md5
regexp -all -line "(.+?)\\s{2}$down_file" $fwrawlist junk fwmd5tocomp
#Compare both MD5 sums
if {[string compare $down_file_md5 $fwmd5tocomp] == 1} {
    puts "Update FW image validated successfully."
} else {
    puts "Unknown error occurred when validating update FW image integrity, MD5 sums do not match. Aborting..."
    return
}

#Set up system boot image with the downloaded update FW image
puts "Updating..."
ios_config "boot system flash:$down_file"
puts "Update is ready. Please run your reload MCP preset..."</code></pre><p>Here is a breakdown of the workflow of this script:</p><ul><li>Script ingests a list of available FW images from the pre-generated list we prepared earlier (with the bash script, the <code>fwlist</code> file).</li><li>Script checks the current version of IOS running on the device.</li><li>Script compares the current IOS version to all available FW images - note we are matching only the same major release version (if your device is running a 12.X release, you will be able to upgrade only to a newer version of the 12.X release, not to a newer major release like 15.X or 17.X).</li><li>If the script matches multiple upgrade candidate image files, it will always choose the last match, which will be the latest FW image (thanks to version-aware sorting in the list of FW images).</li><li>Script processes input arguments, evaluates them and builds a download URL for the device to download the new FW image.</li><li>A new FW image is downloaded. Its integrity verified and compared to the MD5 sum from our known good sums on the image server.</li><li>If the integrity checks out, the script sets up the FW image as the boot image and returns a final message informing the user to reload the device.</li></ul><p>We intentionally don't reload devices here - you can probably imagine the problems it would cause in real networks where a switch higher in the network topology would finish and reload while devices inherently relying on its activity would then fail their downloads and consequently the Mass Config Push from Unimus with it.</p><p>As you can see in the script, we tried our best to add a number of comments to describe parts of the code and what they do to make it easier for anyone to understand and even modify the code according to their needs.</p><h2 id="preparing-unimus-and-mass-config-push-presets-for-ios-upgrade">Preparing Unimus and Mass Config Push presets for IOS upgrade</h2><p>Here are the Config Push presets we will be using to pull the FW images and perform the upgrade and reload the devices:</p><h3 id="config-push-preset-1upgrade-devices">Config Push preset 1 - Upgrade devices</h3><p>Just like the last time, we will run the upgrade in a single Config Push preset. This preset will download the necessary files. Note the use of <code>tclsh</code>, or TCL shell, and <code>log_user</code> command set to <code>0</code> before running copy commands - we did this to suppress the outputs of those two commands, which would otherwise generate some unpredictable output, creating unwanted output groups. This way, we make sure that any actual output will be generated by the script itself.</p><h3 id="scp-1">SCP</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade2_preset_scp-1.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><pre><code>tclsh
log_user 0
exec "copy scp://SCP_USER:SCP_PASS@FW_SRC_ADDR/fwlist flash:"
exec "copy scp://SCP_USER:SCP_PASS@FW_SRC_ADDR/ios_upgrade.tcl flash:"
tclquit
tclsh ios_upgrade.tcl PROTOCOL FW_SRC_ADDR SCP_USER SCP_PASS
</code></pre><p>Where:</p><pre><code>SCP_USER - SCP user
SCP_PASS - SCP password
PROTOCOL - scp or http - protocol used to download an FW image
FW_SRC_ADDR - IP or hostname of FW image source device
</code></pre><p>Please replace all the example values with your actual ones. Don't forget to check <code>Require "enable" (privileged-exec) mode</code> for this Config Push.</p><h3 id="http-1">HTTP</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade2_preset_http.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><pre><code>tclsh
log_user 0
exec "copy http://FW_SRC_ADDR/fwlist flash:"
exec "copy http://FW_SRC_ADDR/ios_upgrade.tcl flash:"
tclquit
tclsh ios_upgrade.tcl PROTOCOL FW_SRC_ADDR FW_SRC_PORT
</code></pre><p>Where:</p><pre><code>PROTOCOL - scp or http - protocol used to download an FW image
FW_SRC_ADDR - IP or hostname of FW image source device
FW_SRC_PORT - OPTIONAL - define this argument only if your webserver is listening on a port other than 80, otherwise remove this argument altogether, script will default to port 80
</code></pre><p>Please replace all the example values with your actual ones. Don't forget to check <code>Require "enable" (privileged-exec) mode</code> for this Config Push.</p><h3 id="config-push-preset-2reload-devices">Config Push preset 2 - Reload devices</h3><pre><code>tclsh
exec "reload in 3"
tclquit
</code></pre><p>This is a simple preset to reload devices and set it to be executed in 3 minutes. Feel free to change it to your needs, there are two formats for <code>reload in</code> command - <em>MMM</em> or <em>HHH:MM</em>, or you can change it to <code>reload at</code> and define a specific date and time for the reload instead.</p><p>Let me quickly address why we use the TCL shell to execute a simple reload - in my testing I encountered an inconsistent sequence of prompts when sending the <code>reload</code> command, and I can imagine there can be even more variations between other devices and IOS versions. Use of the TCL shell handles the issue - it just works without needing to handle various IOS inconsistencies.</p><h3 id="example-of-a-successful-run">Example of a successful run</h3><p>And here's how we want to see the upgrade Config Push preset to go:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade2_success.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><p>While we wish everyone had exactly the same results as we did, some of your devices might not finish successfully. While the most script-specific errors are self-explanatory, let us also add a troubleshooting FAQ below to help you understand and hopefully fix most of the errors reported by Unimus or the script.</p><h2 id="troubleshooting-faq">Troubleshooting FAQ</h2><h3 id="unimus-returned-interactionerror">Unimus returned <code>INTERACTION_ERROR</code></h3><p>This error is caused by Unimus not receiving any recognizable output from a device before a timeout runs out, which is 20 seconds by default for Cisco devices.</p><p>We already touched on overriding timeouts via <code>Advanced settings</code> in <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/">Part 1</a>, but we would like to add some more information here. How to find the right value for you?</p><p>One of the possible ways to go about this is the trial-and-error approach. You can try to increase it gradually (1 minute, 2 minutes, etc.). An alternative would be to try a larger value (e.g. 5 minutes like in my case) right away. We would recommend the latter, as you can have hundreds of devices fail with this error, and choosing random devices and trying to time image download manually to find out if a certain timeout is enough would be time-consuming.</p><p>One of our Config Push features which will come in useful is using the context menu of any push output group. You can choose the option to rerun the preset only on devices in that one output group. You can progressively try increasing the timeout and keep adjusting until there is no device left in the error output group. Alternatively, clone the preset with devices from the error output group to continue tuning only those devices.</p><p>If you get <code>INTERACTION_ERROR</code> from the timeout while the FW image was downloading, then unfortunately after Unimus terminates the session, your device(s) will continue downloading the file in the background. So if you quickly increase your timeout and rerun this preset on these devices, the ones with such a background download still running will finish this push with a <code>device is busy</code> error (see below section) returned by the script.</p><p>Keep in mind that <code>INTERACTION_ERROR</code> could also include some outliers, such as devices that use a different syntax for commands or syntax for response prompts that the script might not catch, so don't go to extreme lengths with this timeout. If there are devices that you can't fix by increasing the timeout, then stop and let us know. As mentioned in the preface, you should expect some errors and tuning to make large scale automation work seamlessly across a large fleet of different IOS devices.</p><h3 id="script-returned-error-message-error-occurred-during-downloaddevice-is-busy-aborting">Script returned error message <code>Error occurred during download - device is busy. Aborting...</code></h3><p>In the preceding section, we described a specific case in which device(s) with <code>INTERACTION_ERROR</code> can create an output group with this script-specific error after a quick rerun of the preset.</p><p>This error can show up when your device cannot download a chosen FW image in time and you rerun your preset on them before they had a chance to finish the download. IOS's copy command return a <code>Device or resource busy</code> error, as it is already downloading the file in the background. Time is the best cure here. Leave devices in this output group for a couple of minutes and try again.</p><h3 id="script-returned-error-message-error-occurred-during-downloadinsufficient-space-left-on-device-aborting">Script returned error message <code>Error occurred during download - insufficient space left on device. Aborting...</code></h3><p>Your device doesn't have enough free space left on it. In the section above, we also mentioned a potential issue when the script failed due to <code>INTERACTION_ERROR</code>, but the device might have kept downloading the firmware. Alternatively, your device may effectively not be able to store more than a single FW image file as its flash is already consumed by your current running FW image.</p><p>If that is your case, add the command <code>del /force *.bin</code> to the Config Push preset just after <code>log_user 0</code> and before the <code>copy</code> command for downloading the <code>fwlist</code> file. This will cause any file with a <code>.bin</code> extension to be removed from the root of your flash (it is not recursive).</p><p>Be careful not to delete anything important, though. This will delete any file with <code>.bin</code> extension. This has (hopefully) obvious drawbacks - it can leave you with an unbootable device if the currently configured boot IOS image gets deleted and a power failure occurs during the update to a new image. Caution recommended.</p><h3 id="script-returned-error-message-error-occurred-during-downloadprotocol-error-aborting">Script returned error message <code>Error occurred during download - protocol error. Aborting...</code></h3><p>This script-specific error most likely suggests an SSH-related problem and is likely related to unsupported KEX algorithms, Ciphers or Host Key Algorithms offered by your IOS device and rejected by your FW image source server. You might have never seen such a problem when interacting with Unimus - that is because Unimus is more liberal with KEX and other crypto than your typical OpenSSH's installation by default (we know many networking devices use older crypto protocols).</p><p>If this happens to you, I would recommend adding the <code>diffie-hellman-group1-sha1</code> KEX to your SSH server's config file. It should resolve most of the devices with this error. If that is not the case, you may want to try manually connecting from your Cisco device to the FW image source server, or you can turn on terminal monitor and debug for SCP with these commands</p><pre><code>terminal monitor
debug scp all
</code></pre><p>and try to manually download, for example, the <code>fwlist</code> file from the image source server with:</p><pre><code>copy scp://unimus:scppass8520@10.30.50.70/fwlist flash:
</code></pre><p>Here's an example of the output you can expect:</p><pre><code>cisco#copy scp://unimus:scppass8520@10.30.50.70/fwlist flash:
Destination filename [fwlist]?
%Error opening scp://unimus:scppass8520@10.30.50.70/fwlist (Protocol error)
cisco#
*Feb  5 05:31:45.181: SSH2 CLIENT 0: kex algo not supported: client diffie-hellman-group1-sha1, server curve25519-sha256,curve25519-sha256@libssh.org
cisco#
</code></pre><p>From the information in the example above, you would then add <code>diffie-hellman-group1-sha1</code> to your server's SSH configuration.</p><h3 id="script-returned-an-error-message-unknown-error">Script returned an error message <code>Unknown error...</code></h3><p>If you encounter this error, let us know. This indicates an unexpected error and will require some debugging.</p><p>There is also a topic on our forums you can use to report any issues, provide feedback or ask questions: <a href="https://forum.unimus.net/viewtopic.php?f=11&amp;t=1426" rel="noopener">https://forum.unimus.net/viewtopic.php?f=11&amp;t=1426</a></p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Automating Cisco IOS updates with Unimus - Part 1 ]]></title>
        <description><![CDATA[ Part 1 of our Cisco IOS update automation series where we look at how to easily automate large scale IOS upgrades with Unimus. ]]></description>
        <link>https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-1/</link>
        <guid isPermaLink="false">61fb33d1e683410001ce9cca</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Wed, 16 Feb 2022 16:24:23 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/unimus---cisco.png" medium="image"/>
        <content:encoded><![CDATA[ <h2 id="intro">Intro</h2><p>We believe that keeping the firmware / software of networking devices up-to-date is one of the most important security benefits in any network. Sadly, this is much easier said than done in any network at scale. Previously, we brought you a guide on automating network-wide upgrades of MikroTik's RouterOS (<a href="https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/">link here</a>).</p><p>This time, we tackle automating upgrades of Cisco IOS devices. We will do so in 2 parts, using 2 different approaches - this article is the first part where we will focus on providing a simple and quick solution.</p><p>This solution requires 3 main components:</p><ul><li>FW image source - a server which will serve as a source / host for FW image(s) used for upgrade</li><li>Cisco IOS devices to upgrade, which will be downloading a provided FW image</li><li>Unimus' Mass Config Push feature to push commands to your devices and automate the FW upgrades</li></ul><p>Here is a conceptual diagram of how our testing topology looks like:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/schema_koncept.png" class="kg-image" alt loading="lazy" width="1360" height="809" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/schema_koncept.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/schema_koncept.png 1000w, https://blog.unimus.net/content/images/2022/02/schema_koncept.png 1360w" sizes="(min-width: 720px) 720px"></figure><p>Unimus will be used to perform and automate the IOS upgrades, but also to help us sort and organize IOS upgrade results into output groups, so we don't have to examine every single device output manually. Rather, Unimus will group devices by the outputs we receive to easily identify successful and / or failed upgrades - if any pop up.</p><h2 id="preparing-the-image-fw-source">Preparing the Image / FW source</h2><p>You will need a server to serve / host your FW image(s) from. Any server which your Cisco IOS devices can download files from will suffice. For our showcase, we will use a VM running Linux. This way we can host our images easily and can leverage pretty much any transfer method we choose.</p><h3 id="fw-transfer-methods">FW Transfer methods</h3><p>When it comes down to transferring files between our FW source and IOS devices, we chose two protocols for this showcase: SCP and HTTP. Usually we see administrators relying on TFTP or FTP, but we seldom see anyone choosing SCP or HTTP. Thus we decided to showcase these, albeit less popular options.</p><h3 id="scp">SCP</h3><p>If you choose SCP, you may need to make some adjustments on your FW image server. As SCP uses the SSH protocol, there is one potential issue you will have to deal with. If you have some older Cisco IOS devices, and especially those which don't offer any sort of addition/exclusion/enforcement of more secure KEX (Key Exchange), Ciphers and Host Key Algorithms; and your source server runs a generally newer SSH server - you may need to enable one or more of the legacy algorithms which your devices are capable of supporting.</p><p>We would recommend adding the <code>diffie-hellman-group1-sha1</code> KEX to your SSH server's config file as a precaution. It should resolve most of these potential issues. If that is not the case though, you will need to check one or more devices manually. Try to download the FW image manually and optionally also turn on terminal monitor and SCP debugging.</p><h3 id="http">HTTP</h3><p>If you already use / run a web server, then choosing HTTP will save you time. There shouldn't be any extra configuration required on either side - assuming your IOS devices will be able to access your web server.</p><h2 id="preparing-unimus-and-mass-config-push-preset-for-cisco-ios-upgrade">Preparing Unimus and Mass Config Push preset for Cisco IOS upgrade</h2><p>Here are the Config Push presets we will be using to pull the FW images and perform the upgrade and reload the devices:</p><h3 id="config-push-preset-1upgrade-devices">Config Push preset 1 - Upgrade devices</h3><p>We start with the main Config Push preset, which will do the upgrade - without reloading the devices. We don't want to reload immediately after pulling an image, as that would likely cause problems (due to reloading devices in wrong / random order and causing connection loss). Some devices could reload sooner than others finished their FW transfer, and this could cause pushes to fail for some of our devices.</p><h3 id="ios-image-transfer-using-scp">IOS image transfer using SCP</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade1_preset_scp.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><pre><code>tclsh
log_user 0
exec "copy scp://SCP_USER:SCP_PASS@FW_SRC_ADDR/UPGRADE_FW_IMAGE flash:"
ios_config "boot system flash:UPGRADE_FW_IMAGE"
tclquit</code></pre><p>Where:</p><pre><code>SCP_USER - SCP user
SCP_PASS - SCP password
FW_SRC_ADDR - IP or hostname of FW image source device
UPGRADE_FW_IMAGE - file name of a chosen upgrade FW image</code></pre><p> Please replace all the example values with your actual ones. Don't forget to check <code>Require "enable" (privileged-exec) mode</code> for this Config Push.</p><h3 id="ios-image-transfer-using-http">IOS image transfer using HTTP</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade1_preset_http.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><pre><code>tclsh
log_user 0
exec "copy http://FW_SRC_ADDR/UPGRADE_FW_IMAGE flash:"
ios_config "boot system flash:UPGRADE_FW_IMAGE"
tclquit</code></pre><p>Where:</p><pre><code>FW_SRC_ADDR - IP or hostname of FW image source device
UPGRADE_FW_IMAGE - file name of a chosen upgrade FW image</code></pre><p>Please replace all the example values with your actual ones. Don't forget to check <code>Require "enable" (privileged-exec) mode</code> for this Config Push.</p><p>If you know some of your devices are slow (will take along time to download the IOS image), then we recommend one more step before running the Mass Config Push preset. In my case, I know one of mine devices required as much as 5 minutes to download the IOS image, which would otherwise hit one of Unimus default timeouts. If this is your case as well, we recommend overriding timeouts and giving devices a much longer time to finish. Here's how you can do it via the <code>Advanced settings</code> of a Push Preset:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade1_timeout.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><p>In my case, I extended it to 5 minutes = 300 seconds = 300,000 milliseconds.</p><h3 id="config-push-preset-2reload-devices">Config Push preset 2 - Reload devices</h3><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade1_preset_reload.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><pre><code>tclsh
exec "reload in 1"
tclquit
</code></pre><p>This is a simple preset to reload devices and set it to be executed in 1 minute. Feel free to change it to your needs, there are two formats for <code>reload in</code> command - <em>MMM</em> or <em>HHH:MM</em>, or you can change it to <code>reload at</code> and define a specific date and time for the reload instead.</p><p>Let me quickly address why we use the TCL shell to execute a simple reload - in my testing I encountered an inconsistent sequence of prompts when sending the <code>reload</code> command, and I can imagine there can be even more variations between other devices and IOS versions. Use of the TCL shell handles the issue - it just works without needing to handle various IOS inconsistencies.</p><h2 id="quickly-setting-up-a-http-server">Quickly setting up a HTTP server</h2><p>While not recommended for production use, on Linux you can very quickly and easily spin up a HTTP server which will serve files in the current directory by running:</p><pre><code>python -m http.server</code></pre><p>You can use this to quickly and easily host images for your devices to pull from. The URL to use in your Config Push presets would look something like this:</p><pre><code>http://your_machine_ip:8000/image_name.bin</code></pre><h2 id="example-of-a-successful-run">Example of a successful run</h2><p>And here's how we want to see the upgrade Mass Config Push preset to go:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_iosupgrade1_success.gif" class="kg-image" alt loading="lazy" width="1640" height="800"></figure><p>While we wish everyone had exactly the same results as we did, some of your devices might not finish successfully. It is possible you may encounter errors, such as some devices not finishing the download in time, some devices not being able to download the file at all, or some other small changes in syntax on some devices that are unaccounted for.</p><p>If any such issue occurs, Unimus will inform you via its own error assessment of the failed pushes, such as <code>CONNECTION_ERROR</code>, <code>COMMAND_UNSUPPORTED</code>, <code>INTERACTION_ERROR</code>, etc. <code>INTERACTION_ERROR</code> may be a bit more difficult to troubleshoot - as the reasons for this error may vary.</p><p>In <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-2/">Part 2</a><em> </em>of this series we will improve the upgrade process significantly by using TCL scripting. There will be an added layer of error detection by the upgrade TCL script itself, which should handle and help identify most of the failure scenarios you can run into.</p><p>Keep an eye on our blog for the <a href="https://blog.unimus.net/automating-cisco-ios-upgrades-with-unimus-part-2/">Part 2</a> of this series coming soon! There is also a topic on our forums you can use to provide feedback or ask questions: <a href="https://forum.unimus.net/viewtopic.php?f=11&amp;t=1426">https://forum.unimus.net/viewtopic.php?f=11&amp;t=1426</a></p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ RouterOS and MTU - a collection of useful scripts ]]></title>
        <description><![CDATA[ MTU on MikroTik is something you don't often deal with (unless you run MPLS, in which case you do). In this article I wanted to share a few useful RouterOS scripts when dealing with MTU. ]]></description>
        <link>https://blog.unimus.net/routeros-and-mtu-a-collection-of-useful-scripts/</link>
        <guid isPermaLink="false">6201bc9be683410001ce9f49</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Tue, 08 Feb 2022 15:20:44 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/blog-mikrotik-1.png" medium="image"/>
        <content:encoded><![CDATA[ <p>MTU on MikroTik's RouterOS is something you usually don't deal with - that is until you have to deal with it because things stopped working. Alternatively, you decide to implement MPLS in your network, and then usually MTU becomes one of the things you deal with daily.</p><p>In this post, I want to share a few useful MikroTik scripts for dealing with MTU.</p><h2 id="1-auditing-l2-mtu">1. Auditing L2 MTU</h2><p>Let's start by looking into how you can find out what's the max L2 MTU your network can currently safely transit. Please note the script below will check only physical interfaces, if you already have some virtual interfaces (like VPLS or any PPP-type interfaces) you would like to check, you will need to adjust the script.</p><p>I will assume you already calculated the maximum size of L2 frames you need on your network. In this case, checking if they can safely transit your RouterOS devices is fairly simple:</p><pre><code>{
:local minimalMtu 1600

:local mtuCheck do={
  :if ([/interface get $1 l2mtu] &lt; $2) do={
    :put ("Interface " . [/interface get $1 name] . " has MTU under " . $2)
  }
}

# ethernet
:foreach i in=[/interface ethernet find running] do={
  $mtuCheck $i $minimalMtu
}

# wireless
:foreach i in=[/interface wireless find disabled=no] do={
  $mtuCheck $i $minimalMtu
}
}</code></pre><p>The script does some filtering - such as only checking <code>running</code> ethernet interfaces and non-disabled wireless interfaces. Adjust as required. </p><h2 id="2-setting-max-l2-mtu-on-all-ports">2. Setting max L2 MTU on all ports</h2><p>This would be an equivalent of enabling jumbo frames on other vendor's gear. If you are asking "how do I enable jumbo frames on MikroTik / RouterOS", this is the answer. We simply allow as large L2 frames on all physical interfaces as our hardware allows.</p><pre><code># ethernet - set maximum supported L2MTU by hardware
/interface ethernet
:foreach i in=[find] do={
  set $i l2mtu=[/interface get $i max-l2mtu]
}

# wireless - max supported L2MTU is 2290
/interface wireless
:foreach i in=[find] do={
  set $i l2mtu=2290
}
</code></pre><p><strong>Please note this will flap each port affected by an MTU change.</strong> This may result in traffic getting dropped for a few seconds, OSPF sessions droping their state, etc.</p><p>This should be safe to run on your devices (other than the above mentioned link flap), as it simply sets maximum allowed L2 frame size, without doing anything to L3 packet MTUs.</p><h2 id="3-auditing-l3-mtu">3. Auditing L3 MTU</h2><p>Now let's jump into setting L3 MTU. The issue with L3 MTU is usually oposite to L2 MTU. In most cases, you want to keep L3 MTU at 1500 (however much we wish we could transit larger packets over the internet...).</p><pre><code>{
:local targetMtu 1500

:foreach i in=[/ip address find] do={
  :local iface [/ip address get $i interface]

  :if ([/interface get $iface actual-mtu] != $targetMtu) do={
    :put ("L3 MTU on interface " . [/interface get $iface name] . " is not " . $targetMtu)
  }
}
}</code></pre><p>PPP-like interfaces might be an exception from the 1500 L3 MTU rule for you. If so, adjust the script as required.</p><p>We use the <code>actual-mtu</code> property of interfaces for checking. This is useful because some protocols (PPP-like and VPN interfaces) support MTU negotiation, so even if you configure <code>mtu</code> at 1500, the other side might not support this, and <code>actual-mtu</code> might be lower.</p><h2 id="4-setting-l3-mtu">4. Setting L3 MTU</h2><p>Here we will set all interfaces with non-1500 L3 MTU to 1500. I included filtering of EoIP and PPP-like interfaces here as an example.</p><pre><code>{
:local targetMtu 1500
:local filterTypes "eoip|ppp-out|l2tp-out"

:foreach i in=[/ip address find] do={
  :local iface [/ip address get $i interface]

  :if ([/interface get $iface type] ~ $filterTypes) do={
    :put ("Ignoring interface " . [/interface get $iface name] . " due to filter")
  } else={
    :if ([/interface get $iface mtu] != $targetMtu) do={
      :put ("Updating MTU to " . $targetMtu . " on " . [/interface get $iface name])
      /interface set $iface mtu=$targetMtu
    }
  }
}
}</code></pre><p>You can notice we filtered based on interface <code>type</code>. You can see types of interfaces like this:</p><pre><code>/interface
:foreach i in=[find] do={
  :put ("Type of interace " . [get $i name] . " is " . [get $i type])
}</code></pre><h2 id="5-checking-and-setting-mpls-mtu">5. Checking and setting MPLS MTU</h2><p>Finally let's see how to check, and set, the MPLS MTU. Here we have 2 simple scripts. One to check MPLS MTU:</p><pre><code>{
:local targetMtu 1550

:if ([/mpls interface get [/mpls interface find default=yes] mpls-mtu] = $targetMtu) do={
  :put "MPLS default interface MTU is CORRECT"
} else={
  :put "MPLS default interface MTU is WRONG"
}
}</code></pre><p>We structure the commands in an <code>if-else</code> block on purpose, so Unimus' Config Push output grouping will nicely group all devices with correct (and incorrect) MTU into 2 groups.</p><p>And finally here is a small script to set MPLS MTU:</p><pre><code>{
:local targetMtu 1580

/mpls interface
set [ find default=yes ] mpls-mtu=$targetMtu
}</code></pre><h2 id="outro">Outro</h2><p>I hope these scripts can make dealing with MTU a little easier for you. If you want to discuss the scripts (or anything related to this topic), please check the <a href="https://forum.unimus.net/viewtopic.php?f=11&amp;t=1420">forum topic corresponding to this blog</a> post on our forums.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Unimus Backup Exporter ]]></title>
        <description><![CDATA[ With the Backup Exporter you can keep copies of your backups stored offsite in case of a catastrophic failure that removes your ability to use your Unimus server. ]]></description>
        <link>https://blog.unimus.net/unimus-backup-exporter/</link>
        <guid isPermaLink="false">61fb270ee683410001ce9ca4</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 04 Feb 2022 02:14:58 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/backup-exporter.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Unimus allows you to backup all of your device configurations into a convenient and accessible platform. However, you may want to keep copies of your backups stored offsite in the case of a catastrophic failure that removes your ability to use your Unimus server.</p><p>To solve this problem, we created the Unimus Backup Exporter. This script will allow you to download all backups from your Unimus server. Once you download them, you may want to export them offsite, for example to AWS S3 using the AWS CLI, or possibly to a local NFS share. We provide built-in GIT functionality in the Exporter script as another alternative solution if desired.</p><p>Let us show you how you can use the Exporter in a few steps. </p><h2 id="step-1preparing-unimus-and-creating-an-api-token">Step 1 - Preparing Unimus and creating an API token</h2><p>The First step we need to take is to create an API token for the Exporter script. After you log into your Unimus system, you will find a <code>User management</code> section on the sidebar. After opening this section, at the bottom of the page, you will see <code>API tokens</code>. Click add to create a new token, and keep this page open for a later step.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_apitoken-1.gif" class="kg-image" alt="creating API token in Unimus" loading="lazy" width="1357" height="860"></figure><h2 id="step-2downloading-the-unimus-backup-exporter">Step 2 - Downloading the Unimus Backup Exporter</h2><p>Navigate to the directory where you would like to install the script from your command line, and we can use <code>wget</code> to download it. Then we will need to <code>unzip</code> the script and make it executable. For example:</p><pre><code>[~] $ mkdir unimus-exporter
[~] $ cd unimus-backups
[~/unimus-exporter] $ wget https://github.com/netcore-jsa/unimus-backup-exporter/releases/latest/download/unimus-backup-exporter.zip
[~/unimus-exporter] $ unzip -x unimus-backup-exporter.zip
[~/unimus-exporter] $ chmod +x unimus-backup-exporter.sh</code></pre><h2 id="step-3setting-up-your-config-file">Step 3 - Setting up your config file.</h2><p>Now that we have our script downloaded, we need to configure an <code>env</code> file to tell it how to access our server. We will do this in the following two parts.</p><h3 id="part-1local-backups">Part 1 - Local backups</h3><p>Provided is a sample env file called <code>unimus-backup-exporter.env</code>. In this file, you will find examples of all options you can use to configure your script.</p><p>The following settings will export all of your backups to your local filesystem in a <code>backups</code> directory in the same directory as the script. In Step 1, we kept open the page with the Unimus API key. We will use that here in the API key section. Please note, everything should be in double quotes. Any options not being used can be removed or commented out with a preceding <code>#</code>.</p><pre><code>unimus_server_address="http://foo.bar:8085"
unimus_api_key="insert api key here"
backup_type="latest"
export_type="fs"</code></pre><p><code>backup_type</code> has two options:</p><ul><li><code>latest</code> - pulls the most recent backups from Unimus</li><li><code>all</code> - pulls all backups from Unimus</li></ul><p>Any options not used can be removed or commented out with a preceding <code>#</code>, as shown below. </p><pre><code>#git_username=”foo”</code></pre><p>After running the script, you will see all of your backups in the <code>backups</code> directory, listed by IP address and the Unimus device ID. </p><pre><code>[~/unimus-exporter/backups] $ ls -l
total 2
drwxr-xr-x 1 user group 4096 Jan 18 09:38 '192.168.4.1 - 6'
drwxr-xr-x 1 user group 4096 Jan 13 23:47 '192.168.3.1 - 2'
[~/unimus-exporter/backups] $</code></pre><p>If you only want to export to your local file system, you can skip part 2 of this step and go to Step 4. </p><h3 id="part-2pushing-to-git">Part 2 - Pushing to Git</h3><p>Some users will want to have the option to move the backups offsite. We have included GIT functionality into the script to make this easier. Inside of your <code>unimus-backup-exporter.env</code> file, you need to add some additional settings. These options are going to be dependent on your GIT server / repo. The most common options are going to be the following.</p><pre><code>git_username="foo"
git_email="foo@bar.org"
git_server_protocal="http"
git_server_address="192.168.4.5"
git_port="22"
git_repo_name="User/unimus-backup-exporter"
git_branch="master"</code></pre><p>You may also need to set the <code>git_password</code> option if you use password authentication.</p><pre><code>git_password="password"</code></pre><p><code>git_server_protocal</code> has three options:</p><ul><li><code>http</code> - pushes to GIT using HTTP</li><li><code>https</code> - pushes to GIT using HTTPS</li><li><code>ssh</code> - pushes to git using SSH</li></ul><p>Please note that you must add the private SSH key to use to authenticate to the server before running the script if you are using SSH key authentication. </p><p>Once you have these set correctly, we are ready to run the script!</p><h2 id="step-4running-the-exporter">Step 4 - Running the Exporter</h2><p>Now that you have a working configuration file, we can execute the script by running the following command in the script directory. </p><pre><code>[~/unimus-exporter] $ ./unimus-backup-exporter.sh
Getting device data
Getting Device Information
Exporting latest backups
2 backups exported
Export successful
Script finished
[~/unimus-exporter] $ </code></pre><p>After running the script, you will see the following directory structure in the <code>backups</code> directory. </p><pre><code>[~/unimus-exporter/backups] $ ls -l
total 2
drwxr-xr-x 1 user group 4096 Jan 18 09:38 '192.168.4.1 - 6'
drwxr-xr-x 1 user group 4096 Jan 13 23:47 '192.168.3.1 - 2'
[~/unimus-exporter/backups] $</code></pre><p>If you are exporting to Git, you will see something similar to the following output.</p><pre><code>[~/unimus-exporter] $ ./unimus-backup-exporter.sh
Getting device data
Getting Device Information
Exporting latest backups
2 backups exported
Export successful
Pushing to git
Initialized empty Git repository in /home/user/unimus-exporter/backups/.git/
[master (root-commit) 5fabcf7] Initial Commit
 2 files changed, 878 insertions(+)
 create mode 100644 192.168.3.1 - 2/Backup 192.168.3.1 2021-02-16-03:00:43-EST 2.txt
 create mode 100644 192.168.4.1 - 6/Backup 192.168.4.1 2021-11-01-03:00:27-EDT 6.txt
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 32 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 7.61 KiB | 3.80 MiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote:
remote: The private project user/test_unimus_git was successfully created.
remote:
To ssh://192.168.4.5/user/test_unimus_git.git
 * [new branch]      master -&gt; master
Everything up-to-date
Push successful
Script finished
[~/unimus-exporter] $</code></pre><p>If using GitHub or GitLab, you should be able to see the backups in the web interface. </p><figure class="kg-card kg-image-card"><img src="https://lh4.googleusercontent.com/9zsRTnnPIWU5UIDS0T0SmIqrfR0MIpNoL2M0p-DmVli5dwSTtyhbILNG4TVf1i6cFLtOrYM7KUXv7Xkpr5ye8Ui-l_nMn9lLt1m4ygYZEhgbl9QbfRrrWw28aVDeJyqzc0nW_IY" class="kg-image" alt="Backups list" loading="lazy"></figure><h2 id="step-5automating-the-exporter">Step 5 - Automating the exporter</h2><p>Typically, you will want to run the Exporter script periodically to ensure you have the latest backups. To do this, you can schedule a cron job. Given the script shouldn’t need any elevated privileges, adding the following line to your user’s <code>crontab -e</code> will set up the script to run every night at 3 AM. You should schedule the script to run after Unimus is done backing up your devices.</p><pre><code>0 3 * * * /path-to-script/unimus-backup-exporter.sh</code></pre><p>A log is generated in the script’s directory. Your output should look like this.</p><pre><code>Log File - 2021-11-04 21:46:16
2021-11-04 21:46:16 Getting device data
2021-11-04 21:46:16 Getting Device Information
2021-11-04 21:46:17 Exporting latest backups
2021-11-04 21:46:17 20 backups exported
2021-11-04 21:46:17 Export successful
2021-11-04 21:46:17 Pushing to git
2021-11-04 21:46:19 Push successful
2021-11-04 21:46:19 Script finished</code></pre><p>You can use the log to monitor results and create alarms and notifications if the export fails for any reason. If the script fails to finish, you will receive an error message. </p><pre><code>Log File - 2021-11-04 21:49:40
ERROR: 2021-11-04 21:50:01 Unable to connect to unimus server</code></pre><p>That’s all that is needed to set up our exporter and automate it to run daily.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Automating FRR backups with Unimus - a how-to guide ]]></title>
        <description><![CDATA[ A guide to create and upload backups of FRRouting (FRR) into Unimus. Useful scripts for use with the Unimus API for FRR backup and config management... ]]></description>
        <link>https://blog.unimus.net/automating-frr-backups-with-unimus-a-how-to-guide/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b81</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Vik@Unimus ]]></dc:creator>
        <pubDate>Tue, 07 Sep 2021 08:17:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/frr.png" medium="image"/>
        <content:encoded><![CDATA[ <p>The goal of Unimus is to automatically, and out-of-the-box support any networking equipment without having to manually feed all the information about it into the system. The overall design of Unimus, and specifically our Discovery mechanism make this possible on your networking devices.</p><p>However, there are some cases in which this is not possible - specifically when networking functions are provided as software running on a generic-purpose machine with a generic OS. For instance a piece of software installed on you Linux Server on Ubuntu, Debian, etc. While going through all installed packages on a Linux machine and properly identifying networking-related software is possible (at the cost of causing load on these machines), natively backing up all the possible configurations of all software packages is just about impossible. Unimus would have to understand the packaging specifics of all of these packages across all the Linux distributions, and would have to understand how config is structured (even when you can include external configuration through config files) for each of these packages, which is not realistic.</p><p>Lately we have seen multiple inquiries about specific networking packages and we decided that this is a good time to share a guide on how you can create and upload backups of almost any software config files into Unimus. The package we chose to feature in this article is a routing software suite <a href="https://frrouting.org">FRRouting</a>.</p><p>With its capabilities and availability across all major Linux distributions (including Debian / Ubuntu, CentOS, RHEL and more), FRR has a large user-base. Other similarly powerful networking focused software package of course exist, and you may want to keep their backups in one place - Unimus, which you use to back up all your networking equipment anyway. This is the good kind of centralization after all.</p><p>Let's get to the good stuff. While we are not able to support software such as FRR directly, you can use one of the features of Unimus to do so. Say hello to the Unimus API! With a bit of simple bash scripting on the host machines running FRR (or other software) you can collect and backup your configuration files and/or binary backups generated by such software and store them in Unimus. All the usual features of Unimus (like change management / change notifications, etc.) will work as expected.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_backup_diff.png" class="kg-image" alt="Unimus FRR backup diff" loading="lazy" width="1091" height="602" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus_backup_diff.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus_backup_diff.png 1000w, https://blog.unimus.net/content/images/2022/02/unimus_backup_diff.png 1091w" sizes="(min-width: 720px) 720px"></figure><p>Without any further ado, let us show you how we can do just that in a few steps.</p><h2 id="step-1preparing-unimus-and-setting-you-up-with-an-api-token">STEP 1 - preparing Unimus and setting you up with an API token</h2><p>As a first step, we want to prepare things in Unimus for our new device and also generate an API token to be able to submit API calls and upload our backups into Unimus. Let's start with the API token:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_apitoken.gif" class="kg-image" alt="Unimus API token" loading="lazy" width="1357" height="860"></figure><p><br>Now, let's create our new device, which will represent a machine running FRRouting and set it to be unmanaged. We will create a device, specify its IP/hostname and add a description (helps with identification in your device list):</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_adddevice.gif" class="kg-image" alt="Unimus add device" loading="lazy" width="1357" height="860"></figure><p>If you see a message informing you about an unsuccessful discovery job, this is expected. This was an automatic discovery triggered as soon as we added the device and before we set it to be Unmanaged.</p><h2 id="step-2getting-familiar-with-unimus-api">STEP 2 - getting familiar with Unimus API</h2><p>Unimus' API is a powerful tool and many functions of Unimus are exposed through it. You can check the <a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation">full API documentation here</a> if you wish. We will use the API to our advantage here as well. The API function we are interested in is a <a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation#FullAPIv.2documentation-Backups-createnewbackup">function to create a new backup</a>.</p><p>Starting from this point, you will need your Linux CLI. Let's start with a curl command we will use to upload our backups into Unimus:</p><pre><code>curl -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer &lt;token&gt;" \
-d '{"backup":"&lt;backup&gt;","type":"&lt;TEXT&gt;"}' "http://example.unimus/api/v2/devices/&lt;deviceId&gt;/backups"
</code></pre><p>There are 5 parameters we need to provide in order to successfully push it:</p><pre><code>&lt;token&gt;            - this is our API token we generated in step 1
&lt;backup&gt;           - this will be our encoded backup we will prepare in step 3
&lt;TEXT&gt;             - this will be a type of backup we will choose (BINARY/TEXT) also in step 3
&lt;example.unimus&gt;   - this is your Unimus server address
&lt;deviceId&gt;         - this will be an ID of our device we created in step 1</code></pre><p>Now, let's get the ID of our newly created device representing our machine running FRR. We can use one of two functions to do so, one searching our device by its IP/hostname, the other one searching for our device by its description. Let's check out both:</p><p><strong>Option 1 - searching device by its IP/hostname </strong><br><a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation#FullAPIv.2documentation-Devices-getdevicebyaddress">https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation#FullAPIv.2documentation-Devices-getdevicebyaddress</a></p><pre><code>curl -H "Accept: application/json" -H "Authorization: Bearer &lt;token&gt;" \
"http://&lt;example.unimus&gt;/api/v2/devices/findByAddress/&lt;address&gt;?attr=s,c"</code></pre><p>There are 3 parameters we need to insert in this command:</p><pre><code>&lt;token&gt;            - this is our API token we generated in step 1
&lt;example.unimus&gt;   - this is Unimus' server address
&lt;address&gt;          - this is IP/hostname of our device</code></pre><p>As per the example, let us show you our version of the curl call, inserting the API key, Unimus' address and device's IP:</p><pre><code>curl -H "Accept: application/json" -H "Authorization: Bearer \
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.ACruAhyEiipDrX7-QRsPfAJpsTooibm5RznqSHSMtuM" \
"http://10.10.10.10:8085/api/v2/devices/findByAddress/10.20.30.40?attr=s,c"</code></pre><p>And here is a response we got:</p><pre><code>"data":[{"id":234,"createTime":1629477261,"address":"10.20.30.40","description":"FRRouter@Deb9@123",
"schedule":null,"vendor":null,"type":null,"model":null,"lastJobStatus":"FAILED","connections":[]}],
"paginator":{"totalCount":1,"totalPages":1,"page":0,"size":20}}</code></pre><p>Our device's ID is 234.</p><p><strong>Option 2 - searching device by its description </strong><br><a href="https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation#FullAPIv.2documentation-Devices-getdevicesbydescription"> https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation#FullAPIv.2documentation-Devices-getdevicesbydescription </a><br><br>As in option 1, here's our version of a curl call, inserting the API key, Unimus' address and device's description:</p><pre><code>curl -H "Accept: application/json" -H "Authorization: Bearer \
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.ACruAhyEiipDrX7-QRsPfAJpsTooibm5RznqSHSMtuM" \
"http://10.10.10.10:8085/api/v2/devices/findByDescription/FRRouter@Deb9@123attr=s,c"</code></pre><p>And here is a response we got:</p><pre><code>"data":[{"id":234,"createTime":1629477261,"address":"10.20.30.40","description":"FRRouter@Deb9@123",
"schedule":null,"vendor":null,"type":null,"model":null,"lastJobStatus":"FAILED","connections":[]}],
"paginator":{"totalCount":1,"totalPages":1,"page":0,"size":20}}</code></pre><p>Same as before, our device's ID is 234.</p><p>At this point, we know what API calls we need to use, we know our API token and we know our device's ID. Let's move to pushing a backup of our config to Unimus.</p><h2 id="step-3preparing-a-backup-and-uploading-it-into-unimus">STEP 3 - preparing a backup and uploading it into Unimus</h2><p>For this article we tested FRRouting as our weapon of choice and installed it on three Linux machines; running Debian 9, Ubuntu 20 and CentOS 7. We can happily report we haven't found any difference in configuration files' locations, which we will focus on below. This, however, may be different for the software you would look to backup with Unimus, thus always follow specific instructions for the installation and where backups and/or configuration files are stored.</p><p>FRR doesn't have a backup feature per se, instead as with much of other software packages, we can simply back up its configuration files. In case of FRR we will be backing up two configuration files:</p><pre><code>/etc/frr/daemons
/etc/frr/frr.conf</code></pre><p>We will do so in two ways to show you the two formats of backups you can choose to suit almost any scenario.</p><h3 id="method-1text-backup">Method 1 - TEXT backup</h3><p>One of the ways you can choose to create and upload your backup into Unimus is in a form of a text file. This method is generally recommended (if possible) as you will be able to see contents of this text file and receive appropriate configuration change notifications of its contents as well. In our case, we will be backing up two text files which we merge into one and upload it as a single text file. Here is a very simple script to do so:</p><pre><code>#!/bin/bash

cd /tmp

#BACKUP PREP
echo -e "#BEGIN /etc/frr/daemons" &gt; frrbackup.txt
cat /etc/frr/daemons &gt;&gt; frrbackup.txt
echo -e "#END /etc/frr/daemons\n\n\n" &gt;&gt; frrbackup.txt
echo -e "#BEGIN /etc/frr/frr.conf" &gt;&gt; frrbackup.txt
cat /etc/frr/frr.conf &gt;&gt; frrbackup.txt
echo -e "#END /etc/frr/frr.conf" &gt;&gt; frrbackup.txt

#BASE64 ENCODING
encodedbackup=$(base64 -w 0 frrbackup.txt)

#BACKUP PUSH INTO UNIMUS
curl -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer \
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.ACruAhyEiipDrX7-QRsPfAJpsTooibm5RznqSHSMtuM" \
-d '{"backup":"'"$encodedbackup"'","type":"TEXT"}' "http://10.10.10.10:8085/api/v2/devices/234/backups"

#CLEANUP
rm frrbackup.txt</code></pre><p>Here is a breakdown of the workflow of this script:</p><ul><li>First it prepares a backup file with some additional formatting so that it is easier to distinguish beginning/end of each in the final text file.</li><li>Then a content of the prepared backup text file is passed to BASE64 encoder and loaded into a variable - this encoding is very important as it encodes the contents into a single streamlined string of characters allowing us to move it efficiently. Note, BASE64 encoding doesn't encrypt the content of your files, anyone could decode it with any BASE64 decoder.</li><li>Then using curl call from step 2 we fill in all parameters required with actual data, and changed backup type to TEXT - note the use of extra single/double quotes to insert the variable containing our encoded backup, This format is important so that the variable is processed correctly.</li><li>Lastly we clean up.</li></ul><p>We can now run the script. As specified in our API documentation, we are expecting this output:</p><pre><code>{"data":{"success":"true"}}</code></pre><p>If you don't see this output, it indicates there was a problem sending the backup. Refer to our API documentation to find more information if needed. Now, let's check Unimus and see if we got our backup and all is readable:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_txtbackup-1.gif" class="kg-image" alt="Unimus add device" loading="lazy" width="1357" height="860"></figure><p>We properly see the content of this text file - Unimus automatically decodes it our BASE64 encoded string. We can download the backup, send it or check diffs in case something changes - just like any other backup.</p><h3 id="method-2binary-backup">Method 2 - BINARY backup</h3><p>The second format of backup is in form of a binary file. Binary backups can be useful if your software generates has configuration files spread in multiple files across multiple formats so generating a single TXT file is not feasible. In such case packing all files into a single archive and uploading the archive is the way to go.</p><pre><code>#!/bin/bash

cd /tmp

#BACKUP PREP
tar -czvf frrbackup.tar.gz -C /etc/frr/ daemons frr.conf

#BASE64 ENCODING
encodedbackup=$(base64 -w 0 frrbackup.tar.gz)

#BACKUP PUSH INTO UNIMUS
curl -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer \
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.ACruAhyEiipDrX7-QRsPfAJpsTooibm5RznqSHSMtuM" \
-d '{"backup":"'"$encodedbackup"'","type":"BINARY"}' "http://10.10.10.10:8085/api/v2/devices/234/backups"

#CLEANUP
rm frrbackup.tar.gz</code></pre><p>Here is a breakdown of the workflow of this script:</p><ul><li>First it prepares and packs our target files into a single .tar.gz archive, but you can use other formats if you prefer different archiver/compressor.</li><li>Then it is passed to a BASE64 encoder and loaded into a variable - this encoding is very important as it allows the binary file to be transferred as a single streamlined string of characters. Note, BASE64 encoding doesn't encrypt the content of your files, anyone could decode it with any BASE64 decoder.</li><li>Then using the curl call from step 2 we fill in all parameters required with actual data, and change backup type to BINARY - note the use of extra single/double quotes to insert the variable containing our encoded backup, This format is important so that the variable is processed correctly.</li><li>Lastly we clean up.</li></ul><p>We can now run this script. Again we are expecting this output:</p><pre><code>{"data":{"success":"true"}}</code></pre><p>If you don't see this output, it indicates there was a problem sending the backup. Refer to our API documentation to find more information if needed. Now, let's check Unimus and see if we got our backup and what we can do with it:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus_binbackup-1.gif" class="kg-image" alt="Unimus add device" loading="lazy" width="1357" height="860"></figure><p>As you can see, Unimus received our backup, however compared to a text backup, we cannot see contents of this file and that is because it is a binary file and it could be in any format - .tar.gz, .bin, .zip, etc. We can still download or send it. If there is a change to this binary file we will see a difference in SHA1 sum. Note, when downloading a binary backup, Unimus will not append any extension to this file, we recommend renaming it right away.</p><h2 id="step-4job-scheduling">STEP 4 - job scheduling</h2><p>One-time execution of scripts is nice, however just as with any backup job, we want it to be run periodically. The last part of this article will be adding a scheduled job to Cron. Depending on your software and/or user privileges you have, you might need to change the way to set up a cron job, e.g. via user-specific jobs using "crontab -e". Since with our setup we need root privileges for accessing cron configuration files, we will add a job to /etc/crontab directly, and set our script to run every night at 3AM (just like our default schedule in Unimus).</p><pre><code>0 3 * * *    root    /root/frrbackup.sh</code></pre><p>And that's it! We have set up a device for FRR in Unimus, and created an automated way to generate and upload backups into Unimus.</p><h2 id="final-words">Final words</h2><p>We hope this article can serve as a template that can be used to upload any files / backups into Unimus. If you have any questions, or run into any issues using the examples in this article, please feel free to post in <a href="https://forum.unimus.net/viewforum.php?f=11">the Automation section of our Forums</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.1.0 ]]></title>
        <description><![CDATA[ Unimus 2.1.0 is the latest major release, including new major features and security and bug fixes. This release overview highlights the new features, other changes and fixes in 2.1.0 - read on to find out more... ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-1-0/</link>
        <guid isPermaLink="false">615c40c8ed7c6b0001874bf4</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Sun, 15 Aug 2021 12:14:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/10/unimus-210.png" medium="image"/>
        <content:encoded><![CDATA[ <p>With each new release, we also upload a release overview video, so if you prefer a video format, you can find it here: <br><a href="https://youtu.be/XWbtM3x3fos">Youtube - 2.1.0 Release Overview video</a></p><p>For those who prefer readable content, read on!</p><hr><h2 id="backup-filters-feature">"Backup filters" feature</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/backup-filters-preview.png" class="kg-image" alt="Backup filters" loading="lazy" width="786" height="357" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/backup-filters-preview.png 600w, https://blog.unimus.net/content/images/2022/02/backup-filters-preview.png 786w" sizes="(min-width: 720px) 720px"></figure><p>You can now create custom backup filters, which allow you to ignore or completely delete parts of the backup received from your devices. This can be useful if you do not want to store some data that the device outputs, or if you want to ignore parts of the backup for config change notifications. For example, if your device outputs some data that is different on every backup, you can create ignore filters to tell Unimus this data should be ignored. Using the backup filters, you can create completely custom rules on what to ignore or delete to cut down on change notifications.</p><p>For more information and examples, we have a dedicated <a href="https://blog.unimus.net/new-backup-filters-feature-in-unimus-2-1-0/">blog article</a>.</p><hr><h2 id="nms-sync-upgraded-to-preset-based">"NMS Sync" upgraded to preset-based</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/nms-sync-presets-preview.png" class="kg-image" alt="NMS sync" loading="lazy" width="913" height="576" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/nms-sync-presets-preview.png 600w, https://blog.unimus.net/content/images/2022/02/nms-sync-presets-preview.png 913w" sizes="(min-width: 720px) 720px"></figure><p>We have migrated NMS sync to use presets, instead of static configuration. This provides multiple benefits. For example, you can now configure sync from as many NMS systems of the same type as you like simply by creating more presets. The second benefit is that NMS sync is now fully compatible with Zones. You can specify to which Zone an NMS Sync preset should import to. These updates make NMS Sync very flexible. You can import all your customers' networks from a single NMS into multiple Unimus Zones. Or you can import from multiple NMS systems, each representing a separate Zone.</p><p>For more information and examples, please check our NMS Sync <a href="https://blog.unimus.net/nms-sync-improvements-unimus-210/">blog article</a>. </p><hr><h2 id="advanced-settings-for-mass-config-push">"Advanced Settings" for Mass Config Push</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/push-advanced-settings-preview.png" class="kg-image" alt="Push advanced settings" loading="lazy" width="427" height="283"></figure><p>We have added a new "Advanced Settings" window to Config Push. In most use cases our default Config Push behavior was sufficient, and worked as expected. However, in a few specific cases there was a need to fine tune how Config Push behaved, and the new Advanced Settings allow these adjustments.</p><p>These new settings are covered in <a href="https://blog.unimus.net/config-push-new-advanced-settings-in-unimus-2-1-0/">this blog article</a>.</p><hr><h2 id="netxms-agent-as-a-proxy-for-zones">NetXMS Agent as a proxy for Zones</h2><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-netxms-proxy-preview.png" class="kg-image" alt="Push advanced settings" loading="lazy" width="948" height="408" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-netxms-proxy-preview.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-netxms-proxy-preview.png 948w" sizes="(min-width: 720px) 720px"></figure><p>In 2.1, we are adding the option to use a NetXMS Agent as a Unimus Zone proxy. If you are using NetXMS and already have an Agent deployed in a remote network, you don't need to deploy Unimus Core - you can just use your existing Agent. This cuts down on the amount of software you need to deploy, and if you already use NetXMS, you can now onboard your networks into Unimus much faster.</p><p>More details in the documentation <a href="https://wiki.unimus.net/display/UNPUB/NetXMS+Agent+as+Zone+Proxy">on our Wiki</a>.</p><hr><h2 id="other-minor-new-features-and-improvements">Other minor new features and improvements</h2><p>We have also added many new minor features, such as improvements to notifications, UX improvements to Config Push, warnings when an older version Core is connected, performance improvements, and many other UI and UX improvements across Unimus. Head down to the full Changelog to learn more!</p><hr><h2 id="security-improvements-and-bug-fixes">Security improvements and bug fixes</h2><p>We focused heavily on security in 2.1, as outlined in our <a href="https://blog.unimus.net/update-on-unimus-codebase-and-release-security/">Update on Unimus codebase and release security</a> article earlier this year. We have updated both our backend and frontend frameworks to latest LTS releases, reviewed Unimus build security and infrastructure and introduced code-signing to all binary releases.</p><hr><p>With each new release, we also add support for new network vendors and devices. This release is no different and we are adding support for 21 new device types, from 15 separate networking vendors.</p><p>The Changelog for 2.1.0 is quite large, so this article doesn't cover it completely. If you want to see all the changes in this release, please check the full Changelog below:</p><h2 id="full-changelog">Full changelog:</h2><pre><code>= Version 2.1.0 =
Features:
  Added notifications if a Zone goes offline (can be configured in the "Notifications" screen)
  You can now select which Zone the "Basic import" imports devices into
  Config Push History (on the Dashboard) now shows which user ran the Push, or if it was scheduled
  Improved Diff performance (diffs with a large change-set could be very slow)
  Improved Import / NMS Sync handling - import/sync jobs are now queued, so a single job doesn't block you from queueing others
  You can now disable the Core connection listener if desired (if not using remote Cores)
  The "get" endpoints for "devices" in the API ("/api/v2/devices/...") now also return the status of the last device job
  "Sensitive data stripping" has been moved from "Other settings" to "Backups &gt; Configuration"
  "Advanced settings" &gt; "Discover un-discovered devices when new Credentials are added", "added" was changed to "added or bound"
  Improved Output Group matching in Mass Config Push
  Added new icons for Comments / Tags / Filters in all tables
  Added "unimus.core.tcp.connect-timeout" config option to control Core-&gt;Unimus connection timeout (default 5 seconds)
  You can now disable specific job types (Discovery, Backup, Scan, Config Push) if desired
  Added support for devices which ask "Configure from terminal?" when the "configure" command is sent
  On JunOS devices, "monitor stop" will be sent before backup to make sure logs are not present in backup
  Improved support for Adtran NetVanta devices
  Improved support for Datacom devices
  Improved support for ExtremeWare devices
  Improved support for Quanta devices

  New "Custom Backup Filters" feature:
    - you can create custom filtering rules on backups to filter any data you don't want inside backups
    - both completely deleting and/or replacing data for a filtered text are available
    - allows for creation of rules based on Tags, device vendors or device types
    - https://unimus.net/blog/backup-filters-unimus-210

  "NMS Sync" is now configured using Presets, and now properly works with Zones
    - you can now define as many NMS Sync connections as you like
    - fully integrated with Zones, you can now use NMS Sync to sync devices to multiple Zones
    - existing configuration automatically migrated to Presets
    - https://unimus.net/blog/nms-sync-improvements-unimus-210

  Ability to use the NetXMS Agent as a proxy for Zones:
    - you can use a NetXMS Agent as a poller for an Unimus Zone instead of an Unimus Core
    - if you use NetXMS, you no longer need to deploy both a NetXMS Agent and an Unimus Core for the Zone
    - https://wiki.unimus.net/display/UNPUB/NetXMS+Agent+as+Zone+Proxy

  New "Advanced Settings" feature for Mass Config Push:
    - allows overriding credentials used to connect to devices by this Push Preset
    - allows overriding timeouts used in device communication by this Push Preset
    - allows settings the prompt matching mode used by this Push Preset
    - https://unimus.net/blog/config-push-advanced-settings-unimus-210

  Unimus Core version is now checked by Unimus and shown in Zones
    - added versioning to the Core communication protocol
    - Unimus now checks if Cores are using a supported version during connection
    - Unimus will notify on the Dashboard if any "older" version Cores are connected
    - Core version is now shown in the "Zones" screen (if the Zone is using an Unimus Core as it's proxy)

  Added support for:
    - Adit 600 series
    - Brocade G620
    - Cisco FirePOWER for AWS
    - Datacom DmOS devices
    - Datacom DmSwitch devices
    - D-Link DXS 5000 series
    - Extreme 200 series
    - Extreme VOSS / VSP OS
    - Extreme Wing AP 510
    - Fiberhome devices
    - Fiberstore (FS.com) Campus switches
    - Fortinet FortiWeb
    - IBM Flex System Fabric
    - IBM RackSwitch
    - Netgear GSM switches
    - Nomadix EG devices
    - Nomadix NSE devices
    - Siklu Terragraph
    - Ubiquiti airFiber 60 5G
    - Ubiquiti airFiber 60 LR
    - Ubiquiti GigaBeam
    - ZTE ZXA devices

Fixes:
  Fixed Slack notifications not working with new Slack Apps (changes in Slack API for new Apps)
  Fixed issue when writing into an input box, a desync may occur that caused a character to get lost, and the cursor to jump to the start of the input box
  Fixed built-in backup filtering in rare cases could add many "&lt;--filtered--&gt;" text instances into a backup
  Fixed config change notification not sent if a backup was pushed over the API
  Fixed API limited max page size to 50, even if user specified a much larger size
  Fixed Import and/or NMS Sync could get stuck if there was an internal error during import/sync
  Fixed Unimus could stop working on HSQL after a long period of time if data retention cleanup settings were enabled
  Fixed running discovery/backups on all devices over the API did not work (single device requests worked properly)
  Fixed Import and NMS Sync running UI notifications could get lost when moving around the application
  Fixed wrong Config Change Notifications on specific Cisco IOS versions
  Fixed wrong Config Change Notifications when a few specific config items were present on F5 devices
  Fixed "Backup it very long, do you want to continue?" warning boxes not working properly
  Fixed errors when trying to add extremely long passwords (over 130) characters in the "Credentials" screen
  Fixed inconsistent case sensitivity in Config Search (normal matching is now always CI, regex matching is done per regex settings)
  Fixed Config Search showing whole backup when Context Size was set to 0
  Fixed multiple edge-case issues and errors in Config Search
  Fixed multiple edge-cases where a device address with a whitespace was not properly trimmed
  Fixed checkbox and selection not being properly reloaded after refresh (F5)
  Fixed "last run" value not being updated in a Config Push preset in some circumstances
  Fixed multiple UI issues (element overflows, wrong element sizing on small resolutions) in Config Push
  Fixed "$[no-wait]" not properly working in Config Push under certain circumstances
  Fixed Discovery failing on newer firmware version of HP/HPE ProCurve devices
  Fixed devices that used "enter" as pagination not working over Telnet
  Fixed jobs failing on newer Adtran NetVanta devices
  Fixed jobs failing on a few specific HP Comware devices
  Fixed jobs failing on a few specific devices over Telnet
  Fixed jobs failing on specific configurations of ExtremeWare devices

Security fixes:
  Upgraded the frontend framework to the latest LTS version
  Upgraded the backend framework to the latest LTS version
  Fixed not properly invalidating all sessions of a logged-in account if it was removed (sessions would work until session timeout)
  Fixed users being able to see Tags they did not have access to in Config Search (only list of Tags affected, search results were properly secured)

Embedded Core version:
  2.1.0

Known issues:
  ISSUE: When many jobs are running, and you scroll or re-order the Devices table, some rows can be duplicated and/or malformed
  WORKAROUND: table re-render fixes this - scroll out and back in, or reorder again and elements will rerender properly
  STATUS: issues in framework after upgrade to latest LTS - we are investigating how to fix :(

  ISSUE: Opening a Config Push preset is slow and locks the UI session with large results (1000+ devices)
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Search in Config Push preset outputs is slow and locks the UI session with large results (1000+ devices)
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Device ownership (Owner) is not properly set when using Basic Import
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: An API call to the "devices" endpoint using PATCH doesn't work
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Already logged in user gets 'Access denied' when they manually navigate to '/'
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: When moving many devices across Zones, an error can occur
  WORKAROUND: none, retry the move again
  STATUS: issue scheduled for fix in next version

  ISSUE: Notifications don't contain a list of addresses of filtered devices (no connectors, Core offline, etc.)
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Config push creation - 'Require "enable" mode' is not automatically checked when 'Require "configure"' is checked
  WORKAROUND: none, works properly after preset has already been created
  STATUS: issue scheduled for fix in next version

  ISSUE: Data put into HTTP query parameters are not escaped in NMS Sync importers that use HTTP protocols
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Sorting by Job Status in Devices does not work properly if Unmanaged devices are present
  WORKAROUND: none
  STATUS: issue scheduled for fix in next version

  ISSUE: Some screens in Unimus show time in server's time zone, others in client's (browser's) time zone
  WORKAROUND: none, issue only relevant if client has different time zone than server
  STATUS: we are debating on how to fix this - will likely create a setting to select which TZ should be used
        </code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Config Push - new Advanced Settings in Unimus 2.1.0 ]]></title>
        <description><![CDATA[ A new "Advanced Settings" menu is being added to Mass Config Push in the Unimus 2.1.0 release. In this article we look at when these new settings can be useful and how they change the default Config Push behavior. ]]></description>
        <link>https://blog.unimus.net/config-push-new-advanced-settings-in-unimus-2-1-0/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b80</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Thu, 01 Jul 2021 08:14:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/advancedstttins.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Based on user requests, we are adding a few new <code>Advanced Settings</code> to Config Push in Unimus 2.1.0. For the vast majority of use cases the default Config Push behavior doesn't need adjustments and works as expected. However, in a few specific cases there was a need to fine tune how Config Push behaves, and the new <code>Advanced Settings</code> allow those adjustments.</p><h1 id="prompt-recognition-mode">Prompt recognition mode</h1><p>Normally, Unimus learns the full prompt of the device, and waits for it before sending the next command from the Push preset (unless the <code>$[no-wait]</code> modifier is used). Check out the Mass Config Push <a href="https://wiki.unimus.net/display/UNPUB/Mass+Config+Push">documentation on the Wiki</a> for more details.</p><p>Here is an example of device communication during a Push. In the Config Push preset, we would check <code>Require enable (privileged-exec) mode</code> and <code>Require configure (configuration) mode</code>, and provide these commands:</p><pre><code>interface ethernet 1/1
description "Server Link"
exit
</code></pre><p>Here is how the device communication would looks like:</p><pre><code>switch-rack1&gt; &lt;enter&gt;                                    # "device-rack1" is learned as the hostname
switch-rack1&gt; enable&lt;enter&gt;                              # bring devices into desired CLI mode (enable first)
switch-rack1# &lt;enter&gt;
switch-rack1# configure terminal&lt;enter&gt;                  # now bring devices to configure mode
switch-rack1(config)#interface ethernet 1/1&lt;enter&gt;       # device is in desired CLI mode, send commands
switch-rack1(config-if)#description "Server Link"&lt;enter&gt;
switch-rack1(config-if)#exit
switch-rack1(config)#
</code></pre><p>The prompt learning behavior in Unimus exists to make sure Unimus knows when to interact with the device (send commands to it) and when to wait and collect output from the devices while it's outputting the output of those commands. There is however one case where this is not desired, when you actually want to change the hostname (and therefore change the prompt) on the device. Here is an example:</p><p>Commands in Config Push preset:</p><pre><code>hostname switch-backup-r1
write memory</code></pre><p>Here is how the device communication would looks like (skipping the CLI mode changes):</p><pre><code>...
switch-rack1(config)#hostname switch-backup-r1&lt;enter&gt;
switch-backup-r1(config)#                             # Unimus fails here, unable to recognize new prompt
</code></pre><p>The issue here is that even tho the change was deployed, Unimus failed to recognize the new prompt, and the <code>write memory</code> command was not sent. This is where the new <code>Prompt recognition mode</code> setting comes into effect. You can now set the prompt to <code>Simple</code> recognition mode, which will make Unimus use a much simpler prompt recognition method (just looking at the ending character), and the above preset would work as expected.</p><p><strong>Please note the <code>Learning</code> prompt mode should be used whenever possible, as it makes Config Push much more reliable. </strong>You should only use the <code>Simple</code> recognition mode when you know the hostname / prompt of the device will change as a part of the Push.</p><h1 id="overriding-timeouts">Overriding timeouts</h1><p>As described in our Wiki article on <a href="https://wiki.unimus.net/display/UNPUB/Changing+default+timeouts">timeout configuration</a>, Unimus uses multiple different timeouts when communicating with your devices. For some very long operations however, you might want to increase this timeout - since if the device takes too long to finish outputting some output, or if some operation on the device (such as saving it's config) takes longer than the timeout, the job would be considered as failed. Here is an example:</p><pre><code>copy tftp://server.local/file nvram:file
y$[no-enter]</code></pre><p>In this example, we are copying a file from a remote server to all devices this Push will be executed on. Here is how the device communication would look like:</p><pre><code>(lab-swch1) #copy tftp://server.local/file nvram:file&lt;enter&gt;

Mode........................................... TFTP
Set Server IP.................................. server.local
Path........................................... ./
Filename....................................... file
Data Type...................................... file

Management access will be blocked for the duration of the transfer
Are you sure you want to start? (y/n) &lt;y&gt;

#
# this operation take a very long time - over 1 minute
#

File transfer operation completed successfully.

(lab-swch1) #</code></pre><p>Since the file transfer in this case takes over 1 minute, the job would fail due to the timeout running out. In this case, checking <code>Override timeouts</code> and setting the timeouts to 100000 (100 seconds) would fix the failure. Please note that the override will set ALL the timeouts to the specified value (more in the <a href="https://wiki.unimus.net/display/UNPUB/Changing+default+timeouts">Wiki article</a>).</p><h1 id="overriding-credentials">Overriding credentials</h1><p>The final new option in the <code>Advanced Settings</code> is the ability to override the credentials Unimus will use when communicating with the device. This is useful in cases when you have read-only credentials in the <code>Credentials</code> screen for security purposes. You do however want to be able to use Config Push, but without having full credentials available system-wide. In this case, you can check the <code>Override credentials</code> box, and provide whichever of the available credentials you want to override. You can keep the credentials you don't want to override empty.</p><h1 id="final-words">Final words</h1><p>We hope these settings provide another bit of missing flexibility to Config Push and fix a few edge-cases which were Config Push could fail before, These new <code>Advanced Settings</code> are available in Config Push starting with Unimus 2.1.0. Please head over to the <a href="https://unimus.net/download.html">Download</a> section to download the latest Unimus release.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Improvements to NMS Sync in Unimus 2.1.0 ]]></title>
        <description><![CDATA[ The "NMS Sync" feature of Unimus was heavily reworked and improved in Unimus 2.1.0. It is now fully compatible with Zones, and allows for much more flexibility in how you adopt devices into Unimus. This article is an overview of the changes and new options available for NMS Sync. ]]></description>
        <link>https://blog.unimus.net/nms-sync-improvements-unimus-210/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7f</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 11 Jun 2021 08:11:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/nmssync.png" medium="image"/>
        <content:encoded><![CDATA[ <p>The <code>NMS Sync</code> functionality was originally intended to ease device management (adding / removing devices) and onboarding of networks into Unimus. If you use a monitoring system (NMS / RMM), your network infrastructure should already be present in your NMS - so instead of having to manually add your network(s) into Unimus, you could just configure Unimus to pull in devices from your NMS. This makes it much easier and faster to deploy Unimus into new network(s), but also automates the ongoing management of devices in Unimus. If you deploy a new device, you can just add it to your NMS, and Unimus will automatically adopt it from there instead of having to manually add the device to multiple systems. <br><br><code>NMS Sync</code> was originally introduced all the way back in 0.1.3, and while we have been adding connectors for new NMS systems continually over the years, the way how <code>NMS Sync</code> was configured and worked has remained mostly the same. With the introduction of Zones in 2.0 however, the way <code>NMS Sync</code> was implemented started to show it's age.</p><p>We started seeing requests for importing to different Zones from different containers / tags inside an NMS, or to import into a single Unimus instance from multiple NMSes. With 2.1, we have reworked how <code>NMS Sync</code> is configured and works, and it should now be flexible enough to support a wide range of use-cases. </p><h2 id="sync-presets">Sync Presets</h2><p>We have changed <code>NMS Sync</code> configuration to be preset-based. A preset defines a single policy of where from, what, and where to import. You can create as many presets as you would like, pointing to a single NMS system, or pointing to multiple different NMS systems. <br><br>Here is how a configured preset looks like:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-nms-sync-preset.png" class="kg-image" alt="Unimus NMS sync preset" loading="lazy" width="1916" height="707" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-nms-sync-preset.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus-nms-sync-preset.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/unimus-nms-sync-preset.png 1600w, https://blog.unimus.net/content/images/2022/02/unimus-nms-sync-preset.png 1916w" sizes="(min-width: 720px) 720px"></figure><p>The left section defines where to import from - the details of the NMS itself. The right section defines <code>Sync rules</code>, or what to import, and where to import it to.</p><h2 id="sync-rules">Sync Rules</h2><p>Sync Rules tell the Sync Presets from where inside the NMS to adopt devices from, and to which Zone in Unimus these devices should be imported into. A single Sync Rule can specify multiple sources from within the NMS - for example multiple containers, or Tags or tree roots where devices in the NMS are located. For each NMS we support, the options are a little different, as it depends on how devices inside the NMS are organized. You can create as many rules for a single preset as you need. <br><br>Here is an example where we configure 2 rules for sync from Zabbix. One rule imports from the <code>Internal infrastructure</code> Zabbix group into the <code>Default</code> Zone in Unimus, and the 2nd rule import from 2 Zabbix groups (`Managed routers` and <code>Customer CPEs</code>) into <code>Zone 3</code> in Unimus:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-nms-sync-rules.png" class="kg-image" alt="Unimus NMS sync preset" loading="lazy" width="1917" height="748" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-nms-sync-rules.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus-nms-sync-rules.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/unimus-nms-sync-rules.png 1600w, https://blog.unimus.net/content/images/2022/02/unimus-nms-sync-rules.png 1917w" sizes="(min-width: 720px) 720px"></figure><h2 id="examples">Examples</h2><p>The easiest example is if you have a single NMS system and would like to import devices only to a single Zone in Unimus. In this case, a single Sync Preset with a single Sync Rule will be completely sufficient. Just setup your NMS details, and create a single Sync Rule. Inside the Sync Rule, you can setup multiple sources (containers, tags or IDs) from that NMS, and point them all to the Default Zone. <br><br>If you have a single NMS system, but would like to import different nodes from the NMS into different Zones in Unimus, a single Sync Preset will also be sufficient. You can create multiple Sync Rules, each rule would define the sources inside the NMS, and would point the devices to be imported into separate Zones. So in the end, you would create as many Sync Rules as you have Zones in Unimus you want to import devices into. <br><br>Finally, If you have multiple NMS systems from which you would like to import to Unimus, you can create multiple Sync Presets - one for each NMS. Inside these presets, you can create rules that define which devices from each of the NMSes (presets) are adopted, and into which Zone they should be imported to. </p><h2 id="supported-nmses">Supported NMSes</h2><p>As of 2.1.0, Unimus supports syncing from 7 different NMS systems:</p><ul><li>NetXMS</li><li>Zabbix</li><li>PRTG</li><li>LibreNMS</li><li>Panopta</li><li>Powercode</li><li>Observium</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-supported-nms-list.png" class="kg-image" alt="Unimus NMS sync preset" loading="lazy" width="885" height="623" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-supported-nms-list.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-supported-nms-list.png 885w" sizes="(min-width: 720px) 720px"></figure><p>If you use a different NMS system that we don't have support for yet, please get in touch with us!</p><h2 id="migration">Migration</h2><p>If you already use Unimus and have an existing NMS Sync configuration, upgrading to 2.1 will automatically migrate your configuration to presets. After the upgrade, you don't need to do any manual reconfiguration - everything should continue working as expected. <br><br>The updates to NMS Sync are available starting with Unimus 2.1.0. Please head over to the <a href="https://unimus.net/download.html">Download</a> section to download the latest Unimus release.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ New Backup Filters feature in Unimus 2.1.0 ]]></title>
        <description><![CDATA[ The Unimus 2.1.0 release brings a new "Backup Filters" feature, allowing you to create custom rules that can influence what and how Unimus stores as backup contents for your devices. This article explains how the new Backup Filters work, and shows an example of how filters can be used... ]]></description>
        <link>https://blog.unimus.net/new-backup-filters-feature-in-unimus-2-1-0/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7e</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 09 Jun 2021 08:08:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/newbackupfilters.png" medium="image"/>
        <content:encoded><![CDATA[ <p>To fully explain when and how the new Backup Filters are useful, let's start with an overview of how Unimus stores backups for your devices without using the new filters. Normally, the backup procedure Unimus performs works as follows:</p><ol><li>connect to the device</li><li>switch to a desired CLI mode to perform backup</li><li>retrieve configuration from the device (for example <code>show running-config</code>)</li><li>remove pagination, perform formatting, etc.</li><li>remove dynamic contents from the backup</li><li>compare retrieved config to currently known config of the device</li><li>create a new configuration point (backup) or update existing</li></ol><p>The last step of this process is the most complex one. The "backups" that Unimus retrieves are used to build a versioned configuration history for your device. Or to simplify - if there are no changes to the config of your device, a new "backup" point is not required. So rather than show you individual backups, Unimus will show you configuration points - ranges of valid configurations on your device to build a configuration timeline of the device. Any point in this timeline is a valid backup, but you can also see when and how the config of your device changes over time. This is actually more difficult to explain than it is in reality - simply you see a timeline of how your device was configured over time, and you can use this to restore to any point in the past. <br><br>Here is an example of a configuration timeline for a device:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-device-config-timeline.png" class="kg-image" alt="Unimus device config timeline" loading="lazy" width="1634" height="599" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-device-config-timeline.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus-device-config-timeline.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/unimus-device-config-timeline.png 1600w, https://blog.unimus.net/content/images/2022/02/unimus-device-config-timeline.png 1634w" sizes="(min-width: 720px) 720px"></figure><p>We can see that this particular device has had 9 unique configuration points since we added it to Unimus in Dec. 2019. The current configuration of this device (the one on top) has been applied to the device on Apr. 9th, and is valid up until today (Jun 9th). <br><br>To be able to do this, Unimus needs to be able to figure out if there was a config change on the device, which is not as trivial as it might seem. For example, if the configuration of the device contains timestamp (Cisco IOS will show the current timestamp of when <code>show running-config</code> was executed), this needs to be ignored so this "change" in the configuration contents doesn't create a new configuration point. Over the years, we have built a large set of what we call "dynamic content filters" - and actually for each device type we support in Unimus, we write these dynamic content filters as a part of adding support for a device. <br><br>While our built-in filters work properly in the vast majority of cases, sometimes you might have dynamic data as a part of your configuration - some output that is unique on each configuration printout. This can break the configuration timeline for your device, as Unimus will consider each backup run as a unique configuration point, which makes the configuration timeline pretty useless. <br><br>This is where the new Backup Filters come in - you can define rules which will tell Unimus that a part of the configuration should be ignored for the purposes of comparing the current known config to the new received config. </p><h2 id="how-the-new-backup-filters-work">How the new Backup Filters work</h2><p>There are 2 types of Backup Filters you can define:</p><ul><li><code>Deleted data</code> filters</li><li><code>Ignored data</code> filters</li></ul><p>How <code>Deleted data</code> filters work - before comparison of the new backup to the currently stored backup, Unimus will REMOVE the matched data from the "new" backup. This means that this data will not be stored as a part of the backup at all - as if this data was not received from the device at all. <br><br>How <code>Ignored data</code> filters work - during comparison of the new backup to the currently stored backup, Unimus will IGNORE the matched data for the comparison purposes. The data will still be stored as a part of the backup, but if changes in the data happen, those changes will be ignored. <br><br>The order of filtering is:</p><ol><li><code>Deleted data</code> filters</li><li>Built-in "dynamic content filters"</li><li><code>Ignored data</code> filters</li></ol><p>In the previous section of the article we described how the <code>Ignored data</code> filters can be used to ignore changes to parts of the backup. The <code>Deleted data</code> filters on the other hand can be used to completely remove some parts of the backup. For example, if your device outputs the DHCP lease database as a part of it's configuration, but you don't want to store that in Unimus, you could create "Deleted data" filters that would filter out the appropriate lines from the config. In effect, Unimus would then never store this part of the config of the device. </p><h2 id="how-to-configure-backup-filters">How to configure Backup Filters</h2><p>The Backup Filters can be added in the new <code>Backup &gt; Configuration</code> window:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/filter-configuration.png" class="kg-image" alt="Backup filter configuration" loading="lazy" width="1377" height="762" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/filter-configuration.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/filter-configuration.png 1000w, https://blog.unimus.net/content/images/2022/02/filter-configuration.png 1377w" sizes="(min-width: 720px) 720px"></figure><p>After clicking to add a new filter, you have multiple options of how and to what it should apply:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/new-filter.png" class="kg-image" alt="New backup filter" loading="lazy" width="654" height="401" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/new-filter.png 600w, https://blog.unimus.net/content/images/2022/02/new-filter.png 654w"></figure><p>There are a few ways to filter:</p><ul><li>Line starts with</li><li>Line ends with</li><li>Regex</li></ul><p>For the "line" filters, if there is a match, the whole line is filtered / deleted. Regex is a bit more flexible. If there are no capture groups in the regex, the whole regex match is filtered / deleted. However if you use capture groups, only the content captured by the capture groups will be filtered / deleted. This allows for very precise and flexible matching using regex and capture groups, so you can filter / delete only exactly what you need. <br><br>There are also multiple ways on how the filter applies:</p><ul><li>Vendor</li><li>Device Type</li><li>Tag</li></ul><p>If a vendor is chosen, then the filter will apply to all devices from that vendor. If a device type is chosen, the filter will apply to all devices of that device type. Finally you can choose to apply the filter only to devices tagged with a specific Tag, for targeted filtering on specific devices. </p><h2 id="example">Example</h2><p>Filtering changes to IPSec secrets. In this example, we setup an IGNORE filter for IPSec passwords:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/mikrotik-ipsec-filter.png" class="kg-image" alt="MikroTik ipsec filter" loading="lazy" width="654" height="497" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/mikrotik-ipsec-filter.png 600w, https://blog.unimus.net/content/images/2022/02/mikrotik-ipsec-filter.png 654w"></figure><p>You will notice this is using a "Regex" filter, and using a capture group to only filter the actual password (note also the use of a non-capturing group for more flexible matching). As described earlier, this would cause Unimus to IGNORE and changes of IPSec password, and not create new configurations if these passwords are changed. HOWEVER, since this is an IGNORE filter, the configuration will still be present in your backups:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-filtered-diff.png" class="kg-image" alt="Unimus filtered diff" loading="lazy" width="1617" height="959" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-filtered-diff.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/unimus-filtered-diff.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/unimus-filtered-diff.png 1600w, https://blog.unimus.net/content/images/2022/02/unimus-filtered-diff.png 1617w" sizes="(min-width: 720px) 720px"></figure><p>We could uncheck the <code>Filter dynamic content</code> checkbox to show the actual cleartext which was saved. If we wanted to completely remove these secrets from the backups, we could have used a DELETE filter instead of an IGNORE filter. <br><br>The new Backup Filters feature is available starting with Unimus 2.1.0. Please head over to the <a href="https://unimus.net/download.html">Download</a> section to download the latest Unimus release.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ New Mass Config Push features in Unimus 2.0.11 ]]></title>
        <description><![CDATA[ Unimus 2.0.11 brings often requested quality-of-life improvements to Mass Config Push. Read on to see the new options for Config Push with Tags, and a new omni-search feature for push results... ]]></description>
        <link>https://blog.unimus.net/new-mass-config-push-features-in-unimus-2-0-11/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7d</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Tue, 09 Mar 2021 07:46:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/mcpfeatures.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Starting with Unimus 2.0.11, we have added 2 major quality-of-life improvements to our Mass Config Push:</p><ul><li>you can now use Tags to push to groups of devices</li><li>new omni-search for easily finding anything in push results</li></ul><p>Let's look at each of these in more details...</p><h2 id="new-targets-configuration-for-config-push">New "Targets" configuration for Config Push:</h2><p>In previous Unimus versions, you would choose individual devices, and "Add" them to a Push preset to tell Unimus which devices it should push to. We have changed this to a new "Targets" configuration. Rather than individual devices, you now define Targets - which can either be devices and / or Tags. Unimus will take all the devices from all the tags, and the individual devices, and create a final device set to push to. <br><br>Here is an UI screenshot of how this configuration looks like:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/targets-overview.png" class="kg-image" alt="Config Push targets" loading="lazy" width="1920" height="1010" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/targets-overview.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/targets-overview.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/targets-overview.png 1600w, https://blog.unimus.net/content/images/2022/02/targets-overview.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>Here is an example of how to configure this in practice:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-config-push-targets.gif" class="kg-image" alt="Config Push targets in practise" loading="lazy" width="1918" height="994"></figure><h3 id="omni-search-for-config-push-results"><br>Omni-search for Config Push results</h3><p>The other new feature is an universal "Search" box for push results. This will search across any data in a push results - group names, device addresses, descriptions, and all outputs in all output groups. Simply - anything you want to find, just type into the search box. <br><br>Here is an example of using the search to first find a part of the output, and then to find all devices whose description contains a particular string:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-config-push-search-1.gif" class="kg-image" alt="Config Push search" loading="lazy" width="1918" height="994"></figure><p>Both of these features are available in Unimus 2.0.11 <a href="https://unimus.net/download.html">released today</a>.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Update on Unimus codebase and release security ]]></title>
        <description><![CDATA[ The last few months have seen a significant increase in focus on security in our industry. In this article, we want to share the steps and precautions we are taking right now, and what we plan to implement going forward to ensure the Unimus codebase and releases stay as secure as possible. ]]></description>
        <link>https://blog.unimus.net/update-on-unimus-codebase-and-release-security/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7c</guid>
        <category><![CDATA[ Other ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Mon, 15 Feb 2021 07:43:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/06/unimus-security-update-2021.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Most of our readers (Hello!) will be familiar with the SolarWinds saga. In December 2020, SolarWinds announced that it's <a href="https://www.reuters.com/article/us-usa-solarwinds-cyber-idUSKBN28N0Y7">Orion software was exploited</a> in a supply-chain attack. This <a href="https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html">FireEye article</a> has a nice write-up of the original attack, called SUNBURST. Since then, at least 2 other malicious payloads were found present in Orion - first SUPERNOVA <a href="https://www.bleepingcomputer.com/news/security/new-supernova-backdoor-found-in-solarwinds-cyberattack-analysis/">was discovered</a>, and later on Raindrop and Teardrop <a href="https://symantec-enterprise-blogs.security.com/blogs/threat-intelligence/solarwinds-raindrop-malware">were also discovered</a> hiding inside the Orion executables.</p><p>Many network were affected, and I'm sure this resulted in a significant ammount of work for many of you reading this article. CISA at one point recommended all systems accessed by Orion in government agencies be rebuilt from scratch (latest CISA guidelines can be <a href="https://cyber.dhs.gov/ed/21-01/">found here</a>).</p><p>Security in infrastructure management tools is extremely important. We take incidents like these very seriously, and want to do everything we can to make sure something like this doesn't happen to Unimus. As such, we wanted to publish an article on what we have been doing in the past months to make sure our systems and Unimus itself are safe, and what we plan to do going forward to.</p><p>Our (NetCore j.s.a.) systems are separated into 3 different groups:</p><ul><li>Unimus instances customers run in their network (we have no access to these)</li><li>Our websites, Portal and Licensing Server (our public resources)</li><li>Our internal environments, tools, servers, workstations, etc. (our internal resources)</li></ul><p><strong>Let's start with Unimus:</strong></p><p>We have audited our codebase and our build process, and we can happily report that we have found no issues and no signs of tampering nor malicious activity on any systems involved in the Unimus development or build process.</p><p>We do however see areas for improvements:</p><ul><li>Some of the dependencies / libraries we use are not the latest available versions. This includes our backend and frontend frameworks. To fix this, we are updating all dependencies / libraries to the latest versions. This is actually a large amount of work, since both frontend and backend frameworks have new major LTS releases available. The dev team has been working since December migrating to these new LTS versions.</li><li>We can improve a lot in the code-signing area. Unimus is built from 12 different modules. We will implement code-signing on the module-level, and validate module signage when the final Unimus binary is built out of the individual modules.</li><li>We will be introducing a Bug Bounty / Security Bounty program for Unimus. More on this to come soon.</li></ul><p>In general, we see the security of the Unimus codebase and the build process as good, with areas for improvement that we are now working on. We are giving this priority, so all of the above mentioned improvements will be coming sooner rather than later.</p><p><strong>Websites, Portal and Licensing Server:</strong></p><p>We have audited all the servers running our public services, as well as the Portal and the Licensing Server codebases and build processes. We can also happily report that we have found no issues and no signs of tampering nor malicious activity on any of our public systems.</p><p>Since the Portal holds customer data (your billing info), we want to make sure all your data with us is properly protected (we don't hold any payment data on our Portal directly). We however see the same areas for improvement for our Portal and Licensing server as for Unimus. As such, the dev team has also been migrating both these services to the latest LTS versions of all dependencies / libraries / frameworks. We will also be implementing the same per-module code-signing and validation processes for our Portal and Licensing Server as we discussed for Unimus.</p><p>In keeping with full transparency, we also recently published a post on our forums explaining what data our Licensing Server collects from local Unimus instances. You can find the post <a href="https://forum.unimus.net/viewtopic.php?p=2876#p2876">here</a>.</p><p>Finally, we will also launch a Bug Bounty / Security Bounty program for the Portal. More details on this will be released soon, together with the same program for Unimus itself.</p><p><strong>Our internal environments, servers, workstations, etc:</strong><br><br>We have also reviewed our internal systems and assured that there is no outside access (other than our offices and our VPNs) to these systems. We have found no issues and no signs of external access or tampering with our internal systems.</p><p>We are also continuing to educate our staff on security best-practices, and we heavily value and encourage a security-minded culture in our company. The internal culture and mindset of our company in regards to security is very important to us, and we will increase investment in internal and external security trainings to make sure all our developers and staff stay mindful of security best-practices going foward.</p><p><strong>To summarize:</strong></p><ul><li>We audited the Unimus codebase and build process and found no security issues</li><li>We audited all our public servers and services and found no security issues</li><li>We plan to introduce more code-signing and integrity checks into the Unimus build process</li><li>We are updating all dependencies / libraries to the latest versions across all our software / services</li><li>We plan to start a Bug Bounty / Security Bounty program</li></ul><p>We are happy to answer any security-related questions you might have; and we would also love to hear if you have any feedback or suggestions on what you think we should do better. Please feel free to post any feedback in <a href="https://forum.unimus.net/viewtopic.php?f=3&amp;t=1258">this forum topic</a>. Thanks!</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 2.0.0 ]]></title>
        <description><![CDATA[ Overview of the Unimus 2.0.0 release, bringing the Zones feature with multi-tenancy, remote network support, binary backups, performance improvements and more! ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-2-0-0/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7b</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 15 May 2020 07:07:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/release-200.png" medium="image"/>
        <content:encoded><![CDATA[ <p>This article highlights the most significant changes and new major features in the Unimus 2.0.0 and Unimus Core release.</p><p>With each new release, we also upload a release overview video, so if you prefer a video format, you can find it here: <a href="https://youtu.be/C8kXmep2Avk">Youtube - 2.0.0 Release Overview video</a></p><p>For those who prefer readable content, read on!</p><hr><h2 id="%E2%80%9Czones%E2%80%9D-feature-and-multi-tenancy">“Zones” feature and multi-tenancy</h2><p>"Zones" add support for multi-tenancy, remote networks and distributed polling. You can have a central Unimus server to manage many remote networks, or you can split devices in your network across Zones, with each Zone using a separate Core to spread load from your server across multiple poller Cores.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-5.png" class="kg-image" alt="Unimus Zones" loading="lazy" width="610" height="300" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-5.png 600w, https://blog.unimus.net/content/images/2021/10/image-5.png 610w"></figure><hr><h2 id="unimus-core-and-remote-networks">Unimus Core and remote networks</h2><p>The Core can serve as a remote poller / remote agent for Unimus, and the Zones you create in Unimus can either be polled directly from the Unimus server, or from a Unimus Core. You can check the new <a href="https://wiki.unimus.net/display/UNPUB/Architecture+overview">Architecture Overview</a> and <a href="https://wiki.unimus.net/display/UNPUB/Zones"> Zones</a> articles on our Wiki for more details on Zones and Unimus Core.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-6.png" class="kg-image" alt="Unimus &amp; Unimus Core architecture" loading="lazy" width="1185" height="581" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-6.png 600w, https://blog.unimus.net/content/images/size/w1000/2021/10/image-6.png 1000w, https://blog.unimus.net/content/images/2021/10/image-6.png 1185w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="full-config-change-notifications-over-slack">Full Config Change Notifications over Slack</h2><p>Previously Unimus would only send config change summaries over Slack, but due to community demand, we have implemented full diffs over Slack. You will need to reconfigure the Slack notification sender in Unimus to use a Slack Bot. Please check our <a href="https://blog.unimus.net/slack-app-configuration-for-unimus/">blog article here</a> on how to create and integrate a Slack Bot with Unimus.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-8.png" class="kg-image" alt="Slack diffs" loading="lazy" width="878" height="290" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-8.png 600w, https://blog.unimus.net/content/images/2021/10/image-8.png 878w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="mass-config-push-scheduling">Mass Config Push scheduling</h2><p>You can now easily schedule Config Push jobs from you already existing Push Presets. This heavily extends the automation capabilities of Unimus, allowing you to schedule any configuration deployments. Push results are available in the Push Preset, and in a new “Config Push history” table on the Dashboard.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-9.png" class="kg-image" alt="Config Push Scheduling" loading="lazy" width="835" height="360" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-9.png 600w, https://blog.unimus.net/content/images/2021/10/image-9.png 835w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="support-for-binary-backups">Support for binary backups</h2><p>With 2.0.0, we have added support for storing binary backup files, and also extended all systems in Unimus to support binary backups. This means change detection, diffs, notifications, and everything else will properly work with binary backups.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-10.png" class="kg-image" alt="Unimus binary backup" loading="lazy" width="719" height="281" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-10.png 600w, https://blog.unimus.net/content/images/2021/10/image-10.png 719w"></figure><hr><h2 id="push-binary-backups-into-unimus">Push binary backups into Unimus</h2><p>With support for binary backups, we have also added a new API end-point, which allows you to push binary files to Unimus as device backups. This opens new use-cases for Unimus, as you can now push files to Unimus from external systems or scripts, and Unimus will perform change detection, notifications, and all other functions as expected.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-11.png" class="kg-image" alt="Unimus Binary Backup Push" loading="lazy" width="735" height="235" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-11.png 600w, https://blog.unimus.net/content/images/2021/10/image-11.png 735w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="push-text-backups-into-unimus">Push text backups into Unimus</h2><p>The new API endpoint also supports pushing text files to Unimus, which allows you to extend Unimus with support for any device even if we don't support it directly. You can script backup retrieval yourself, and push the resulting backup file to Unimus for processing, storage, notifications, etc...</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-12.png" class="kg-image" alt="Unimus Text Backup Push" loading="lazy" width="905" height="272" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-12.png 600w, https://blog.unimus.net/content/images/2021/10/image-12.png 905w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="prtg-and-observium-in-nms-sync">PRTG and Observium in NMS Sync</h2><p>We continue to add new sync connectors to NMS sync, and with this release, you can now adopt devices into Unimus from PRTG or Observium. We are adding new connectors with each Unimus release, so if your NMS doesn't yet have a connector available, please keep an eye on our Roadmap and future changelogs.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-14.png" class="kg-image" alt="Unimus New NMS Sync" loading="lazy" width="1090" height="390" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-14.png 600w, https://blog.unimus.net/content/images/size/w1000/2021/10/image-14.png 1000w, https://blog.unimus.net/content/images/2021/10/image-14.png 1090w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="ui-and-ux-improvements">UI and UX improvements</h2><p>There are also MANY UI and user experience improvements in this release. Some of the more notable ones are the new "Device Info" table in Devices, last job status indicator in Devices, much more detailed job failure logs for failed Discoveries, new result history tables on the Dashboard, and the "Export backups" functionality in the "Backup" screen.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-13.png" class="kg-image" alt="UI UX improvements" loading="lazy" width="775" height="386" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-13.png 600w, https://blog.unimus.net/content/images/2021/10/image-13.png 775w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="performance-improvements">Performance improvements</h2><p>We have worked hard to improve the UI performance of Unimus in 2.0.0. The UI should now be much more responsive when working with many devices. Specifically large tables should load much faster - such as Devices, Backups, etc. Config Search server-side logic has been also improved to deliver search results faster.</p><h2 id="bug-squashing-and-issue-fixing">Bug squashing and issue fixing</h2><p>We have fixed more than 50 various bugs, issues and UI problems in 2.0.0, some of which have been present since 1.0.0. There are also security fixes for users with Tag-based access restrictions. A big thank-you here goes to everyone testing the 2.0 Beta and RC releases, and helping us iron out all of these.</p><hr><p>With each new release, we add support for new network vendors and devices. This time around, we are releasing support for 22 new device types, across multiple networking vendors. <br>            <br>The Changelog for 2.0.0 is quite large, and this article doesn't cover it completely. If you want to see the full scope of changes in this release, please check the full Changelog below:</p><h2 id="full-changelog">Full changelog:</h2><pre><code>= Version 2.0.0 =
Important:
  Slack integration has been migrated from a Webhook to a Slack App. Your Slack notifications will not work without reconfiguration.
  Manual migration and reconfiguration is required, please see more in the "Migration warnings" section.

Features:
  New "Zones" feature for support of remote networks - includes a new "Unimus Core" that serves as the remote proxy / remote agent
  Configuration Change Notifications with full diffs are now supported over Slack (if Slack Notification Sender enabled)
  Failed discovery logs now show full details of discovery and why it failed (Dashboard &gt; Latest Failed Jobs)
  Added visual indicator (grey/green/red) of last job result to the Devices table
  "Devices &gt; Info" window completely reworked, now shows much more useful information about the device
  Added Last Backup Date to device info window ("Devices &gt; Info")
  Added REST endpoint to upload backup (Push backup into Unimus)
  Added support for binary backups (currently only possible with API Backup Push)
  Added an "Export backups" button to the "Backups" view - allows to export all or only latest backups for all devices
  Added support for specifying a CRON expression for Schedules (in addition to current options)
  Changed pagination on the "Config Search" view to 500 (up from 10)
  NetXMS client API updated to version 3.1 (NMS Sync)
  Zabbix importer will now import nodes with only Agent-type interfaces
  Added a new help link on the Backups view, "How does Unimus store backups?"
  Added a new Backup Retention Policy - "Number of backups" (will only keep last x backup for device)
  Added a new "Send Diff" and "Send Backup" popup that replaces the old email input form
  The "Send Diff" and "Send Backup" features now also supports sending diffs over Slack
  New global notification options to control where the system FQDN is displayed in notifications (title or body)
  Added system FQDN to notifications which were missing it (all notifications now contain system FQDN)
  Improved system FQDN lookup for notifications on Windows
  Improved message formatting in all Email and Slack notifications
  Improved UX in all sections of the Notification view ("Save" buttons now only active on change, added "Discard" button, etc.)
  Added retention cleanup jobs to the "Show scheduled tasks" window
  Added new "http.proxyType" and "https.proxyType" settings to configure proxy type when running Unimus behind a HTTP(S) proxy
  Improved responsiveness in multiple views in Mass Config Push
  Added a new Easter Egg (hint: "mike", also, Hi Mike!)
  Improved handling of CLI mode changes, many previously unhandled edge-cases now work properly
  Added support for empty password (just press enter) CLI mode changes (enable, configure)
  Improved detection of "Press any key to continue" and "Press enter to continue" prompts
  Added support for "Do you accept this statement [yes/no]" prompts during login
  Added support for shortened prompts on Cisco IOS in Configure mode
  Added support for line-break prompts in Cisco IOS when using tclsh
  Improved support for Cisco ASA Thread Defense and Cisco FirePOWER TDM
  Added support for read-only user accounts on ExtremeOS
  Improved support for Enhanced Security Mode on HP/HPE ProCurve/Provision/ArubaOS
  Added output of "show bof" to TiMOS backup
  Improved support for ArubaOS Wireless Controllers in various edge-cases
  Improved banner detection during CLI login process

  "Zones" feature for support of remote networks and distributed polling
    - you can create as many Zones as required, each zone signifying a unique network
    - new top level "Zones" view for Zone management
    - Zones can be polled directly from Unimus, or using the new Unimus Core serving as the remote proxy / remote agent for the Zone
    - architectural overview: https://wiki.unimus.net/display/UNPUB/Architecture+overview
    - more info about Zones: https://wiki.unimus.net/display/UNPUB/Zones

  "Debug Mode" options moved to the "Zones" menu
    - Unimus allows debugging remote cores directly from the Unimus UI
    - you can also download logs from Remote Cores directly in Unimus
    - this requires setting debug options per-zone, so Debug Mode moved to "Zones"

  Mass Config Pushes can now be scheduled
    - You can now schedule Config Push jobs for more automation power
    - More details on Push result notifications and Push result history below

  Other Mass Config Push improvements:
    - Added "Config Push History" table to the Dashboard
    - Added new "Config Push Result" notifications (enabled by default)
    - Push job status is displayed for each Push preset in Mass Config Push Home view
    - Improved the responsiveness (UI scaling) of the Config Push view

  PRTG importer was added to the "NMS Sync" view
    - uses PRTG API to sync devices from PRTG to Unimus
    - sync possible based on Tags, or by node hierarchy in the device tree

  Observium importer was added to the "NMS Sync" view
    - uses Observium API to sync devices from Observium to Unimus
    - sync only specific devices from Groups, or all devices in Observium

  Updated dynamic (runtime) data filtering from backups in diffs:
    - improved filtering of dynamic (runtime) data from backups in all diff views
    - whenever possible, filtering will no longer make a backup invalid by changing it's syntax
    - this only influences diffs - in Unimus and in Config Change notifications
      (View, Download and Send Backup features were always sending raw, unfiltered backups)
    - See more info below in the "Migration warnings" section

  Network Scan improvements:
    - Added "Network Scan History" table to the Dashboard
    - Added new "Network Scan Result" notifications (disabled by default)

  Added support for:
    - ArubaOS-CX devices (Aruba / HPE 8320)
    - more variants of AudioCode devices
    - Blonder Tongue CMTS
    - Casa CMTS
    - Cisco ASA TD
    - Cisco IE (industrial ethernet) switches
    - CTS switches (FOS-3128)
    - more variants of Dell PowerConnect switches
    - Draytek Vigor (Discovery and Config Push only, Backup not supported)
    - Exinda devices
    - Fortinet FortiAnalyzer
    - Fortinet FortiOS v6
    - Harmonics CMTS
    - HPE StoreFabric devices
    - HPE VirtualConnect
    - Huawei Eudemon
    - Huawei VRP in HRP mode
    - Huawei VRP multi-context
    - LANCOM switches (Discovery and Config Push only, Backup not supported)
    - more variants of Mellanox switches
    - Moxa switches
    - Omnitron RuggedNet switches
    - Ubiquiti AirOS CS (custom script) firmwares
    - Ubiquiti UFiber OLT
    - Zhone MXK

Fixes:
  Fixed discovery not running for undiscovered devices when credential was added and discovery should run according to system settings
  Fixed the password of a High Security credential being visible in "Device -&gt; Show Info -&gt; Show credentials"
  Fixed Mass Config Push not working when it contained Un-managed or Undiscovered devices
  Fixed Mass Config Push not working when it contained devices with all connectors disabled
  Fixed Config Search showing only first 500 backups that matched the search
  Fixed Config Search "Expand all" not working
  Fixed wrong (empty) config change notifications on Calix OccamOS based devices
  Fixed device selection selecting devices randomly if they were imported from "Address Importer" or ".csv File Importer"
  Fixed Zabbix importer not importing nodes which only had Agent-type interfaces
  Fixed .csv importer sometimes importing the file header even when "Ignore header" was enabled
  Fixed wrong config change notification for Cisco WLC caused by CDP peer changes
  Fixed wrong config change notification for FortiOS caused by dynamic certificate key output
  Fixed Mass Config Push status showing "Scan Status" instead of "Push Status"
  Fixed multiple extremely rare bugs where Config Search did not show some backups that matched (normally this would never happen)
  Fixed scheduling services to run on Schedule deletion, even if no Push or Scan presets were scheduled (did not schedule jobs, just enabled service)
  Fixed a very rare bug that could cause backup failing with very short backups
  Fixed change of backup retention only being applied after service restart
  Fixed not being able to delete schedules in the Deployment Wizard
  Fixed multiple UI inconsistencies and UX pain-points
  Fixed multiple rare edge-case failures when switching CLI modes (enable, configure)
  Fixed ExtremeOS devices not working when used with read-only accounts
  Fixed FS S3900 switching being discovered as Allied Telesis
  Fixed some HP 1910 models not being discovered
  Fixed Network Scan very slow when DNS requests were timing out
  Fixed Network Scan subnets import incorrectly accepting invalid some subnets as valid
  Fixed DNS timeout configuration being ignored
  Fixed some Mellanox switch models not being discovered
  Fixed backup not working on specific TelcoSys T-Marc firmwares
  Fixed backup not working on a few specific Brocade devices
  Fixed very rare login failure on devices with extremely slow data output during login
  Fixed Config Push that required Configure mode not working on some Fiberstore switches
  Fixed Patton/Inalp devices not working (discovery/backup/push) in certain cases
  Fixed ArubaOS Wireless Controllers not working in very rare edge-cases
  Fixed extremely rare login failure on devices with a post-login menu
  Fixed multiple other extremely rare login failures in various edge-cases
  Fixed backup failing on Adtran Total Access with extremely long configurations
  Fixed discovery failing on newer AudioCodes Mediant devices / firmwares
  Fixed extremely rare cases where VT100 control sequences were not properly stripped from backups

  Fixed a bug that could cause ~1% of scheduled backups to fail on slow, or heavily loaded devices
    - each scheduled backup, a small random subset of devices would fail their scheduled backups
    - slow (older) devices, devices under sufficient load to slow down the control plane, or devices with slower external AAA were most affected
    - in the long run, all devices would be properly backed up, as the subset was usually different for each scheduled run
    - running backups manually would work, only scheduled backups were affected

Security fixes:
  Fixed issue that caused imports from HTTPS URLs in "NMS Sync" to not check HTTPS certificates even if "Do not check HTTPS certificates" was not checked
  Fixed users being able to change "Other settings &gt; Sensitive data stripping" even for Tags they didn't have access to
  Fixed users being able to change "Other settings &gt; Per-Tag connectors" even for Tags they didn't have access to

Embedded Core version:
  2.0.0

Migration warnings:
  Slack integration has been migrated from a Webhook to a Slack App. This is due to the addition of sending Configuration
  Change Notifications over Slack. The Webhook API did not support sending Snippets, which Config Notifications require.
  You will need to setup a new Slack App for Unimus, and reconfigure Unimus Slack sender in "Notifications &gt; Slack".

  For some devices, there may be a single config-change notification after the first backup following the 2.0.0 upgrade.
  This will show a change occurred inside a comment or a non-config line. This is expected due to changes to the dynamic
  (runtime) backup content filtering mentioned in the "Features" section. This is caused by changes to what Unimus
  considers as dynamic (runtime) data inside backups, and you can safely ignore this change notification.

Known issues:
  ISSUE: under rare circumstances, when a Unimus Core disconnects due to packet loss, some jobs may become stuck in Unimus
  WORKAROUND: restarting Unimus is necessary
  STATUS: fixing already in progress - fix coming in 2.0.1

  ISSUE: when you delete the "Device Output Log" file in "Debug mode", any jobs that started before deletion, but finish after
         deletion will recreate the file and write their output to the file
  WORKAROUND: delete "Device Output Log" after all jobs finish / no jobs are running
  STATUS: issue scheduled for fix in 2.0.1

  ISSUE: session timeout doesn't work in certain situations when browser tab is not closed - user's web session can remain logged-in forever
  WORKAROUND: close all tabs in which Unimus is opened, or log-out manually
  STATUS: issue scheduled for fix in 2.0.1

  ISSUE: Importing is possible even with accounts that don't have access to the Default Zone due to Tag-based access restrictions
  WORKAROUND: none, account can be made read-only
  STATUS: issue scheduled for fix in 2.0.1

  ISSUE: Unable to export all backups when two zones have devices with same addresses
  WORKAROUND: none
  STATUS: issue scheduled for fix in 2.0.1

  ISSUE: with higher latency, when writing text into an input box, a desync may occur that causes a character to get lost,
         and the cursor to jump to the start of the input box
  WORKAROUND: none
  STATUS: we are investigating

  ISSUE: unable to set connection timeout in Core - this doesn't influence Core functionality in any way
  WORKAROUND: none
  STATUS: currently no ETA, framework limitations

  ISSUE: special characters can be replaced by '?' under specific circumstances
  WORKAROUND: none
  STATUS: currently no ETA, framework limitations
                    </code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Slack App configuration for Unimus ]]></title>
        <description><![CDATA[ In Unimus 2.0.0 we migrated our Slack integration to a full-featured Slack App. This was necessary since we added full Config Change notifications over Slack in 2.0.0. This blog article contains steps to full configure a Slack Bot for usage with Unimus. ]]></description>
        <link>https://blog.unimus.net/slack-app-configuration-for-unimus/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b7a</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 14 Feb 2020 16:24:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2022/02/unimus---slack.png" medium="image"/>
        <content:encoded><![CDATA[ <p>In Unimus 2.0.0 we migrated our Slack integration to a full-featured Slack App. This was necessary since we added full Config Change notifications over Slack in 2.0.0.</p><p>Here is an example of a Slack Config Change notification:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-slack-config-diff.png" class="kg-image" alt="Slack diff" loading="lazy" width="912" height="568" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-slack-config-diff.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-slack-config-diff.png 912w" sizes="(min-width: 720px) 720px"></figure><p>This article shows how to configure a Slack App for usage with Unimus.</p><p><strong>1)</strong> Open <a href="https://api.slack.com/apps">https://api.slack.com/apps</a> and click "Create App" to start:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/create-slack-app.png" class="kg-image" alt="Slack create app" loading="lazy" width="756" height="453" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/create-slack-app.png 600w, https://blog.unimus.net/content/images/2022/02/create-slack-app.png 756w" sizes="(min-width: 720px) 720px"></figure><p><strong>2)</strong> Slack will ask you if you want to create an app from scratch, or from a manifest. Select "From a manifest", then select the workspace where the app will be installed:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/09/image.png" class="kg-image" alt loading="lazy" width="554" height="383"></figure><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/09/image-1.png" class="kg-image" alt loading="lazy" width="555" height="377"></figure><p><strong>3)</strong> Next you will need to paste this manifest into the app creation:</p><pre><code>{
    "display_information": {
        "name": "Your-Unimus-bot-name-here"
    },
    "features": {
        "bot_user": {
            "display_name": "Your-Unimus-bot-name-here",
            "always_online": false
        }
    },
    "oauth_config": {
        "scopes": {
            "bot": [
                "users:read",
                "channels:read",
                "groups:read",
                "im:read",
                "mpim:read",
                "im:write",
                "mpim:write",
                "files:write",
                "chat:write"
            ]
        }
    },
    "settings": {
        "org_deploy_enabled": false,
        "socket_mode_enabled": false,
        "token_rotation_enabled": false
    }
}</code></pre><p>Make sure to replace the <code>name</code> and <code>display_name</code> for something more relevant to you.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/09/image-2.png" class="kg-image" alt loading="lazy" width="539" height="896"></figure><p><strong>4) </strong>Click "Create":</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/09/image-3.png" class="kg-image" alt loading="lazy" width="551" height="345"></figure><p><strong>5) </strong>At the bottom of the "Basic Information" section, you can Change the name and add a logo that your Slack Bot will use in your Workspace:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/slack-bot-display-information.png" class="kg-image" alt="Slack Bot display information" loading="lazy" width="683" height="412" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/slack-bot-display-information.png 600w, https://blog.unimus.net/content/images/2022/02/slack-bot-display-information.png 683w"></figure><p></p><p><strong>6)</strong> Go to "OAuth &amp; Permissions" in the left menu, and install the bot to your Workspace:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2024/09/image-4.png" class="kg-image" alt loading="lazy" width="707" height="268" srcset="https://blog.unimus.net/content/images/size/w600/2024/09/image-4.png 600w, https://blog.unimus.net/content/images/2024/09/image-4.png 707w"></figure><p><strong>7)</strong> You will get an "Bot User OAuth Access Token", copy the token:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/slack-bot-oauth-access-token.png" class="kg-image" alt="Slack OAuth access token" loading="lazy" width="672" height="245" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/slack-bot-oauth-access-token.png 600w, https://blog.unimus.net/content/images/2022/02/slack-bot-oauth-access-token.png 672w"></figure><p><strong>8)</strong> In your Slack Workspace, invite the Unimus Bot user to the channel it will post to:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/slack-bot-channel-invite.png" class="kg-image" alt="Slack Bot channel invite" loading="lazy" width="926" height="652" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/slack-bot-channel-invite.png 600w, https://blog.unimus.net/content/images/2022/02/slack-bot-channel-invite.png 926w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/slack-bot-channel-add.png" class="kg-image" alt="Add Slack channel" loading="lazy" width="702" height="296" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/slack-bot-channel-add.png 600w, https://blog.unimus.net/content/images/2022/02/slack-bot-channel-add.png 702w"></figure><p><strong>9)</strong> Now navigate to the "Notifications &gt; Slack settings" in your Unimus server:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-slack-settings.png" class="kg-image" alt="Slack settings" loading="lazy" width="714" height="344" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-slack-settings.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-slack-settings.png 714w"></figure><p><strong>10)</strong> Paste in your "Bot User OAuth Access Token" and your channel. You can use "#channel" to post in a channel, or "@user" to DM messages to a person:</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/unimus-slack-configured.png" class="kg-image" alt="Slack configured" loading="lazy" width="751" height="336" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/unimus-slack-configured.png 600w, https://blog.unimus.net/content/images/2022/02/unimus-slack-configured.png 751w" sizes="(min-width: 720px) 720px"></figure><p>Don't forget to "Save" your settings after pasting them in. You can use the "Run test" feature to check if your notifications are working.</p><p>If the test works, the Unimus Slack Sender is fully configured. You will now receive Config Change Notifications into Slack as soon as Unimus detects a config change in your network.</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 1.10.0 ]]></title>
        <description><![CDATA[ Overview of the Unimus 1.10.0 release, bringing a Dark Theme, new Diff features, performance improvements and more! ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-1-10-0/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b79</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 17 Apr 2019 16:21:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/release-1100.png" medium="image"/>
        <content:encoded><![CDATA[ <p>This release overview showcases the new features and improvements in the Unimus 1.10.0 release.</p><p>With each new release, we also upload a release overview video, so if you prefer a video format, you can find it here: <a href="https://youtu.be/T_Wy06LbvAw">Youtube - 1.10.0 Release Overview video</a></p><p>For those who prefer readable content, read on!</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/dark-theme.png" class="kg-image" alt="Device Tags Dark Theme" loading="lazy" width="730" height="282" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/dark-theme.png 600w, https://blog.unimus.net/content/images/2022/02/dark-theme.png 730w" sizes="(min-width: 720px) 720px"></figure><h2 id="dark-theme">Dark Theme</h2><p>The first new feature of this release is a Dark Theme. We have added a dark theme for both Unimus and our Customer Portal, which should make working with Unimus easier in dark environments. You can switch themes on the Dashboard.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/diff-word-highligh.png" class="kg-image" alt="Unimus Config Diff" loading="lazy" width="738" height="271" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/diff-word-highligh.png 600w, https://blog.unimus.net/content/images/2022/02/diff-word-highligh.png 738w" sizes="(min-width: 720px) 720px"></figure><h2 id="diff-word-level-change-highlighting">Diff word-level change highlighting</h2><p>The biggest change to diffs is that they now display changes down to the word level. Previously Diffs would show which line in the config changed, but now you can see which words inside the line changed - these will be additionally highlighted within the line.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/diff-ux-improvements.png" class="kg-image" alt="Unimus Config Diff" loading="lazy" width="749" height="269" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/diff-ux-improvements.png 600w, https://blog.unimus.net/content/images/2022/02/diff-ux-improvements.png 749w" sizes="(min-width: 720px) 720px"></figure><h2 id="diff-usability-improvements">Diff usability improvements</h2><p>Diffs now also by default ignore dynamic backup content (such as timestamps, changing hashes, and other dynamic text) both in Unimus and in config change notifications emails. We have also added a "Send diff" button when you have a diff displayed and want to send it to someone.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/config-search-filters.png" class="kg-image" alt="Config Search Filters" loading="lazy" width="964" height="380" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/config-search-filters.png 600w, https://blog.unimus.net/content/images/2022/02/config-search-filters.png 964w" sizes="(min-width: 720px) 720px"></figure><h2 id="config-search-filters">Config search filters</h2><p>We have added filters to Config Search to be able to search only devices tagged with a certain tag. We have also added time-based filters, and you can even search the entire history of device configurations for anything. This makes it useful when you, for example, want to find out when particular configuration items were added to a device.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/config-search-performance.png" class="kg-image" alt="Unimus Devices List" loading="lazy" width="713" height="314" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/config-search-performance.png 600w, https://blog.unimus.net/content/images/2022/02/config-search-performance.png 713w"></figure><h2 id="performance-improvements">Performance improvements</h2><p>We have massively improved diff performance, with very large diffs rendering about 10 times faster than before. We have also massively improved the performance of Config Search. For example, a large inverse search which took 2 minutes before will now take about 15 seconds. We also worked on Importers in the performance improvement area. An import of 10.000 devices previously took about 1.5 minutes, but will now complete in under 20 seconds.</p><hr><p>In addition to these major features, we have also added support for 10 new devices from various vendors, as well as 7 other minor features. This release also contains fixes for 16 various issues and bugs reported since the 1.9-branch release. </p><p>We recommend checking out the full changelog below for more details. We highly recommend all users to upgrade to this release.</p><h2 id="full-changelog">Full changelog:</h2><pre><code>= Version 1.10.0 =
Features:
  Added official support for Java 9/10/11
  Added a Dark Theme (theme switching possible on the Dashboard)
  Added fallback to 'show startup-config' for Cisco IOS when full content of 'running-config' is not available (due to privilege issues)
  Cisco ASA will now be discovered even if "show version" is not available in user-exec mode
  Do not retrieve rogue AP table as part of Cisco WLC backups (it caused config change notifications on every backup)
  When a discovered device fails rediscovery with UNABLE_TO_IDENTIFY_DEVICE error it's discovered details will be cleared
  Emails expire from email sender queue after 1 hour if they fail to send within this period
  Improved compatibility with certain Dell N series switches
  Improved compatibility with certain Juniper JunOS devices
  Deploy Wizard table width increased (credential and schedule creation screens in the Deploy Wizard)
  New "Ignore dynamic content" option for diffs
  "Only changed lines" and "Ignore dynamic content" options now on by default for diffs
  New "Send diff" button that allows to send currently shown diff over email
  When an import is in progress, a notification will be shown on the Import and Devices screens
  Performance and UX improvements when importing large number of devices (5k+) through Basic Import

  Diffs now highlight actual word changes within change lines:
    - new diffing logic can recognized changes inside changed lines on a per-word basis
    - changed lines are still highlighted, but changes within lines will be highlighted even more
    - this features makes it even easier to see what has changed when looking at a diff

  New features for Config Search:
    - added ability to search by Device Tag (search filtering by Tag)
    - added Config Search in a time-range (search filter by date / time)
    - added full historic Config Search option (search in all config history)
    - added option to specify context size (lines before and after match)

  Performance improvements for Config Search and Diff:
    - complete rewrite of rendering logic, performance of rendering improved by 10x
    - Diff rendering performance improvements, cosmetic/color changes
    - for inverse Config Search, do not render config until stack panel is opened
    - added paging for very big config search results (this is to avoid browser limitations)

  Added support for:
    - 3Com 29XX switch series
    - 3Com non-switch devices (chassis, routers, etc.)
    - Dell PowerConnect 8024 (and similar PowerConnect switches)
    - Exablaze Fusion switches
    - Lenovo RackSwitch switches
    - Lenovo Flex System Fabric
    - OcNOS (switches running OcNOS)
    - FireBrick devices
    - Ruckus Wireless Bridges
    - Turris OS devices (Turris Omnia, etc.)
    - InfiNet WANFleX devices
    - SonicWall devices (SonicOS)
    - Telco Systems T-Marc devices

Fixes:
  Fixed a deadlock when using Network Scan with HSQL DB
  Fixed expiring session in other tabs when a new tab was open
  Fixed backup 'Download' returning a wrong backup in certain cases
  Fixed address validation not accepting a device if it contained certain special characters in its FQDN
  Fixed Mass Config Push merging first line of output with the command line (missing newline)
  Fixed Import / NMS sync considering failed imports / syncs as successful in certain cases
  Fixed Import / NMS sync not sending failure notifications on failed imports / syncs in certain cases
  Fixed newer versions of Fiberstore switches not being discovered
  Fixed incorrect configuration change notifications on newer versions of IgniteNet MetroLinq
  Fixed rare incorrect configuration change notifications for MikroTik RouterOS
  Fixed incorrect configuration change notifications on F5 BIG-IP and F5 BIG-IQ
  Fixed incorrect configuration change notifications on Cisco WLC
  Yet another round of fixes for more incorrect configuration change notifications on FortiOS
  Fixed discovery on certain HP 1920S switches
  Fixed error when searching in bound / not bound device tables in initial config push binding
  Fixed Powercode importer not being rescheduled when default schedule changed
  Fixed Cisco IOS driver wrongly discovering CDB series switches as IOS routers
  Fixed incorrectly identifying some Cisco Catalyst switches as IOS routers
  Fixed config search for HSQL and PostgreSQL databases

  Fixed multiple cases where a Read-Only account could modify objects:
    - read-only account was able to re-run Mass Config Push on an output group through the re-run menu
    - read-only account was able to clone or delete a Mass Config Push preset through the right-corner menu
    - read-only account was able to clone or delete a Network Scan preset through the right-corner menu

Tickets closed by this release:
  UN-126, UN-191, UN-336, UN-348, UN-350, UN-361, UN-385, UN-392, UN-406

Known issues:
  Special characters can be replaced by '?' under specific circumstances
                    </code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Release Overview - Unimus 1.9.0 ]]></title>
        <description><![CDATA[ Unimus 1.9.0 is the biggest release of Unimus to date. This release overview showcases the new features and improvements present in the 1.9.0 release. ]]></description>
        <link>https://blog.unimus.net/release-overview-unimus-1-9-0/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b78</guid>
        <category><![CDATA[ Release Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Wed, 09 Jan 2019 16:16:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/release-190.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Unimus 1.9.0 is the biggest release of Unimus to date. This release overview showcases the new features and improvements present in the 1.9.0 release.</p><p>With each new release, we also upload a release overview video, so if you prefer a video format, you can find it here: <a href="https://youtu.be/7ltU-3JE6Xs">Youtube - 1.9.0 Release Overview video</a></p><p>For those who prefer readable content, read on!</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/device-tags.png" class="kg-image" alt="Device Tags" loading="lazy" width="725" height="298" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/device-tags.png 600w, https://blog.unimus.net/content/images/2022/02/device-tags.png 725w" sizes="(min-width: 720px) 720px"></figure><h2 id="device-tagsfriendlier-and-available-in-more-places">Device Tags - friendlier and available in more places</h2><p>We improved the usability of Device Tags and made them more available across Unimus. Device Tags now have their own top-level menu, and managing them is more user-friendly. We have also added a new "Tags" window on the Devices screen, so you can tag devices directly from the Devices screen.</p><hr><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.unimus.net/content/images/2022/02/device-access.png" class="kg-image" alt loading="lazy" width="807" height="387" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/device-access.png 600w, https://blog.unimus.net/content/images/2022/02/device-access.png 807w" sizes="(min-width: 720px) 720px"><figcaption>Device Access Limitation</figcaption></figure><h2 id="device-access-limitationusability-improvements">Device Access Limitation - usability improvements</h2><p>Since we were working on improvements to Device Tags we also improved the Device Access Limitation system. The Device Access table in User management has been simplified and made more intuitive. In the Devices screen, we added a new table that shows which accounts have access to the selected device, and what grants user the access. You can find this under the new "Tags" window.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/device-owner.png" class="kg-image" alt="Device Edit" loading="lazy" width="673" height="346" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/device-owner.png 600w, https://blog.unimus.net/content/images/2022/02/device-owner.png 673w"></figure><h2 id="device-ownershipnew-feature-to-simplify-access-management">Device Ownership - new feature to simplify access management</h2><p>We have also added Device Ownership - when a user creates a device, he is the owner of that device. Owners have access to their devices even if limited by Tags, or other access-limitation. This fixes an issue where an access-limited Operator-level user was not able to see the devices they themselves created. You can of course change the owner of any device, or set the owner for a device to "None" in the device "Edit" window.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/credentials-high-security.png" class="kg-image" alt="New Credentials Creation" loading="lazy" width="592" height="343"></figure><h2 id="new-high-security-mode-for-credentials-and-enableconfigure-passwords">New "High Security Mode" for credentials and enable/configure passwords</h2><p>When creating a new credential, you can now create it in "High security mode". This will make the password for this credential completely private, and disables any "Show Password" features for that credential across Unimus. The password will be un-retrievable even to administrator-level accounts, making it easier to comply with security requirements in strict environments.</p><hr><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/comments-window.png" class="kg-image" alt="Device Comments" loading="lazy" width="589" height="366"></figure><h2 id="comments-windowsreal-time-updates">"Comments" windows - real-time updates</h2><p>The "Comments" windows now have live-update, so when someone comments all other open Comments windows will refresh automatically. When a new comment is added to any entity in the system, all other users also immediately see the comment icon updated as well. This makes the commenting feature in Unimus update in real-time for all users, and improves UX dramatically.</p><hr><p>In addition to these major features, we have also added support for 9 new devices from various vendors, as well as 9 other minor features. There are also new API endpoints present for config change and diff retrieval over the API. <br>                <br>We recommend checking out the full changelog below for more details. <br>                <br>This release also contains fixes for more than 20 various issues and bugs reported since the 1.8-branch release. We have worked very hard to eliminate all issues small and large, and fixed many edge-cases where device interaction would fail. We are happy to report that from beta and RC testing, the 1.9.0 release is showing to be the most stable release of Unimus yet. <br>                <br>We highly recommend all users to upgrade to this release.</p><h2 id="full-changelog">Full changelog:</h2><pre><code>= Version 1.9.0 =
Features:
  Juniper JunOS driver completely rewritten, solving multiple issues with JunOS
  Added hints (contextual help) to "Do not manage device" and "High security mode" checkboxes
  Improved compatibility with certain Cisco MDS models
  Backups for Cisco IOS and NXOS will now complete even if the "show vlan brief" command is not available
  Increased subnets text area limit in the Network Scan to 65k characters
  Improved support for devices with certain versions of Comware / Huawei VRP
  Improved support for certain models of Cisco SMB switches
  Mass Config Push now supports devices which present a menu after login (pfSense, ProCurve stacks, etc.)
  Improved support for devices that display "Press any key to continue" but really ignore that and proceed to prompt
  Improved visuals for all input fields to better handle IPv6 addresses or long FQDNs
  Event system (top right corner popups) enhanced and visually unified
  Added support for "enhanced security-mode" on HP ProVision / HPE ProCurve / Aruba ArubaOS
  Improved support for "(y/n)" prompts

  Improved the usability of Device Tags:
    - Device Tags now have their own top-level menu for tag management
    - improved the tag management (add/remove/assign/unassign) process
    - various other UI and UX improvements related to tags

  Improved the Device Access Limitation system:
    - Added a new "Users with access" window to devices which have Tags
      (will show which users have access to this device, and where the access comes from)
    - Added device ownership - account which creates a device is now the owner of that device
    - owners always see the devices they own, even if limited Access Limited by Tags
      (solves Operator-level users not seeing the devices they create when they are access-limited)
    - Accounts are not able to changed their own access role anymore - you can't cut yourself from Admin access anymore

  Update to the Comments windows across Unimus:
    - Comments now have live updates, if one user adds a new comment, all users will immediately see it
    - multiple graphical and visual updates and fixes to the Comments windows

  Added two new API endpoints to retrieve configuration changes
    - you can now retrieve list of devices with config changes over the API
    - external integrations can now use the API to display diffs generated by Unimus
    - more details at: https://wiki.unimus.net/display/UNPUB/Full+API+v.2+documentation

  Added high security mode option for credentials and enable passwords
    - will disable "Show password" for these credentials anywhere in Unimus
    - useful to make sure no one (no matter their access level) can retrieve password from Unimus

  New "Sensitive data striping" feature:
    - will not store any sensitive data in backups (passwords, keys, etc.)
    - can be configured globally, or per-Tag
    - currently supported on Cisco ASA, IOS, IOS-XR, Nexus, NXOS
    - more details at: https://wiki.unimus.net/display/UNPUB/Sensitive+data+striping

  The CLI login handler has been rewritten from scratch:
    - this improves overall compatibility with all devices that we support
    - many edge-cases where login to device would fail were solved

  Added support for:
    - A10 Networks Thunder series
    - Adtran TA (Total Access) 2nd gen.
    - Cisco SMB SF2xx / SG2xx / SX2xx switch series
    - more H3C Comware switches
    - HPE 1920S
    - Nomadix AG
    - more Zyxel MGS switches (37xx and newer)
    - more Zyxel USG models
    - Zyxel XGS switches
    - more Zyxel ZyWALL models

Fixes:
  Fixed a bug casing some login banner to cause Unimus to fail the login to devices
  Solved access-limited users not seeing the devices they create when they are Operator level (see device ownership in Features section)
  Fixed users with Device Access limitations seeing devices they should not see in Mass Config Push device binding
  Fixed UBNT devices not discovering / backing-up if firmware version contained 4 digits
  Fixed some environment configuration being ignored on startup (logging, proxy, etc.)
  Fixed a rare failure when trying to switch to enable / configure mode during discovery
  Fixed enable / configure mode switch failing on devices which responded with a lot of data immediately during mode switching
  Fixed Mass Config Push incorrectly reporting "unsupported command" in very rare cases
  Fixed multiple issues with Juniper JunOS backups (parts of backups missing, incorrect change notifications, etc.)
  Fixed not discovering / backing-up some models of UBNT AirFibers
  Fixed some versions of ZyXel USGs not being discovered
  Fixed some versions of Comware / Huawei VRP devices not being discovered
  Fixed some models of Cisco SMB switches not being discovered
  Fixed "--More--" not being properly removed from FortiOS backups in rare circumstances
  Another round of fixes for more incorrect configuration change notifications for FortiOS
  Fixed table search not behaving as expected with certain special characters
  Fixed incorrect username format tooltip message on the login screen
  Fixed inconsistent width of subnets area in the NetworkScanView when changing browser window size
  Fixed Unimus switching view when licensing server went offline and then back online
  Fixed device output logging not working after enabling/disabling it multiple times
  Fixed a UI error when trying to backup multiple unmanaged devices
  Fixed a UI error when a 'read only' account navigated to the Notifications view
  Fixed a UI error when 'Expand command(s) windows' clicked when creating new mass config push preset
  Fixed various cases where login would fail to devices that display "Press any key to continue" but
    really ignore that and proceed to prompt
  Fixed some models of Zyxel MGS switches not being discovered

Tickets closed by this release:
  UN-245, UN-272, UN-316, UN-334, UN-362, UN-365, UN-371, UN-372, UN-377, UN-378, UN-379, UN-380, UN-382

Known issues:
  Special characters can be replaced by '?' under specific circumstances
                    </code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Validating the security of your MikroTik routers network-wide ]]></title>
        <description><![CDATA[ Using Unimus to check if your RouterOS devices are compromised across your whole network. Also a look at how to audit and fix security holes for old and new MikroTik exploits alike. ]]></description>
        <link>https://blog.unimus.net/validating-the-security-of-your-mikrotik-routers-network-wide/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b77</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Mon, 06 Aug 2018 16:06:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/blog-mikrotik.png" medium="image"/>
        <content:encoded><![CDATA[ <h1 id="introduction">Introduction</h1><p>Recently, there has been a resurgence of attacks on MikroTik RouterOS devices (articles <a href="https://thehackernews.com/2018/08/mikrotik-router-hacking.html">here</a>, <a href="https://www.trendmicro.com/vinfo/us/security/news/cybercrime-and-digital-threats/over-200-000-mikrotik-routers-compromised-in-cryptojacking-campaign">here</a> and <a href="https://www.zdnet.com/article/mikrotik-routers-enslaved-in-massive-coinhive-cryptojacking-campaign/">here</a>) using vulnerabilities that were fixed in April 2018 (release 6.42.1), but also falling back to some older vulnerabilities.</p><p>These new attacks primarily use an exploit in Winbox (one of MikroTiks management interfaces), to gain control of the router, and perform various malicious tasks. These Winbox exploit are far from the only exploits that exist for RouterOS however. For example, the http(s) server in older versions of RouterOS contains an exploit that was made public during the <a href="https://wikileaks.org/ciav7p1/">Vault7 leaks</a>.</p><p>In this article, we will use Unimus to check if any of your routers are compromised across your whole network. We will also look into how to use Unimus to both audit and fix potential security holes for old and new MikroTik exploits alike.</p><h2 id="preparations">Preparations</h2><p>For this article, we will assume you have the devices you want to audit / secure already in Unimus, and they are properly discovered. If this is not the case, we suggest checking out our Wiki, and/or video tutorials.</p><p>We will be creating various Mass Config Push presets, but if you wish you can run all of these steps in sequence inside a single Mass Config Push. Using multiple presets just makes it easier and more organized to audit your network.</p><p>Each of the following sections contains a single security check / fix. When you run these commands, you might see multiple Mass Config Push output groups. We recommend checking each group for corresponding output.</p><p>For example, when running the "Checking if routers are exploited" preset, you might see 2 output groups. One output group will contain 45 devices, and the other 2 devices. In this example, when you inspect both output groups, you should find that 45 devices are exploit-free, and the other 2 have been infected.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/mass-config-push.png" class="kg-image" alt="Unimus MikroTik exploit check" loading="lazy" width="1920" height="974" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/mass-config-push.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/mass-config-push.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/mass-config-push.png 1600w, https://blog.unimus.net/content/images/2022/02/mass-config-push.png 1920w" sizes="(min-width: 720px) 720px"></figure><h2 id="checking-if-routers-are-exploited">Checking if routers are exploited</h2><p>This preset will check if any of your devices have been exploited using any of the latest RouterOS exploits.</p><pre><code>:if ([/ip socks get enabled]) do={
  :put "Socks is enabled, if you didn't do this manually, this device has been breached!"
}
:if ([:len [/file find name~".*[Mm]ikrotik\\.php.*"]] &gt;= 1) do={
  :put "\"mikrotik.php\" file found on the file system, high chance this device has been breached!"
}
:if ([:len [/system script find source~".*[Mm]ikrotik\\.php.*"]] &gt;= 1) do={
  :put "A script containing \"mikrotik.php\" found, high chance this device has been breached!"
}
:if ([:len [/system scheduler find on-event~".*[Mm]ikrotik\\.php.*"]] &gt;= 1) do={
  :put "A scheduled script containing \"mikrotik.php\" found, high chance this device has been breached!"
}
:if ([:len [/user find name~".*service.*"]] &gt;= 1) do={
  :put "\"service\" user exists, if you didn't create this user manually, this device has been breached!"
}</code></pre><p>If no devices in your network have been breached, you will see only a single output group, with empty output. If you see any other output groups, devices in those output groups have been compromised. The content of the output group will tell you which of the exploit checks matched on those devices.</p><p><strong>How to remediate if a device has been exploited</strong>                            <br>Simply create a Mass Config Push preset like this:</p><pre><code>/ip socks
set enabled=no
set port=1080

/file
:foreach i in=[find name~".*[Mm]ikrotik\\.php.*"] do={
  remove $i
}

/system script
:foreach i in=[find source~".*[Mm]ikrotik\\.php.*"] do={
  remove $i
}

/system scheduler
:foreach i in=[find on-event~".*[Mm]ikrotik\\.php.*"] do={
  remove $i
}

/user
:foreach i in=[find name~".*service.*"] do={
  remove $i
}</code></pre><p>Run this on the affected devices.</p><p><strong>Make sure to change the password (preferably also username) you use to access the affected devices AFTER you remediate the exploits.</strong></p><h2 id="checking-if-your-routers-have-firewall">Checking if your routers have firewall</h2><p>Having a properly firewalled input chain on your MikroTiks is super important. This preset checks if your firewall exists, and has an explicit drop rule in the input chain.</p><pre><code>/ip firewall filter
:if ([:len [find]] = 0) do={
  :put "No firewall configured on this device"
} else={
  :if ([:len [find chain=input]] = 0) do={
    :put "No input firewall configured on this device"
  } else={
    :if ([:len [find chain=input action=drop !connection-state]] = 0) do={
      :put "No explicit drop rule in input firewall configured on this device"
    }
  }
}</code></pre><p><strong>How to remediate</strong>            <br>There is sadly no simple response to this one. You will have to build a proper firewall for your needs. (if there is enough interest, we might write a separate article on this one)</p><h2 id="checking-service-acls-address-restrictions">Checking service ACLs (address restrictions)</h2><p>If for some reason you do not want to have firewall on your routers (this should never be the case - you can use "/ip firewall raw" if you want to fasttrack some traffic), you must secure the services on RouterOS to only certain IPs (or IP ranges).</p><p>If you DO have firewall properly configured, it will already protect these services, so with a proper firewall setup, limiting access to services is not essential. You can however use this as a 2nd line of defence.</p><pre><code>/ip service
:foreach i in=[find] do={
  :if ((![get $i disabled]) &amp;&amp; ([get $i address] = "")) do={
    :put ([get $i name] . " service is not restricted to any address!")
  }
}</code></pre><p><strong>How to remediate</strong>                            <br>You can push the following change to your devices:</p><pre><code>{
:local address "1.2.3.4"

/ip service
:foreach i in=[find] do={
  set $i address=$address
}
}</code></pre><p>Make sure to properly change the address variable in the 1st line.</p><h2 id="update-routeros-to-latest-the-version">Update RouterOS to latest the version</h2><p>All of the exploits currently running wild use vulnerabilities that have been fixed in the latest RouterOS versions. Even after improving security using the above methods, it is highly recommended to upgrade to the latest RouterOS release.</p><p>We have a separate blog article on how to update RouterOS to the latest version across your entire network here: <a href="https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/">https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/</a></p><h2 id="final-words">Final words</h2><p>That's it for this blog article, we hope it will help you at least a little with your network security. If you are new to Unimus, check out our <a href="https://unimus.net/">website</a> to learn more about us!</p><p>We are offering an unlimited trial license if you want to give Unimus a try (independently of our free tier)! <a href="https://promo.unimus.net/unlimited-trial/get.php">Click here to learn more</a>!</p> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Unimus 1.7.0 and Network Automation ]]></title>
        <description><![CDATA[ Release 1.7.0 marks a huge milestone for Unimus with the inclusion of our new Network Automation / Mass Config Push features. In this article, we share more details on this release, and talk about network automation in general. ]]></description>
        <link>https://blog.unimus.net/unimus-1-7-0-and-network-automation/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b76</guid>
        <category><![CDATA[ Feature Overview ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Thu, 10 May 2018 14:27:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/release-170.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Last week we released Unimus 1.7.0. The biggest part of this release was our new Network Automation / Mass Config Push feature.</p><p>This release marks a huge milestone for Unimus, and in this article we would like to tell you more about it!</p><p>Before we go any further, here is our release overview video:</p><figure class="kg-card kg-embed-card"><iframe src="https://www.youtube.com/embed/Giju310c0ZA?rel=0&amp;showinfo=0" style="border:0;" allowfullscreen=""></iframe></figure><p>This article will mostly focus on Network Automation, since this is the biggest and the most interesting feature in this release.</p><h2 id="how-to-make-network-automation-easy">How to make Network Automation easy?</h2><p>Ever since we started developing Unimus, our main goal always was "simple and fast to use, but powerful enough to solve hard problems".</p><p>We approached Network Automation with the same mind-set:</p><ul><li>you should not need to know an enormous technology stack simply to automate deployment of a VLAN across 50 switches</li><li>new networkers should not have to spend weeks learning to understand and use a network automation platform</li><li>it should just work, no matter the type of the device, or its vendor</li></ul><p>Before we go any further, let's state one basic premise - Unimus will not turn your network into IaC (infrastructure as code). This is not our goal, nor our aim, and if you want your network to be IaC managed, the automation features in Unimus are likely not what your are looking for.</p><p>So who are the Network Automation features in Unimus aimed at?</p><p>Every single network administrator who wants to save time and automate, without having to learn programming or entire automation frameworks. Automate without needing to learn new languages, change work-flows, and without having to worry if their vendor supports it.</p><p>With the Network Automation features in Unimus, you can use the CLI commands you already know for your equipment. You can automate configuration for all of the 249+ vendors we support in Unimus. And most importantly - everything is easy to understand and fast to use.</p><p>Historically, the barriers of entry into network automation have been huge. With this release, we are hoping to remove the large learning curve associate with network automation.</p><p>We are hoping to give the power to every network administrator to mass-deploy configuration across their network, and to enable network scaling by making automation easy and painless.</p><p>For an example on how to automate MikroTik RouterOS deployments network-wide, you can check our previous article: <a href="https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/">https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/</a></p><h2 id="unimus-170-changelog">Unimus 1.7.0 changelog</h2><p>Finally, the full changelog of 1.7.0:</p><pre><code>= Version 1.7.0 =
Features:
  Improved configuration change detection on Cisco IOS and Cisco NXOS
  Unimus now detects when devices return "permission denied" / "access denied" errors and fails the backup job
  Improved error reporting in Dashboard "Show log" for all jobs
  Improved logging of various errors and failed jobs in the log file
  Improved Enable/Configure mode switching for all supported vendors
  Added detection of command unsupported and permission denied errors in output of devices that do not use paging
  Added support for devices which require entering login password twice
  Added device description to job logs on the Dashboard

  Added a new "Mass config push" / "Mass reconfig" feature:
    - Unimus is now able to push configuration to your devices
    - you can create as many "push presets" as needed to automate your network
    - devices will be switched to Enable or Configure mode automatically if a push preset requires it
    - output from the push job is grouped, no need to check output of each device manually

  Added support for Enable/Configure passwords separate from Credentials (username/password combinations):
    - you can specify a list of Enable/Configure passwords on the Credentials screen
    - Unimus will automatically discover which Enable/Configure password is valid for a device

  Added support for Credential and Enable/Configure password binding:
    - you can bind specific Credentials or Enable/Configure passwords to devices
    - this will disable Credential and Enable/Configure password discovery on the device
    - only the bound Credential and Enable/Configure passwords will be used for the device
    - discovery, backup and any other operations on the device will fail if the bound Credentials are invalid

   Added a new "Network scan" (device discovery) feature
     - Unimus is now able to adopt devices by scanning your network
     - you can define multiple subnets for scanning, and Unimus will find available devices
     - networks scans can be scheduled to periodically adopt devices from the network

  Added support for:
    - Adtran NetVanta
    - Adtran Total Access
    - Brocade NetIron
    - HP 1950 switches
    - Ruckus Unleashed
    - ZyXel ZyWALL

Fixes:
  Fixed Brocade NetIron/FastIron/TurboIron SX/CX/GS/LS/WS not being discovered
  Fixed wrong backup contents for multi-context ASA
  Fixed not properly stripping pagination on some models of Netonix switches
  Devices with very long backup time (3+ minutes) would not be backed up, now they will be
  Fixed discovery not working when quickly removing and then re-adding the same device
  Fixed Citrix NetScaler driver not working with newer versions of NetScaler
  Fixed connections sometimes failing to slow devices
  Fixed missing scroll-bar in "View backup"
  Fixed wrong backups table columns width
  Fixed account with READ_ONLY role could access 'Adding the first device' screen
  Fixed not properly handling empty device address in Basic Importers
  Fixed license key change handling (it was not possible to change license key in certain situations)

Tickets closed by this release:
  UN-34, UN-127, UN-209, UN-232, UN-251, UN-309, UN-310, UN-311, UN-312, UN-313

Known issues:
  Special characters can be replaced by '?' under specific circumstances
</code></pre> ]]></content:encoded>
    </item>
    <item>
        <title><![CDATA[ Network-wide MikroTik RouterOS upgrade with Unimus ]]></title>
        <description><![CDATA[ Keeping the firmware/software of your networking equipment up to date is important. Learn how to do a network wide mass upgrade of MikroTik RouterOS with the network automation and config push features present in Unimus. ]]></description>
        <link>https://blog.unimus.net/network-wide-mikrotik-routeros-upgrade-with-unimus/</link>
        <guid isPermaLink="false">6155a969ed7c6b0001874b75</guid>
        <category><![CDATA[ Guide ]]></category>
        <dc:creator><![CDATA[ Tomas Kirnak ]]></dc:creator>
        <pubDate>Fri, 27 Apr 2018 12:57:00 +0000</pubDate>
        <media:content url="https://blog.unimus.net/content/images/2021/09/blog-mikrotik-1.png" medium="image"/>
        <content:encoded><![CDATA[ <p>Let's face it - doing a network-wide roll-out of a new version of software for your switches or routers is painful and takes a LOT of time. However, it is absolutely necessary to keep the firmware/software of your networking equipment up to date.</p><p>Recently, MikroTik has had a series of severe vulnerabilities. More details can be found <a href="https://forum.mikrotik.com/viewtopic.php?f=21&amp;t=133533">here</a> and <a href="https://forum.mikrotik.com/viewtopic.php?f=21&amp;t=132499">here</a>. But MikroTik is not alone in this. Cisco also recently had a bad ASA vulnerability (<a href="https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20180129-asa1">info here</a>), and just last year Ubiquiti had a massive exploit which brought down many networks around the world (<a href="https://www.symantec.com/connect/blogs/thousands-ubiquiti-airos-routers-hit-worm-attacks">more details</a>).</p><p>This article will focus on MikroTik - we will show you how you can do a network wide mass upgrade of RouterOS using Unimus, and the RouterOS Package Source feature. What's even better, doing the entire upgrade process (including setup of Unimus and RouterOS Package Source) can be done in under an hour.</p><h2 id="topology-of-our-test-network">Topology of our test network</h2><p>The topology for the network we will be testing on is simple:</p><ul><li>we have a Package Source (which is just a normal RouterOS device - for this demo a CHR)</li><li>then we have 5 MikroTiks we want to update RouterOS on</li><li>and finally we have our Unimus system</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/image.png" class="kg-image" alt="test network topology" loading="lazy" width="689" height="370" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/image.png 600w, https://blog.unimus.net/content/images/2022/02/image.png 689w"></figure><h2 id="configuring-the-routeros-package-source">Configuring the RouterOS Package Source</h2><p>The Package Source will be a MikroTik CHR for this demo. To make this CHR a package source for all our other MikroTiks, we first need to get the packages we are interested in. You can get packages from the <a href="https://mikrotik.com/download">MikroTik download page</a>. For this demo, I just downloaded the latest packages for ARM and x86.</p><p>After you get the packages, you can upload them to your package source router using SCP. I created a <code>ros-packages</code> directory, and put them there. </p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/ros-packages.png" class="kg-image" alt="MikroTik RouterOS Package Source packages" loading="lazy" width="647" height="410" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/ros-packages.png 600w, https://blog.unimus.net/content/images/2022/02/ros-packages.png 647w"></figure><p>And that's actually everything you need to do on our Package Source "server".</p><h2 id="configuring-unimus-to-talk-to-the-devices-we-want-to-upgrade">Configuring Unimus to talk to the devices we want to upgrade</h2><p>For this article, we assume that you have an empty, but fully deployed Unimus instance ready. If you don't, you can get Unimus from <a href="https://unimus.net/download.html">our downloads page</a>, and we have guides on our Wiki <a href="https://wiki.unimus.net/display/UNPUB/Installing+Unimus">here</a> and <a href="https://wiki.unimus.net/display/UNPUB/Initial+configuration">here</a> that will help you deploy Unimus.</p><p>First we need to make sure we have proper credentials in Unimus which we will be using to connect to our routers. You can check this in the <code>Credentials</code> screen. If the proper credentials are present, we need to add our devices into Unimus. We can use the address list import to make this happen.</p><p>You can go to <code>Basic import &gt; Address list import</code> and just paste in the list of IPs.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/adding-devices.gif" class="kg-image" alt="adding devices to Unimus" loading="lazy" width="1920" height="1040"></figure><p>Unimus should discover your devices, and you should see them properly in the <code>Devices</code> screen.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/added-devices.png" class="kg-image" alt="devices added in Unimus" loading="lazy" width="1920" height="1040" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/added-devices.png 600w, https://blog.unimus.net/content/images/size/w1000/2022/02/added-devices.png 1000w, https://blog.unimus.net/content/images/size/w1600/2022/02/added-devices.png 1600w, https://blog.unimus.net/content/images/2022/02/added-devices.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>Before we go any further, it's a good idea to make a backup of the configuration on your routers (in case the routers don't survive RouterOS upgrade for some reason). Unimus normally backs device up on a schedule (by default at 3AM every day), but since we just added our devices, lets make a manual backup. Simply select all your devices, and do <code>Backup now</code>.</p><h2 id="pushing-package-source-settings-to-our-network">Pushing Package Source settings to our network</h2><p>Before we can perform a mass-upgrade, we need to configure our entire network to use our package source. We will need to create a config push preset in Unimus. Go to the <code>Mass config push &gt; Add preset</code> screen. Give it a name and a description, and bind all of your MikroTiks to this preset (using <code>Select devices &gt; Not bound devices &gt; Bind</code>). Then save the config push preset.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/config-push-preset-create.gif" class="kg-image" alt="Adding push targets" loading="lazy" width="1920" height="1040"></figure><p><br>After you created your preset, you can open it (clicking on it in the <code>Mass config push</code> screen). The commands we will be pushing are these:</p><pre><code>/system upgrade upgrade-package-source
add address=10.9.21.235 user=admin
password</code></pre><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/image-1.png" class="kg-image" alt="Mass config push setup" loading="lazy" width="1000" height="542" srcset="https://blog.unimus.net/content/images/size/w600/2022/02/image-1.png 600w, https://blog.unimus.net/content/images/2022/02/image-1.png 1000w" sizes="(min-width: 720px) 720px"></figure><p>You will need to adjust the address and username/password here. The address will be the address of our Package Source, and username/password to use to log into it. Now we can save and run our config push (use the <code>Save</code> and <code>Run now</code> buttons).</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2022/02/pushing-package-source.gif" class="kg-image" alt="Running a mass config push" loading="lazy" width="1920" height="1040"></figure><p>You should see one output group when the push finishes. If there are any errors, you can check the error output groups to see on which devices the errors occurred.</p><h2 id="performing-a-network-wide-routeros-upgrade">Performing a network-wide RouterOS upgrade</h2><p>Now that the configuration to use our package source has been distributed to our network, we can perform a mass upgrade. Change the commands for your config push preset to:</p><pre><code>/system upgrade
refresh
:delay 5
print</code></pre><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-2.png" class="kg-image" alt="Check package source for upgrade" loading="lazy" width="1920" height="1040" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-2.png 600w, https://blog.unimus.net/content/images/size/w1000/2021/10/image-2.png 1000w, https://blog.unimus.net/content/images/size/w1600/2021/10/image-2.png 1600w, https://blog.unimus.net/content/images/2021/10/image-2.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>And now run the config push. This will cause all MikroTiks to check the package source for upgrade, and print out the available packages. <br>            <br>Please inspect the output groups of this command when running on your network. <br>You can have multiple output groups (due to different architectures that RouterOS supports), but you should have no errors, and all output groups should see newest RouterOS packages available.</p><p>If everything is in order, we can pull newest packages to all our routers. Change the commands for your config push preset to:</p><pre><code>/system upgrade
download [find version=6.42.1]</code></pre><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-3.png" class="kg-image" alt="Pulling new packages to routers" loading="lazy" width="1920" height="1040" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-3.png 600w, https://blog.unimus.net/content/images/size/w1000/2021/10/image-3.png 1000w, https://blog.unimus.net/content/images/size/w1600/2021/10/image-3.png 1600w, https://blog.unimus.net/content/images/2021/10/image-3.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>This will tell all our MikroTiks to pull our package from the package source. Please note that in bigger networks, this will take a while. You can always check when all of your MikroTiks are done with the commands:</p><pre><code>/system upgrade
:put [get [find version=6.42.1] status]</code></pre><p>Running this would give us different output groups based on if the router is already finished downloading the upgrade package, or if the download is still in progress.</p><figure class="kg-card kg-image-card"><img src="https://blog.unimus.net/content/images/2021/10/image-4.png" class="kg-image" alt="Checking upgrade status" loading="lazy" width="1920" height="1040" srcset="https://blog.unimus.net/content/images/size/w600/2021/10/image-4.png 600w, https://blog.unimus.net/content/images/size/w1000/2021/10/image-4.png 1000w, https://blog.unimus.net/content/images/size/w1600/2021/10/image-4.png 1600w, https://blog.unimus.net/content/images/2021/10/image-4.png 1920w" sizes="(min-width: 720px) 720px"></figure><p>After all your routers downloaded your package, they simply need a reboot to install it. Change the commands for your config push preset to:</p><pre><code>/system
reboot
y
</code></pre><p>And push that to the routers. They will reboot and should come back up with the latest RouterOS.</p><h2 id="final-words">Final words</h2><p>Please remember to also update RouterBOOT (the firmware / Bootloader of RouterBOARDs). </p><p>You can do this just by changing the push preset we created in this guide and pushing the appropriate commands to your RouterBOARDs.</p><p>If you are new to Unimus, check out <a href="https://unimus.net">our website</a> to learn more about us! <br>            <br>We are offering an unlimited trial license if you want to give Unimus a try (independently of our free tier)! <a href="https://promo.unimus.net/unlimited-trial/get.php">Click here to learn more</a>!</p> ]]></content:encoded>
    </item>

</channel>
</rss>
