Skip to content

Commit

Permalink
Merge pull request #31 from Keukeiland/fizitzfux
Browse files Browse the repository at this point in the history
v2.4.0
  • Loading branch information
fizitzfux authored May 15, 2024
2 parents 185e400 + a9806ea commit 7e6969f
Show file tree
Hide file tree
Showing 30 changed files with 484 additions and 381 deletions.
25 changes: 25 additions & 0 deletions classes/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,29 @@ module.exports = class Extension {
res.writeHead(200, args)
return res.end(data)
}

set_cookie(key, value, secure=false) {
if (secure)
return this.cookie.serialize(
key,
value, {
secure: true,
httpOnly: true
}
)
else
return this.cookie.serialize(
key,
value
)
}

del_cookie(key) {
return this.cookie.serialize(
key,
'', {
expires: new Date(1)
}
)
}
}
5 changes: 4 additions & 1 deletion config/config.js.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const {tmpdir} = require('os')

/** Whether the server is running behind an nginx instance */
exports.nginx = false
/** The domain of the instance. */
Expand All @@ -12,7 +13,9 @@ exports.salt = "Password salt here!"
/** Location of the temporary directory. Defaults to your system's default temp dir */
exports.tmp_dir = tmpdir()
/** The location where the dicebear instance is hosted */
exports.dicebear_host = "api.dicebear.com"
exports.dicebear_host = "https://api.dicebear.com/7.x/lorelei/svg"
/** The location where the client files are hosted */
exports.client_location = "https://github.com/keukeiland/keuknet-client/releases/latest/download/"
/** Whether connections are logged */
exports.logging = false

Expand Down
111 changes: 63 additions & 48 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

// import http servers
const http2 = require('http2')
const http = require('http')
const cookie = require('cookie')

// enable use of dotenv
require('dotenv').config()
Expand All @@ -17,6 +15,7 @@ let texts = require('./config/texts')

// set up global context
const global = require('./global')
global.cookie = cookie
global.config = config
global.wg_config = wg_config
global.texts = texts
Expand All @@ -30,9 +29,7 @@ log.init(config.logging)
fetch.init(`${__dirname}/www/static/`)
log.status("Initializing database")
data.init(`${__dirname}/data/`, config.salt, function (err) {
if (err) {
log.err(err)
}
if (err) log.err(err)
log.status("Database initialized")
// set up request handler
handle.init(Object.freeze(global))
Expand All @@ -45,21 +42,26 @@ const requestListener = function (req, res) {
req.ip = process.env.IP
}

// if no authorization headers set it to false, to prevent errors
req.headers.authorization ??= false
req.cookies = cookie.parse(req.headers.cookie || '')
// get authorization info
req.headers.authorization ??= req.cookies.auth
// get requested host, HTTP/<=1.1 uses host, HTTP/>=2 uses :authority
req.headers.host ??= req.headers[':authority']
// set user agent to "NULL", to prevent errors
req.headers['user-agent'] ??= "NULL"
// make sure cookie is defined
if (isNaN(req.headers.cookie)) req.headers.cookie = 0
// get requesting IP
req.ip ??= req.headers['x-real-ip'] || req.socket?.remoteAddress || req.connection?.remoteAddress || req.connection.socket?.remoteAddress

// if request is not for any domain served here, act like server isn't here
if (req.headers.host != config.domain) {
log.con_err(req)
return

// If standalone
if (!config.nginx) {
// get requesting IP
req.ip ??= req.socket?.remoteAddress || req.connection?.remoteAddress || req.connection.socket?.remoteAddress

// if request is not for any domain served here, act like server isn't here
if (req.headers.host != config.domain) {
log.con_err(req)
return
}
// If behind NGINX
} else {
// get requesting IP
req.ip = req.headers['x-real-ip'] || '0.0.0.0'
}

// separate url arguments from the url itself
Expand Down Expand Up @@ -109,39 +111,52 @@ const requestListener = function (req, res) {
}
}


// Handle insecure HTTP requests
const insecureRequestListener = function (req, res) {
// redirect request to HTTPS
// Redirect requests to HTTPS
const httpsRedirect = function (req, res) {
res.writeHead(307, {"Location": `https://${req.headers.host}${req.url}`})
res.end()
}

if (!config.nginx) {
// fetch https encryption keys
log.status("Fetching encryption keys")
fetch.key(config.private_key_path, function(private_key, err) {
if (err) log.err("Failed fetching private key")
fetch.key(config.server_cert_path, function(server_cert, err) {
if (err) log.err("Failed fetching server certificate")
fetch.key(config.ca_cert_path, function(ca_cert, err) {
if (err) log.err("Failed fetching CA certificate")
log.status("Encryption keys fetched")
http2.createSecureServer({
key: private_key,
cert: server_cert,
ca: ca_cert,
allowHTTP1: true,
}, requestListener)
.listen(config.https_port, config.host, () => {
console.log(`\x1b[1mHTTP/2 & HTTPS server running on https://${config.domain}:${config.https_port}, interface '${config.host}'\n\x1b[0m`)
})

function startServer(http, https) {
if (https) {
log.status("Fetching encryption keys")
// Private key
fetch.key(config.private_key_path, function(key, err) {
if (err) log.err("Failed fetching private key")
// Certificate
fetch.key(config.server_cert_path, function(cert, err) {
if (err) log.err("Failed fetching server certificate")
// Certificate chain
fetch.key(config.ca_cert_path, function(ca, err) {
if (err) log.err("Failed fetching CA certificate")
log.status("Encryption keys fetched")
// Start server
require('http2').createSecureServer({
key,
cert,
ca,
allowHTTP1: true,
}, requestListener).listen(
config.https_port,
config.host,
() => log.serverStart("https", config.domain, config.host, config.https_port)
)
})
})
})
})
}
if (http) {
// Start server
require('http').createServer(
https ? httpsRedirect : requestListener
).listen(
config.http_port,
config.host,
() => log.serverStart("http", config.domain, config.host, config.http_port)
)
}
}
// Start HTTP server
http.createServer(config.nginx ? requestListener : insecureRequestListener)
.listen(config.http_port, config.host, () => {
console.log(`\x1b[1mHTTP/1.1 server running on http://${config.domain}:${config.http_port}, interface '${config.host}'\n\x1b[0m`)
})

startServer(true, !config.nginx)

68 changes: 42 additions & 26 deletions modules/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ exports.init = function(path, saltq, callback) {
}
exports.db = () => {return db}

function __hash_pw(password) {
if (!password) return
return crypto.pbkdf2Sync(password, salt, 10000, 128, 'sha512').toString('base64')
}

function __decrypt_auth(auth, callback) {
if (!auth) {
return callback(undefined, undefined, new Error("Quit early"))
Expand All @@ -25,9 +30,13 @@ function __decrypt_auth(auth, callback) {
return callback(undefined, undefined, new Error("Missing name or password"))
}
[name, password] = data.split(":")

// hash password
hash = crypto.pbkdf2Sync(password, salt, 10000, 128, 'sha512').toString('base64')
return callback(name, hash)
password = __hash_pw(password)
if (!password)
return callback(undefined, undefined, new Error("Missing name or password"))

return callback(name, password)
}

function __exists(name, callback) {
Expand All @@ -38,42 +47,49 @@ function __exists(name, callback) {
}


exports.addUser = function (auth, callback) {
__decrypt_auth(auth, function (name, password, err) {
if (err) return callback(err)
exports.addUser = function (name, password, callback) {
if (name && password) {
password = __hash_pw(password)
// Check if username is already taken
__exists(name, function (exists, err) {
if (err) return callback(err)
if (exists) return callback(new Error("Username already taken"))
// add user to db
db.run("INSERT INTO user(name,password) VALUES($name,$password)", [name, password], function (err) {
db.run("INSERT INTO user(name,password,pfp_code) VALUES($name,$password,$pfp_code)", [name, password, 'seed='+name], function (err) {
return callback(err)
})
})
})
} else {
return callback(new Error("Missing name or password"))
}
}


exports.authenticate = function (auth, ip, ip_scope, callback) {
// Try to get name and password
__decrypt_auth(auth, function (name, password, err) {
if (err) {
if (ip.startsWith(ip_scope)) {
// Try using IP-address if no name and password
db.get("SELECT u.* FROM user u JOIN profile p ON p.user_id = u.id WHERE p.ip=$ip", ip, function (err, user) {
return callback(user, err)
})
return
}
return callback(undefined, err)
}
// Auth using name and password
db.get("SELECT * FROM user WHERE name=$name", name, function (err, user) {
if (user) {
if (password == user.password) {
return callback(user, err)
if (auth) {
// Try to get name and password
__decrypt_auth(auth, function (name, password, err) {
if (err) {
if (ip.startsWith(ip_scope)) {
// Try using IP-address if no name and password
db.get("SELECT u.* FROM user u JOIN _profile_device p ON p.user_id = u.id WHERE p.ip=$ip", ip, function (err, user) {
return callback(user, err)
})
return
}
return callback(undefined, err)
}
return callback(undefined, err)
// Auth using name and password
db.get("SELECT * FROM user WHERE name=$name", name, function (err, user) {
if (user) {
if (password == user.password) {
return callback(user, err)
}
}
return callback(undefined, new Error("Wrong name or password"))
})
})
})
} else {
return callback(undefined, null)
}
}
8 changes: 6 additions & 2 deletions modules/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exports.init = function(we_log) {
function __mask_ip(ip) {
let tmp = ""
// if IPv4
if (ip.startsWith("::ffff:")) {
if (ip.includes('.')) {
// strip 4to6 prefix
ip = ip.substring(ip.lastIndexOf(':')+1,ip.length)
// mask ip
Expand Down Expand Up @@ -37,7 +37,7 @@ exports.con = function(req) {
url = __mask_url(req.url)
console.log(
`\x1b[32m [${ip}]=>'${req.method} ${url}
HTTP/${req.httpVersion} ${req.headers['user-agent'].split(" ",1)[0]} ${req.headers.authorization? "auth" : "noauth"}\x1b[0m`
HTTP/${req.httpVersion} ${(req.headers['user-agent'] ?? "NULL").split(" ",1)[0]} ${req.headers.authorization? "auth" : "noauth"}\x1b[0m`
)
}
}
Expand All @@ -58,3 +58,7 @@ exports.status = function(msg) {
exports.err = function(err) {
console.log(`\x1b[31m>> ${err}\x1b[0m`)
}

exports.serverStart = function(type, domain, host, port) {
console.log(`\x1b[1m${type.toUpperCase()} server running on ${type}://${domain}:${port}, interface '${host}'\n\x1b[0m`)
}
Loading

0 comments on commit 7e6969f

Please sign in to comment.