Skip to content

Instantly share code, notes, and snippets.

@ravnx
Created September 11, 2025 15:20
Show Gist options
  • Select an option

  • Save ravnx/e8e39c70e65cc0d3bc86c131d599b0d0 to your computer and use it in GitHub Desktop.

Select an option

Save ravnx/e8e39c70e65cc0d3bc86c131d599b0d0 to your computer and use it in GitHub Desktop.
Mojolicious Service to Check if a host has a port open
#!/usr/bin/perl -w
# This mojo will just check for an open port.
# I use this with nagios to alert me if a firewalled webserver is open to the public internet.
# I run this in a nano cloud instance off network. I use nginx to serve it, and hypnotoad to run it.
# Nagios calls it like "http://foo.app.com/check?host=1.2.3.4&port=443&key=abc123"
#
# You can setup a nagios command like this:
# define command {
# command_name check_api_port_open
# command_line /usr/bin/curl -s "https://foo.app.com/check?host=$HOSTADDRESS$&port=443" | /usr/bin/grep -q '"status":"open"' && echo "Port 443 is OPEN" && exit 2 || echo "Port 443 is closed" && exit 0
#}
#
# Last update: 2025-09-11 19:54:20 UTC
use strict;
use Mojolicious::Lite -signatures;
use IO::Socket::INET;
# Logging setup
app->log( Mojo::Log->new( path => '/var/log/canary.log', level => 'warn' ) );
# Server Setup
app->config(hypnotoad => {listen => ['http://*:8090'], hypnotoad => {proxy => 1}, });
# ---------------------------------------------------------------------- #
# Ping route for monitoring this service being up #
# ---------------------------------------------------------------------- #
# I like to use updown.io to monitor service monitors, i like to get a pong reply
get '/ping' => sub {
my $self = shift;
$self->render(text => 'pong');
};
# If you want to save even more bandwidth, you can just render 200 ok.
#head '/ping' => sub {
# my $c = shift;
# $c->rendered(200);
#};
# ---------------------------------------------------------------------- #
# render main screen #
# ---------------------------------------------------------------------- #
# Just a default text render for bots and stuff
any '/' => sub {
my $self = shift;
$self->render(text => 'chirp');
};
# ---------------------------------------------------------------------- #
# render main screen #
# ---------------------------------------------------------------------- #
# Lets keep bots from crawling this site
any '/robots.txt' => sub {
my $self = shift;
$self->render(
text => "User-agent: *\nDisallow: /",
format => 'txt'
);
};
# ---------------------------------------------------------------------- #
# Allow GET /check?host=1.2.3.4&port=443 #
# ---------------------------------------------------------------------- #
get '/check' => sub ($c) {
# Check for token, die if there isnt one, you can change this to a static if you dont want to mess with ENV vars.
return $c->render(text => 'unauthorized', status => 403)
unless $c->param('token') eq $ENV{CANARY_TOKEN};
# it might be smart to limit the hosts to a list of allowed hosts, but I've got mine firewalled to
# only allow my nagios server to make requests here, so im leaving it open.
my $host = $c->param('host') // return $c->render(json => { error => 'Missing host' }, status => 400);
my $port = 443; # I'm forcing 443 only, but you can use any port like this: $c->param('port') // 443;
my $sock = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
Timeout => 2
);
if ($sock) {
close($sock);
return $c->render(json => { host => $host, port => $port, status => 'open' });
} else {
return $c->render(json => { host => $host, port => $port, status => 'closed' });
}
};
app->start;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment