diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0f967d3 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +TURN_SECRET=COTURN_SECRET_STRING +TURN_TTL=3600 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 36420af..80df32d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules +.env config.json \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..e1d2e95 --- /dev/null +++ b/config.json.example @@ -0,0 +1,17 @@ +{ + "iceServers": [ + { + "urls": [ + "stun:somehost:port" + ] + }, + { + "urls": [ + "turn:somehost:port" + ], + "username": "someuser", + "credential": "somepassword" + } + ], + "iceTransportPolicy": "all" +} \ No newline at end of file diff --git a/coturn/setup.sh b/coturn/setup.sh new file mode 100755 index 0000000..7d956b7 --- /dev/null +++ b/coturn/setup.sh @@ -0,0 +1,28 @@ +# Install coturn +sudo apt-get update -y +sudo apt-get install coturn + +# Configure coturn +sudo mv /etc/turnserver.conf /etc/turnserver.conf.backup +sudo cp turnserver.conf /etc/turnserver.conf + +# Update IPs in config +private_ip=$(ip route get 1 | awk '{print $7}') +public_ip=$(curl ifconfig.me) +sudo sed -i "s/PRIVATE_IP/$private_ip/g" /etc/turnserver.conf +sudo sed -i "s/PUBLIC_IP/$public_ip/g" /etc/turnserver.conf + +if [ -z "$1" ]; then + read -p "Enter the coturn auth secret: " secret +else + secret=$1 +fi +sudo sed -i "s/COTURN_AUTH_SECRET/$secret/g" /etc/turnserver.conf + +# Open firewall +sudo iptables -I INPUT -p tcp -m tcp --dport 3478 -j ACCEPT +sudo iptables -I INPUT -p udp -m udp --dport 3478 -j ACCEPT +sudo iptables -I INPUT -p udp -m udp --dport 49152:65535 -j ACCEPT + +# Restart service +sudo systemctl restart coturn.service \ No newline at end of file diff --git a/coturn/turnserver.conf b/coturn/turnserver.conf new file mode 100644 index 0000000..6907326 --- /dev/null +++ b/coturn/turnserver.conf @@ -0,0 +1,15 @@ +# WebRTC Demo Coturn Config +realm=somedomain +server-name=somedomain +listening-ip=PRIVATE_IP +external-ip=PUBLIC_IP +min-port=49152 +max-port=65535 +# verbose +fingerprint +# lt-cred-mech +# user=someusername:somepassword +use-auth-secret +status-auth-secret=COTURN_AUTH_SECRET +# log-file=/var/tmp/turn.log +syslog \ No newline at end of file diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..e552623 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +# +# $1 = private_key_path +# $2 = username@server +# +rsync -avzP --exclude node_modules -e "ssh -i \"$1\"" . $2:~/WebRTC \ No newline at end of file diff --git a/index.js b/index.js index 13061fc..f64b1b3 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,12 @@ +const crypto = require("crypto"); +const dotenv = require("dotenv"); const express = require("express"); const app = express(); const http = require("http"); const fs = require("fs"); const server = http.createServer(app); const port = process.env.PORT || 3000; +const config = dotenv.config().parsed; const { Server } = require("socket.io"); const io = new Server(server); @@ -16,6 +19,23 @@ try { console.error("Error reading config file: ", err); } +const generateTurnCredentials = () => { + const secret = config.TURN_SECRET; + const ttl = parseInt(config.TURN_TTL); + const timestamp = Math.floor(Date.now() / 1000) + ttl; + const userId = "turnuser"; + const userCombo = `${timestamp}:${userId}`; + console.debug(`Generating password for ${userCombo}`); + + const hmac = crypto.createHmac("sha1", secret); + hmac.setEncoding("base64"); + hmac.write(userCombo); + hmac.end(); + + const password = hmac.read(); + return [userCombo, password]; +}; + io.on("connection", (socket) => { socket.emit("welcome"); console.debug("New connection: ", socket.id); @@ -57,7 +77,27 @@ io.on("connection", (socket) => { app.use(express.static("public")); app.get("/config", (_, res) => { - res.json(peerConfig); + const [username, password] = generateTurnCredentials(); + console.log(`TURN username: ${username}, password: ${password}`); + + const updatedIceServers = peerConfig.iceServers.map((server) => { + if (server.urls[0].startsWith("turn:")) { + return { + urls: server.urls, + username: username, + credential: password, + }; + } else { + return server; + } + }); + + const updatedPeerConfig = { + iceServers: updatedIceServers, + iceTransportPolicy: peerConfig.iceTransportPolicy, + }; + + res.json(updatedPeerConfig); }); server.listen(port, () => { diff --git a/nginx/setup.sh b/nginx/setup.sh new file mode 100755 index 0000000..e94da42 --- /dev/null +++ b/nginx/setup.sh @@ -0,0 +1,16 @@ +# Setup reverse proxy +sudo apt-get update +sudo apt-get install nginx + +# Generate a self signed certificate +openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname" +sudo cp *.pem /etc/ssl + +# Set up the reverse proxy to the node app +sudo cp webrtc /etc/nginx/sites-available/ +sudo ln -s /etc/nginx/sites-available/webrtc /etc/nginx/sites-enabled + +sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT +sudo iptables -I INPUT -p tcp -m tcp --dport 443 -j ACCEPT + +sudo systemctl restart nginx.service \ No newline at end of file diff --git a/nginx/webrtc b/nginx/webrtc new file mode 100644 index 0000000..c644d50 --- /dev/null +++ b/nginx/webrtc @@ -0,0 +1,12 @@ +server { + listen 80; + listen 443 ssl; + server_name webrtc; + + ssl_certificate /etc/ssl/cert.pem; + ssl_certificate_key /etc/ssl/key.pem; + + location / { + proxy_pass http://127.0.0.1:3000/; + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8762757..0039b8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.0", "license": "ISC", "dependencies": { + "crypto": "^1.0.1", + "dotenv": "^16.4.5", "express": "^4.18.1", "socket.io": "^4.5.1" } @@ -157,6 +159,12 @@ "node": ">= 0.10" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -198,6 +206,17 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1046,6 +1065,11 @@ "vary": "^1" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1074,6 +1098,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/package.json b/package.json index 593eb5c..c78d23b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ }, "homepage": "https://github.com/mattslaney/WebRTC#readme", "dependencies": { + "crypto": "^1.0.1", + "dotenv": "^16.4.5", "express": "^4.18.1", "socket.io": "^4.5.1" } diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..01f016f --- /dev/null +++ b/setup.sh @@ -0,0 +1,12 @@ +# Setup services +cd coturn +source ./setup.sh +cd .. +cd nginx +source ./nginx/setup.sh +cd .. + +# Run app +sudo apt-get install nodejs npm +npm install +node index.js \ No newline at end of file