NTPinfo hosted on https://ntpinfo.sidnlabs.nl
- This fork is derived from the original's ones provided by TU Delft students
- Their final report is available at the original project and here in this PDF
The product is split into 2 parts:
- Handles time measurement logic and API interactions.
- Uses
ntpliband the RIPE Atlas API for performing measurements. - Stores results in a PostgreSQL database.
- Provides an API to:
- Trigger and manage measurements
- Access historical data
- Communicate with the front-end
- Built with
ReactandVite - Uses:
ChartJSfor data visualizationaxiosfor interacting with the API- Base
CSSfor styling
- Presents all the data in a user-friendly dashboard
There are 2 ways in starting the server. The first one is to manually configure it, and the second one is using a docker container.
To set up and run the back-end server, follow these steps:
-
Create a virtual environment:
python -m venv venv
Then activate the virtual environment:
- On macOS/Linux:
source venv/bin/activate - On Windows:
.\venv\Scripts\activate
- On macOS/Linux:
-
Install and prepare PostgreSQL database
2.1. Go to: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads and select the version of PostgreSQL you want to install.
2.2. Go to download file location Double click the
.exefile
Follow through the installation process and keep track of:- where you installed it
- the superuser (usually
postgres) - the port (usually
5432) - the password (you should remember this one)
2.3.
pgAdminshould automatically be installed, so accept to install it when prompted.2.4. Restart your computer.
2.5.
pgAdminshould be in your system if you followed the installation correctly.
Open it, click on Server and put in your password if necessary.2.6. Right click on Databases, click Create and create an empty database, preferably named
"measurements".
Tables will be handled once you run the back-end server, so do not worry about them right now. -
Create a
.envfile in therootdirectory with your accounts credentials in the following format:# needed for back-end (server) # the database that you want to use from PostgreSQL, preferably named "measurements" DB_NAME=measurements DB_USER=postgres DB_PASSWORD=postgres # change DB_HOST to "localhost" if you run the project locally DB_HOST=db DB_PORT=5432 ripe_api_token={ripe API with persmission to perform measurments} ripe_account_email={email of your ripe account (you need to have credits)} ACCOUNT_ID={geolite account id} LICENSE_KEY={geolite key} # once every day UPDATE_CRON_SCHEDULE=0 0 * * * # when running local CLIENT_URL=http://127.0.0.1:5173 # when runing on docker CLIENT_URL=https://myapp.local #needed for front-end (client) DOCKER_NETWORK_SUBNET=2001:db8:1::/64 DOCKER_NETWORK_GATEWAY=2001:db8:1::1 # when running locally VITE_CLIENT_HOST=127.0.0.1 VITE_CLIENT_PORT=5173 # when running locally VITE_SERVER_HOST_ADDRESS=http://127.0.0.1:8000 # replace with actual domain_name/api VITE_SERVER_HOST_ADDRESS=https://myapp.local/api # in milliseconds, choose a value you think is reasonable for the offset threshold VITE_STATUS_THRESHOLD=1000 # port to launch the back-end server (must match the local one in docker-compose) # !! must be the same as the internal one in docker-compose !! SERVER_BIND=[::]:8000 # how many workers the backend should work (2 * nr_of_cores + 1) SERVER_WORKERS=4 # ports exposed from local machien used for local testing on localhost DB_DOCKER_PORT=15432 # when this is changed, CLIENT_URL must also be changed CLIENT_DOCKER_PORT=5173 # when this is changed, VITE_SERVER_HOST_ADDRESS must also be changed SERVER_DOCKER_PORT=8000 # ports where website is served HTTP_PORT=80 HTTPS_PORT=443
Besides, the config file with public data for the server is
server/server_config.yamland it contains the following variables that you can change:ntp: version: 4 timeout_measurement_s: 7 # in seconds number_of_measurements_for_calculating_jitter: 8 server_timeout: 60 # in seconds edns: mask_ipv4: 24 # bits mask_ipv6: 56 # bits default_order_of_edns_servers: # you can add multiple servers ipv4 or ipv6. The first one has the highest priority. # The others are used in case the first one cannot solve the domain name - "8.8.8.8" - "1.1.1.1" - "2001:4860:4860::8888" edns_timeout_s: 3 # in seconds ripe_atlas: timeout_per_probe_ms: 4000 packets_per_probe: 3 number_of_probes_per_measurement: 3 bgp_tools: anycast_prefixes_v4_url: "https://raw.githubusercontent.com/bgptools/anycast-prefixes/master/anycatch-v4-prefixes.txt" anycast_prefixes_v6_url: "https://raw.githubusercontent.com/bgptools/anycast-prefixes/master/anycatch-v6-prefixes.txt" max_mind: # see load_config_data if you want to change the path path_city: "GeoLite2-City.mmdb" path_country: "GeoLite2-Country.mmdb" path_asn: "GeoLite2-ASN.mmdb"
Note:
- Ensure PostgreSQL is running and accessible with the credentials provided in the
.envfile. - You can edit the config variables, but if there are any variables that are missing or have invalid data, the server will not start, and it will tell you exactly which config variables have problems.
- Ensure PostgreSQL is running and accessible with the credentials provided in the
-
Install the backend dependencies:
cd server pip install -r requirements.txt -
Download the max mind and BGP tools databases, and schedule running this file once every day
This will initialise the local dbs for geolocation and detecting anycast, and will schedule downloading them every day at 1 AM. Be sure that you are in the root folder, and
.envfile has all variables.If you want to schedule updating the databases, run this:
cd .. crontab -e 0 1 * * * /bin/bash /full_path_to/update_geolite_and_bgptools_dbs.sh >> /full_path_to/update_geolite_and_bgptools_dbs.log 2>&1
But replace
/full_path_towith the output of running :pwdOr if you just want to download the databases once without scheduling:
./update_geolite_and_bgptools_dbs.sh
Common errors:
- If you run
update_geolite_and_bgptools_dbs.shfrom Linux or WSL, the file.envmay contain invisible Windows carriage return characters and this may make the.shscript to fail. You can see them usingcat -A .env. Look for any "^M" at the end of lines. You can remove them by running this command:dos2unix .env. This should solve the problem. - If you are using Linux or WSL and you received
/bin/bash^M: bad interpreter: No such file or directorythen it may mean that your script has Windows-style line endings (CRLF, \r\n) instead of Unix-style (LF, \n). Another solution to change from CRLF to LF is to open the file in VS Code and to change them to LF. - If downloading the Geolite databases fails, consider that downloading them has a daily limit per account. (This limit is only for geolite databases)
Notes:
- Be sure to schedule running this file once every day or to manually update them, if you want up-to-date information.
- If you run
-
Run the server (from the root directory):
uvicorn server.app.main:create_app --reload --factory
You should see the server running now!
To set up and run the client, follow these steps carefully:
- Ensure you have the prerequisites installed
- Node.js
-
npm (comes bundled with Node.js)
node -v npm -v
If not, install from https://nodejs.org/
-
-
Create
.envfile in clientCreate a
.envfile inclientand add the following to the file:# address of our server (back-end) VITE_SERVER_HOST_ADDRESS=http://127.0.0.1:8000/ VITE_STATUS_THRESHOLD=1000 VITE_CLIENT_HOST=127.0.0.1 VITE_CLIENT_PORT=5173 CLIENT_URL=http://127.0.0.1:5173
-
Install the dependencies
Ensure you are in the client folder
cd ./client npm install -
Running the client
npm run dev
Everything should be set now!
To run the full stack (server + client + database) using docker-compose, follow these steps:
-
Install Docker
Follow the instructions for your OS here: 👉 https://docs.docker.com/engine/install/
-
Install
docker-compose(Linux)sudo apt update sudo apt install docker-compose
-
Add a .env file in the root directory
Create a
.envfile** in therootdirectory (the same directory with youdocker-compose.yml) with your accounts credentials. (You can see this.envat the above of the page) -
Create a temporary certbot container to generate your SSL certs
Replace the last 2 lines
--email your@email.comand-d yourdomain.comwith your own datadocker run --rm \ -v $(pwd)/nginx/certbot/www:/var/www/certbot \ -v $(pwd)/nginx/certbot/conf:/etc/letsencrypt \ certbot/certbot certonly \ --webroot \ --webroot-path=/var/www/certbot \ --agree-tos \ --no-eff-email \ --email your@email.com \ -d yourdomain.com
Once completed, the certificates should be in
nginx/certbot/conf/live/yourdomain.com/. These are then automatically moved when running docker-compose toetc/letsencrypt/live/yourdomain.com/ -
Make sure to add the docker path to your certificates to
nginx/conf.d/default.confThis is an example
Also make sure to replace
yourdomain.comwith your actual domain.server { listen 80; server_name yourdomain.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; location /api/ { proxy_pass http://backend:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { proxy_pass http://frontend:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } -
Build the Docker containers
From the root of the project, run this, but make sure that Docker Desktop is open:
sudo docker-compose build
or this command if the first one failed:
sudo docker-compose build --no-cache
Common Errors
- If it fails, and you received error
error during connect, then make sure that you have Docker Desktop open.
- If it fails, and you received error
-
Start the containers
sudo docker-compose up
- Every time after you run
sudo docker-compose upand it failed, and you want to try again, you need to runsudo docker-compose downbefore trying again. This also applies when you want to build again.
Common Errors
- If it fails with
exec /app/docker-entrypoint.sh: no such file or directory, exited with code 255then it means that the filedocker-entrypoint.sh(orupdate.sh) has CRLF format, and you need to change it to LF.
Use
-dto run it in the background:sudo docker-compose up -d
Make sure there is not any network name
my-netalready in use.This can be checked by running:
sudo docker network ls
- Every time after you run
-
Shut down the containers
To gracefully stop all services:
sudo docker-compose down
sudo docker-compose down -v
- Frontend: http://127.0.0.1:5173
- Backend API: http://127.0.0.1:8000
Make sure ports
5173(frontend) and8000(backend) are not in use before starting the containers.
- On the
docker-entrypoint.shfile the backend server runs with multiple workers, and because of that the logs for the server were moved to theapp/logs/access.logfile in the docker container of the back-end component.
We appreciate any new contributions from the open-source community
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
- Fork the Project
- Create your Feature Branch (
git checkout -b branch-name) - Commit your Changes (
git commit -m 'Added feature') - Push to the Branch (
git push origin branch-name) - Open a Pull Request
