push: code changes x2
This commit is contained in:
@@ -16,7 +16,7 @@ RUN bun run build
|
|||||||
FROM --platform=$TARGETARCH registry.fedoraproject.org/fedora:latest
|
FROM --platform=$TARGETARCH registry.fedoraproject.org/fedora:latest
|
||||||
RUN dnf -y update && dnf install -y python3.13-devel @development-tools gcc-c++ \
|
RUN dnf -y update && dnf install -y python3.13-devel @development-tools gcc-c++ \
|
||||||
libnetfilter_queue-devel libnfnetlink-devel libmnl-devel libcap-ng-utils nftables \
|
libnetfilter_queue-devel libnfnetlink-devel libmnl-devel libcap-ng-utils nftables \
|
||||||
vectorscan-devel libtins-devel python3-nftables libpcap-devel boost-devel uv redis
|
vectorscan-devel libtins-devel python3-nftables libpcap-devel boost-devel uv
|
||||||
|
|
||||||
RUN mkdir -p /execute/modules
|
RUN mkdir -p /execute/modules
|
||||||
WORKDIR /execute
|
WORKDIR /execute
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ if __name__ == '__main__':
|
|||||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"app:app",
|
"app:app",
|
||||||
host="::" if DEBUG else None,
|
host="0.0.0.0" if DEBUG else None,
|
||||||
port=FIREGEX_PORT,
|
port=FIREGEX_PORT,
|
||||||
reload=DEBUG and not NORELOAD,
|
reload=DEBUG and not NORELOAD,
|
||||||
access_log=True,
|
access_log=True,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <tins/tcp_ip/stream_identifier.h>
|
#include <tins/tcp_ip/stream_identifier.h>
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
#include <tins/tins.h>
|
#include <tins/tins.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -17,7 +18,17 @@ enum class FilterAction{ DROP, ACCEPT, MANGLE, NOACTION };
|
|||||||
enum class L4Proto { TCP, UDP, RAW };
|
enum class L4Proto { TCP, UDP, RAW };
|
||||||
typedef Tins::TCPIP::StreamIdentifier stream_id;
|
typedef Tins::TCPIP::StreamIdentifier stream_id;
|
||||||
|
|
||||||
//TODO DUBBIO: I PACCHETTI INVIATI A PYTHON SONO GIA' FIXATI?
|
struct tcp_ack_seq_ctx{
|
||||||
|
int64_t in = 0;
|
||||||
|
int64_t out = 0;
|
||||||
|
tcp_ack_seq_ctx(){}
|
||||||
|
void reset(){
|
||||||
|
in = 0;
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef map<stream_id, tcp_ack_seq_ctx*> tcp_ack_map;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class PktRequest {
|
class PktRequest {
|
||||||
@@ -28,6 +39,7 @@ class PktRequest {
|
|||||||
uint32_t packet_id;
|
uint32_t packet_id;
|
||||||
size_t _original_size;
|
size_t _original_size;
|
||||||
size_t _data_original_size;
|
size_t _data_original_size;
|
||||||
|
size_t _header_size;
|
||||||
bool need_tcp_fixing = false;
|
bool need_tcp_fixing = false;
|
||||||
public:
|
public:
|
||||||
bool is_ipv6;
|
bool is_ipv6;
|
||||||
@@ -39,18 +51,15 @@ class PktRequest {
|
|||||||
bool is_input;
|
bool is_input;
|
||||||
|
|
||||||
string packet;
|
string packet;
|
||||||
char* data;
|
|
||||||
size_t data_size;
|
|
||||||
stream_id sid;
|
stream_id sid;
|
||||||
|
|
||||||
int64_t* tcp_in_offset = nullptr;
|
tcp_ack_seq_ctx* ack_seq_offset = nullptr;
|
||||||
int64_t* tcp_out_offset = nullptr;
|
|
||||||
|
|
||||||
T* ctx;
|
T* ctx = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static size_t inner_data_size(Tins::PDU* pdu){
|
static inline size_t inner_data_size(Tins::PDU* pdu){
|
||||||
if (pdu == nullptr){
|
if (pdu == nullptr){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -61,9 +70,9 @@ class PktRequest {
|
|||||||
return inner->size();
|
return inner->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void fetch_data_size(Tins::PDU* pdu){
|
inline void __internal_fetch_data_size(Tins::PDU* pdu){
|
||||||
data_size = inner_data_size(pdu);
|
_data_original_size = inner_data_size(pdu);
|
||||||
_data_original_size = data_size;
|
_header_size = _original_size - _data_original_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
L4Proto fill_l4_info(){
|
L4Proto fill_l4_info(){
|
||||||
@@ -72,14 +81,14 @@ class PktRequest {
|
|||||||
if (tcp == nullptr){
|
if (tcp == nullptr){
|
||||||
udp = ipv6->find_pdu<Tins::UDP>();
|
udp = ipv6->find_pdu<Tins::UDP>();
|
||||||
if (udp == nullptr){
|
if (udp == nullptr){
|
||||||
fetch_data_size(ipv6);
|
__internal_fetch_data_size(ipv6);
|
||||||
return L4Proto::RAW;
|
return L4Proto::RAW;
|
||||||
}else{
|
}else{
|
||||||
fetch_data_size(udp);
|
__internal_fetch_data_size(udp);
|
||||||
return L4Proto::UDP;
|
return L4Proto::UDP;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
fetch_data_size(tcp);
|
__internal_fetch_data_size(tcp);
|
||||||
return L4Proto::TCP;
|
return L4Proto::TCP;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@@ -87,73 +96,23 @@ class PktRequest {
|
|||||||
if (tcp == nullptr){
|
if (tcp == nullptr){
|
||||||
udp = ipv4->find_pdu<Tins::UDP>();
|
udp = ipv4->find_pdu<Tins::UDP>();
|
||||||
if (udp == nullptr){
|
if (udp == nullptr){
|
||||||
fetch_data_size(ipv4);
|
__internal_fetch_data_size(ipv4);
|
||||||
return L4Proto::RAW;
|
return L4Proto::RAW;
|
||||||
}else{
|
}else{
|
||||||
fetch_data_size(udp);
|
__internal_fetch_data_size(udp);
|
||||||
return L4Proto::UDP;
|
return L4Proto::UDP;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
fetch_data_size(tcp);
|
__internal_fetch_data_size(tcp);
|
||||||
return L4Proto::TCP;
|
return L4Proto::TCP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool need_tcp_fix(){
|
bool need_tcp_fix(){
|
||||||
return (tcp_in_offset != nullptr && *tcp_in_offset != 0) || (tcp_out_offset != nullptr && *tcp_out_offset != 0);
|
return tcp && ack_seq_offset != nullptr && (ack_seq_offset->in != 0 || ack_seq_offset->out != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tins::PDU::serialization_type reserialize_raw_data(const uint8_t* data, const size_t& data_size){
|
|
||||||
if (is_ipv6){
|
|
||||||
Tins::IPv6 ipv6_new = Tins::IPv6(data, data_size);
|
|
||||||
if (tcp){
|
|
||||||
Tins::TCP* tcp_new = ipv6_new.find_pdu<Tins::TCP>();
|
|
||||||
}
|
|
||||||
return ipv6_new.serialize();
|
|
||||||
}else{
|
|
||||||
Tins::IP ipv4_new = Tins::IP(data, data_size);
|
|
||||||
if (tcp){
|
|
||||||
Tins::TCP* tcp_new = ipv4_new.find_pdu<Tins::TCP>();
|
|
||||||
}
|
|
||||||
return ipv4_new.serialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _fix_ack_seq_tcp(Tins::TCP* this_tcp){
|
|
||||||
need_tcp_fixing = need_tcp_fix();
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (need_tcp_fixing){
|
|
||||||
cerr << "[DEBUG] Fixing ack_seq with offsets " << *tcp_in_offset << " " << *tcp_out_offset << endl;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(this_tcp == nullptr){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (is_input){
|
|
||||||
if (tcp_in_offset != nullptr){
|
|
||||||
this_tcp->seq(this_tcp->seq() + *tcp_in_offset);
|
|
||||||
}
|
|
||||||
if (tcp_out_offset != nullptr){
|
|
||||||
this_tcp->ack_seq(this_tcp->ack_seq() - *tcp_out_offset);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (tcp_in_offset != nullptr){
|
|
||||||
this_tcp->ack_seq(this_tcp->ack_seq() - *tcp_in_offset);
|
|
||||||
}
|
|
||||||
if (tcp_out_offset != nullptr){
|
|
||||||
this_tcp->seq(this_tcp->seq() + *tcp_out_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (need_tcp_fixing){
|
|
||||||
size_t new_size = inner_data_size(this_tcp);
|
|
||||||
cerr << "[DEBUG] FIXED PKT " << (is_input?"-> IN ":"<- OUT") << " [SEQ: " << this_tcp->seq() << "] \t[ACK: " << this_tcp->ack_seq() << "] \t[SIZE: " << new_size << "]" << endl;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PktRequest(const char* payload, size_t plen, T* ctx, mnl_socket* nl, nfgenmsg *nfg, nfqnl_msg_packet_hdr *ph, bool is_input):
|
PktRequest(const char* payload, size_t plen, T* ctx, mnl_socket* nl, nfgenmsg *nfg, nfqnl_msg_packet_hdr *ph, bool is_input):
|
||||||
@@ -168,22 +127,129 @@ class PktRequest {
|
|||||||
sid = stream_id::make_identifier(*ipv6);
|
sid = stream_id::make_identifier(*ipv6);
|
||||||
_original_size = ipv6->size();
|
_original_size = ipv6->size();
|
||||||
}else{
|
}else{
|
||||||
ipv4 = new Tins::IP((uint8_t*)packet.data(), plen);
|
ipv4 = new Tins::IP((uint8_t*)packet.c_str(), plen);
|
||||||
sid = stream_id::make_identifier(*ipv4);
|
sid = stream_id::make_identifier(*ipv4);
|
||||||
_original_size = ipv4->size();
|
_original_size = ipv4->size();
|
||||||
}
|
}
|
||||||
l4_proto = fill_l4_info();
|
l4_proto = fill_l4_info();
|
||||||
data = packet.data()+(plen-data_size);
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (tcp){
|
if (tcp){
|
||||||
cerr << "[DEBUG] NEW_PACKET " << (is_input?"-> IN ":"<- OUT") << " [SEQ: " << tcp->seq() << "] \t[ACK: " << tcp->ack_seq() << "] \t[SIZE: " << data_size << "]" << endl;
|
cerr << "[DEBUG] NEW_PACKET " << (is_input?"-> IN ":"<- OUT") << " [SEQ: " << tcp->seq() << "] \t[ACK: " << tcp->ack_seq() << "] \t[SIZE: " << data_size() << "]" << endl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_tcp_ack(){
|
inline size_t header_size(){
|
||||||
|
return _header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data(){
|
||||||
|
return packet.data()+_header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t data_size(){
|
||||||
|
return packet.size()-_header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t data_original_size(){
|
||||||
|
return _data_original_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserialize(){
|
||||||
|
auto data = serialize();
|
||||||
|
packet.resize(data.size());
|
||||||
|
memcpy(packet.data(), data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_data(const char* data, const size_t& data_size){
|
||||||
|
auto bef_raw = before_raw_pdu_ptr();
|
||||||
|
if (bef_raw){
|
||||||
|
delete before_raw_pdu_ptr()->release_inner_pdu();
|
||||||
|
if (data_size > 0){
|
||||||
|
before_raw_pdu_ptr() /= move(Tins::RawPDU((uint8_t*)data, data_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tins::PDU* before_raw_pdu_ptr(){
|
||||||
if (tcp){
|
if (tcp){
|
||||||
_fix_ack_seq_tcp(tcp);
|
return tcp;
|
||||||
|
}else if (udp){
|
||||||
|
return udp;
|
||||||
|
}else if (ipv4){
|
||||||
|
return ipv4;
|
||||||
|
}else if (ipv6){
|
||||||
|
return ipv6;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_packet(const char* data, size_t data_size){
|
||||||
|
// Parsing only the header with libtins
|
||||||
|
Tins::PDU *data_pdu = nullptr;
|
||||||
|
size_t total_size;
|
||||||
|
if (is_ipv6){
|
||||||
|
delete ipv6;
|
||||||
|
ipv6 = new Tins::IPv6((uint8_t*)data, data_size);
|
||||||
|
if (tcp){
|
||||||
|
tcp = ipv6->find_pdu<Tins::TCP>();
|
||||||
|
data_pdu = tcp;
|
||||||
|
}else if (udp){
|
||||||
|
udp = ipv6->find_pdu<Tins::UDP>();
|
||||||
|
data_pdu = udp;
|
||||||
|
}else{
|
||||||
|
data_pdu = ipv6;
|
||||||
|
}
|
||||||
|
total_size = ipv6->size();
|
||||||
|
}else{
|
||||||
|
delete ipv4;
|
||||||
|
ipv4 = new Tins::IP((uint8_t*)data, data_size);
|
||||||
|
if (tcp){
|
||||||
|
tcp = ipv4->find_pdu<Tins::TCP>();
|
||||||
|
data_pdu = tcp;
|
||||||
|
}else if(udp){
|
||||||
|
udp = ipv4->find_pdu<Tins::UDP>();
|
||||||
|
data_pdu = udp;
|
||||||
|
}else{
|
||||||
|
data_pdu = ipv4;
|
||||||
|
}
|
||||||
|
total_size = ipv4->size();
|
||||||
|
}
|
||||||
|
_header_size = total_size - inner_data_size(data_pdu);
|
||||||
|
// Libtins can skip data if the lenght is changed to a bigger len (due to ip header total lenght), so we need to specify the data section manually
|
||||||
|
set_data(data+_header_size, data_size-_header_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fix_tcp_ack(){
|
||||||
|
need_tcp_fixing = need_tcp_fix();
|
||||||
|
if(!need_tcp_fixing){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
cerr << "[DEBUG] Fixing ack_seq with offsets " << ((int32_t)ack_seq_offset->in) << " " << ((int32_t)ack_seq_offset->out) << endl;
|
||||||
|
#endif
|
||||||
|
if (is_input){
|
||||||
|
tcp->seq(tcp->seq() + ack_seq_offset->in);
|
||||||
|
tcp->ack_seq(tcp->ack_seq() - ack_seq_offset->out);
|
||||||
|
}else{
|
||||||
|
tcp->ack_seq(tcp->ack_seq() - ack_seq_offset->in);
|
||||||
|
tcp->seq(tcp->seq() + ack_seq_offset->out);
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
size_t new_size = inner_data_size(tcp);
|
||||||
|
cerr << "[DEBUG] FIXED PKT " << (is_input?"-> IN ":"<- OUT") << " [SEQ: " << tcp->seq() << "] \t[ACK: " << tcp->ack_seq() << "] \t[SIZE: " << new_size << "]" << endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void fix_data_payload(){
|
||||||
|
//Stream follower move the payload data, so we need to reinizialize RawPDU
|
||||||
|
auto bef_raw = before_raw_pdu_ptr();
|
||||||
|
if (bef_raw){
|
||||||
|
delete bef_raw->release_inner_pdu();
|
||||||
|
auto new_data_size = packet.size()-_header_size;
|
||||||
|
if (new_data_size > 0){
|
||||||
|
bef_raw /= move(Tins::RawPDU((uint8_t*)packet.data()+_header_size, new_data_size));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,10 +262,6 @@ class PktRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t data_original_size(){
|
|
||||||
return _data_original_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t original_size(){
|
size_t original_size(){
|
||||||
return _original_size;
|
return _original_size;
|
||||||
}
|
}
|
||||||
@@ -225,11 +287,11 @@ class PktRequest {
|
|||||||
void reject(){
|
void reject(){
|
||||||
if (tcp){
|
if (tcp){
|
||||||
//If the packet has data, we have to remove it
|
//If the packet has data, we have to remove it
|
||||||
delete tcp->release_inner_pdu();
|
set_data(nullptr, 0);
|
||||||
//For the first matched data or only for data packets, we set FIN bit
|
//For the first matched data or only for data packets, we set FIN bit
|
||||||
//This only for client packets, because this will trigger server to close the connection
|
//This only for client packets, because this will trigger server to close the connection
|
||||||
//Packets will be filtered anyway also if client don't send packets
|
//Packets will be filtered anyway also if client don't send packets
|
||||||
if (_data_original_size != 0 && is_input){
|
if (_data_original_size != 0){
|
||||||
tcp->set_flag(Tins::TCP::FIN,1);
|
tcp->set_flag(Tins::TCP::FIN,1);
|
||||||
tcp->set_flag(Tins::TCP::ACK,1);
|
tcp->set_flag(Tins::TCP::ACK,1);
|
||||||
tcp->set_flag(Tins::TCP::SYN,0);
|
tcp->set_flag(Tins::TCP::SYN,0);
|
||||||
@@ -241,10 +303,16 @@ class PktRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mangle_custom_pkt(uint8_t* pkt, const size_t& pkt_size){
|
void mangle_custom_pkt(const char* raw_pkt, size_t raw_pkt_size){
|
||||||
if (action == FilterAction::NOACTION){
|
if (action == FilterAction::NOACTION){
|
||||||
action = FilterAction::MANGLE;
|
try{
|
||||||
perfrom_action(pkt, pkt_size);
|
set_packet(raw_pkt, raw_pkt_size);
|
||||||
|
reserialize();
|
||||||
|
action = FilterAction::MANGLE;
|
||||||
|
}catch(...){
|
||||||
|
action = FilterAction::DROP;
|
||||||
|
}
|
||||||
|
perfrom_action(false);
|
||||||
}else{
|
}else{
|
||||||
throw invalid_argument("Cannot mangle a packet that has already been accepted or dropped");
|
throw invalid_argument("Cannot mangle a packet that has already been accepted or dropped");
|
||||||
}
|
}
|
||||||
@@ -259,7 +327,7 @@ class PktRequest {
|
|||||||
delete ipv6;
|
delete ipv6;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Tins::PDU::serialization_type serialize(){
|
Tins::PDU::serialization_type serialize(){
|
||||||
if (is_ipv6){
|
if (is_ipv6){
|
||||||
return ipv6->serialize();
|
return ipv6->serialize();
|
||||||
}else{
|
}else{
|
||||||
@@ -268,15 +336,17 @@ class PktRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void perfrom_action(uint8_t* custom_data = nullptr, size_t custom_data_size = 0){
|
void perfrom_action(bool do_serialize = true){
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
struct nlmsghdr *nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(res_id));
|
struct nlmsghdr *nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(res_id));
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case FilterAction::ACCEPT:
|
case FilterAction::ACCEPT:
|
||||||
if (need_tcp_fixing){
|
if (need_tcp_fixing){
|
||||||
Tins::PDU::serialization_type data = serialize();
|
if (do_serialize){
|
||||||
nfq_nlmsg_verdict_put_pkt(nlh_verdict, data.data(), data.size());
|
reserialize();
|
||||||
|
}
|
||||||
|
nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.data(), packet.size());
|
||||||
}
|
}
|
||||||
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(packet_id), NF_ACCEPT );
|
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(packet_id), NF_ACCEPT );
|
||||||
break;
|
break;
|
||||||
@@ -285,32 +355,20 @@ class PktRequest {
|
|||||||
break;
|
break;
|
||||||
case FilterAction::MANGLE:{
|
case FilterAction::MANGLE:{
|
||||||
//If not custom data, use the data in the packets
|
//If not custom data, use the data in the packets
|
||||||
Tins::PDU::serialization_type data;
|
if(do_serialize){
|
||||||
if (custom_data == nullptr){
|
reserialize();
|
||||||
data = serialize();
|
|
||||||
}else{
|
|
||||||
try{
|
|
||||||
data = reserialize_raw_data(custom_data, custom_data_size);
|
|
||||||
}catch(...){
|
|
||||||
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(packet_id), NF_DROP );
|
|
||||||
action = FilterAction::DROP;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.data(), packet.size());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
size_t new_size = _data_original_size+((int64_t)custom_data_size) - ((int64_t)_original_size);
|
cerr << "[DEBUG] MANGLEDPKT " << (is_input?"-> IN ":"<- OUT") << " [SIZE: " << packet.size()-header_size() << "]" << endl;
|
||||||
cerr << "[DEBUG] MANGLEDPKT " << (is_input?"-> IN ":"<- OUT") << " [SIZE: " << new_size << "]" << endl;
|
|
||||||
#endif
|
#endif
|
||||||
if (tcp && custom_data_size != _original_size){
|
if (tcp && ack_seq_offset && packet.size() != _original_size){
|
||||||
int64_t delta = ((int64_t)custom_data_size) - ((int64_t)_original_size);
|
if (is_input){
|
||||||
|
ack_seq_offset->in += packet.size() - _original_size;
|
||||||
if (is_input && tcp_in_offset != nullptr){
|
}else{
|
||||||
*tcp_in_offset += delta;
|
ack_seq_offset->out += packet.size() - _original_size;
|
||||||
}else if (!is_input && tcp_out_offset != nullptr){
|
|
||||||
*tcp_out_offset += delta;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nfq_nlmsg_verdict_put_pkt(nlh_verdict, data.data(), data.size());
|
|
||||||
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(packet_id), NF_ACCEPT );
|
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(packet_id), NF_ACCEPT );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <syncstream>
|
#include <syncstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "../classes/netfilter.cpp"
|
#include "../classes/netfilter.cpp"
|
||||||
|
#include "../classes/nfqueue.cpp"
|
||||||
#include "stream_ctx.cpp"
|
#include "stream_ctx.cpp"
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
@@ -46,7 +47,7 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
};
|
};
|
||||||
PyThreadState *tstate = NULL;
|
PyThreadState *tstate = NULL;
|
||||||
NfQueue::PktRequest<PyProxyQueue>* pkt;
|
NfQueue::PktRequest<PyProxyQueue>* pkt;
|
||||||
tcp_ack_seq_ctx* current_tcp_ack = nullptr;
|
NfQueue::tcp_ack_seq_ctx* current_tcp_ack = nullptr;
|
||||||
|
|
||||||
void before_loop() override {
|
void before_loop() override {
|
||||||
PyStatus pystatus;
|
PyStatus pystatus;
|
||||||
@@ -89,7 +90,7 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
pyq->pkt->drop();// This is needed because the callback has to take the updated pkt pointer!
|
pyq->pkt->drop();// This is needed because the callback has to take the updated pkt pointer!
|
||||||
}
|
}
|
||||||
|
|
||||||
void filter_action(NfQueue::PktRequest<PyProxyQueue>* pkt, Stream& stream){
|
void filter_action(NfQueue::PktRequest<PyProxyQueue>* pkt, Stream& stream, const string& data){
|
||||||
auto stream_search = sctx.streams_ctx.find(pkt->sid);
|
auto stream_search = sctx.streams_ctx.find(pkt->sid);
|
||||||
pyfilter_ctx* stream_match;
|
pyfilter_ctx* stream_match;
|
||||||
if (stream_search == sctx.streams_ctx.end()){
|
if (stream_search == sctx.streams_ctx.end()){
|
||||||
@@ -108,7 +109,7 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
stream_match = stream_search->second;
|
stream_match = stream_search->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = stream_match->handle_packet(pkt);
|
auto result = stream_match->handle_packet(pkt, data);
|
||||||
switch(result.action){
|
switch(result.action){
|
||||||
case PyFilterResponse::ACCEPT:
|
case PyFilterResponse::ACCEPT:
|
||||||
return pkt->accept();
|
return pkt->accept();
|
||||||
@@ -125,7 +126,7 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
stream.server_data_callback(bind(keep_fin_packet, this));
|
stream.server_data_callback(bind(keep_fin_packet, this));
|
||||||
return pkt->reject();
|
return pkt->reject();
|
||||||
case PyFilterResponse::MANGLE:
|
case PyFilterResponse::MANGLE:
|
||||||
pkt->mangle_custom_pkt((uint8_t*)result.mangled_packet->data(), result.mangled_packet->size());
|
pkt->mangle_custom_pkt(result.mangled_packet->c_str(), result.mangled_packet->size());
|
||||||
if (pkt->get_action() == NfQueue::FilterAction::DROP){
|
if (pkt->get_action() == NfQueue::FilterAction::DROP){
|
||||||
cerr << "[error] [filter_action] Failed to mangle: the packet sent is not serializzable... the packet was dropped" << endl;
|
cerr << "[error] [filter_action] Failed to mangle: the packet sent is not serializzable... the packet was dropped" << endl;
|
||||||
print_blocked_reason(*result.filter_match_by);
|
print_blocked_reason(*result.filter_match_by);
|
||||||
@@ -146,20 +147,21 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void on_data_recv(Stream& stream, PyProxyQueue* proxy_info, string data) {
|
static void on_data_recv(Stream& stream, PyProxyQueue* pyq, const string& data) {
|
||||||
proxy_info->pkt->data = data.data();
|
pyq->pkt->fix_data_payload();
|
||||||
proxy_info->pkt->data_size = data.size();
|
pyq->filter_action(pyq->pkt, stream, data); //Only here the rebuilt_tcp_data is set
|
||||||
proxy_info->filter_action(proxy_info->pkt, stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Input data filtering
|
//Input data filtering
|
||||||
static void on_client_data(Stream& stream, PyProxyQueue* proxy_info) {
|
static void on_client_data(Stream& stream, PyProxyQueue* pyq) {
|
||||||
on_data_recv(stream, proxy_info, string(stream.client_payload().begin(), stream.client_payload().end()));
|
auto data = stream.client_payload();
|
||||||
|
on_data_recv(stream, pyq, string((char*)data.data(), data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Server data filtering
|
//Server data filtering
|
||||||
static void on_server_data(Stream& stream, PyProxyQueue* proxy_info) {
|
static void on_server_data(Stream& stream, PyProxyQueue* pyq) {
|
||||||
on_data_recv(stream, proxy_info, string(stream.server_payload().begin(), stream.server_payload().end()));
|
auto data = stream.server_payload();
|
||||||
|
on_data_recv(stream, pyq, string((char*)data.data(), data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A stream was terminated. The second argument is the reason why it was terminated
|
// A stream was terminated. The second argument is the reason why it was terminated
|
||||||
@@ -178,10 +180,9 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
if (pyq->current_tcp_ack != nullptr){
|
if (pyq->current_tcp_ack != nullptr){
|
||||||
pyq->current_tcp_ack->reset();
|
pyq->current_tcp_ack->reset();
|
||||||
}else{
|
}else{
|
||||||
pyq->current_tcp_ack = new tcp_ack_seq_ctx();
|
pyq->current_tcp_ack = new NfQueue::tcp_ack_seq_ctx();
|
||||||
pyq->sctx.tcp_ack_ctx.insert_or_assign(pyq->pkt->sid, pyq->current_tcp_ack);
|
pyq->sctx.tcp_ack_ctx.insert_or_assign(pyq->pkt->sid, pyq->current_tcp_ack);
|
||||||
pyq->pkt->tcp_in_offset = &pyq->current_tcp_ack->in_tcp_offset;
|
pyq->pkt->ack_seq_offset = pyq->current_tcp_ack; // Set ack context
|
||||||
pyq->pkt->tcp_out_offset = &pyq->current_tcp_ack->out_tcp_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Should not happen, but with this we can be sure about this
|
//Should not happen, but with this we can be sure about this
|
||||||
@@ -205,18 +206,17 @@ class PyProxyQueue: public NfQueue::ThreadNfQueue<PyProxyQueue> {
|
|||||||
auto tcp_ack_search = sctx.tcp_ack_ctx.find(pkt->sid);
|
auto tcp_ack_search = sctx.tcp_ack_ctx.find(pkt->sid);
|
||||||
if (tcp_ack_search != sctx.tcp_ack_ctx.end()){
|
if (tcp_ack_search != sctx.tcp_ack_ctx.end()){
|
||||||
current_tcp_ack = tcp_ack_search->second;
|
current_tcp_ack = tcp_ack_search->second;
|
||||||
pkt->tcp_in_offset = ¤t_tcp_ack->in_tcp_offset;
|
pkt->ack_seq_offset = current_tcp_ack;
|
||||||
pkt->tcp_out_offset = ¤t_tcp_ack->out_tcp_offset;
|
|
||||||
}else{
|
}else{
|
||||||
current_tcp_ack = nullptr;
|
current_tcp_ack = nullptr;
|
||||||
//If necessary will be created by libtis new_stream callback
|
//If necessary will be created by libtis new_stream callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pkt->fix_tcp_ack();
|
||||||
|
|
||||||
if (pkt->is_ipv6){
|
if (pkt->is_ipv6){
|
||||||
pkt->fix_tcp_ack();
|
|
||||||
follower.process_packet(*pkt->ipv6);
|
follower.process_packet(*pkt->ipv6);
|
||||||
}else{
|
}else{
|
||||||
pkt->fix_tcp_ack();
|
|
||||||
follower.process_packet(*pkt->ipv4);
|
follower.process_packet(*pkt->ipv4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include "../classes/netfilter.cpp"
|
#include "../classes/netfilter.cpp"
|
||||||
|
#include "../classes/nfqueue.cpp"
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
|
#include "../utils.cpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -50,17 +52,6 @@ struct py_filter_response {
|
|||||||
|
|
||||||
typedef Tins::TCPIP::StreamIdentifier stream_id;
|
typedef Tins::TCPIP::StreamIdentifier stream_id;
|
||||||
|
|
||||||
struct tcp_ack_seq_ctx{
|
|
||||||
//Can be negative, so we use int64_t (for a uint64_t value)
|
|
||||||
int64_t in_tcp_offset = 0;
|
|
||||||
int64_t out_tcp_offset = 0;
|
|
||||||
tcp_ack_seq_ctx(){}
|
|
||||||
void reset(){
|
|
||||||
in_tcp_offset = 0;
|
|
||||||
out_tcp_offset = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pyfilter_ctx {
|
struct pyfilter_ctx {
|
||||||
|
|
||||||
PyObject * glob = nullptr;
|
PyObject * glob = nullptr;
|
||||||
@@ -105,12 +96,14 @@ struct pyfilter_ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
py_filter_response handle_packet(
|
py_filter_response handle_packet(
|
||||||
NfQueue::PktRequest<PyProxyQueue>* pkt
|
NfQueue::PktRequest<PyProxyQueue>* pkt,
|
||||||
|
const string& data
|
||||||
){
|
){
|
||||||
PyObject * packet_info = PyDict_New();
|
PyObject * packet_info = PyDict_New();
|
||||||
|
|
||||||
set_item_to_dict(packet_info, "data", PyBytes_FromStringAndSize(pkt->data, pkt->data_size));
|
pkt->reserialize();
|
||||||
set_item_to_dict(packet_info, "l4_size", PyLong_FromLong(pkt->data_original_size()));
|
set_item_to_dict(packet_info, "data", PyBytes_FromStringAndSize(data.c_str(), data.size()));
|
||||||
|
set_item_to_dict(packet_info, "l4_size", PyLong_FromLong(pkt->data_size()));
|
||||||
set_item_to_dict(packet_info, "raw_packet", PyBytes_FromStringAndSize(pkt->packet.c_str(), pkt->packet.size()));
|
set_item_to_dict(packet_info, "raw_packet", PyBytes_FromStringAndSize(pkt->packet.c_str(), pkt->packet.size()));
|
||||||
set_item_to_dict(packet_info, "is_input", PyBool_FromLong(pkt->is_input));
|
set_item_to_dict(packet_info, "is_input", PyBool_FromLong(pkt->is_input));
|
||||||
set_item_to_dict(packet_info, "is_ipv6", PyBool_FromLong(pkt->is_ipv6));
|
set_item_to_dict(packet_info, "is_ipv6", PyBool_FromLong(pkt->is_ipv6));
|
||||||
@@ -129,8 +122,7 @@ struct pyfilter_ctx {
|
|||||||
#endif
|
#endif
|
||||||
return py_filter_response(PyFilterResponse::EXCEPTION);
|
return py_filter_response(PyFilterResponse::EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
|
|
||||||
result = get_item_from_glob("__firegex_pyfilter_result");
|
result = get_item_from_glob("__firegex_pyfilter_result");
|
||||||
@@ -235,11 +227,12 @@ struct pyfilter_ctx {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef map<stream_id, pyfilter_ctx*> matching_map;
|
typedef map<stream_id, pyfilter_ctx*> matching_map;
|
||||||
typedef map<stream_id, tcp_ack_seq_ctx*> tcp_ack_map;
|
|
||||||
|
|
||||||
struct stream_ctx {
|
struct stream_ctx {
|
||||||
|
|
||||||
matching_map streams_ctx;
|
matching_map streams_ctx;
|
||||||
tcp_ack_map tcp_ack_ctx;
|
NfQueue::tcp_ack_map tcp_ack_ctx;
|
||||||
|
|
||||||
void clean_stream_by_id(stream_id sid){
|
void clean_stream_by_id(stream_id sid){
|
||||||
auto stream_search = streams_ctx.find(sid);
|
auto stream_search = streams_ctx.find(sid);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "../classes/netfilter.cpp"
|
#include "../classes/netfilter.cpp"
|
||||||
#include "stream_ctx.cpp"
|
#include "stream_ctx.cpp"
|
||||||
#include "regex_rules.cpp"
|
#include "regex_rules.cpp"
|
||||||
|
#include "../utils.cpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -30,8 +31,6 @@ namespace Regex {
|
|||||||
using Tins::TCPIP::Stream;
|
using Tins::TCPIP::Stream;
|
||||||
using Tins::TCPIP::StreamFollower;
|
using Tins::TCPIP::StreamFollower;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RegexNfQueue : public NfQueue::ThreadNfQueue<RegexNfQueue> {
|
class RegexNfQueue : public NfQueue::ThreadNfQueue<RegexNfQueue> {
|
||||||
public:
|
public:
|
||||||
stream_ctx sctx;
|
stream_ctx sctx;
|
||||||
@@ -39,7 +38,7 @@ public:
|
|||||||
StreamFollower follower;
|
StreamFollower follower;
|
||||||
NfQueue::PktRequest<RegexNfQueue>* pkt;
|
NfQueue::PktRequest<RegexNfQueue>* pkt;
|
||||||
|
|
||||||
bool filter_action(NfQueue::PktRequest<RegexNfQueue>* pkt){
|
bool filter_action(NfQueue::PktRequest<RegexNfQueue>* pkt, const string& data){
|
||||||
shared_ptr<RegexRules> conf = regex_config;
|
shared_ptr<RegexRules> conf = regex_config;
|
||||||
|
|
||||||
auto current_version = conf->ver();
|
auto current_version = conf->ver();
|
||||||
@@ -85,12 +84,12 @@ public:
|
|||||||
stream_match = stream_search->second;
|
stream_match = stream_search->second;
|
||||||
}
|
}
|
||||||
err = hs_scan_stream(
|
err = hs_scan_stream(
|
||||||
stream_match,pkt->data, pkt->data_size,
|
stream_match, data.c_str(), data.size(),
|
||||||
0, scratch_space, match_func, &match_res
|
0, scratch_space, match_func, &match_res
|
||||||
);
|
);
|
||||||
}else{
|
}else{
|
||||||
err = hs_scan(
|
err = hs_scan(
|
||||||
regex_matcher,pkt->data, pkt->data_size,
|
regex_matcher, data.c_str(), data.size(),
|
||||||
0, scratch_space, match_func, &match_res
|
0, scratch_space, match_func, &match_res
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -102,7 +101,7 @@ public:
|
|||||||
throw invalid_argument("Cannot close stream match on hyperscan");
|
throw invalid_argument("Cannot close stream match on hyperscan");
|
||||||
}
|
}
|
||||||
if (err != HS_SUCCESS && err != HS_SCAN_TERMINATED) {
|
if (err != HS_SUCCESS && err != HS_SCAN_TERMINATED) {
|
||||||
cerr << "[error] [filter_callback] Error while matching the stream (hs)" << endl;
|
cerr << "[error] [filter_callback] Error while matching the stream (hs) " << err << endl;
|
||||||
throw invalid_argument("Error while matching the stream with hyperscan");
|
throw invalid_argument("Error while matching the stream with hyperscan");
|
||||||
}
|
}
|
||||||
if (match_res.has_matched){
|
if (match_res.has_matched){
|
||||||
@@ -113,41 +112,13 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_next_packet(NfQueue::PktRequest<RegexNfQueue>* _pkt) override{
|
|
||||||
pkt = _pkt; // Setting packet context
|
|
||||||
if (pkt->tcp){
|
|
||||||
if (pkt->ipv4){
|
|
||||||
follower.process_packet(*pkt->ipv4);
|
|
||||||
}else{
|
|
||||||
follower.process_packet(*pkt->ipv6);
|
|
||||||
}
|
|
||||||
//Fallback to the default action
|
|
||||||
if (pkt->get_action() == NfQueue::FilterAction::NOACTION){
|
|
||||||
return pkt->accept();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (!pkt->udp){
|
|
||||||
throw invalid_argument("Only TCP and UDP are supported");
|
|
||||||
}
|
|
||||||
if(pkt->data_size == 0){
|
|
||||||
return pkt->accept();
|
|
||||||
}else if (filter_action(pkt)){
|
|
||||||
return pkt->accept();
|
|
||||||
}else{
|
|
||||||
return pkt->drop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If the stream has already been matched, drop all data, and try to close the connection
|
//If the stream has already been matched, drop all data, and try to close the connection
|
||||||
static void keep_fin_packet(RegexNfQueue* nfq){
|
static void keep_fin_packet(RegexNfQueue* nfq){
|
||||||
nfq->pkt->reject();// This is needed because the callback has to take the updated pkt pointer!
|
nfq->pkt->reject(); // This is needed because the callback has to take the updated pkt pointer!
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_data_recv(Stream& stream, RegexNfQueue* nfq, string data) {
|
static void on_data_recv(Stream& stream, RegexNfQueue* nfq, const string& data) {
|
||||||
nfq->pkt->data = data.data();
|
if (!nfq->filter_action(nfq->pkt, data)){
|
||||||
nfq->pkt->data_size = data.size();
|
|
||||||
if (!nfq->filter_action(nfq->pkt)){
|
|
||||||
nfq->sctx.clean_stream_by_id(nfq->pkt->sid);
|
nfq->sctx.clean_stream_by_id(nfq->pkt->sid);
|
||||||
stream.client_data_callback(bind(keep_fin_packet, nfq));
|
stream.client_data_callback(bind(keep_fin_packet, nfq));
|
||||||
stream.server_data_callback(bind(keep_fin_packet, nfq));
|
stream.server_data_callback(bind(keep_fin_packet, nfq));
|
||||||
@@ -157,12 +128,14 @@ public:
|
|||||||
|
|
||||||
//Input data filtering
|
//Input data filtering
|
||||||
static void on_client_data(Stream& stream, RegexNfQueue* nfq) {
|
static void on_client_data(Stream& stream, RegexNfQueue* nfq) {
|
||||||
on_data_recv(stream, nfq, string(stream.client_payload().begin(), stream.client_payload().end()));
|
auto data = stream.client_payload();
|
||||||
|
on_data_recv(stream, nfq, string((char*)data.data(), data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Server data filtering
|
//Server data filtering
|
||||||
static void on_server_data(Stream& stream, RegexNfQueue* nfq) {
|
static void on_server_data(Stream& stream, RegexNfQueue* nfq) {
|
||||||
on_data_recv(stream, nfq, string(stream.server_payload().begin(), stream.server_payload().end()));
|
auto data = stream.server_payload();
|
||||||
|
on_data_recv(stream, nfq, string((char*)data.data(), data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A stream was terminated. The second argument is the reason why it was terminated
|
// A stream was terminated. The second argument is the reason why it was terminated
|
||||||
@@ -181,6 +154,32 @@ public:
|
|||||||
stream.stream_closed_callback(bind(on_stream_close, placeholders::_1, nfq));
|
stream.stream_closed_callback(bind(on_stream_close, placeholders::_1, nfq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_next_packet(NfQueue::PktRequest<RegexNfQueue>* _pkt) override{
|
||||||
|
pkt = _pkt; // Setting packet context
|
||||||
|
if (pkt->tcp){
|
||||||
|
if (pkt->ipv4){
|
||||||
|
follower.process_packet(*pkt->ipv4);
|
||||||
|
}else{
|
||||||
|
follower.process_packet(*pkt->ipv6);
|
||||||
|
}
|
||||||
|
//Fallback to the default action
|
||||||
|
if (pkt->get_action() == NfQueue::FilterAction::NOACTION){
|
||||||
|
return pkt->accept();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (!pkt->udp){
|
||||||
|
throw invalid_argument("Only TCP and UDP are supported");
|
||||||
|
}
|
||||||
|
if(pkt->data_size() == 0){
|
||||||
|
return pkt->accept();
|
||||||
|
}else if (filter_action(pkt, string(pkt->data(), pkt->data_size()))){
|
||||||
|
return pkt->accept();
|
||||||
|
}else{
|
||||||
|
return pkt->drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void before_loop() override{
|
void before_loop() override{
|
||||||
follower.new_stream_callback(bind(on_new_stream, placeholders::_1, this));
|
follower.new_stream_callback(bind(on_new_stream, placeholders::_1, this));
|
||||||
follower.stream_termination_callback(bind(on_stream_close, placeholders::_1, this));
|
follower.stream_termination_callback(bind(on_stream_close, placeholders::_1, this));
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import asyncio
|
|||||||
import traceback
|
import traceback
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
import time
|
import time
|
||||||
|
from utils import run_func
|
||||||
|
|
||||||
nft = FiregexTables()
|
nft = FiregexTables()
|
||||||
|
|
||||||
|
OUTSTREAM_BUFFER_SIZE = 1024*10
|
||||||
|
|
||||||
class FiregexInterceptor:
|
class FiregexInterceptor:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -28,14 +31,20 @@ class FiregexInterceptor:
|
|||||||
self.sock_writer:asyncio.StreamWriter = None
|
self.sock_writer:asyncio.StreamWriter = None
|
||||||
self.sock_conn_lock:asyncio.Lock
|
self.sock_conn_lock:asyncio.Lock
|
||||||
self.last_time_exception = 0
|
self.last_time_exception = 0
|
||||||
|
self.outstrem_function = None
|
||||||
|
self.expection_function = None
|
||||||
|
self.outstrem_task: asyncio.Task
|
||||||
|
self.outstrem_buffer = ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def start(cls, srv: Service):
|
async def start(cls, srv: Service, outstream_func=None, exception_func=None):
|
||||||
self = cls()
|
self = cls()
|
||||||
self.srv = srv
|
self.srv = srv
|
||||||
self.filter_map_lock = asyncio.Lock()
|
self.filter_map_lock = asyncio.Lock()
|
||||||
self.update_config_lock = asyncio.Lock()
|
self.update_config_lock = asyncio.Lock()
|
||||||
self.sock_conn_lock = asyncio.Lock()
|
self.sock_conn_lock = asyncio.Lock()
|
||||||
|
self.outstrem_function = outstream_func
|
||||||
|
self.expection_function = exception_func
|
||||||
if not self.sock_conn_lock.locked():
|
if not self.sock_conn_lock.locked():
|
||||||
await self.sock_conn_lock.acquire()
|
await self.sock_conn_lock.acquire()
|
||||||
self.sock_path = f"/tmp/firegex_nfproxy_{srv.id}.sock"
|
self.sock_path = f"/tmp/firegex_nfproxy_{srv.id}.sock"
|
||||||
@@ -50,16 +59,37 @@ class FiregexInterceptor:
|
|||||||
await self.ack_lock.acquire()
|
await self.ack_lock.acquire()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
async def _stream_handler(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = (await self.process.stdout.readuntil()).decode(errors="ignore")
|
||||||
|
print(line, end="")
|
||||||
|
except Exception as e:
|
||||||
|
self.ack_arrived = False
|
||||||
|
self.ack_status = False
|
||||||
|
self.ack_fail_what = "Can't read from nfq client"
|
||||||
|
self.ack_lock.release()
|
||||||
|
await self.stop()
|
||||||
|
raise HTTPException(status_code=500, detail="Can't read from nfq client") from e
|
||||||
|
self.outstrem_buffer+=line
|
||||||
|
if len(self.outstrem_buffer) > OUTSTREAM_BUFFER_SIZE:
|
||||||
|
self.outstrem_buffer = self.outstrem_buffer[-OUTSTREAM_BUFFER_SIZE:]+"\n"
|
||||||
|
if self.outstrem_function:
|
||||||
|
await run_func(self.outstrem_function, self.srv.id, line)
|
||||||
|
|
||||||
async def _start_binary(self):
|
async def _start_binary(self):
|
||||||
proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"../cpproxy")
|
proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"../cpproxy")
|
||||||
self.process = await asyncio.create_subprocess_exec(
|
self.process = await asyncio.create_subprocess_exec(
|
||||||
proxy_binary_path, stdin=asyncio.subprocess.DEVNULL,
|
proxy_binary_path, stdin=asyncio.subprocess.DEVNULL,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.STDOUT,
|
||||||
env={
|
env={
|
||||||
"NTHREADS": os.getenv("NTHREADS","1"),
|
"NTHREADS": os.getenv("NTHREADS","1"),
|
||||||
"FIREGEX_NFQUEUE_FAIL_OPEN": "1" if self.srv.fail_open else "0",
|
"FIREGEX_NFQUEUE_FAIL_OPEN": "1" if self.srv.fail_open else "0",
|
||||||
"FIREGEX_NFPROXY_SOCK": self.sock_path
|
"FIREGEX_NFPROXY_SOCK": self.sock_path
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
self.outstrem_task = asyncio.create_task(self._stream_handler())
|
||||||
try:
|
try:
|
||||||
async with asyncio.timeout(3):
|
async with asyncio.timeout(3):
|
||||||
await self.sock_conn_lock.acquire()
|
await self.sock_conn_lock.acquire()
|
||||||
@@ -101,9 +131,7 @@ class FiregexInterceptor:
|
|||||||
filter_name = line.split()[1]
|
filter_name = line.split()[1]
|
||||||
print("BLOCKED", filter_name)
|
print("BLOCKED", filter_name)
|
||||||
async with self.filter_map_lock:
|
async with self.filter_map_lock:
|
||||||
print("LOCKED MAP LOCK")
|
|
||||||
if filter_name in self.filter_map:
|
if filter_name in self.filter_map:
|
||||||
print("ADDING BLOCKED PACKET")
|
|
||||||
self.filter_map[filter_name].blocked_packets+=1
|
self.filter_map[filter_name].blocked_packets+=1
|
||||||
await self.filter_map[filter_name].update()
|
await self.filter_map[filter_name].update()
|
||||||
if line.startswith("MANGLED "):
|
if line.startswith("MANGLED "):
|
||||||
@@ -113,8 +141,9 @@ class FiregexInterceptor:
|
|||||||
self.filter_map[filter_name].edited_packets+=1
|
self.filter_map[filter_name].edited_packets+=1
|
||||||
await self.filter_map[filter_name].update()
|
await self.filter_map[filter_name].update()
|
||||||
if line.startswith("EXCEPTION"):
|
if line.startswith("EXCEPTION"):
|
||||||
self.last_time_exception = time.time()
|
self.last_time_exception = int(time.time()*1000) #ms timestamp
|
||||||
print("TODO EXCEPTION HANDLING") # TODO
|
if self.expection_function:
|
||||||
|
await run_func(self.expection_function, self.srv.id, self.last_time_exception)
|
||||||
if line.startswith("ACK "):
|
if line.startswith("ACK "):
|
||||||
self.ack_arrived = True
|
self.ack_arrived = True
|
||||||
self.ack_status = line.split()[1].upper() == "OK"
|
self.ack_status = line.split()[1].upper() == "OK"
|
||||||
@@ -132,6 +161,7 @@ class FiregexInterceptor:
|
|||||||
self.server_task.cancel()
|
self.server_task.cancel()
|
||||||
self.update_task.cancel()
|
self.update_task.cancel()
|
||||||
self.unix_sock.close()
|
self.unix_sock.close()
|
||||||
|
self.outstrem_task.cancel()
|
||||||
if os.path.exists(self.sock_path):
|
if os.path.exists(self.sock_path):
|
||||||
os.remove(self.sock_path)
|
os.remove(self.sock_path)
|
||||||
if self.process and self.process.returncode is None:
|
if self.process and self.process.returncode is None:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from modules.nfproxy.firegex import FiregexInterceptor
|
|||||||
from modules.nfproxy.nftables import FiregexTables, FiregexFilter
|
from modules.nfproxy.nftables import FiregexTables, FiregexFilter
|
||||||
from modules.nfproxy.models import Service, PyFilter
|
from modules.nfproxy.models import Service, PyFilter
|
||||||
from utils.sqlite import SQLite
|
from utils.sqlite import SQLite
|
||||||
|
from utils import run_func
|
||||||
|
|
||||||
class STATUS:
|
class STATUS:
|
||||||
STOP = "stop"
|
STOP = "stop"
|
||||||
@@ -11,13 +12,20 @@ class STATUS:
|
|||||||
nft = FiregexTables()
|
nft = FiregexTables()
|
||||||
|
|
||||||
class ServiceManager:
|
class ServiceManager:
|
||||||
def __init__(self, srv: Service, db):
|
def __init__(self, srv: Service, db, outstream_func=None, exception_func=None):
|
||||||
self.srv = srv
|
self.srv = srv
|
||||||
self.db = db
|
self.db = db
|
||||||
self.status = STATUS.STOP
|
self.status = STATUS.STOP
|
||||||
self.filters: dict[str, FiregexFilter] = {}
|
self.filters: dict[str, FiregexFilter] = {}
|
||||||
self.lock = asyncio.Lock()
|
self.lock = asyncio.Lock()
|
||||||
self.interceptor = None
|
self.interceptor = None
|
||||||
|
self.outstream_function = outstream_func
|
||||||
|
self.last_exception_time = 0
|
||||||
|
async def excep_internal_handler(srv, exc_time):
|
||||||
|
self.last_exception_time = exc_time
|
||||||
|
if exception_func:
|
||||||
|
await run_func(exception_func, srv, exc_time)
|
||||||
|
self.exception_function = excep_internal_handler
|
||||||
|
|
||||||
async def _update_filters_from_db(self):
|
async def _update_filters_from_db(self):
|
||||||
pyfilters = [
|
pyfilters = [
|
||||||
@@ -52,10 +60,16 @@ class ServiceManager:
|
|||||||
self.status = status
|
self.status = status
|
||||||
self.__update_status_db(status)
|
self.__update_status_db(status)
|
||||||
|
|
||||||
|
def read_outstrem_buffer(self):
|
||||||
|
if self.interceptor:
|
||||||
|
return self.interceptor.outstrem_buffer
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
if not self.interceptor:
|
if not self.interceptor:
|
||||||
nft.delete(self.srv)
|
nft.delete(self.srv)
|
||||||
self.interceptor = await FiregexInterceptor.start(self.srv)
|
self.interceptor = await FiregexInterceptor.start(self.srv, outstream_func=self.outstream_function, exception_func=self.exception_function)
|
||||||
await self._update_filters_from_db()
|
await self._update_filters_from_db()
|
||||||
self._set_status(STATUS.ACTIVE)
|
self._set_status(STATUS.ACTIVE)
|
||||||
|
|
||||||
@@ -75,10 +89,12 @@ class ServiceManager:
|
|||||||
await self._update_filters_from_db()
|
await self._update_filters_from_db()
|
||||||
|
|
||||||
class FirewallManager:
|
class FirewallManager:
|
||||||
def __init__(self, db:SQLite):
|
def __init__(self, db:SQLite, outstream_func=None, exception_func=None):
|
||||||
self.db = db
|
self.db = db
|
||||||
self.service_table: dict[str, ServiceManager] = {}
|
self.service_table: dict[str, ServiceManager] = {}
|
||||||
self.lock = asyncio.Lock()
|
self.lock = asyncio.Lock()
|
||||||
|
self.outstream_function = outstream_func
|
||||||
|
self.exception_function = exception_func
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
for key in list(self.service_table.keys()):
|
for key in list(self.service_table.keys()):
|
||||||
@@ -100,7 +116,7 @@ class FirewallManager:
|
|||||||
srv = Service.from_dict(srv)
|
srv = Service.from_dict(srv)
|
||||||
if srv.id in self.service_table:
|
if srv.id in self.service_table:
|
||||||
continue
|
continue
|
||||||
self.service_table[srv.id] = ServiceManager(srv, self.db)
|
self.service_table[srv.id] = ServiceManager(srv, self.db, outstream_func=self.outstream_function, exception_func=self.exception_function)
|
||||||
await self.service_table[srv.id].next(srv.status)
|
await self.service_table[srv.id].next(srv.status)
|
||||||
|
|
||||||
def get(self,srv_id) -> ServiceManager:
|
def get(self,srv_id) -> ServiceManager:
|
||||||
|
|||||||
@@ -28,22 +28,22 @@ class FiregexTables(NFTableManager):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__([
|
super().__init__([
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{ #Input chain attached before conntrack see it
|
||||||
"family":"inet",
|
"family":"inet",
|
||||||
"table":self.table_name,
|
"table":self.table_name,
|
||||||
"name":self.input_chain,
|
"name":self.input_chain,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"prerouting",
|
"hook":"prerouting",
|
||||||
"prio":-150,
|
"prio":-301,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}},
|
}}},
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{ #Output chain attached after conntrack saw it
|
||||||
"family":"inet",
|
"family":"inet",
|
||||||
"table":self.table_name,
|
"table":self.table_name,
|
||||||
"name":self.output_chain,
|
"name":self.output_chain,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"postrouting",
|
"hook":"postrouting",
|
||||||
"prio":-150,
|
"prio":-290,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}}
|
}}}
|
||||||
],[
|
],[
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.input_chain,
|
"name":self.input_chain,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"prerouting",
|
"hook":"prerouting",
|
||||||
"prio":-150,
|
"prio":-301,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}},
|
}}},
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{
|
||||||
@@ -35,7 +35,7 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.output_chain,
|
"name":self.output_chain,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"postrouting",
|
"hook":"postrouting",
|
||||||
"prio":-150,
|
"prio":-301,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}}
|
}}}
|
||||||
],[
|
],[
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.prerouting_porthijack,
|
"name":self.prerouting_porthijack,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"prerouting",
|
"hook":"prerouting",
|
||||||
"prio":-300,
|
"prio":-310,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}},
|
}}},
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{
|
||||||
@@ -37,7 +37,7 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.postrouting_porthijack,
|
"name":self.postrouting_porthijack,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"postrouting",
|
"hook":"postrouting",
|
||||||
"prio":-300,
|
"prio":-310,
|
||||||
"policy":"accept"
|
"policy":"accept"
|
||||||
}}}
|
}}}
|
||||||
],[
|
],[
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from modules.nfproxy.nftables import convert_protocol_to_l4
|
|||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
from utils import DEBUG
|
from utils import DEBUG
|
||||||
|
import utils
|
||||||
|
|
||||||
class ServiceModel(BaseModel):
|
class ServiceModel(BaseModel):
|
||||||
service_id: str
|
service_id: str
|
||||||
@@ -107,6 +108,10 @@ async def startup():
|
|||||||
await firewall.init()
|
await firewall.init()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("WARNING cannot start firewall:", e)
|
print("WARNING cannot start firewall:", e)
|
||||||
|
utils.socketio.on("nfproxy-outstream-join", join_outstream)
|
||||||
|
utils.socketio.on("nfproxy-outstream-leave", leave_outstream)
|
||||||
|
utils.socketio.on("nfproxy-exception-join", join_exception)
|
||||||
|
utils.socketio.on("nfproxy-exception-leave", leave_exception)
|
||||||
|
|
||||||
async def shutdown():
|
async def shutdown():
|
||||||
db.backup()
|
db.backup()
|
||||||
@@ -121,7 +126,13 @@ def gen_service_id():
|
|||||||
break
|
break
|
||||||
return res
|
return res
|
||||||
|
|
||||||
firewall = FirewallManager(db)
|
async def outstream_func(service_id, data):
|
||||||
|
await utils.socketio.emit(f"nfproxy-outstream-{service_id}", data, room=f"nfproxy-outstream-{service_id}")
|
||||||
|
|
||||||
|
async def exception_func(service_id, timestamp):
|
||||||
|
await utils.socketio.emit(f"nfproxy-exception-{service_id}", timestamp, room=f"nfproxy-exception-{service_id}")
|
||||||
|
|
||||||
|
firewall = FirewallManager(db, outstream_func=outstream_func, exception_func=exception_func)
|
||||||
|
|
||||||
@app.get('/services', response_model=list[ServiceModel])
|
@app.get('/services', response_model=list[ServiceModel])
|
||||||
async def get_service_list():
|
async def get_service_list():
|
||||||
@@ -355,3 +366,33 @@ async def get_pyfilters(service_id: str):
|
|||||||
return f.read()
|
return f.read()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
#Socket io events
|
||||||
|
async def join_outstream(sid, data):
|
||||||
|
"""Client joins a room."""
|
||||||
|
srv = data.get("service")
|
||||||
|
if srv:
|
||||||
|
room = f"nfproxy-outstream-{srv}"
|
||||||
|
await utils.socketio.enter_room(sid, room)
|
||||||
|
await utils.socketio.emit(room, firewall.get(srv).read_outstrem_buffer(), room=sid)
|
||||||
|
|
||||||
|
async def leave_outstream(sid, data):
|
||||||
|
"""Client leaves a room."""
|
||||||
|
srv = data.get("service")
|
||||||
|
if srv:
|
||||||
|
await utils.socketio.leave_room(sid, f"nfproxy-outstream-{srv}")
|
||||||
|
|
||||||
|
async def join_exception(sid, data):
|
||||||
|
"""Client joins a room."""
|
||||||
|
srv = data.get("service")
|
||||||
|
if srv:
|
||||||
|
room = f"nfproxy-exception-{srv}"
|
||||||
|
await utils.socketio.enter_room(sid, room)
|
||||||
|
await utils.socketio.emit(room, firewall.get(srv).last_exception_time, room=sid)
|
||||||
|
|
||||||
|
async def leave_exception(sid, data):
|
||||||
|
"""Client leaves a room."""
|
||||||
|
srv = data.get("service")
|
||||||
|
if srv:
|
||||||
|
await utils.socketio.leave_room(sid, f"nfproxy-exception-{srv}")
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from fastapi.responses import FileResponse
|
|||||||
from utils import DEBUG, ON_DOCKER, ROUTERS_DIR, list_files, run_func
|
from utils import DEBUG, ON_DOCKER, ROUTERS_DIR, list_files, run_func
|
||||||
from utils.models import ResetRequest
|
from utils.models import ResetRequest
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import traceback
|
||||||
|
|
||||||
REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
|
REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
|
||||||
REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
|
REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
|
||||||
@@ -70,6 +71,7 @@ def get_router_modules():
|
|||||||
name=route
|
name=route
|
||||||
))
|
))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
print(f"Router {route} failed to load: {e}")
|
print(f"Router {route} failed to load: {e}")
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import { ImCross } from 'react-icons/im';
|
|||||||
import { Outlet, Route, Routes } from 'react-router-dom';
|
import { Outlet, Route, Routes } from 'react-router-dom';
|
||||||
import MainLayout from './components/MainLayout';
|
import MainLayout from './components/MainLayout';
|
||||||
import { PasswordSend, ServerStatusResponse } from './js/models';
|
import { PasswordSend, ServerStatusResponse } from './js/models';
|
||||||
import { DEV_IP_BACKEND, errorNotify, getstatus, HomeRedirector, IS_DEV, login, setpassword } from './js/utils';
|
import { errorNotify, getstatus, HomeRedirector, IS_DEV, login, setpassword, socketio } from './js/utils';
|
||||||
import NFRegex from './pages/NFRegex';
|
import NFRegex from './pages/NFRegex';
|
||||||
import io from 'socket.io-client';
|
|
||||||
import ServiceDetailsNFRegex from './pages/NFRegex/ServiceDetails';
|
import ServiceDetailsNFRegex from './pages/NFRegex/ServiceDetails';
|
||||||
import PortHijack from './pages/PortHijack';
|
import PortHijack from './pages/PortHijack';
|
||||||
import { Firewall } from './pages/Firewall';
|
import { Firewall } from './pages/Firewall';
|
||||||
@@ -15,22 +14,6 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import NFProxy from './pages/NFProxy';
|
import NFProxy from './pages/NFProxy';
|
||||||
import ServiceDetailsNFProxy from './pages/NFProxy/ServiceDetails';
|
import ServiceDetailsNFProxy from './pages/NFProxy/ServiceDetails';
|
||||||
|
|
||||||
export const socket = import.meta.env.DEV?
|
|
||||||
io("ws://"+DEV_IP_BACKEND, {
|
|
||||||
path:"/sock/socket.io",
|
|
||||||
transports: ['websocket'],
|
|
||||||
auth: {
|
|
||||||
token: localStorage.getItem("access_token")
|
|
||||||
}
|
|
||||||
}):
|
|
||||||
io({
|
|
||||||
path:"/sock/socket.io",
|
|
||||||
transports: ['websocket'],
|
|
||||||
auth: {
|
|
||||||
token: localStorage.getItem("access_token")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -162,16 +145,16 @@ const PageRouting = ({ getStatus }:{ getStatus:()=>void }) => {
|
|||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
getStatus()
|
getStatus()
|
||||||
socket.on("update", (data) => {
|
socketio.on("update", (data) => {
|
||||||
queryClient.invalidateQueries({ queryKey: data })
|
queryClient.invalidateQueries({ queryKey: data })
|
||||||
})
|
})
|
||||||
socket.on("connect_error", (err) => {
|
socketio.on("connect_error", (err) => {
|
||||||
errorNotify("Socket.Io connection failed! ",`Error message: [${err.message}]`)
|
errorNotify("Socket.Io connection failed! ",`Error message: [${err.message}]`)
|
||||||
getStatus()
|
getStatus()
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("update")
|
socketio.off("update")
|
||||||
socket.off("connect_error")
|
socketio.off("connect_error")
|
||||||
}
|
}
|
||||||
},[])
|
},[])
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ function HeaderPage(props: any) {
|
|||||||
|
|
||||||
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
||||||
const [resetFiregexModal, setResetFiregexModal] = useState(false);
|
const [resetFiregexModal, setResetFiregexModal] = useState(false);
|
||||||
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
|
|
||||||
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
|
|
||||||
return <AppShell.Header className="firegex__header__header" {...props}>
|
return <AppShell.Header className="firegex__header__header" {...props}>
|
||||||
<Burger
|
<Burger
|
||||||
hiddenFrom='md'
|
hiddenFrom='md'
|
||||||
@@ -64,19 +62,16 @@ function HeaderPage(props: any) {
|
|||||||
<Menu.Item color="red" leftSection={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
<Menu.Item color="red" leftSection={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
||||||
</MenuDropDownWithButton>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md" />
|
<Space w="md" />
|
||||||
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
|
<Tooltip label="Home" position='bottom' color="teal">
|
||||||
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
||||||
size="xl" radius="md" variant="filled"
|
size="xl" radius="md" variant="filled"
|
||||||
onClick={go_to_home}
|
onClick={go_to_home}>
|
||||||
onFocus={() => setTooltipHomeOpened(false)} onBlur={() => setTooltipHomeOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipHomeOpened(true)} onMouseLeave={() => setTooltipHomeOpened(false)}>
|
|
||||||
<AiFillHome size="25px" />
|
<AiFillHome size="25px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip label="Logout" position='bottom' color="blue" opened={tooltipLogoutOpened}>
|
<Tooltip label="Logout" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={logout_action} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={logout_action} size="xl" radius="md" variant="filled">
|
||||||
onFocus={() => setTooltipLogoutOpened(false)} onBlur={() => setTooltipLogoutOpened(false)}
|
<ImExit size={23} style={{marginTop:"3px", marginLeft:"2px"}}/></ActionIcon>
|
||||||
onMouseEnter={() => setTooltipLogoutOpened(true)} onMouseLeave={() => setTooltipLogoutOpened(false)}><ImExit size={23} style={{marginTop:"3px", marginLeft:"2px"}}/></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
|
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
|
||||||
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
|
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
|
||||||
|
|||||||
17
frontend/src/components/ModalLog.tsx
Normal file
17
frontend/src/components/ModalLog.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Code, Modal, ScrollArea } from "@mantine/core"
|
||||||
|
|
||||||
|
export const ModalLog = (
|
||||||
|
{ title, opened, close, data }:
|
||||||
|
{
|
||||||
|
title: string,
|
||||||
|
opened: boolean,
|
||||||
|
close: () => void,
|
||||||
|
data: string,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
return <Modal size="90%" title={title} opened={opened} onClose={close} centered>
|
||||||
|
<ScrollArea h={500} style={{ minWidth: "100%",whiteSpace: "pre-wrap"}}>
|
||||||
|
<Code block mih={500}>{data}</Code>
|
||||||
|
</ScrollArea>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
27
frontend/src/components/NFProxy/ExceptionWarning.tsx
Normal file
27
frontend/src/components/NFProxy/ExceptionWarning.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { IoIosWarning } from "react-icons/io"
|
||||||
|
import { socketio, WARNING_NFPROXY_TIME_LIMIT } from "../../js/utils"
|
||||||
|
import { Tooltip } from "@mantine/core"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
|
||||||
|
export const ExceptionWarning = ({ service_id }: { service_id: string }) => {
|
||||||
|
const [lastExceptionTimestamp, setLastExceptionTimestamp] = useState<number>(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socketio.emit("nfproxy-exception-join", { service: service_id });
|
||||||
|
socketio.on(`nfproxy-exception-${service_id}`, (data) => {
|
||||||
|
setLastExceptionTimestamp(data)
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
socketio.emit("nfproxy-exception-leave", { service: service_id });
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <>
|
||||||
|
{(new Date().getTime()-lastExceptionTimestamp <= WARNING_NFPROXY_TIME_LIMIT)?
|
||||||
|
<Tooltip label={`There was an exception less than ${WARNING_NFPROXY_TIME_LIMIT/(1000*60)} minutes ago: check the logs`} color="yellow">
|
||||||
|
<IoIosWarning size={30} style={{ color: "yellow" }} />
|
||||||
|
</Tooltip>
|
||||||
|
:null}
|
||||||
|
</>
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import { FaFilter } from "react-icons/fa";
|
|||||||
import { IoSettingsSharp } from 'react-icons/io5';
|
import { IoSettingsSharp } from 'react-icons/io5';
|
||||||
import AddEditService from '../AddEditService';
|
import AddEditService from '../AddEditService';
|
||||||
import { FaPencilAlt } from "react-icons/fa";
|
import { FaPencilAlt } from "react-icons/fa";
|
||||||
|
import { ExceptionWarning } from '../ExceptionWarning';
|
||||||
|
|
||||||
export default function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
export default function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
||||||
|
|
||||||
@@ -26,7 +27,6 @@ export default function ServiceRow({ service, onClick }:{ service:Service, onCli
|
|||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false)
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
const [renameModal, setRenameModal] = useState(false)
|
const [renameModal, setRenameModal] = useState(false)
|
||||||
const [editModal, setEditModal] = useState(false)
|
const [editModal, setEditModal] = useState(false)
|
||||||
@@ -109,6 +109,8 @@ export default function ServiceRow({ service, onClick }:{ service:Service, onCli
|
|||||||
</Box>
|
</Box>
|
||||||
{isMedium?<Space w="xl" />:<Space h="lg" />}
|
{isMedium?<Space w="xl" />:<Space h="lg" />}
|
||||||
<Box className="center-flex">
|
<Box className="center-flex">
|
||||||
|
<ExceptionWarning service_id={service.service_id} />
|
||||||
|
<Space w="sm"/>
|
||||||
<MenuDropDownWithButton>
|
<MenuDropDownWithButton>
|
||||||
<Menu.Item><b>Edit service</b></Menu.Item>
|
<Menu.Item><b>Edit service</b></Menu.Item>
|
||||||
<Menu.Item leftSection={<IoSettingsSharp size={18} />} onClick={()=>setEditModal(true)}>Service Settings</Menu.Item>
|
<Menu.Item leftSection={<IoSettingsSharp size={18} />} onClick={()=>setEditModal(true)}>Service Settings</Menu.Item>
|
||||||
@@ -118,13 +120,11 @@ export default function ServiceRow({ service, onClick }:{ service:Service, onCli
|
|||||||
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</MenuDropDownWithButton>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red">
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
onClick={stopService} size="xl" radius="md" variant="filled"
|
onClick={stopService} size="xl" radius="md" variant="filled"
|
||||||
disabled={service.status === "stop"}
|
disabled={service.status === "stop"}
|
||||||
aria-describedby="tooltip-stop-id"
|
aria-describedby="tooltip-stop-id">
|
||||||
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
|
|
||||||
<FaStop size="20px" />
|
<FaStop size="20px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export default function ServiceRow({ service, onClick }:{ service:Service, onCli
|
|||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false)
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
const [renameModal, setRenameModal] = useState(false)
|
const [renameModal, setRenameModal] = useState(false)
|
||||||
const [editModal, setEditModal] = useState(false)
|
const [editModal, setEditModal] = useState(false)
|
||||||
@@ -115,13 +114,11 @@ export default function ServiceRow({ service, onClick }:{ service:Service, onCli
|
|||||||
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</MenuDropDownWithButton>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red">
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
onClick={stopService} size="xl" radius="md" variant="filled"
|
onClick={stopService} size="xl" radius="md" variant="filled"
|
||||||
disabled={service.status === "stop"}
|
disabled={service.status === "stop"}
|
||||||
aria-describedby="tooltip-stop-id"
|
aria-describedby="tooltip-stop-id">
|
||||||
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
|
|
||||||
<FaStop size="20px" />
|
<FaStop size="20px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ActionIcon, Badge, Box, Divider, Menu, Space, Title, Tooltip } from '@mantine/core';
|
import { ActionIcon, Badge, Box, Divider, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||||
import React, { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { FaPlay, FaStop } from 'react-icons/fa';
|
import { FaPlay, FaStop } from 'react-icons/fa';
|
||||||
import { porthijack, Service } from '../utils';
|
import { porthijack, Service } from '../utils';
|
||||||
import YesNoModal from '../../YesNoModal';
|
import YesNoModal from '../../YesNoModal';
|
||||||
@@ -17,11 +17,9 @@ export default function ServiceRow({ service }:{ service:Service }) {
|
|||||||
let status_color = service.active ? "teal": "red"
|
let status_color = service.active ? "teal": "red"
|
||||||
|
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false)
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
const [renameModal, setRenameModal] = useState(false)
|
const [renameModal, setRenameModal] = useState(false)
|
||||||
const [changeDestModal, setChangeDestModal] = useState(false)
|
const [changeDestModal, setChangeDestModal] = useState(false)
|
||||||
const portInputRef = React.createRef<HTMLInputElement>()
|
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
@@ -113,13 +111,11 @@ export default function ServiceRow({ service }:{ service:Service }) {
|
|||||||
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</MenuDropDownWithButton>
|
</MenuDropDownWithButton>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red">
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
onClick={stopService} size="xl" radius="md" variant="filled"
|
onClick={stopService} size="xl" radius="md" variant="filled"
|
||||||
disabled={!service.active}
|
disabled={!service.active}
|
||||||
aria-describedby="tooltip-stop-id"
|
aria-describedby="tooltip-stop-id">
|
||||||
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
|
|
||||||
<FaStop size="20px" />
|
<FaStop size="20px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { FaPencilAlt } from 'react-icons/fa';
|
|||||||
|
|
||||||
export default function PyFilterView({ filterInfo }:{ filterInfo:PyFilter }) {
|
export default function PyFilterView({ filterInfo }:{ filterInfo:PyFilter }) {
|
||||||
|
|
||||||
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
|
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
|
|
||||||
const changeRegexStatus = () => {
|
const changeRegexStatus = () => {
|
||||||
@@ -24,24 +23,22 @@ export default function PyFilterView({ filterInfo }:{ filterInfo:PyFilter }) {
|
|||||||
|
|
||||||
return <Box my="sm" display="flex" style={{alignItems:"center"}}>
|
return <Box my="sm" display="flex" style={{alignItems:"center"}}>
|
||||||
|
|
||||||
<Text className="firegex__regexview__pyfilter_text" style={{ width: "100%", alignItems: "center"}} display="flex" >
|
<Box className="firegex__regexview__pyfilter_text" style={{ width: "100%", alignItems: "center"}} display="flex" >
|
||||||
<Badge size="sm" radius="lg" mr="sm" color={filterInfo.active?"lime":"red"} variant="filled" />
|
<Badge size="sm" radius="lg" mr="sm" color={filterInfo.active?"lime":"red"} variant="filled" />
|
||||||
{filterInfo.name}
|
{filterInfo.name}
|
||||||
<Box className='flex-spacer' />
|
<Box className='flex-spacer' />
|
||||||
<Space w="xs" />
|
|
||||||
{isMedium?<>
|
|
||||||
<Badge size="md" radius="md" color="yellow" variant="filled"><FaFilter style={{ marginBottom: -2, marginRight: 2}} /> {filterInfo.blocked_packets}</Badge>
|
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Badge size="md" radius="md" color="orange" variant="filled"><FaPencilAlt style={{ marginBottom: -1, marginRight: 2}} /> {filterInfo.edited_packets}</Badge>
|
{isMedium?<>
|
||||||
<Space w="lg" />
|
<Badge size="md" radius="md" color="yellow" variant="filled"><FaFilter style={{ marginBottom: -2, marginRight: 2}} /> {filterInfo.blocked_packets}</Badge>
|
||||||
</>:null}
|
<Space w="xs" />
|
||||||
<Tooltip label={filterInfo.active?"Deactivate":"Activate"} zIndex={0} color={filterInfo.active?"orange":"teal"} opened={statusTooltipOpened}>
|
<Badge size="md" radius="md" color="orange" variant="filled"><FaPencilAlt style={{ marginBottom: -1, marginRight: 2}} /> {filterInfo.edited_packets}</Badge>
|
||||||
<ActionIcon color={filterInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="lg" radius="md" variant="filled"
|
<Space w="lg" />
|
||||||
onFocus={() => setStatusTooltipOpened(false)} onBlur={() => setStatusTooltipOpened(false)}
|
</>:null}
|
||||||
onMouseEnter={() => setStatusTooltipOpened(true)} onMouseLeave={() => setStatusTooltipOpened(false)}
|
<Tooltip label={filterInfo.active?"Deactivate":"Activate"} zIndex={0} color={filterInfo.active?"orange":"teal"}>
|
||||||
>{filterInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
|
<ActionIcon color={filterInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="lg" radius="md" variant="filled">
|
||||||
</Tooltip>
|
{filterInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
|
||||||
</Text>
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
let regex_expr = b64decode(regexInfo.regex);
|
let regex_expr = b64decode(regexInfo.regex);
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
|
|
||||||
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
|
|
||||||
const clipboard = useClipboard({ timeout: 500 });
|
const clipboard = useClipboard({ timeout: 500 });
|
||||||
const isMedium = isMediumScreen();
|
|
||||||
|
|
||||||
const deleteRegex = () => {
|
const deleteRegex = () => {
|
||||||
nfregex.regexdelete(regexInfo.id).then(res => {
|
nfregex.regexdelete(regexInfo.id).then(res => {
|
||||||
@@ -54,18 +51,14 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
|||||||
}}>{regex_expr}</Text>
|
}}>{regex_expr}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label={regexInfo.active?"Deactivate":"Activate"} zIndex={0} color={regexInfo.active?"orange":"teal"} opened={statusTooltipOpened}>
|
<Tooltip label={regexInfo.active?"Deactivate":"Activate"} zIndex={0} color={regexInfo.active?"orange":"teal"}>
|
||||||
<ActionIcon color={regexInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="xl" radius="md" variant="filled"
|
<ActionIcon color={regexInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="xl" radius="md" variant="filled"
|
||||||
onFocus={() => setStatusTooltipOpened(false)} onBlur={() => setStatusTooltipOpened(false)}
|
|
||||||
onMouseEnter={() => setStatusTooltipOpened(true)} onMouseLeave={() => setStatusTooltipOpened(false)}
|
|
||||||
>{regexInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
|
>{regexInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Delete regex" zIndex={0} color="red" opened={deleteTooltipOpened} >
|
<Tooltip label="Delete regex" zIndex={0} color="red" >
|
||||||
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"
|
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled">
|
||||||
onFocus={() => setDeleteTooltipOpened(false)} onBlur={() => setDeleteTooltipOpened(false)}
|
<BsTrashFill size={22} /></ActionIcon>
|
||||||
onMouseEnter={() => setDeleteTooltipOpened(true)} onMouseLeave={() => setDeleteTooltipOpened(false)}
|
|
||||||
><BsTrashFill size={22} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" mt="sm" ml="xs">
|
<Box display="flex" mt="sm" ml="xs">
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import { showNotification } from "@mantine/notifications";
|
|||||||
import { ImCross } from "react-icons/im";
|
import { ImCross } from "react-icons/im";
|
||||||
import { TiTick } from "react-icons/ti"
|
import { TiTick } from "react-icons/ti"
|
||||||
import { Navigate } from "react-router-dom";
|
import { Navigate } from "react-router-dom";
|
||||||
import { nfregex } from "../components/NFRegex/utils";
|
|
||||||
import { ChangePassword, IpInterface, LoginResponse, PasswordSend, ServerResponse, ServerResponseToken, ServerStatusResponse } from "./models";
|
import { ChangePassword, IpInterface, LoginResponse, PasswordSend, ServerResponse, ServerResponseToken, ServerStatusResponse } from "./models";
|
||||||
import { Buffer } from "buffer"
|
import { Buffer } from "buffer"
|
||||||
import { QueryClient, useQuery } from "@tanstack/react-query";
|
import { QueryClient, useQuery } from "@tanstack/react-query";
|
||||||
import { useMediaQuery } from "@mantine/hooks";
|
import { useMediaQuery } from "@mantine/hooks";
|
||||||
import { nfproxy } from "../components/NFProxy/utils";
|
import { io } from "socket.io-client";
|
||||||
|
|
||||||
export const IS_DEV = import.meta.env.DEV
|
export const IS_DEV = import.meta.env.DEV
|
||||||
|
|
||||||
@@ -19,6 +18,24 @@ export const regex_port = "^([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|
|
|||||||
export const regex_range_port = "^(([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?)?)?$"
|
export const regex_range_port = "^(([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?)?)?$"
|
||||||
export const DEV_IP_BACKEND = "127.0.0.1:4444"
|
export const DEV_IP_BACKEND = "127.0.0.1:4444"
|
||||||
|
|
||||||
|
export const WARNING_NFPROXY_TIME_LIMIT = 1000*60*10 // 10 minutes
|
||||||
|
|
||||||
|
export const socketio = import.meta.env.DEV?
|
||||||
|
io("ws://"+DEV_IP_BACKEND, {
|
||||||
|
path:"/sock/socket.io",
|
||||||
|
transports: ['websocket'],
|
||||||
|
auth: {
|
||||||
|
token: localStorage.getItem("access_token")
|
||||||
|
}
|
||||||
|
}):
|
||||||
|
io({
|
||||||
|
path:"/sock/socket.io",
|
||||||
|
transports: ['websocket'],
|
||||||
|
auth: {
|
||||||
|
token: localStorage.getItem("access_token")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export const queryClient = new QueryClient({ defaultOptions: { queries: {
|
export const queryClient = new QueryClient({ defaultOptions: { queries: {
|
||||||
staleTime: Infinity
|
staleTime: Infinity
|
||||||
} }})
|
} }})
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ import { PiWallLight } from "react-icons/pi";
|
|||||||
|
|
||||||
export const Firewall = () => {
|
export const Firewall = () => {
|
||||||
|
|
||||||
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
|
|
||||||
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
|
|
||||||
const [tooltipApplyOpened, setTooltipApplyOpened] = useState(false);
|
|
||||||
const [tooltipSettingsOpened, setTooltipSettingsOpened] = useState(false);
|
|
||||||
const [currentPolicy, setCurrentPolicy] = useState<ActionType>(ActionType.ACCEPT)
|
const [currentPolicy, setCurrentPolicy] = useState<ActionType>(ActionType.ACCEPT)
|
||||||
const [tooltipAddRulOpened, setTooltipAddRulOpened] = useState(false)
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const rules = firewallRulesQuery()
|
const rules = firewallRulesQuery()
|
||||||
const [state, handlers] = useListState<Rule & {rule_id:string}>([]);
|
const [state, handlers] = useListState<Rule & {rule_id:string}>([]);
|
||||||
@@ -364,31 +359,22 @@ export const Firewall = () => {
|
|||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Badge size="md" radius="sm" color="green" variant="filled"><FaDirections style={{ marginBottom: -1, marginRight: 4}}/>Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge>
|
<Badge size="md" radius="sm" color="green" variant="filled"><FaDirections style={{ marginBottom: -1, marginRight: 4}}/>Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge>
|
||||||
<Space w="md" />
|
<Space w="md" />
|
||||||
<Tooltip label="Add a new rule" position='bottom' color="blue" opened={tooltipAddOpened}>
|
<Tooltip label="Add a new rule" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={emptyRuleAdd} size="lg" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={emptyRuleAdd} size="lg" radius="md" variant="filled"><BsPlusLg size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
|
<Tooltip label="Refresh" position='bottom' color="indigo">
|
||||||
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["firewall"])} size="lg" radius="md" variant="filled"
|
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["firewall"])} size="lg" radius="md" variant="filled"
|
||||||
loading={rules.isFetching}
|
loading={rules.isFetching}><TbReload size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Settings" position='bottom' color="cyan" opened={tooltipSettingsOpened}>
|
<Tooltip label="Settings" position='bottom' color="cyan">
|
||||||
<ActionIcon color="cyan" onClick={()=>setSettingsModal(true)} size="lg" radius="md" variant="filled"
|
<ActionIcon color="cyan" onClick={()=>setSettingsModal(true)} size="lg" radius="md" variant="filled"><IoSettingsSharp size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipSettingsOpened(false)} onBlur={() => setTooltipSettingsOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipSettingsOpened(true)} onMouseLeave={() => setTooltipSettingsOpened(false)}><IoSettingsSharp size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Apply" position='bottom' color="grape" opened={tooltipApplyOpened}>
|
<Tooltip label="Apply" position='bottom' color="grape">
|
||||||
<ActionIcon color="grape" onClick={applyChanges} size="lg" radius="md" variant="filled"
|
<ActionIcon color="grape" onClick={applyChanges} size="lg" radius="md" variant="filled" disabled={!valuesChanged}>
|
||||||
onFocus={() => setTooltipApplyOpened(false)} onBlur={() => setTooltipApplyOpened(false)}
|
<TiTick size={22} /></ActionIcon>
|
||||||
onMouseEnter={() => setTooltipApplyOpened(true)} onMouseLeave={() => setTooltipApplyOpened(false)}
|
|
||||||
disabled={!valuesChanged}
|
|
||||||
><TiTick size={22} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -424,10 +410,9 @@ export const Firewall = () => {
|
|||||||
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No rule found! Add one clicking the "+" buttons</Title>
|
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No rule found! Add one clicking the "+" buttons</Title>
|
||||||
<Space h="xl" /> <Space h="xl" />
|
<Space h="xl" /> <Space h="xl" />
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Add a new rule" color="blue" opened={tooltipAddRulOpened}>
|
<Tooltip label="Add a new rule" color="blue">
|
||||||
<ActionIcon color="blue" onClick={emptyRuleAdd} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={emptyRuleAdd} size="xl" radius="md" variant="filled">
|
||||||
onFocus={() => setTooltipAddRulOpened(false)} onBlur={() => setTooltipAddRulOpened(false)}
|
<BsPlusLg size="20px" /></ActionIcon>
|
||||||
onMouseEnter={() => setTooltipAddRulOpened(true)} onMouseLeave={() => setTooltipAddRulOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { ActionIcon, Box, Code, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
|
import { ActionIcon, Box, Code, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
|
||||||
import { Navigate, useNavigate, useParams } from 'react-router-dom';
|
import { Navigate, useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Badge, Divider, Menu } from '@mantine/core';
|
import { Badge, Divider, Menu } from '@mantine/core';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { FaFilter, FaPencilAlt, FaPlay, FaStop } from 'react-icons/fa';
|
import { FaFilter, FaPencilAlt, FaPlay, FaStop } from 'react-icons/fa';
|
||||||
import { nfproxy, nfproxyServiceFilterCodeQuery, nfproxyServicePyfiltersQuery, nfproxyServiceQuery, serviceQueryKey } from '../../components/NFProxy/utils';
|
import { nfproxy, nfproxyServiceFilterCodeQuery, nfproxyServicePyfiltersQuery, nfproxyServiceQuery, serviceQueryKey } from '../../components/NFProxy/utils';
|
||||||
import { MdDoubleArrow } from "react-icons/md"
|
import { MdDoubleArrow } from "react-icons/md"
|
||||||
import YesNoModal from '../../components/YesNoModal';
|
import YesNoModal from '../../components/YesNoModal';
|
||||||
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../js/utils';
|
import { errorNotify, isMediumScreen, okNotify, regex_ipv4, socketio } from '../../js/utils';
|
||||||
import { BsTrashFill } from 'react-icons/bs';
|
import { BsTrashFill } from 'react-icons/bs';
|
||||||
import { BiRename } from 'react-icons/bi'
|
import { BiRename } from 'react-icons/bi'
|
||||||
import RenameForm from '../../components/NFProxy/ServiceRow/RenameForm';
|
import RenameForm from '../../components/NFProxy/ServiceRow/RenameForm';
|
||||||
@@ -19,6 +19,10 @@ import PyFilterView from '../../components/PyFilterView';
|
|||||||
import { TbPlugConnected } from 'react-icons/tb';
|
import { TbPlugConnected } from 'react-icons/tb';
|
||||||
import { CodeHighlight } from '@mantine/code-highlight';
|
import { CodeHighlight } from '@mantine/code-highlight';
|
||||||
import { FaPython } from "react-icons/fa";
|
import { FaPython } from "react-icons/fa";
|
||||||
|
import { FiFileText } from "react-icons/fi";
|
||||||
|
import { ModalLog } from '../../components/ModalLog';
|
||||||
|
import { useListState } from '@mantine/hooks';
|
||||||
|
import { ExceptionWarning } from '../../components/NFProxy/ExceptionWarning';
|
||||||
|
|
||||||
export default function ServiceDetailsNFProxy() {
|
export default function ServiceDetailsNFProxy() {
|
||||||
|
|
||||||
@@ -31,11 +35,30 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
const [editModal, setEditModal] = useState(false)
|
const [editModal, setEditModal] = useState(false)
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
|
||||||
const [tooltipBackOpened, setTooltipBackOpened] = useState(false);
|
|
||||||
const filterCode = nfproxyServiceFilterCodeQuery(srv??"")
|
const filterCode = nfproxyServiceFilterCodeQuery(srv??"")
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
|
const [openLogModal, setOpenLogModal] = useState(false)
|
||||||
|
const [logData, logDataSetters] = useListState<string>([]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if (srv){
|
||||||
|
if (openLogModal){
|
||||||
|
socketio.emit("nfproxy-outstream-join", { service: srv });
|
||||||
|
socketio.on(`nfproxy-outstream-${srv}`, (data) => {
|
||||||
|
logDataSetters.append(data)
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
logDataSetters.setState([])
|
||||||
|
socketio.emit("nfproxy-outstream-leave", { service: srv });
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
logDataSetters.setState([])
|
||||||
|
socketio.emit("nfproxy-outstream-leave", { service: srv });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [openLogModal, srv])
|
||||||
|
|
||||||
if (services.isLoading) return <LoadingOverlay visible={true} />
|
if (services.isLoading) return <LoadingOverlay visible={true} />
|
||||||
if (!srv || !serviceInfo || filtersList.isError) return <Navigate to="/" replace />
|
if (!srv || !serviceInfo || filtersList.isError) return <Navigate to="/" replace />
|
||||||
@@ -101,6 +124,8 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
</Box>
|
</Box>
|
||||||
{isMedium?null:<Space h="md" />}
|
{isMedium?null:<Space h="md" />}
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
|
<ExceptionWarning service_id={srv} />
|
||||||
|
<Space w="sm" />
|
||||||
<Badge color={status_color} radius="md" size="xl" variant="filled" mr="sm">
|
<Badge color={status_color} radius="md" size="xl" variant="filled" mr="sm">
|
||||||
{serviceInfo.status}
|
{serviceInfo.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -115,7 +140,13 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
<Divider />
|
<Divider />
|
||||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||||
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||||
</MenuDropDownWithButton>
|
</MenuDropDownWithButton>
|
||||||
|
<Space w="md"/>
|
||||||
|
<Tooltip label="Show logs" zIndex={0} color="cyan">
|
||||||
|
<ActionIcon color="cyan" size="lg" radius="md" onClick={()=>setOpenLogModal(true)} loading={buttonLoading} variant="filled">
|
||||||
|
<FiFileText size="20px" />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{isMedium?null:<Space h="md" />}
|
{isMedium?null:<Space h="md" />}
|
||||||
@@ -133,23 +164,19 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
</Box>
|
</Box>
|
||||||
{isMedium?null:<Space h="xl" />}
|
{isMedium?null:<Space h="xl" />}
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Go back" zIndex={0} color="cyan" opened={tooltipBackOpened}>
|
<Tooltip label="Go back" zIndex={0} color="cyan">
|
||||||
<ActionIcon color="cyan"
|
<ActionIcon color="cyan"
|
||||||
onClick={() => navigate("/")} size="xl" radius="md" variant="filled"
|
onClick={() => navigate("/")} size="xl" radius="md" variant="filled"
|
||||||
aria-describedby="tooltip-back-id"
|
aria-describedby="tooltip-back-id">
|
||||||
onFocus={() => setTooltipBackOpened(false)} onBlur={() => setTooltipBackOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipBackOpened(true)} onMouseLeave={() => setTooltipBackOpened(false)}>
|
|
||||||
<FaArrowLeft size="25px" />
|
<FaArrowLeft size="25px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red">
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
onClick={stopService} size="xl" radius="md" variant="filled"
|
onClick={stopService} size="xl" radius="md" variant="filled"
|
||||||
disabled={serviceInfo.status === "stop"}
|
disabled={serviceInfo.status === "stop"}
|
||||||
aria-describedby="tooltip-stop-id"
|
aria-describedby="tooltip-stop-id">
|
||||||
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
|
|
||||||
<FaStop size="20px" />
|
<FaStop size="20px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -177,7 +204,7 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
<Title className='center-flex' style={{textAlign:"center"}} order={3}>Install the firegex client:<Space w="xs" /><Code mb={-4} >pip install fgex</Code></Title>
|
<Title className='center-flex' style={{textAlign:"center"}} order={3}>Install the firegex client:<Space w="xs" /><Code mb={-4} >pip install fgex</Code></Title>
|
||||||
<Space h="xs" />
|
<Space h="xs" />
|
||||||
<Title className='center-flex' style={{textAlign:"center"}} order={3}>Then run the command:<Space w="xs" /><Code mb={-4} >fgex nfproxy</Code></Title>
|
<Title className='center-flex' style={{textAlign:"center"}} order={3}>Then run the command:<Space w="xs" /><Code mb={-4} >fgex nfproxy</Code></Title>
|
||||||
</>:<>{filtersList.data?.map( (filterInfo) => <PyFilterView filterInfo={filterInfo} />)}</>
|
</>:<>{filtersList.data?.map( (filterInfo) => <PyFilterView filterInfo={filterInfo} key={filterInfo.name}/>)}</>
|
||||||
}
|
}
|
||||||
<YesNoModal
|
<YesNoModal
|
||||||
title='Are you sure to delete this service?'
|
title='Are you sure to delete this service?'
|
||||||
@@ -196,5 +223,11 @@ export default function ServiceDetailsNFProxy() {
|
|||||||
onClose={()=>setEditModal(false)}
|
onClose={()=>setEditModal(false)}
|
||||||
edit={serviceInfo}
|
edit={serviceInfo}
|
||||||
/>
|
/>
|
||||||
|
<ModalLog
|
||||||
|
opened={openLogModal}
|
||||||
|
close={()=>setOpenLogModal(false)}
|
||||||
|
title={`Logs for service ${serviceInfo.name}`}
|
||||||
|
data={logData.join("")}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ export default function NFProxy({ children }: { children: any }) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const {srv} = useParams()
|
const {srv} = useParams()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
|
|
||||||
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
|
|
||||||
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
|
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
const services = nfproxyServiceQuery()
|
const services = nfproxyServiceQuery()
|
||||||
const fileDialog = useFileDialog({
|
const fileDialog = useFileDialog({
|
||||||
@@ -116,27 +113,22 @@ export default function NFProxy({ children }: { children: any }) {
|
|||||||
{isMedium?null:<Space h="md" />}
|
{isMedium?null:<Space h="md" />}
|
||||||
<Box className='center-flex' >
|
<Box className='center-flex' >
|
||||||
{ srv?
|
{ srv?
|
||||||
<Tooltip label="Upload a new filter code" position='bottom' color="blue" opened={tooltipAddOpened}>
|
<Tooltip label="Upload a new filter code" position='bottom' color="blue">
|
||||||
<ActionIcon
|
<ActionIcon color="blue" size="lg" radius="md" variant="filled" onClick={fileDialog.open}>
|
||||||
color="blue" size="lg" radius="md" variant="filled"
|
<MdUploadFile size={18} />
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)}
|
|
||||||
onMouseLeave={() => setTooltipAddOpened(false)} onClick={fileDialog.open}>
|
|
||||||
<MdUploadFile size={18} />
|
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
: <Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}>
|
: <Tooltip label="Add a new service" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled">
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
<BsPlusLg size={18} />
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
|
<Tooltip label="Refresh" position='bottom' color="indigo">
|
||||||
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["nfproxy"])} size="lg" radius="md" variant="filled"
|
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["nfproxy"])} size="lg" radius="md" variant="filled" loading={services.isFetching}>
|
||||||
loading={services.isFetching}
|
<TbReload size={18} />
|
||||||
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
|
</ActionIcon>
|
||||||
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -148,10 +140,10 @@ export default function NFProxy({ children }: { children: any }) {
|
|||||||
navigator("/nfproxy/"+srv.service_id)
|
navigator("/nfproxy/"+srv.service_id)
|
||||||
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
|
<Tooltip label="Add a new service" color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled">
|
||||||
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
|
<BsPlusLg size="20px" />
|
||||||
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>}
|
||||||
|
|||||||
@@ -27,15 +27,12 @@ export default function ServiceDetailsNFRegex() {
|
|||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const services = nfregexServiceQuery()
|
const services = nfregexServiceQuery()
|
||||||
const serviceInfo = services.data?.find(s => s.service_id == srv)
|
const serviceInfo = services.data?.find(s => s.service_id == srv)
|
||||||
const [tooltipAddRegexOpened, setTooltipAddRegexOpened] = useState(false)
|
|
||||||
const regexesList = nfregexServiceRegexesQuery(srv??"")
|
const regexesList = nfregexServiceRegexesQuery(srv??"")
|
||||||
const [deleteModal, setDeleteModal] = useState(false)
|
const [deleteModal, setDeleteModal] = useState(false)
|
||||||
const [renameModal, setRenameModal] = useState(false)
|
const [renameModal, setRenameModal] = useState(false)
|
||||||
const [editModal, setEditModal] = useState(false)
|
const [editModal, setEditModal] = useState(false)
|
||||||
const [buttonLoading, setButtonLoading] = useState(false)
|
const [buttonLoading, setButtonLoading] = useState(false)
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
|
||||||
const [tooltipBackOpened, setTooltipBackOpened] = useState(false);
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
|
|
||||||
@@ -133,23 +130,19 @@ export default function ServiceDetailsNFRegex() {
|
|||||||
</Box>
|
</Box>
|
||||||
{isMedium?null:<Space h="xl" />}
|
{isMedium?null:<Space h="xl" />}
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Go back" zIndex={0} color="cyan" opened={tooltipBackOpened}>
|
<Tooltip label="Go back" zIndex={0} color="cyan">
|
||||||
<ActionIcon color="cyan"
|
<ActionIcon color="cyan"
|
||||||
onClick={() => navigate("/")} size="xl" radius="md" variant="filled"
|
onClick={() => navigate("/")} size="xl" radius="md" variant="filled"
|
||||||
aria-describedby="tooltip-back-id"
|
aria-describedby="tooltip-back-id">
|
||||||
onFocus={() => setTooltipBackOpened(false)} onBlur={() => setTooltipBackOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipBackOpened(true)} onMouseLeave={() => setTooltipBackOpened(false)}>
|
|
||||||
<FaArrowLeft size="25px" />
|
<FaArrowLeft size="25px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="md"/>
|
<Space w="md"/>
|
||||||
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
|
<Tooltip label="Stop service" zIndex={0} color="red">
|
||||||
<ActionIcon color="red" loading={buttonLoading}
|
<ActionIcon color="red" loading={buttonLoading}
|
||||||
onClick={stopService} size="xl" radius="md" variant="filled"
|
onClick={stopService} size="xl" radius="md" variant="filled"
|
||||||
disabled={serviceInfo.status === "stop"}
|
disabled={serviceInfo.status === "stop"}
|
||||||
aria-describedby="tooltip-stop-id"
|
aria-describedby="tooltip-stop-id">
|
||||||
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
|
|
||||||
<FaStop size="20px" />
|
<FaStop size="20px" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -168,11 +161,9 @@ export default function ServiceDetailsNFRegex() {
|
|||||||
<Title className='center-flex' style={{textAlign:"center"}} order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
|
<Title className='center-flex' style={{textAlign:"center"}} order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
|
||||||
<Space h="xl" /> <Space h="xl" />
|
<Space h="xl" /> <Space h="xl" />
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Add a new regex" zIndex={0} color="blue" opened={tooltipAddRegexOpened}>
|
<Tooltip label="Add a new regex" zIndex={0} color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
||||||
aria-describedby="tooltip-AddRegex-id"
|
aria-describedby="tooltip-AddRegex-id"><BsPlusLg size="20px" /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</>:
|
</>:
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ function NFRegex({ children }: { children: any }) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const {srv} = useParams()
|
const {srv} = useParams()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
|
|
||||||
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
|
|
||||||
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
|
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
const services = nfregexServiceQuery()
|
const services = nfregexServiceQuery()
|
||||||
|
|
||||||
@@ -50,23 +47,17 @@ function NFRegex({ children }: { children: any }) {
|
|||||||
{isMedium?null:<Space h="md" />}
|
{isMedium?null:<Space h="md" />}
|
||||||
<Box className='center-flex' >
|
<Box className='center-flex' >
|
||||||
{ srv?
|
{ srv?
|
||||||
<Tooltip label="Add a new regex" position='bottom' color="blue" opened={tooltipAddOpened}>
|
<Tooltip label="Add a new regex" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"><BsPlusLg size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
: <Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}>
|
: <Tooltip label="Add a new service" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"><BsPlusLg size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
|
<Tooltip label="Refresh" position='bottom' color="indigo">
|
||||||
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["nfregex"])} size="lg" radius="md" variant="filled"
|
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["nfregex"])} size="lg" radius="md" variant="filled"
|
||||||
loading={services.isFetching}
|
loading={services.isFetching}><TbReload size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -78,10 +69,8 @@ function NFRegex({ children }: { children: any }) {
|
|||||||
navigator("/nfregex/"+srv.service_id)
|
navigator("/nfregex/"+srv.service_id)
|
||||||
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
|
<Tooltip label="Add a new service" color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"><BsPlusLg size="20px" /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>}
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ import { GrDirections } from 'react-icons/gr';
|
|||||||
function PortHijack() {
|
function PortHijack() {
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
|
|
||||||
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
|
|
||||||
const isMedium = isMediumScreen()
|
const isMedium = isMediumScreen()
|
||||||
|
|
||||||
const services = porthijackServiceQuery()
|
const services = porthijackServiceQuery()
|
||||||
@@ -37,17 +34,13 @@ function PortHijack() {
|
|||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Badge size="md" radius="sm" color="yellow" variant="filled"><FaServer style={{ marginBottom: -1, marginRight: 4}} />Services: {services.isLoading?0:services.data?.length}</Badge>
|
<Badge size="md" radius="sm" color="yellow" variant="filled"><FaServer style={{ marginBottom: -1, marginRight: 4}} />Services: {services.isLoading?0:services.data?.length}</Badge>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}>
|
<Tooltip label="Add a new service" position='bottom' color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"><BsPlusLg size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Space w="xs" />
|
<Space w="xs" />
|
||||||
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
|
<Tooltip label="Refresh" position='bottom' color="indigo">
|
||||||
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["porthijack"])} size="lg" radius="md" variant="filled"
|
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["porthijack"])} size="lg" radius="md" variant="filled"
|
||||||
loading={services.isFetching}
|
loading={services.isFetching}><TbReload size={18} /></ActionIcon>
|
||||||
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -57,10 +50,8 @@ function PortHijack() {
|
|||||||
{(services.data && services.data.length > 0) ?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} />):<>
|
{(services.data && services.data.length > 0) ?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} />):<>
|
||||||
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
|
||||||
<Box className='center-flex'>
|
<Box className='center-flex'>
|
||||||
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
|
<Tooltip label="Add a new service" color="blue">
|
||||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"><BsPlusLg size="20px" /></ActionIcon>
|
||||||
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
|
|
||||||
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ else:
|
|||||||
|
|
||||||
def exit_test(code):
|
def exit_test(code):
|
||||||
if service_id:
|
if service_id:
|
||||||
server.stop()
|
server.kill()
|
||||||
if(firegex.nf_delete_service(service_id)):
|
if(firegex.nf_delete_service(service_id)):
|
||||||
puts("Sucessfully deleted service ✔", color=colors.green)
|
puts("Sucessfully deleted service ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -27,5 +27,10 @@ python3 ph_test.py -p $PASSWORD -m udp || ERROR=1
|
|||||||
echo "Running Port Hijack UDP ipv6"
|
echo "Running Port Hijack UDP ipv6"
|
||||||
python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1
|
python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1
|
||||||
|
|
||||||
|
if [[ "$ERROR" == "0" ]] then
|
||||||
|
python3 benchmark.py -p $PASSWORD -r 5 -d 1 -s 10 || ERROR=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
exit $ERROR
|
exit $ERROR
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user