move files around
This commit is contained in:
parent
4594215a23
commit
a7540a9504
45 changed files with 21 additions and 3337 deletions
10
ESPFirewall/.vscode/extensions.json
vendored
Normal file
10
ESPFirewall/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
9
ESPFirewall/README.md
Normal file
9
ESPFirewall/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# IoT Firewall on ESP8266/ESP32
|
||||
|
||||
Student: Florian Hoss
|
||||
|
||||
[flhoit00@hs-esslingen.de](mailto:flhoit00@hs-esslingen.de)
|
||||
|
||||
Professor: Prof. Dr. rer. nat. Tobias Heer
|
||||
|
||||
[tobias.heer@hs-esslingen.de](mailto:tobias.heer@hs-esslingen.de)
|
9
ESPFirewall/include/theSecrets-example.h
Normal file
9
ESPFirewall/include/theSecrets-example.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef THESECRETS_H
|
||||
#define THESECRETS_H
|
||||
|
||||
const char *ssid = "Wifi";
|
||||
const char *psk = "password";
|
||||
const char *api_username = "username";
|
||||
const char *api_password = "password";
|
||||
|
||||
#endif
|
9
ESPFirewall/lib/Firewall/docs/bib/book.bib
Normal file
9
ESPFirewall/lib/Firewall/docs/bib/book.bib
Normal file
|
@ -0,0 +1,9 @@
|
|||
@book{example-book,
|
||||
title = {{Example Book}},
|
||||
author = {Lastname, Surname},
|
||||
isbn = {123456789},
|
||||
address = {Germany},
|
||||
publisher = {Goverment},
|
||||
year = {2019},
|
||||
edition = {Second}
|
||||
}
|
23
ESPFirewall/lib/Firewall/docs/bib/online.bib
Normal file
23
ESPFirewall/lib/Firewall/docs/bib/online.bib
Normal file
|
@ -0,0 +1,23 @@
|
|||
@online{pio-install,
|
||||
author = {PlatformIO},
|
||||
title = {Installation},
|
||||
urldate = {2022-03-24},
|
||||
year = {2022},
|
||||
url = {https://docs.platformio.org/en/latest//core/installation.html}
|
||||
}
|
||||
|
||||
@online{pio-about,
|
||||
author = {PlatformIO},
|
||||
title = {What is PlatformIO},
|
||||
urldate = {2022-03-24},
|
||||
year = {2022},
|
||||
url = {https://docs.platformio.org/en/latest//what-is-platformio.html}
|
||||
}
|
||||
|
||||
@online{vscode-about,
|
||||
author = {Visual Studio Code},
|
||||
title = {Getting Started},
|
||||
urldate = {2022-03-24},
|
||||
year = {2022},
|
||||
url = {https://docs.platformio.org/en/latest//what-is-platformio.html}
|
||||
}
|
21
ESPFirewall/lib/Firewall/docs/firststeps/firststeps.tex
Normal file
21
ESPFirewall/lib/Firewall/docs/firststeps/firststeps.tex
Normal file
|
@ -0,0 +1,21 @@
|
|||
\section{PlatformIO Core}
|
||||
|
||||
There are several ways to compile and upload code to a Mikrocontroller. The first Tests where written and compiled in the Arduino IDE\footnote{\href{https://www.arduino.cc/en/software}{https://www.arduino.cc/en/software}}. Because references and cross-platform developing was not easy with this approach, developing in this project will be done with the help of PlatformIO.
|
||||
|
||||
``PlatformIO is a must-have tool for professional embedded systems engineers who develop solutions on more than one specific platform. In addition, by having a decentralized architecture, PlatformIO offers both new and existing developers a quick integration path for developing commercial-ready products, and reduces the overall time-to-market. [...] The build system structure automatically tags software dependencies and applies them using a modular hierarchy that takes away the usual complexity and pain. Developers no longer have to manually find and assemble an environment of toolchains, compilers, and library dependencies to develop applications for a specific target. With PlatformIO, clicking the compile button will bring in all necessary dependencies automatically.'
|
||||
|
||||
\cite[cf.][]{pio-about}
|
||||
|
||||
\subsubsection{Install}
|
||||
|
||||
A dependency of PlatformIO is Python\footnote{\href{https://www.python.org/}{https://www.python.org/}}. To install the latest version of python, follow the installation instructions on their \href{https://www.python.org/}{website}.
|
||||
|
||||
PlatformIO can now simply installed by downloading a script called ``get-platformio.py'' and executing it. On Apple MacOS\footnote{\href{https://en.wikipedia.org/wiki/MacOS}{https://en.wikipedia.org/wiki/MacOS}} it can simply be installed with the help of Homebrew Packages Manager\footnote{\href{https://brew.sh/}{https://brew.sh/}}.
|
||||
|
||||
\subsubsection{Configure IDE}
|
||||
|
||||
Developing and writing code for this project will be done in Visual Studio Code\footnote{\href{https://code.visualstudio.com/}{https://code.visualstudio.com/}}.
|
||||
|
||||
``Visual Studio Code is a lightweight but powerful source code editor which runs on your desktop and is available for Windows, macOS and Linux. It comes with built-in support for JavaScript, TypeScript and Node.js and has a rich ecosystem of extensions for other languages (such as C++, C\#, Java, Python, PHP, Go) and runtimes (such as .NET and Unity).''
|
||||
|
||||
\cite[][]{vscode-about}
|
1400
ESPFirewall/lib/Firewall/docs/images/hs-esslingen.eps
Normal file
1400
ESPFirewall/lib/Firewall/docs/images/hs-esslingen.eps
Normal file
File diff suppressed because it is too large
Load diff
111
ESPFirewall/lib/Firewall/docs/main.tex
Normal file
111
ESPFirewall/lib/Firewall/docs/main.tex
Normal file
|
@ -0,0 +1,111 @@
|
|||
\documentclass[
|
||||
a4paper,
|
||||
oneside,
|
||||
parskip=half,
|
||||
listof=entryprefix,
|
||||
listof=totoc,
|
||||
index=totoc,
|
||||
bibliography=totoc
|
||||
]{scrartcl}
|
||||
|
||||
\usepackage{silence}
|
||||
\WarningFilter{biblatex}{File 'british-iso.lbx'}
|
||||
\WarningFilter{biblatex}{'\mainlang'}
|
||||
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[british]{babel}
|
||||
\usepackage[T1]{fontenc}
|
||||
|
||||
\usepackage{pdfpages,graphicx,subcaption,lastpage}
|
||||
\graphicspath{ {./images} }
|
||||
|
||||
\usepackage{geometry}
|
||||
\geometry{a4paper, top=2.5cm, left=2.5cm, right=2.5cm, bottom=2.5cm}
|
||||
\usepackage{float,listings,xcolor,csquotes,microtype,scrlayer-scrpage,etoolbox}
|
||||
\usepackage[official]{eurosym}
|
||||
|
||||
\definecolor{codegreen}{rgb}{0,0.6,0}
|
||||
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
|
||||
\definecolor{codepurple}{rgb}{0.58,0,0.82}
|
||||
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
|
||||
\definecolor{weborange}{rgb}{1,0.65,0}
|
||||
|
||||
\lstdefinestyle{mystyle}{
|
||||
backgroundcolor=\color{backcolour},
|
||||
commentstyle=\color{codegreen},
|
||||
keywordstyle=\color{magenta},
|
||||
numberstyle=\tiny\color{codegray},
|
||||
stringstyle=\color{codepurple},
|
||||
emph={int,char,double,float,unsigned,void,bool},
|
||||
emphstyle={\color{weborange}},
|
||||
basicstyle=\ttfamily\footnotesize,
|
||||
breakatwhitespace=false,
|
||||
breaklines=true,
|
||||
captionpos=b,
|
||||
keepspaces=true,
|
||||
numbers=left,
|
||||
numbersep=5pt,
|
||||
showspaces=false,
|
||||
showstringspaces=false,
|
||||
showtabs=false,
|
||||
tabsize=2,
|
||||
firstnumber=1,
|
||||
}
|
||||
\lstset{style=mystyle}
|
||||
|
||||
\setuptoc{toc}{totoc}
|
||||
|
||||
\usepackage[
|
||||
backend=biber,
|
||||
urldate=long,
|
||||
style=iso-authoryear,
|
||||
natbib=true,
|
||||
useauthor=true,
|
||||
mincitenames=1,
|
||||
maxcitenames=3
|
||||
]{biblatex}
|
||||
\addbibresource{bib/online.bib}
|
||||
\addbibresource{bib/book.bib}
|
||||
|
||||
|
||||
\DeclareNameAlias{default}{family-given/given-family}
|
||||
|
||||
\renewcommand*{\finalnamedelim}{\addspace{}und\space}
|
||||
\AtEveryCite{
|
||||
\renewcommand*{\multinamedelim}{,\space}
|
||||
\renewcommand*{\nameyeardelim}{\space}
|
||||
}
|
||||
|
||||
\AtBeginBibliography{
|
||||
\renewcommand*{\multinamedelim}{,\space}
|
||||
}
|
||||
\AfterTOCHead[lof]{\appto\autodot{:}}
|
||||
|
||||
\definecolor{must-have-brown}{RGB}{51, 22, 18}
|
||||
\definecolor{should-have-red}{RGB}{229, 24, 31}
|
||||
\definecolor{could-have-orange}{RGB}{246, 121, 28}
|
||||
\definecolor{wont-have-blue}{RGB}{32, 169, 215}
|
||||
\newcommand{\morequirement}[1]{\paragraph{#1}\hfill\textbf{\textcolor{must-have-brown}{must-have}}\\}
|
||||
\newcommand{\srequirement}[1]{\paragraph{#1}\hfill\textbf{\textcolor{should-have-red}{should-have}}\\}
|
||||
\newcommand{\corequirement}[1]{\paragraph{#1}\hfill\textbf{\textcolor{could-have-orange}{could-have}}\\}
|
||||
\newcommand{\wrequirement}[1]{\paragraph{#1}\hfill\textbf{\textcolor{wont-have-blue}{won't-have}}\\}
|
||||
|
||||
\ihead{Example Header}
|
||||
\chead{}
|
||||
\ohead{Surname Lastname}
|
||||
\ofoot{Seite~\thepage{}/\pageref{LastPage}}
|
||||
\cfoot{}
|
||||
\title{Example Title}
|
||||
\usepackage[breaklinks,colorlinks,linkcolor=black,citecolor=black,filecolor=black,urlcolor=black]{hyperref}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\include{titlepage/titlepage}
|
||||
\tableofcontents
|
||||
\newpage
|
||||
\listoffigures
|
||||
\include{firststeps/firststeps}
|
||||
|
||||
\printbibliography[title=Literaturverzeichnis]
|
||||
|
||||
\end{document}
|
28
ESPFirewall/lib/Firewall/docs/titlepage/titlepage.tex
Normal file
28
ESPFirewall/lib/Firewall/docs/titlepage/titlepage.tex
Normal file
|
@ -0,0 +1,28 @@
|
|||
\newcommand{\HRule}[2]{\noindent\rule[#1]{\linewidth}{#2}}
|
||||
\newcommand{\vlinespace}[1]{\vspace*{#1\baselineskip}}
|
||||
\newcommand{\titleemph}[1]{\textbf{#1}}
|
||||
\begin{titlepage}
|
||||
\sffamily
|
||||
\hfill
|
||||
\includegraphics[width=5cm]{hs-esslingen}
|
||||
\HRule{13pt}{1pt}
|
||||
\centering
|
||||
\Large
|
||||
\vlinespace{5}\\
|
||||
Student research project\\
|
||||
\huge
|
||||
\textbf{IoT Firewall\\for ESP8266/ESP32}\\
|
||||
\Large
|
||||
\vlinespace{5}
|
||||
Software Engineering course\\
|
||||
of the Faculty of Information Technology\\
|
||||
in the 6th semester\\
|
||||
\vlinespace{6}
|
||||
\huge
|
||||
\textbf{Florian Hoss}\\
|
||||
\Large
|
||||
\vfill
|
||||
\raggedright{}
|
||||
\HRule{13pt}{1pt} \\
|
||||
\titleemph{Dozent:} Prof. Dr. rer. nat. Tobias Heer
|
||||
\end{titlepage}
|
11
ESPFirewall/lib/Firewall/library.json
Normal file
11
ESPFirewall/lib/Firewall/library.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "firewall",
|
||||
"license": "MIT",
|
||||
"version": "0.0.1",
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["espressif32"],
|
||||
"dependencies": {
|
||||
"bblanchon/ArduinoJson": "^6.19.4",
|
||||
"external-repo": "https://github.com/fhessel/esp32_https_server/pull/91"
|
||||
}
|
||||
}
|
57
ESPFirewall/lib/Firewall/src/Utils.cpp
Normal file
57
ESPFirewall/lib/Firewall/src/Utils.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "Utils.hpp"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
String protocol_to_string(firewall_protocol_t &protocol)
|
||||
{
|
||||
switch (protocol)
|
||||
{
|
||||
case FW_TCP:
|
||||
return "TCP";
|
||||
case FW_UDP:
|
||||
return "UDP";
|
||||
default:
|
||||
return "ALL";
|
||||
}
|
||||
}
|
||||
|
||||
firewall_protocol_t string_to_protocol(std::string &protocol)
|
||||
{
|
||||
if (protocol.compare("TCP") == 0)
|
||||
return FW_TCP;
|
||||
else if (protocol.compare("UDP") == 0)
|
||||
return FW_UDP;
|
||||
else
|
||||
return FW_ALL;
|
||||
}
|
||||
|
||||
String target_to_string(firewall_target_t &target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case FW_REJECT:
|
||||
return "REJECT";
|
||||
case FW_DROP:
|
||||
return "DROP";
|
||||
default:
|
||||
return "ACCEPT";
|
||||
}
|
||||
}
|
||||
|
||||
firewall_target_t string_to_target(std::string &target)
|
||||
{
|
||||
if (target.compare("REJECT") == 0)
|
||||
return FW_REJECT;
|
||||
else if (target.compare("DROP") == 0)
|
||||
return FW_DROP;
|
||||
else
|
||||
return FW_ACCEPT;
|
||||
}
|
||||
|
||||
void endless_loop()
|
||||
{
|
||||
log_e("Something went wrong. Running endless loop until fixed...");
|
||||
while (true)
|
||||
sleep(500);
|
||||
}
|
||||
}
|
56
ESPFirewall/lib/Firewall/src/Utils.hpp
Normal file
56
ESPFirewall/lib/Firewall/src/Utils.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef UTILS_HPP
|
||||
#define UTILS_HPP
|
||||
|
||||
#include "string"
|
||||
#include "WString.h"
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
static const uint8_t IPV4ADDRESS_LENGTH = 16;
|
||||
|
||||
typedef enum firewall_targets : uint8_t
|
||||
{
|
||||
FW_REJECT = 0,
|
||||
FW_DROP = 1,
|
||||
FW_ACCEPT = 2,
|
||||
} firewall_target_t;
|
||||
|
||||
typedef enum firewall_protocols : uint8_t
|
||||
{
|
||||
FW_TCP = 0,
|
||||
FW_UDP = 1,
|
||||
FW_ALL = 255,
|
||||
} firewall_protocol_t;
|
||||
|
||||
typedef enum ok : uint8_t
|
||||
{
|
||||
SUCCESS = 0,
|
||||
ERROR = 1,
|
||||
NO_ACTION = 2,
|
||||
} ok_t;
|
||||
|
||||
typedef enum auth : uint8_t
|
||||
{
|
||||
AUTHENTICATED = 0,
|
||||
DENIED = 1,
|
||||
} auth_t;
|
||||
|
||||
typedef struct firewall_rules
|
||||
{
|
||||
uint8_t key;
|
||||
char source[IPV4ADDRESS_LENGTH];
|
||||
char destination[IPV4ADDRESS_LENGTH];
|
||||
firewall_protocol_t protocol;
|
||||
firewall_target_t target;
|
||||
struct firewall_rules *next;
|
||||
} firewall_rule_t;
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
String protocol_to_string(firewall_protocol_t &protocol);
|
||||
firewall_protocol_t string_to_protocol(std::string &protocol);
|
||||
String target_to_string(firewall_target_t &target);
|
||||
firewall_target_t string_to_target(std::string &target);
|
||||
void endless_loop();
|
||||
}
|
||||
|
||||
#endif
|
235
ESPFirewall/lib/Firewall/src/esp32API.cpp
Normal file
235
ESPFirewall/lib/Firewall/src/esp32API.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
#include "esp32API.hpp"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
API::API(const char *username, const char *password, const uint16_t port)
|
||||
{
|
||||
if (this->setup_auth(username, password) == ERROR)
|
||||
endless_loop();
|
||||
if (this->setup_certificate() == ERROR)
|
||||
endless_loop();
|
||||
this->server = new HTTPSServer(this->certificate, port, 5);
|
||||
this->setup_routing();
|
||||
log_i("Starting server...");
|
||||
this->server->start();
|
||||
if (this->server->isRunning())
|
||||
log_i("Server ready on port: %i", port);
|
||||
}
|
||||
|
||||
API::~API()
|
||||
{
|
||||
}
|
||||
|
||||
void API::handle_clients()
|
||||
{
|
||||
this->server->loop();
|
||||
}
|
||||
|
||||
ok_t API::setup_auth(const char *username, const char *password)
|
||||
{
|
||||
if (!username || *username == 0x00 || strlen(username) > sizeof(this->username))
|
||||
{
|
||||
log_e("Username too long or missing!");
|
||||
return ERROR;
|
||||
}
|
||||
strncpy(this->username, username, sizeof(this->username));
|
||||
if (!password || *password == 0x00 || strlen(password) > sizeof(this->password))
|
||||
{
|
||||
log_e("Password too long or missing!");
|
||||
return ERROR;
|
||||
}
|
||||
strncpy(this->password, password, sizeof(this->password));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
auth_t API::check_auth(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
std::string reqUsername = request->getBasicAuthUser();
|
||||
std::string reqPassword = request->getBasicAuthPassword();
|
||||
if ((strncmp(this->username, reqUsername.c_str(), sizeof(this->username)) != 0) ||
|
||||
(strncmp(this->password, reqPassword.c_str(), sizeof(this->password)) != 0))
|
||||
{
|
||||
this->json_message_response(response, "unauthorized", 403);
|
||||
return DENIED;
|
||||
}
|
||||
return AUTHENTICATED;
|
||||
}
|
||||
|
||||
ok_t API::setup_certificate()
|
||||
{
|
||||
this->certificate = retrieve_certificate();
|
||||
if (certificate != NULL)
|
||||
return NO_ACTION;
|
||||
log_i("Creating a new certificate...");
|
||||
this->certificate = new SSLCert();
|
||||
int createCertResult = createSelfSignedCert(
|
||||
*this->certificate,
|
||||
KEYSIZE_2048,
|
||||
"CN=myesp32.local,O=Firewall,C=DE",
|
||||
"20220101000000",
|
||||
"20320101000000");
|
||||
if (createCertResult != 0)
|
||||
{
|
||||
log_e("Cannot create a server-certificate");
|
||||
return ERROR;
|
||||
}
|
||||
store_certificate(certificate);
|
||||
log_i("Creating a server-certificate was successful");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void API::setup_routing()
|
||||
{
|
||||
ResourceNode *get_firewall_rule = new ResourceNode("/api/v1/firewall/*", "GET", std::bind(&API::get_firewall_rule_handler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
ResourceNode *get_firewall_rules = new ResourceNode("/api/v1/firewall", "GET", std::bind(&API::get_firewall_rules_handler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
ResourceNode *post_firewall = new ResourceNode("/api/v1/firewall", "POST", std::bind(&API::post_firewall_handler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
ResourceNode *delete_firewall = new ResourceNode("/api/v1/firewall/*", "DELETE", std::bind(&API::delete_firewall_handler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
ResourceNode *not_found = new ResourceNode("", "GET", std::bind(&API::not_found_handler, this, std::placeholders::_1, std::placeholders::_2));
|
||||
this->server->registerNode(get_firewall_rule);
|
||||
this->server->registerNode(get_firewall_rules);
|
||||
this->server->registerNode(post_firewall);
|
||||
this->server->registerNode(delete_firewall);
|
||||
this->server->setDefaultNode(not_found);
|
||||
}
|
||||
|
||||
void API::not_found_handler(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
this->json_message_response(response, "not found", 404);
|
||||
}
|
||||
|
||||
void API::get_firewall_rule_handler(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
if (this->check_auth(request, response) == DENIED)
|
||||
return;
|
||||
ResourceParameters *params = request->getParams();
|
||||
int rule_number = atoi(params->getPathParameter(0).c_str());
|
||||
firewall_rule_t *rule_ptr = get_rule_from_firewall(rule_number);
|
||||
if (rule_ptr == NULL)
|
||||
{
|
||||
this->json_message_response(response, "rule not found", 404);
|
||||
}
|
||||
else
|
||||
{
|
||||
response->setHeader("Content-Type", "application/json");
|
||||
response->setStatusCode(200);
|
||||
response->print(this->construct_json_firewall_rule(rule_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
void API::get_firewall_rules_handler(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
if (this->check_auth(request, response) == DENIED)
|
||||
return;
|
||||
this->json_generic_response(response, this->construct_json_firewall(), 200);
|
||||
}
|
||||
|
||||
bool API::request_has_firewall_parameter(ResourceParameters *params)
|
||||
{
|
||||
return params->isQueryParameterSet("source") ||
|
||||
params->isQueryParameterSet("destination") ||
|
||||
params->isQueryParameterSet("protocol") ||
|
||||
params->isQueryParameterSet("target");
|
||||
}
|
||||
|
||||
void API::post_firewall_handler(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
if (this->check_auth(request, response) == DENIED)
|
||||
return;
|
||||
ResourceParameters *params = request->getParams();
|
||||
if (request_has_firewall_parameter(params))
|
||||
{
|
||||
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
|
||||
rule_ptr->key = ++amount_of_rules;
|
||||
|
||||
std::string source;
|
||||
params->getQueryParameter("source", source);
|
||||
strncpy(rule_ptr->source, source.c_str(), sizeof(rule_ptr->source));
|
||||
std::string destination;
|
||||
params->getQueryParameter("destination", destination);
|
||||
strncpy(rule_ptr->destination, destination.c_str(), sizeof(rule_ptr->destination));
|
||||
|
||||
std::string protocol;
|
||||
params->getQueryParameter("protocol", protocol);
|
||||
rule_ptr->protocol = string_to_protocol(protocol);
|
||||
std::string target;
|
||||
params->getQueryParameter("target", target);
|
||||
rule_ptr->target = string_to_target(target);
|
||||
|
||||
add_rule_to_firewall(rule_ptr);
|
||||
this->json_generic_response(response, this->construct_json_firewall_rule(rule_ptr), 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->json_message_response(response, "not enough parameter", 400);
|
||||
}
|
||||
}
|
||||
|
||||
void API::delete_firewall_handler(HTTPRequest *request, HTTPResponse *response)
|
||||
{
|
||||
if (this->check_auth(request, response) == DENIED)
|
||||
return;
|
||||
ResourceParameters *params = request->getParams();
|
||||
int rule_number = atoi(params->getPathParameter(0).c_str());
|
||||
if (delete_rule_from_firewall(rule_number) == SUCCESS)
|
||||
{
|
||||
this->json_message_response(response, "firewall rule deleted", 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->json_message_response(response, "cannot delete firewall rule", 500);
|
||||
}
|
||||
}
|
||||
|
||||
void API::json_generic_response(HTTPResponse *response, String serialized, const uint16_t response_code)
|
||||
{
|
||||
response->setHeader("Content-Type", "application/json");
|
||||
response->setStatusCode(response_code);
|
||||
response->println(serialized);
|
||||
}
|
||||
|
||||
void API::json_message_response(HTTPResponse *response, String message, const uint16_t response_code)
|
||||
{
|
||||
response->setHeader("Content-Type", "application/json");
|
||||
response->setStatusCode(response_code);
|
||||
StaticJsonDocument<96> json;
|
||||
String serialized;
|
||||
json["message"] = message;
|
||||
serializeJson(json, serialized);
|
||||
response->println(serialized);
|
||||
}
|
||||
|
||||
String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
|
||||
{
|
||||
StaticJsonDocument<256> doc;
|
||||
doc["key"] = rule_ptr->key;
|
||||
doc["source"] = rule_ptr->source;
|
||||
doc["destination"] = rule_ptr->destination;
|
||||
doc["protocol"] = protocol_to_string(rule_ptr->protocol);
|
||||
doc["target"] = target_to_string(rule_ptr->target);
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
String API::construct_json_firewall()
|
||||
{
|
||||
firewall_rule_t *rule_ptr = head;
|
||||
// Size for approx. 12 Rules
|
||||
StaticJsonDocument<2048> doc;
|
||||
String response;
|
||||
doc["amount_of_rules"] = amount_of_rules;
|
||||
JsonArray rules = doc.createNestedArray("rules");
|
||||
while (rule_ptr != NULL)
|
||||
{
|
||||
JsonObject rule = rules.createNestedObject();
|
||||
rule["key"] = rule_ptr->key;
|
||||
rule["source"] = rule_ptr->source;
|
||||
rule["destination"] = rule_ptr->destination;
|
||||
rule["protocol"] = protocol_to_string(rule_ptr->protocol);
|
||||
rule["target"] = target_to_string(rule_ptr->target);
|
||||
rule_ptr = rule_ptr->next;
|
||||
}
|
||||
serializeJson(doc, response);
|
||||
return response;
|
||||
}
|
||||
}
|
49
ESPFirewall/lib/Firewall/src/esp32API.hpp
Normal file
49
ESPFirewall/lib/Firewall/src/esp32API.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef ESP32_API_HPP
|
||||
#define ESP32_API_HPP
|
||||
|
||||
#include "HTTPSServer.hpp"
|
||||
#include "SSLCert.hpp"
|
||||
#include "HTTPRequest.hpp"
|
||||
#include "HTTPResponse.hpp"
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "esp32Firewall.hpp"
|
||||
|
||||
using namespace httpsserver;
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
class API : public Firewall
|
||||
{
|
||||
private:
|
||||
HTTPSServer *server;
|
||||
SSLCert *certificate;
|
||||
char username[32];
|
||||
char password[32];
|
||||
|
||||
ok_t setup_auth(const char *, const char *);
|
||||
auth_t check_auth(HTTPRequest *, HTTPResponse *);
|
||||
ok_t setup_certificate();
|
||||
|
||||
void setup_routing();
|
||||
void get_firewall_rule_handler(HTTPRequest *, HTTPResponse *);
|
||||
void get_firewall_rules_handler(HTTPRequest *, HTTPResponse *);
|
||||
bool request_has_firewall_parameter(ResourceParameters *);
|
||||
void post_firewall_handler(HTTPRequest *, HTTPResponse *);
|
||||
void delete_firewall_handler(HTTPRequest *, HTTPResponse *);
|
||||
void not_found_handler(HTTPRequest *, HTTPResponse *);
|
||||
|
||||
void json_generic_response(HTTPResponse *, String, const uint16_t);
|
||||
void json_message_response(HTTPResponse *, String, const uint16_t);
|
||||
String construct_json_firewall_rule(firewall_rule_t *);
|
||||
String construct_json_firewall();
|
||||
|
||||
public:
|
||||
API(const char *, const char *, const uint16_t = 8080);
|
||||
~API();
|
||||
void handle_clients();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
100
ESPFirewall/lib/Firewall/src/esp32Firewall.cpp
Normal file
100
ESPFirewall/lib/Firewall/src/esp32Firewall.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "esp32Firewall.hpp"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
Firewall::Firewall()
|
||||
{
|
||||
this->amount_of_rules = retrieve_settings_value("amount_of_rules");
|
||||
for (uint8_t i = 1; i <= this->amount_of_rules; i++)
|
||||
{
|
||||
firewall_rule_t *rule_ptr = retrieve_firewall_rule(i);
|
||||
add_rule_to_firewall(rule_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
Firewall::~Firewall()
|
||||
{
|
||||
}
|
||||
|
||||
void Firewall::add_rule_to_firewall(firewall_rule_t *rule_ptr)
|
||||
{
|
||||
store_settings_value("amount_of_rules", this->amount_of_rules);
|
||||
store_firewall_rule(rule_ptr);
|
||||
firewall_rule_t *temp;
|
||||
if (this->head == NULL)
|
||||
{
|
||||
this->head = rule_ptr;
|
||||
rule_ptr->next = NULL;
|
||||
return;
|
||||
}
|
||||
temp = this->head;
|
||||
while (temp->next != NULL)
|
||||
{
|
||||
temp = temp->next;
|
||||
}
|
||||
temp->next = rule_ptr;
|
||||
rule_ptr->next = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
firewall_rule_t *Firewall::get_rule_from_firewall(uint8_t key)
|
||||
{
|
||||
firewall_rule_t *rule_ptr = this->head;
|
||||
if (this->head == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
while (rule_ptr->key != key)
|
||||
{
|
||||
if (rule_ptr->next == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
rule_ptr = rule_ptr->next;
|
||||
}
|
||||
}
|
||||
return rule_ptr;
|
||||
}
|
||||
|
||||
ok_t Firewall::delete_rule_from_firewall(uint8_t key)
|
||||
{
|
||||
if (this->head == NULL)
|
||||
return NO_ACTION;
|
||||
firewall_rule_t *current_rule_ptr = this->head;
|
||||
firewall_rule_t *previous_rule_ptr = NULL;
|
||||
firewall_rule_t *temp = NULL;
|
||||
while (current_rule_ptr->key != key)
|
||||
{
|
||||
if (current_rule_ptr->next == NULL)
|
||||
return NO_ACTION;
|
||||
else
|
||||
{
|
||||
previous_rule_ptr = current_rule_ptr;
|
||||
current_rule_ptr = current_rule_ptr->next;
|
||||
}
|
||||
}
|
||||
if (current_rule_ptr == this->head)
|
||||
{
|
||||
this->head = head->next;
|
||||
temp = this->head;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous_rule_ptr->next = current_rule_ptr->next;
|
||||
temp = previous_rule_ptr->next;
|
||||
}
|
||||
while (temp != NULL)
|
||||
{
|
||||
temp->key--;
|
||||
temp = temp->next;
|
||||
}
|
||||
free(current_rule_ptr);
|
||||
this->amount_of_rules--;
|
||||
store_settings_value("amount_of_rules", this->amount_of_rules);
|
||||
if (this->amount_of_rules != 0)
|
||||
store_all_firewall_rules(head);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
26
ESPFirewall/lib/Firewall/src/esp32Firewall.hpp
Normal file
26
ESPFirewall/lib/Firewall/src/esp32Firewall.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef ESP32_FIREWALL_HPP
|
||||
#define ESP32_FIREWALL_HPP
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "esp32Storage.hpp"
|
||||
#include "WString.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
class Firewall : public Storage
|
||||
{
|
||||
protected:
|
||||
uint8_t amount_of_rules = 0;
|
||||
firewall_rule_t *head = NULL;
|
||||
|
||||
void add_rule_to_firewall(firewall_rule_t *);
|
||||
firewall_rule_t *get_rule_from_firewall(uint8_t);
|
||||
ok_t delete_rule_from_firewall(uint8_t);
|
||||
|
||||
public:
|
||||
Firewall();
|
||||
~Firewall();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
149
ESPFirewall/lib/Firewall/src/esp32Storage.cpp
Normal file
149
ESPFirewall/lib/Firewall/src/esp32Storage.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include "esp32Storage.hpp"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
Storage::Storage()
|
||||
{
|
||||
if (this->mount_spiffs() == ERROR)
|
||||
endless_loop();
|
||||
}
|
||||
|
||||
Storage::~Storage()
|
||||
{
|
||||
}
|
||||
|
||||
ok_t Storage::mount_spiffs()
|
||||
{
|
||||
if (!SPIFFS.begin(false))
|
||||
{
|
||||
if (!SPIFFS.begin(true))
|
||||
{
|
||||
log_e("SPIFFS cannot be mounted");
|
||||
return ERROR;
|
||||
};
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t Storage::retrieve_settings_value(const char *key)
|
||||
{
|
||||
uint8_t amount_of_rules;
|
||||
|
||||
this->memory.begin("settings", true);
|
||||
amount_of_rules = memory.getUChar(key, 0);
|
||||
this->memory.end();
|
||||
|
||||
return amount_of_rules;
|
||||
}
|
||||
|
||||
void Storage::store_settings_value(const char *key, const uint8_t new_amount)
|
||||
{
|
||||
this->memory.begin("settings", false);
|
||||
this->memory.putUChar(key, new_amount);
|
||||
this->memory.end();
|
||||
}
|
||||
|
||||
firewall_rule_t *Storage::retrieve_firewall_rule(const uint8_t key)
|
||||
{
|
||||
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
|
||||
rule_ptr->key = key;
|
||||
|
||||
char rulename[9]; // fwRule99\n
|
||||
sprintf(rulename, "fwRule%i", key);
|
||||
|
||||
this->memory.begin(rulename, true);
|
||||
strncpy(rule_ptr->source, this->memory.getString("source", "0.0.0.0").c_str(), sizeof(rule_ptr->source));
|
||||
strncpy(rule_ptr->destination, this->memory.getString("destination", "0.0.0.0").c_str(), sizeof(rule_ptr->source));
|
||||
rule_ptr->protocol = static_cast<firewall_protocol_t>(this->memory.getUChar("protocol", FW_ALL));
|
||||
rule_ptr->target = static_cast<firewall_target_t>(this->memory.getUChar("target", FW_REJECT));
|
||||
this->memory.end();
|
||||
|
||||
return rule_ptr;
|
||||
}
|
||||
|
||||
void Storage::store_all_firewall_rules(firewall_rule_t *head)
|
||||
{
|
||||
firewall_rule_t *temp = head;
|
||||
while (temp != NULL)
|
||||
{
|
||||
store_firewall_rule(temp);
|
||||
temp = temp->next;
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::store_firewall_rule(firewall_rule_t *rule_ptr)
|
||||
{
|
||||
char rulename[9]; // fwRule99\n
|
||||
sprintf(rulename, "fwRule%i", rule_ptr->key);
|
||||
|
||||
this->memory.begin(rulename, false);
|
||||
this->memory.putString("source", rule_ptr->source);
|
||||
this->memory.putString("destination", rule_ptr->destination);
|
||||
this->memory.putUChar("protocol", rule_ptr->protocol);
|
||||
this->memory.putUChar("target", rule_ptr->target);
|
||||
this->memory.end();
|
||||
}
|
||||
|
||||
httpsserver::SSLCert *Storage::retrieve_certificate()
|
||||
{
|
||||
File keyFile = SPIFFS.open("/key.der");
|
||||
File certFile = SPIFFS.open("/cert.der");
|
||||
if (!keyFile || !certFile || keyFile.size() == 0 || certFile.size() == 0)
|
||||
{
|
||||
log_e("No server-certificate found in SPIFFS");
|
||||
return NULL;
|
||||
}
|
||||
size_t keySize = keyFile.size();
|
||||
size_t certSize = certFile.size();
|
||||
|
||||
uint8_t *keyBuffer = new uint8_t[keySize];
|
||||
if (keyBuffer == NULL)
|
||||
{
|
||||
log_w("Not enough memory to load private key");
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *certBuffer = new uint8_t[certSize];
|
||||
if (certBuffer == NULL)
|
||||
{
|
||||
delete[] keyBuffer;
|
||||
log_w("Not enough memory to load server-certificate");
|
||||
return NULL;
|
||||
}
|
||||
keyFile.read(keyBuffer, keySize);
|
||||
certFile.read(certBuffer, certSize);
|
||||
|
||||
keyFile.close();
|
||||
certFile.close();
|
||||
return new httpsserver::SSLCert(certBuffer, certSize, keyBuffer, keySize);
|
||||
}
|
||||
|
||||
void Storage::store_certificate(httpsserver::SSLCert *certificate)
|
||||
{
|
||||
File keyFile = SPIFFS.open("/key.der");
|
||||
File certFile = SPIFFS.open("/cert.der");
|
||||
bool failure = false;
|
||||
|
||||
keyFile = SPIFFS.open("/key.der", FILE_WRITE);
|
||||
if (!keyFile || !keyFile.write(certificate->getPKData(), certificate->getPKLength()))
|
||||
{
|
||||
log_w("Cannot write /key.der");
|
||||
failure = true;
|
||||
}
|
||||
if (keyFile)
|
||||
keyFile.close();
|
||||
|
||||
certFile = SPIFFS.open("/cert.der", FILE_WRITE);
|
||||
if (!certFile || !certFile.write(certificate->getCertData(), certificate->getCertLength()))
|
||||
{
|
||||
log_w("Cannot write /cert.der");
|
||||
failure = true;
|
||||
}
|
||||
if (certFile)
|
||||
certFile.close();
|
||||
|
||||
if (failure)
|
||||
{
|
||||
log_w("Server-certificate could not be stored permanently, generating new certificate on reboot...");
|
||||
}
|
||||
}
|
||||
}
|
34
ESPFirewall/lib/Firewall/src/esp32Storage.hpp
Normal file
34
ESPFirewall/lib/Firewall/src/esp32Storage.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef ESP32_STORAGE_HPP
|
||||
#define ESP32_STORAGE_HPP
|
||||
|
||||
#include "Preferences.h"
|
||||
#include "SPIFFS.h"
|
||||
#include "Utils.hpp"
|
||||
#include "SSLCert.hpp"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
class Storage
|
||||
{
|
||||
private:
|
||||
Preferences memory;
|
||||
ok_t mount_spiffs();
|
||||
|
||||
protected:
|
||||
uint8_t retrieve_settings_value(const char *);
|
||||
void store_settings_value(const char *, const uint8_t);
|
||||
|
||||
firewall_rule_t *retrieve_firewall_rule(const uint8_t);
|
||||
void store_all_firewall_rules(firewall_rule_t *);
|
||||
void store_firewall_rule(firewall_rule_t *);
|
||||
|
||||
httpsserver::SSLCert *retrieve_certificate();
|
||||
void store_certificate(httpsserver::SSLCert *);
|
||||
|
||||
public:
|
||||
Storage();
|
||||
~Storage();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
29
ESPFirewall/platformio.ini
Normal file
29
ESPFirewall/platformio.ini
Normal file
|
@ -0,0 +1,29 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32-evb]
|
||||
platform = espressif32
|
||||
board = esp32-evb
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
build_flags =
|
||||
-DHTTPS_LOGLEVEL=1
|
||||
-DCORE_DEBUG_LEVEL=3
|
||||
lib_deps = bblanchon/ArduinoJson@^6.19.4
|
||||
|
||||
[env:esp32-dev]
|
||||
platform = espressif32
|
||||
board = az-delivery-devkit-v4
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
build_flags =
|
||||
-DHTTPS_LOGLEVEL=1
|
||||
-DCORE_DEBUG_LEVEL=3
|
||||
lib_deps = bblanchon/ArduinoJson@^6.19.4
|
31
ESPFirewall/src/main.cpp
Normal file
31
ESPFirewall/src/main.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "theSecrets.h"
|
||||
#include "WiFi.h"
|
||||
#include "esp32API.hpp"
|
||||
|
||||
firewall::API *firewall_api;
|
||||
|
||||
void setup_wifi()
|
||||
{
|
||||
uint8_t max_retries = 5;
|
||||
uint8_t retries = 1;
|
||||
log_d("Attempting to connect to WPA SSID: %s", ssid);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, psk);
|
||||
while (WiFi.status() != WL_CONNECTED && retries <= max_retries)
|
||||
{
|
||||
delay(2000);
|
||||
log_d("Connecting... (%i/%i)", retries++, max_retries);
|
||||
}
|
||||
log_i("IP Address: %s", WiFi.localIP().toString().c_str());
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
setup_wifi();
|
||||
firewall_api = new firewall::API(api_username, api_password, 8080);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
firewall_api->handle_clients();
|
||||
}
|
11
ESPFirewall/test/README
Normal file
11
ESPFirewall/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
Reference in a new issue