diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1269488 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +data diff --git a/Dockerfile-i2p b/Dockerfile-i2p new file mode 100644 index 0000000..e78d719 --- /dev/null +++ b/Dockerfile-i2p @@ -0,0 +1,29 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && \ + apt-get install wget sudo -y + +WORKDIR /tmp/i2p +RUN wget https://github.com/PurpleI2P/i2pd/releases/download/2.47.0/i2pd_2.47.0-1jammy1_amd64.deb -O i2pd.deb -q +RUN apt install ./i2pd.deb -y + +RUN rm -rf /tmp/i2p + +RUN adduser \ + --system \ + --shell /bin/bash \ + --gecos 'i2p' \ + --group \ + --disabled-password \ + --home /home/i2p \ + --uid 1000 \ + i2p +COPY conf/i2p-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +EXPOSE 4444 +CMD /entrypoint.sh + + + diff --git a/Dockerfile-torsocks b/Dockerfile-tor similarity index 100% rename from Dockerfile-torsocks rename to Dockerfile-tor diff --git a/conf/i2p-entrypoint.sh b/conf/i2p-entrypoint.sh new file mode 100644 index 0000000..6b2ff5e --- /dev/null +++ b/conf/i2p-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +chown -R i2p:i2p /home/i2p + +# Run i2pd +sudo -u i2p i2pd \ + --httpproxy.enabled 1 \ + --httpproxy.address 0.0.0.0 \ + --httpproxy.port 4444 diff --git a/docker-compose.yaml b/docker-compose.yaml index 101f0c9..e5df7ea 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,10 +1,20 @@ version: '3' services: tor: - container_name: tor + container_name: fail-tor build: context: . - dockerfile: Dockerfile-torsocks + dockerfile: Dockerfile-tor restart: unless-stopped ports: - 127.0.0.1:9050:9050 + i2p: + container_name: fail-i2p + build: + context: . + dockerfile: Dockerfile-i2p + restart: unless-stopped + ports: + - 127.0.0.1:4444:4444 + volumes: + - ./data/i2pd:/home/i2p/.i2pd diff --git a/xmrnodes/cli.py b/xmrnodes/cli.py index cfa2e53..83fc122 100644 --- a/xmrnodes/cli.py +++ b/xmrnodes/cli.py @@ -8,7 +8,7 @@ import requests from flask import Blueprint from urllib.parse import urlparse -from xmrnodes.helpers import determine_crypto, is_onion, make_request +from xmrnodes.helpers import determine_crypto, is_onion, is_i2p, make_request from xmrnodes.helpers import retrieve_peers, rw_cache, get_highest_block from xmrnodes.models import Node, HealthCheck, Peer from xmrnodes import config @@ -171,6 +171,7 @@ def validate(): node.datetime_checked = now node.crypto = crypto node.is_tor = is_onion(node.url) + node.is_i2p = is_i2p(node.url) node.save() else: logging.info("unexpected nettype") diff --git a/xmrnodes/config.py b/xmrnodes/config.py index b9fd613..9c2dede 100644 --- a/xmrnodes/config.py +++ b/xmrnodes/config.py @@ -14,3 +14,5 @@ NODE_HOST = environ.get("NODE_HOST", "singapore.node.xmr.pm") NODE_PORT = environ.get("NODE_PORT", 18080) HEALTHY_BLOCK_DIFF = int(environ.get("HEALTHY_BLOCK_DIFF", 500)) PEER_LIFETIME = int(environ.get("PEER_LIFETIME", 96)) +I2P_HOST = environ.get("I2P_HOST", "127.0.0.1") +I2P_PORT = environ.get("I2P_PORT", 4444) \ No newline at end of file diff --git a/xmrnodes/helpers.py b/xmrnodes/helpers.py index 689f27e..3c60a76 100644 --- a/xmrnodes/helpers.py +++ b/xmrnodes/helpers.py @@ -19,6 +19,10 @@ def make_request(url: str, path="/get_info", data=None): _p = f"socks5h://{config.TOR_HOST}:{config.TOR_PORT}" proxies = {"http": _p, "https": _p} timeout = 30 + elif is_i2p(url): + _p = f"http://{config.I2P_HOST}:{config.I2P_PORT}" + proxies = {"http": _p, "https": _p} + timeout = 30 else: proxies = None timeout = 10 @@ -60,6 +64,14 @@ def determine_crypto(url): except: return "unknown" +def is_i2p(url: str): + _split = url.split(":") + if len(_split) < 2: + return False + if _split[1].endswith(".i2p"): + return True + else: + return False def is_onion(url: str): _split = url.split(":") diff --git a/xmrnodes/models.py b/xmrnodes/models.py index f226cb9..dce9f6d 100644 --- a/xmrnodes/models.py +++ b/xmrnodes/models.py @@ -14,6 +14,7 @@ class Node(Model): id = AutoField() url = CharField(unique=True) is_tor = BooleanField(default=False) + is_i2p = BooleanField(default=False) available = BooleanField(default=False) validated = BooleanField(default=False) web_compatible = BooleanField(default=False) diff --git a/xmrnodes/routes/meta.py b/xmrnodes/routes/meta.py index 795bfbf..22ac136 100644 --- a/xmrnodes/routes/meta.py +++ b/xmrnodes/routes/meta.py @@ -19,6 +19,7 @@ def index(): nettype = request.args.get("network", "mainnet") crypto = request.args.get("chain", "monero") onion = request.args.get("onion", False) + i2p = request.args.get("i2p", False) show_all = "true" == request.args.get("all", "false") web_compatible = request.args.get("cors", False) highest_block = get_highest_block(nettype, crypto) @@ -42,6 +43,8 @@ def index(): nodes = nodes.order_by(Node.datetime_entered.desc()) if onion: nodes = nodes.where(Node.is_tor == True) + if i2p: + nodes = nodes.where(Node.is_i2p == True) nodes = [n for n in nodes] shuffle(nodes) diff --git a/xmrnodes/static/images/clearnet.svg b/xmrnodes/static/images/clearnet.svg new file mode 100644 index 0000000..e937026 --- /dev/null +++ b/xmrnodes/static/images/clearnet.svg @@ -0,0 +1 @@ +internet-cloud \ No newline at end of file diff --git a/xmrnodes/static/images/i2p.svg b/xmrnodes/static/images/i2p.svg new file mode 100644 index 0000000..ab8a1d2 --- /dev/null +++ b/xmrnodes/static/images/i2p.svg @@ -0,0 +1,3 @@ + + + diff --git a/xmrnodes/templates/index.html b/xmrnodes/templates/index.html index 5d0f3c5..dcc0d09 100644 --- a/xmrnodes/templates/index.html +++ b/xmrnodes/templates/index.html @@ -60,6 +60,11 @@ + + + + + @@ -96,6 +101,7 @@ + @@ -109,7 +115,18 @@ {% for node in nodes %} +
Type URL Height Up
- {% if node.is_tor %}{% endif %} + {% if node.is_tor %} + + + {% elif node.is_i2p %} + + + {% else %} + + + {% endif %} + {{ node.url }} {% if node.donation_address | seems_legit %} {{ node.donation_address }}