diff --git a/htdocs/.gitignore b/htdocs/.gitignore index ed222e1..f94f394 100644 --- a/htdocs/.gitignore +++ b/htdocs/.gitignore @@ -1 +1,6 @@ lg_config.php +!lg_logo.gif +*.gif +*.jpg +*.jpeg +*.png diff --git a/htdocs/backend.php b/htdocs/backend.php new file mode 100644 index 0000000..1b1f9a7 --- /dev/null +++ b/htdocs/backend.php @@ -0,0 +1,122 @@ + 503, + "response" => json_decode(curl_error($ch), true) + ); + } + curl_close($ch); + if(! empty($status)){ + if($status < 400){ + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } else { + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } + } else { + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } + } elseif($action == "post"){ + $encodedPostData = http_build_query($postdata); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SSLVERSION, 6); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POSTFIELDS, $encodedPostData); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, "3"); + curl_setopt($ch, CURLOPT_TIMEOUT, "3"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_VERBOSE, true); + $data = curl_exec($ch); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if(curl_error($ch)){ + return array( + "status" => 503, + "response" => json_decode(curl_error($ch), true) + ); + } + curl_close($ch); + if(! empty($status)){ + if($status < 400){ + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } else { + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } + } else { + return array( + "status" => $status, + "response" => json_decode($data, true) + ); + } + } else { + return array( + "status" => 400, + "response" => null + ); + } +} + +function verifyToken($token){ + global $_CONFIG; + $url = $_CONFIG['recaptchaBackendVerifyURL']; + $headers = array( + "Content-Type: application/x-www-form-urlencoded" + ); + return curlCall($url, $headers, "post", [ + "secret" => $_CONFIG['recaptchaSiteSecret'], + "response" => $token + ]); +} + +$request = json_decode(file_get_contents('php://input'), true); +$token = ""; + +if(isset($request['token'])){ + $token = $request['token']; +} + +$response = verifyToken($token); + +if($response){ + echo json_encode($response, JSON_PRETTY_PRINT); +} else { + var_dump($response); +} + + + +?> \ No newline at end of file diff --git a/htdocs/forbidden.php b/htdocs/forbidden.php new file mode 100644 index 0000000..90a8346 --- /dev/null +++ b/htdocs/forbidden.php @@ -0,0 +1,17 @@ + + + + +

403 Forbidden

+

reCAPTCHA was unsuccessful or expired, redirecting...

+ + + + \ No newline at end of file diff --git a/htdocs/index.php b/htdocs/index.php index 3392394..4bd9a1b 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -59,6 +59,13 @@ 'company' => 'My Company Name', 'logo' => 'lg_logo.gif', 'color' => '#E48559', + 'recaptchaEnabled' => false, + 'recaptchaFrontendURL' => 'https://www.google.com/recaptcha/api.js?render=', + 'recaptchaBackendVerifyURL' => 'https://www.google.com/recaptcha/api/siteverify', + 'recaptchaSiteKey' => "", + 'recaptchaSiteSecret' => "", + 'showpeerinfo' => 'TRUE', + 'safesubnet' => '', 'sshauthtype' => 'password', 'sshprivatekeypath' => '', 'sshpwdcommand' => 'plink', @@ -81,6 +88,8 @@ $protocol = isset($_REQUEST['protocol']) ? trim($_REQUEST['protocol']) : FALSE; $command = isset($_REQUEST['command']) ? trim($_REQUEST['command']) : FALSE; $query = isset($_REQUEST['query']) ? trim($_REQUEST['query']) : FALSE; +$token = isset($_REQUEST['token']) ? trim($_REQUEST['token']) : FALSE; + if ($command != 'graph' OR !isset($_REQUEST['render']) OR !isset($_CONFIG['routers'][$router])) { @@ -127,10 +136,25 @@ function load() { } //--> + + + + + -
lg
+
lg

AS Looking Glass


@@ -279,10 +303,76 @@ function load() { ), ); +# Test shell_exec to make sure it's available and working +if(trim(shell_exec('echo lgshellexectest')) != 'lgshellexectest') +{ + print '

shell_exec not enabled

'; + exit; +} + +# Test popen to make sure it's available and working +$popentest = ""; +$fp = popen('echo lgpopentest','r'); +while(!feof($fp)) + { + // send the current file part to the browser + $popentest .= trim(fread($fp, 1024)); + // flush the content to the browser + flush(); + } +fclose($fp); +$popentest = trim($popentest); +if($popentest != "lgpopentest") +{ + print '

popen not working

'; + exit; +} + +# Check if client IP is within safe subnets + +$ipsafe = false; +if(isset($_CONFIG['safesubnets']) AND ! empty($_CONFIG['safesubnets'])) +{ + foreach($_CONFIG['safesubnets'] as $safesubnet) + { + if(! empty($safesubnet)) + { + if(checkIP($_SERVER['REMOTE_ADDR'], $safesubnet)) + { + $ipsafe = true; + } + } + } +} + +if($ipsafe){ + if($command == 'graph' AND isset($_REQUEST['render']) AND $_REQUEST['render'] == true) + { + # Don't display + } + else + { + echo '
Your public IP is ' . $_SERVER['REMOTE_ADDR'] . ' and is within a safe subnet, therefore permitting display of peer information, interface names, and command line.

'; + } + +} + if (isset($_CONFIG['routers'][$router]) AND isset($queries[$_CONFIG['routers'][$router]['os']][$protocol]) AND (isset($queries[$_CONFIG['routers'][$router]['os']][$protocol][$command]) OR $command == 'graph')) { + if(!$ipsafe AND ($_CONFIG['showpeerinfo'] == "FALSE" OR $_CONFIG['routers'][$router]['showpeerinfo'] == "FALSE")) + { + switch ($command) + { + case "summary": + { + print '

Summary not permitted.

'; + exit; + break; + } + } + } if ($protocol == 'ipv6' AND (!isset($_CONFIG['routers'][$router]['ipv6']) OR $_CONFIG['routers'][$router]['ipv6'] !== TRUE)) { @@ -303,7 +393,7 @@ function load() { $url = @parse_url($url); - $routing_instance = $_CONFIG['routers'][$router]['routing-instance']; + $routing_instance = isset($_CONFIG['routers']) AND isset($_CONFIG['routers'][$router]) AND isset($_CONFIG['routers'][$router]['routing-instance']) ? $_CONFIG['routers'][$router]['routing-instance'] : null; $os = $_CONFIG['routers'][$router]['os']; @@ -528,7 +618,12 @@ function load() { } else { - print '

Router: '.$_CONFIG['routers'][$router]['description'].'
Command: '.$exec.'

';
+			print '

Router: '.$_CONFIG['routers'][$router]['description'].'
'; + if($ipsafe OR (isset($_CONFIG['routers'][$router]) AND isset($_CONFIG['routers'][$router]['showpeerinfo']) AND $_CONFIG['routers'][$router]['showpeerinfo'] == "TRUE") OR (isset($_CONFIG['showpeerinfo']) AND $_CONFIG['showpeerinfo'] == "TRUE" AND !isset($_CONFIG['routers'][$router]['showpeerinfo']))){ + print 'Command: '.$exec.'

';
+			} else {
+				print '

';
+			}
 			flush();
 
 			process($url, $exec);
@@ -543,7 +638,7 @@ function load() {
 
 // HTML form
 ?>
-		
+
@@ -552,7 +647,9 @@ function load() { + + @@ -577,7 +674,12 @@ function load() { - + +
Type of QueryAdditional parametersNode

|

+ onclick="captcha(event, document.theForm.command.value, document.theForm.protocol.value, document.theForm.router.value, document.theForm.query.value)" + + > |

@@ -588,7 +690,7 @@ function load() { ?>
-

Information: RIPEstat he.net robtex.com PeeringDB

+

Information: RIPEstat RADb he.net robtex.com PeeringDB

Copyright ©

@@ -602,7 +704,7 @@ function load() { */ function process($url, $exec, $return_buffer = FALSE) { - global $_CONFIG, $router, $protocol, $os, $command, $query, $ros; + global $_SERVER, $_CONFIG, $router, $protocol, $os, $command, $query, $ros, $token; $sshauthtype = null; $buffer = ''; @@ -610,6 +712,19 @@ function process($url, $exec, $return_buffer = FALSE) $index = 0; $str_in = array(); + $urlProtocol = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http'; + $curlUrl = $urlProtocol . "://" . $_SERVER['SERVER_NAME'] . "/backend.php"; + + if($_CONFIG['recaptchaEnabled'] === true) { + $result = verifyToken($curlUrl, $token); + if($result == false) { + echo ""; + exit; + } + } + +// do processing for the $response + switch ($url['scheme']) { case 'ssh': @@ -707,7 +822,14 @@ function process($url, $exec, $return_buffer = FALSE) { $instance_list = parse_list($instance); - print 'BGP router identifier '.$instance_list['router-id'].', local AS number '.link_as($instance_list['as'])."\n"; + if(! empty($instance_list['confederation'])) + { + print 'BGP router identifier '.$instance_list['router-id'].', sub ' . link_as($instance_list['as'], true) . " within confederation " . link_as($instance_list['confederation'], true) . "\n"; + } + else + { + print 'BGP router identifier '.$instance_list['router-id'].', local '.link_as($instance_list['as'])."\n"; + } } } @@ -736,7 +858,7 @@ function process($url, $exec, $return_buffer = FALSE) { @shell_exec('echo n | '.$ssh_path.' '.implode(' ', $params).' screen-length 0 temporary'); }*/ - + if ($fp = @popen('echo n | '.$ssh_path.' '.implode(' ', $params).' '.$exec, 'r')) { while (!feof($fp)) @@ -970,15 +1092,61 @@ function process($url, $exec, $return_buffer = FALSE) flush(); } +function verifyToken($url, $token){ + $headers = array( + "Content-Type: application/json" + ); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["token" => $token])); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, "1"); + curl_setopt($ch, CURLOPT_TIMEOUT, "3"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_VERBOSE, true); + + $data = curl_exec($ch); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if(curl_error($ch)){ + #var_dump(curl_error($ch)); exit; + return false; + } + curl_close($ch); + + if($status > 300){ + #var_dump($status) . var_dump($data); exit; + return false; + } + if(!$data){ + #var_dump($status) . var_dump($data); exit; + return false; + } + $decodedData = json_decode($data, true); + if($decodedData['status'] !== 200){ + #var_dump($decodedData); exit; + return false; + } + if(!$decodedData['response']){ + #var_dump($decodedData); exit; + return false; + } + if($decodedData['response']['success'] !== true){ + #var_dump($decodedData); exit; + return false; + } + return true; +} + /** * Parse output contents */ function parse_out($output, $check = FALSE) { - global $_CONFIG, $router, $protocol, $os, $command, $exec, $query, $index, $lastip, $best, $count, $str_in, $ros; + global $_CONFIG, $router, $protocol, $os, $command, $exec, $query, $index, $lastip, $best, $count, $str_in, $ros, $ipsafe; $output = str_replace("\r\n", "\n", $output); - + // MikroTik if (preg_match("/^\/(ip|ipv6) route print detail/i", $exec) AND $os == 'mikrotik') { @@ -1002,13 +1170,33 @@ function parse_out($output, $check = FALSE) { $data_exp = explode(' ', trim($summary_part), 3); - $summary_part = preg_replace_callback( - "/bgp-as-path=\"([^\"]+)\"/x", - function ($matches) { - return stripslashes('bgp-as-path=\"'.link_as($matches[1]).'\"'); - }, - $summary_part - ); + if(!$ipsafe){ + $summary_part = preg_replace("/\svia\s\s?\S+/x", "", $summary_part); + } + $matches = null; + preg_match('/bgp-as-path\=\"([^\"]+)\"/', $summary_part, $matches); + if(! empty($matches[1])){ + $aspathmatches = array(); + $aspathOriginal = $matches[0]; + $aspathOriginal = str_replace('"', '\"', $aspathOriginal); + $summary_part = str_replace($matches[0], $aspathOriginal, $summary_part); + $aspath = $aspathOriginal; + preg_match_all("/((?:\d+)+)/", $aspath, $matches); + $asns = null; + $asns = $matches[1]; + $matchCount = 0; + if(! empty($asns)){ + foreach($matches[1] as $m){ + if(empty($aspathmatches[$m])){ + $aspathmatches[$m] = link_as($m); + } + } + if(!empty($aspathmatches)){ + $aspath = str_replace(array_keys($aspathmatches), array_values($aspathmatches), $aspath); + $summary_part = str_replace($aspathOriginal, stripslashes($aspath), $summary_part); + } + } + } if (strpos($data_exp[1], 'A') !== FALSE) { @@ -1175,7 +1363,7 @@ function ($matches) { return 'traceroute to '.$query.' ('.get_ptr($query).'), 64 hops max, 60 byte packets'."\n"; } - + if ($index > 0) { $exp = explode(' ', preg_replace('/[\s\t]+/', ' ', trim($output))); @@ -1195,11 +1383,12 @@ function ($matches) { } else { - $radb = get_radb($exp[1]); - + #$radb = get_radb($exp[1]); + $asn = get_as($exp[1], "15835"); $new_exp[1] = get_ptr($exp[1]); $new_exp[2] = '('.$exp[1].')'; - $new_exp[3] = '['.(isset($radb['origin']) ? 'AS '.link_as($radb['origin']) : '').']'; + #$new_exp[3] = '['.(isset($radb['origin']) ? 'AS '.link_as($radb['origin']) : '').']'; + $new_exp[3] = $asn; $new_exp[4] = $exp[5].'ms'; $new_exp[5] = $exp[6].'ms'; $new_exp[6] = $exp[7].'ms'; @@ -2068,7 +2257,7 @@ function ($matches) { if (preg_match("/^trace/", $exec)) { $output = preg_replace("/\[AS0\]\s(.*)/", "\\1", $output); - + // IPv4 $output = preg_replace_callback( "/(\[AS)(\d+)(\])\s(.*)(\))(.*)/", @@ -2404,14 +2593,44 @@ function link_community($line) /** * Link to AS whois */ -function link_as($line, $word = FALSE) +function link_as($line, $word = FALSE, $type = null) { global $_CONFIG; - //print_r($line); - - return preg_replace("/(?:AS)?([\d]+)/is", - "".($word ? 'AS' : '')."\\1", $line); + $asn = intval(preg_replace("/(?:AS)?([\d]+)/is", + "$1", $line)); + + $url = null; + $publicasn = false; + if(($asn >= 1 AND $asn <= 23455) OR ($asn >= 23457 AND $asn <= 64495) OR ($asn >= 131072 AND $asn <= 4199999999)){ + $publicasn = true; + } + + if($word) + { + $asnword = "AS" . $asn; + } + else + { + $asnword = $asn; + } + + if($publicasn AND $type == "url") + { + return htmlspecialchars($_CONFIG['aswhois']) . "AS" . $asn; + } + elseif($publicasn) + { + return '' . $asnword . ''; + } + elseif($type == "url") + { + return null; + } + else + { + return $asnword; + } } function get_as($ip, $original_as) @@ -2570,10 +2789,9 @@ function get_path_graph($router, $query, $as_pathes, $as_best_path, $format = 's $color = isset($as_peer_list[$as_id]) ? ($as_peer_list[$as_id] ? '#CCFFCC' : '#CCCCFF') : 'white'; $asinfo = get_asinfo($as_id); - $graph->addNode($as_id, array ( - 'URL' => $_CONFIG['aswhois'].$as_id, + 'URL' => link_as($as_id, false, "url"), 'target' => '_blank', 'label' => isset($asinfo['description']) ? $as_id."\n".$asinfo['description'] : $as_id, 'style' => 'filled', @@ -2718,16 +2936,23 @@ function get_asinfo($request) } $segments = array_map('trim', explode('|', $dns[0]['txt'], 5)); - + if (sizeof($segments) != 5) { return FALSE; } - - list($segments[4], $segments[5]) = explode(' ', $segments[4], 2); - + if(strpos(explode(',', $segments[4], 2)[0], " ")) + { + list($segments[4], $segments[5]) = explode(' ', $segments[4], 2); + } + else + { + $segments[5] = $segments[4]; + $segments[4] = explode(',', $segments[4], 2)[0]; + } + $segments[5] = str_replace('_', '"', $segments[5]); - + return array ( 'asn' => $segments[0], @@ -2865,6 +3090,31 @@ function group_routers($array) return $return; } +function checkIP($ip, $cidr) +{ + if (strpos($cidr, "/") !== false) + { + list($net, $mask) = explode("/", $cidr); + + $ip_net = ip2long ($net); + $ip_mask = ~((1 << (32 - $mask)) - 1); + + $ip_ip = ip2long ($ip); + + $ip_ip_net = $ip_ip & $ip_mask; + + return ($ip_ip_net == $ip_net); + } + elseif (filter_var(trim($cidr), FILTER_VALIDATE_IP) == true AND $ip === $cidr) + { + return true; + } + else + { + return false; + } +} + // ------------------------------------------------------------------------ /** diff --git a/htdocs/lg_config.php.example b/htdocs/lg_config.php.example index 40afe22..7896a03 100644 --- a/htdocs/lg_config.php.example +++ b/htdocs/lg_config.php.example @@ -24,6 +24,43 @@ $_CONFIG['logo'] = 'lg_logo.gif'; */ $_CONFIG['color'] = '#E48559'; +/* + * Enable reCAPTCHA v3 + */ +$_CONFIG['recaptchaEnabled'] = false; + +/* + * reCAPTCHA v3 Frontend URL + */ +$_CONFIG['recaptchaFrontendURL'] = 'https://www.google.com/recaptcha/api.js?render='; + +/* + * reCAPTCHA v3 Backend Verify URL + */ +$_CONFIG['recaptchaBackendVerifyURL'] = 'https://www.google.com/recaptcha/api/siteverify'; + +/* + * reCAPTCHA v3 Site Key + */ +$_CONFIG['recaptchaSiteKey'] = ''; + +/* + * reCAPTCHA v3 Site Secret + */ +$_CONFIG['recaptchaSiteSecret'] = ''; + +/* + * Should the LG show BGP peer information? + */ +$_CONFIG['showpeerinfo'] = 'TRUE'; + +/* + * If 'showpeerinfo' above is TRUE, and you would like to show BGP peer information to visitors from a specific IPv4 address or subnet, add the IPv4 address or subnet (in CIDR format) to the array below + */ +$_CONFIG['safesubnets'] = array( + '' +); + /* * SSH authentication type (`password` or `privatekey`) */ @@ -57,12 +94,12 @@ $_CONFIG['ssh'] = '/usr/bin/sshpass'; /* * URL address of the IP whois service */ -$_CONFIG['ipwhois'] = 'http://noc.hsdn.org/whois/'; +$_CONFIG['ipwhois'] = 'https://www.radb.net/query?keywords='; /* * URL address of the AS whois service */ -$_CONFIG['aswhois'] = 'http://noc.hsdn.org/aswhois/'; +$_CONFIG['aswhois'] = 'https://www.radb.net/query?keywords='; /** * Router nodes @@ -74,6 +111,7 @@ $_CONFIG['aswhois'] = 'http://noc.hsdn.org/aswhois/'; * pingtraceurl - URL address for ping and traceroute tools (or FALSE) * description - Node description * group - Node group name (of FALSE) + * showpeerinfo - Should the LG show peer information? (TRUE / FALSE) * ipv6 - Node supports IPv6 (TRUE/FALSE) * os - Node OS (ios, mikrotik, quagga, junos) */ @@ -85,6 +123,7 @@ $_CONFIG['routers'] = array 'pingtraceurl' => FALSE, 'description' => 'Example Router 1', 'group' => 'AS12345', + 'showpeerinfo' => TRUE, 'ipv6' => TRUE, 'os' => 'ios', ), @@ -95,6 +134,7 @@ $_CONFIG['routers'] = array 'pingtraceurl' => FALSE, 'description' => 'Example Router 2', 'group' => 'AS12345', + 'showpeerinfo' => FALSE, 'ipv6' => TRUE, 'os' => 'ios', ), @@ -105,6 +145,7 @@ $_CONFIG['routers'] = array 'pingtraceurl' => FALSE, 'description' => 'Example Router 3', 'group' => 'AS12345', + 'showpeerinfo' => TRUE, 'ipv6' => TRUE, 'os' => 'mikrotik', ), @@ -116,6 +157,7 @@ $_CONFIG['routers'] = array 'pingtraceurl' => FALSE, 'description' => 'Example Router 4', 'group' => 'AS12345', + 'showpeerinfo' => FALSE, 'ipv6' => TRUE, 'os' => 'mikrotik', ), diff --git a/keys/.gitignore b/keys/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/keys/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file