In this tutorial we're going to setup nitter, a privacy front end to browse twitter without all the trackers.
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.
Until there is Nothing left.
Creative Commons Zero: No Rights Reserved
Donate XMR: 8AUYjhQeG3D5aodJDtqG499N5jXXM71gYKD8LgSsFB9BUV1o7muLv3DXHoydRTK4SZaaUBq4EAUqpZHLrX2VZLH71Jrd9k8
Contact: nihilist@contact.nowhere.moe (PGP)