Improvements, fixes
This commit is contained in:
parent
6c391e8c0a
commit
c17cace17c
3 changed files with 95 additions and 27 deletions
56
README.md
Normal file
56
README.md
Normal 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.
|
64
index.html
64
index.html
|
@ -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>
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue