Improvements, fixes

This commit is contained in:
Łukasz Moskała 2024-01-24 18:39:01 +01:00
parent 6c391e8c0a
commit c17cace17c
3 changed files with 95 additions and 27 deletions

56
README.md Normal file
View File

@ -0,0 +1,56 @@
# MinIPAM
A simple tool that scans specified subnet(s) and has WEB UI to easily see which IPs are available
I wanted something like space map from PHPIPAM but with less clicking to get to see what I want to see.
Web UI is just a bunch of buttons (one for subnet) and table, representing hosts in said subnet. Hosts that were online
during last scan are marked as green, hosts which were online at least once but were offline during last scan are marked
red, and hosts that were never seen online are marked gray. Also for every online host during a scan RevDNS lookup
is made to attempt to get its hostname. Web UI does not allow editing presented data in any way, which is also why
there is no authorization mechanism built in.
Scanning is kind of slow, but I prefer to keep it that way, since I don't need it to be fast, and I don't want to waste
my compute resources. It doesn't matter for me if my scan completes within 1 minute or within 30 minutes.
TODO: screenshot
# Configuration reference
```yaml
bind_address: "0.0.0.0:8443"
scan_subnets:
- 192.168.145.0/24
- 10.250.100.64.0/27
delay_between_scans: 15m
persistence_location: "data.json"
exclude_special_addresses: true
use_tls: false
tls_key_file: "key.pem"
tls_cert_file: "fullchain.pem"
```
`delay_between_scans` specifies time to wait between scans. For example 15m means that
scan starts at 17:43, takes 2 minutes, finishes at 17:45, next scan will start at 18:00
`exclude_special_addresses` excludes network and broadcast addresses from scanning and results.
With subnets in example above, those would be `192.168.145.0`,`192.168.145.255`,`10.250.100.64`,`10.250.100.95`.
`persistence_location` indicates where data should be stored. It needs to be writable.
# Requirements
Scanning is done by running:
```go
exec.Command("ping", "-n", "-W", "0.2", "-c", "1", addr)
```
So you need OS that has `ping` command with those options.
Tested with GNU coreutils on archlinux. Also works on FreeBSD, however, FreeBSD's ping will wait 1 second instead of
0.2 seconds, making scanning networks with a lot of free IPs slower.
While we're at it, hosts are expected to reply within 0.2 seconds (except on FreeBSD, where ping is not accepting
fractional timeout values). This is currently hard-coded.
# Authentication
Since I don't think that any data presented by this tool would be considered sensitive, I didn't implement any
authentication mechanism. If for some reason you decided that you NEED one, consider following options:
- Use firewall to only allow trusted networks to access this tool
- If this tool happens to be running on your workstation, bind it to localhost only
- Bind this tool to localhost only and use SSH port forwarding
- Set up reverse proxy with authorization, then bind this tool to localhost only.

View File

@ -5,12 +5,19 @@
<style>
.status_online {
background-color: lawngreen;
color: black;
}
.status_offline {
background-color: orangered;
color: black;
}
.status_free {
background-color: dimgray;
color: black;
}
body {
background-color: black;
color: white;
}
</style>
<script>
@ -53,16 +60,20 @@
alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
} else { // show the result
data = JSON.parse(xhr.responseText)
let last=""
for (const subnet in data.Subnets) {
document.getElementById("subnetscontainer").innerHTML+="<button onClick=viewSubnet(this)>"+subnet+"</button>"
document.getElementById("subnetscontainer").innerHTML+="<button onClick=viewSubnet(this.innerText)>"+subnet+"</button>"
last=subnet
}
if(last != "") {
viewSubnet(last)
}
}
};
xhr.send();
}
function viewSubnet(elem) {
subnet = elem.innerText
function viewSubnet(subnet) {
usedAddresses = data.Subnets[subnet].used_addresses
totalAddresses = data.Subnets[subnet].total_addresses
@ -75,33 +86,33 @@
let tableHTML = '<table border="1"><thead><tr><th>IP Address</th><th>RevDNS</th><th>First seen</th><th>Last seen</th></tr></thead><tbody>';
for (const subnet in data.Subnets) {
const hosts = data.Subnets[subnet].hosts;
// Iterate over IPs in the subnet
data.Subnets[subnet].host_list.forEach(ip => {
const hostDetails = hosts[ip] || {}; // Get host details if available
const hosts = data.Subnets[subnet].hosts;
// Determine the status based on the presence in hosts and online status
let revdns="";
let ls=""
let fs=""
let status;
if (ip in hosts) {
status = hostDetails.online ? 'online' : 'offline';
revdns = hostDetails.rev_dns;
ls = formatHumanFriendlyTime(hostDetails.last_seen)
fs = formatHumanFriendlyTime(hostDetails.first_seen)
// Iterate over IPs in the subnet
data.Subnets[subnet].host_list.forEach(ip => {
const hostDetails = hosts[ip] || {}; // Get host details if available
} else {
status = 'free';
}
// Add a row to the HTML table
tableHTML += `<tr class="status_${status}"><td>${ip}</td><td>${revdns}</td><td>${fs}</td><td>${ls}</td></tr>`;
});
}
// Determine the status based on the presence in hosts and online status
let revdns="";
let ls=""
let fs=""
let status;
if (ip in hosts) {
status = hostDetails.online ? 'online' : 'offline';
revdns = hostDetails.rev_dns;
ls = formatHumanFriendlyTime(hostDetails.last_seen)
fs = formatHumanFriendlyTime(hostDetails.first_seen)
} else {
status = 'free';
}
// Add a row to the HTML table
tableHTML += `<tr class="status_${status}"><td>${ip}</td><td>${revdns}</td><td>${fs}</td><td>${ls}</td></tr>`;
});
tableHTML += '</tbody></table>';
document.getElementById("subnetview").innerHTML=""
document.getElementById("subnetview").innerHTML=tableHTML
}
@ -112,7 +123,8 @@
<div id="subnetscontainer"></div>
<hr>
<p>Subnet <span id="subnetCidrField"></span></p>
<p>Space usage <span id="subnetUsedField"></span><progress id="subnetUsageBar"></progress></p>
<p>Space usage <span id="subnetUsedField"></span></p>
<progress id="subnetUsageBar"></progress><br>
<div id="subnetview"></div>
</body>
</html>

View File

@ -37,7 +37,7 @@ type SubnetT struct {
Hosts map[string]HostT `json:"hosts"`
TotalAddresses int `json:"total_addresses"`
UsedAddresses int `json:"used_addresses"`
HostList []string `json:"host_list"`
HostList []string `json:"host_list"` //I _REALLY_ didn't want to calculate this in javascript
}
type PersistenceT struct {