Previous Page

nihilist - 15 / 07 / 2023

nitter Setup

In this tutorial we're going to setup nitter, a privacy front end to browse twitter without all the trackers.

Initial Setup

Clone the repository and start the docker-compose:


[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ apt install nim docker.io docker-compose libsass-dev libpcre3

[ nowhere.moe ] [ /dev/pts/1 ] [/srv]
→ git clone https://github.com/zedeus/nitter
Cloning into 'nitter'...
remote: Enumerating objects: 6639, done.
remote: Counting objects: 100% (163/163), done.
remote: Compressing objects: 100% (118/118), done.
remote: Total 6639 (delta 87), reused 97 (delta 45), pack-reused 6476
Receiving objects: 100% (6639/6639), 5.26 MiB | 1.81 MiB/s, done.
Resolving deltas: 100% (4852/4852), done.

[ nowhere.moe ] [ /dev/pts/1 ] [/srv]
→ cd nitter

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ docker build -t nitter:latest .

Sending build context to Docker daemon  6.428MB
Step 1/18 : FROM nimlang/nim:1.6.10-alpine-regular as nim
1.6.10-alpine-regular: Pulling from nimlang/nim
213ec9aee27d: Pull complete
071ba9e2da5d: Pull complete
8c05e9a65d0e: Pull complete

[...]

Step 17/18 : USER nitter
 ---> Running in b9cbc6b2d0f5
Removing intermediate container b9cbc6b2d0f5
 ---> d887fd7e9c0f
Step 18/18 : CMD ./nitter
 ---> Running in 0dca3c6f9ba3
Removing intermediate container 0dca3c6f9ba3
 ---> 24ad9b89ab85
Successfully built 24ad9b89ab85
Successfully tagged nitter:latest

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ apt install nim

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ nimble build -d:release
  Verifying dependencies for nitter@0.1.0
    Prompt: No local packages.json found, download it from internet? [y/N]
    Answer: y
Downloading Official package list
    Success Package list downloaded.
 Installing jester@#baca3f
Downloading https://github.com/dom96/jester using git
  Verifying dependencies for jester@#baca3f

[...]

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ nimble scss

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ nimble md

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ cp nitter.example.conf nitter.conf

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ cat nitter.conf
[Server]
hostname = "nitter.nowhere.moe"  # for generating links, change this to your own domain/ip
title = "nitter"
address = "0.0.0.0"
port = 8080
https = false  # disable to enable cookies when not using https
httpMaxConnections = 100
staticDir = "./public"

[Cache]
listMinutes = 240  # how long to cache list info (not the tweets, so keep it high)
rssMinutes = 10  # how long to cache rss queries
redisHost = "nitter-redis"  # Change to "nitter-redis" if using docker-compose
redisPort = 6379
redisPassword = ""
redisConnections = 20  # minimum open connections in pool
redisMaxConnections = 30
# new connections are opened when none are available, but if the pool size
# goes above this, they're closed when released. don't worry about this unless
# you receive tons of requests per second

[Config]
hmacKey = "aoQu6le1IQueut8hei3U"  # random key for cryptographic signing of video urls # use pwgen 20 1 to generate it
base64Media = false  # use base64 encoding for proxied media urls
enableRSS = true  # set this to false to disable RSS feeds
enableDebug = false  # enable request logs and debug endpoints (/.tokens)
proxy = ""  # http/https url, SOCKS proxies are not supported
proxyAuth = ""
tokenCount = 10
# minimum amount of usable tokens. tokens are used to authorize API requests,
# but they expire after ~1 hour, and have a limit of 500 requests per endpoint.
# the limits reset every 15 minutes, and the pool is filled up so there's
# always at least `tokenCount` usable tokens. only increase this if you receive
# major bursts all the time and don't have a rate limiting setup via e.g. nginx

# Change default preferences here, see src/prefs_impl.nim for a complete list
[Preferences]
theme = "Nitter"
replaceTwitter = "nitter.nowhere.moe"
replaceYouTube = "iv.nowhere.moe"
replaceReddit = "teddit.net"
proxyVideos = true
hlsPlayback = false
infiniteScroll = false

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ cat docker-compose.yml
version: "3"

services:

  nitter:
    image: zedeus/nitter:latest
    container_name: nitter
    ports:
      - "127.0.0.1:8080:8080" # Replace with "8080:8080" if you don't use a reverse proxy
    volumes:
      - ./nitter.conf:/src/nitter.conf:Z,ro
    depends_on:
      - nitter-redis
    restart: unless-stopped
    healthcheck:
      test: wget -nv --tries=1 --spider http://127.0.0.1:8080/Jack/status/20 || exit 1
      interval: 30s
      timeout: 5s
      retries: 2
    user: "998:998"
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL

  nitter-redis:
    image: redis:6-alpine
    container_name: nitter-redis
    command: redis-server --save 60 1 --loglevel warning
    volumes:
      - nitter-redis:/data
    restart: unless-stopped
    healthcheck:
      test: redis-cli ping
      interval: 30s
      timeout: 5s
      retries: 2
    user: "999:1000"
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL

volumes:
  nitter-redis:

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ docker-compose down
Stopping nitter       ... done
Stopping nitter-redis ... done
Removing nitter       ... done
Removing nitter-redis ... done
Removing network nitter_default

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ docker-compose up -d
Creating network "nitter_default" with the default driver
Creating nitter-redis ... done
Creating nitter       ... done

then you can check your service is up on port 8080:


[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ docker container ls
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS                    PORTS                                       NAMES
3582cdcf9687   zedeus/nitter:latest      "/bin/sh -c ./nitter"    3 minutes ago    Up 3 minutes (healthy)    127.0.0.1:8080->8080/tcp                    nitter
1b9a40a33d83   redis:6-alpine            "docker-entrypoint.s…"   3 minutes ago    Up 3 minutes (healthy)    6379/tcp                                    nitter-redis

Then setup the reverse nginx proxy:


[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ vim /etc/nginx/sites-available/nitter.nowhere.moe.conf

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ cat /etc/nginx/sites-available/nitter.nowhere.moe.conf
server {
        listen 443 ssl;
        server_name nitter.nowhere.moe;

        ssl_certificate /etc/acme/certs/nitter.nowhere.moe/fullchain.cer;
    ssl_certificate_key /etc/acme/certs/nitter.nowhere.moe/iv.nowhere.moe.key;


        ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
        ssl_ecdh_curve secp384r1;
        ssl_session_timeout  10m;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;

        # Security Headers
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none'; media-src 'self' blob: video.twimg.com; worker-src 'self' blob:; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; connect-src 'self' https://*.twimg.com; manifest-src 'self'";
        add_header X-Content-Type-Options nosniff;
        add_header X-Frame-Options DENY;
        add_header X-XSS-Protection "1; mode=block";

        location / {
                proxy_pass http://localhost:YOUR_NITTER_PORT;
        }

        location = /robots.txt {
                add_header Content-Type text/plain;
                return 200 "User-agent: *\nDisallow: /\n";
        }
}

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ bash
root@Datura /srv/nitter # systemctl stop nginx

root@Datura /srv/nitter # acme.sh --issue --standalone -d nitter.nowhere.moe -k 4096

[ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available]
→ ln -s /etc/nginx/sites-available/nitter.nowhere.moe.conf /etc/nginx/sites-enabled

root@Datura /srv/nitter # systemctl start nginx

[ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter]
→ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sat 2023-07-15 22:55:17 CEST; 7min ago
       Docs: man:nginx(8)
    Process: 168567 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 168568 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 168569 (nginx)
      Tasks: 13 (limit: 77000)
     Memory: 15.9M
        CPU: 776ms
     CGroup: /system.slice/nginx.service
             ├─168569 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─169429 "nginx: worker process"
             ├─169430 "nginx: worker process"
             ├─169431 "nginx: worker process"
             ├─169432 "nginx: worker process"
             ├─169433 "nginx: worker process"
             ├─169434 "nginx: worker process"
             ├─169435 "nginx: worker process"
             ├─169436 "nginx: worker process"
             ├─169437 "nginx: worker process"
             ├─169438 "nginx: worker process"
             ├─169439 "nginx: worker process"
             └─169440 "nginx: worker process"

Jul 15 22:55:17 Datura systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Jul 15 22:55:17 Datura systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
	

Then test to see if your website renders fine:

To have it available on tor you can use the following nginx config:


[ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available]
→ cat nitter.nowhere.moe.conf
server {
        listen 443 ssl;
        server_name nitter.nowhere.moe;

        ssl_certificate /etc/acme/certs/nitter.nowhere.moe/nitter.nowhere.moe.cer;
    ssl_certificate_key /etc/acme/certs/nitter.nowhere.moe/nitter.nowhere.moe.key;

    ######## TOR CHANGES ########
    listen 4443;
    listen [::]:4443;
    server_name nitter.daturab6drmkhyeia4ch5gvfc2f3wgo6bhjrv3pz6n7kxmvoznlkq4yd.onion;
    add_header Onion-Location "http://nitter.daturab6drmkhyeia4ch5gvfc2f3wgo6bhjrv3pz6n7kxmvoznlkq4yd.onion$request_uri" always;
    ######## TOR CHANGES ########


        ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
        ssl_ecdh_curve secp384r1;
        ssl_session_timeout  10m;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;

        # Security Headers
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none'; media-src 'self' blob: video.twimg.com; worker-src 'self' blob:; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; connect-src 'self' https://*.twimg.com; manifest-src 'self'";
        add_header X-Content-Type-Options nosniff;
        add_header X-Frame-Options DENY;
        add_header X-XSS-Protection "1; mode=block";

        location / {
                proxy_pass http://localhost:8080;
        }

        location = /robots.txt {
                add_header Content-Type text/plain;
                return 200 "User-agent: *\nDisallow: /\n";
        }
}
	
[ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available]
→ nginx -s reload

And thats it! now last step is to contribute to the overall list of instances by sending a request to get your instance listed publicly here.

Nihilism

Until there is Nothing left.



Creative Commons Zero: No Rights Reserved

About nihilist

Donate XMR: 8AUYjhQeG3D5aodJDtqG499N5jXXM71gYKD8LgSsFB9BUV1o7muLv3DXHoydRTK4SZaaUBq4EAUqpZHLrX2VZLH71Jrd9k8


Contact: nihilist@contact.nowhere.moe (PGP)