A README.md => README.md +9 -0
@@ 0,0 1,9 @@
+# negativefour
+
+negativefour is a project to make it easier to run webpages as tor
+hidden services. negativefour works like many other static web hosting
+services you are likely familiar with.
+
+The project webpage is located at https://negativefour.com you can
+also find documentation about howo negativefour works in the /docs
+subdirectory and at https://man.sr.ht/~ekez/negativefour/ .
M docs/index.md => docs/index.md +1 -0
@@ 9,3 9,4 @@ websites on both the clearnet and as a tor hidden service.
- Abuse mitigation [here](docs/abuse-mitigation.md)
- Notes about configuration of our box [here](docs/multiple-sites-one-host.md)
- How the webpage is served [here](docs/negativefour.com.md)
+- The serve API [here](docs/serve-api.md)
M docs/negativefour.com.md => docs/negativefour.com.md +0 -83
@@ 86,86 86,3 @@ WantedBy=multi-user.target
ProxyPassReverse / http://localhost:2998/
</VirtualHost>
```
-
-### `/deploy`
-
-All requests must have a `token` field which is a signed JSON web
-token. The token must be signed by the same key that the frontend
-uses.
-
-POST JSON body params:
-
-```
-{
- name: <name of the webpage>,
- repo: <link to git repo containing static webpage>,
- token: <json web token containing user info>
-}
-```
-
-POST JSON response:
-
-```
-{
- jobID: <id for querying the status of the deploy job>
-}
-```
-
-An invalid request will result in a JSON reponse containing an error
-field and the HTTP status code will be set appropriately.
-
-```
-{
- error: <error message for the invalid request>
-}
-```
-
-### `/undeploy`
-
-POST JSON body params:
-
-```
-{
- name: <name of the webpage>,
- token: <json web token containing user info>
-}
-```
-
-POST JSON response:
-
-```
-{
- jobID: <id for querying the status of the undeploy job>
-}
-```
-
-An invalid request will result in a JSON reponse containing an error
-field and the HTTP status code will be set appropriately.
-
-```
-{
- error: <error message for the invalid request>
-}
-```
-
-### `/status`
-
-GET JSON body params:
-
-```
-{
- jobID: <previously provided jobID>,
- token: <json web token containing user info>
-}
-```
-
-GET JSON response:
-
-```
-{
- stdout: <standard out from job so far>,
- stderr: <standard err from job so far>,
- exitCode: <present if job has exited - exit code of deploy script>,
- serverError: <present if there was a server starting the script - contains error message>
-}
-```
A docs/serve-api.md => docs/serve-api.md +92 -0
@@ 0,0 1,92 @@
+# Serve API
+
+All requests begin in the form:
+
+```
+{
+ token: <JWT token signed with internal api key>
+}
+```
+
+The token signature is then checked. If the signature validates
+alright the token is unpacked and its contents are sent to the API
+endpoints.
+
+This is a long winded way of saying that the API paramaters shown
+below must match the contents of the signed token you send.
+
+## `/deploy`
+
+POST JSON body params:
+
+```
+{
+ name: <name of the webpage>,
+ repo: <link to git repo containing static webpage>,
+}
+```
+
+POST JSON response:
+
+```
+{
+ jobID: <id for querying the status of the deploy job>
+}
+```
+
+An invalid request will result in a JSON reponse containing an error
+field and the HTTP status code will be set appropriately.
+
+```
+{
+ error: <error message for the invalid request>
+}
+```
+
+## `/undeploy`
+
+POST JSON body params:
+
+```
+{
+ name: <name of the webpage>,
+}
+```
+
+POST JSON response:
+
+```
+{
+ jobID: <id for querying the status of the undeploy job>
+}
+```
+
+An invalid request will result in a JSON reponse containing an error
+field and the HTTP status code will be set appropriately.
+
+```
+{
+ error: <error message for the invalid request>
+}
+```
+
+## `/status`
+
+GET JSON body params:
+
+```
+{
+ jobID: <previously provided jobID>,
+}
+```
+
+GET JSON response:
+
+```
+{
+ stdout: <standard out from job so far>,
+ stderr: <standard err from job so far>,
+ exitCode: <present if job has exited - exit code of deploy script>,
+ serverError: <present if there was a server starting the script - contains error message>
+}
+```
M serve/app.js => serve/app.js +37 -46
@@ 12,6 12,26 @@ const jobStatusMap = new Map()
app.use(express.json())
+// Middleware that checks that incoming requests are correctly signed.
+const requireAuth = (req, res, next) => {
+ const signedToken = req.body.token
+ if (!signedToken) {
+ res.status(400).json({
+ error: 'Invalid request - no token.'
+ })
+ }
+
+ try {
+ const decoded = jwt.verify(signedToken, process.env.JWT_INTERNAL_KEY)
+ req.jwt = decoded
+ } catch (err) {
+ return res.status(401).json({
+ error: 'Invalid authentication token.'
+ })
+ }
+ return next()
+}
+
// Sends a message to the zekebots discord channel. Doesn't wait for
// the message to send and logs an error / success message as needed.
const sendDiscordAlert = message => {
@@ 27,28 47,19 @@ const sendDiscordAlert = message => {
// Posting to deploy starts a build and returns a unique ID for that
// build. The status of the build can then be queried by making a get
// request to deploy with that ID as a URL param.
-app.post('/deploy', function(req, res) {
- const name = req.body.name
- const repo = req.body.repo
- const token = req.body.token
+app.post('/deploy', requireAuth, function(req, res) {
+ const name = req.jwt.name
+ const repo = req.jwt.repo
+ const user = req.jwt.user
- if (!name || !repo || !token) {
+ if (!name || !repo || !user) {
return res.status(400).json({
- error: 'Invalid request.'
- })
- }
-
- let user
- try {
- const decoded = jwt.verify(token, process.env.JWT_KEY)
- user = decoded.email
- } catch (err) {
- return res.status(401).json({
- error: 'Invalid authentication token.'
+ error: 'Invalid request - missing fields.'
})
}
const child = execFile('./deploy.sh', [repo, name])
+ // const child = execFile('./dummy.sh', [repo, name])
const childProcessID = jobStatusMap.size.toString()
jobStatusMap.set(childProcessID, {
stdout: '',
@@ 87,23 98,12 @@ app.post('/deploy', function(req, res) {
})
})
-app.get('/status', function(req, res) {
- const token = req.body.token
- const jobID = req.body.jobID
+app.get('/status', requireAuth, function(req, res) {
+ const jobID = req.jwt.jobID
- if (!token || !jobID) {
+ if (!jobID) {
return res.status(400).json({
- error: 'Invalid request.'
- })
- }
-
- let user
- try {
- const decoded = jwt.verify(token, process.env.JWT_KEY)
- user = decoded.email
- } catch (err) {
- return res.status(401).json({
- error: 'Invalid authentication token.'
+ error: 'Invalid request - missing `jobID`.'
})
}
@@ 118,27 118,18 @@ app.get('/status', function(req, res) {
res.status(200).json(jobInfo)
})
-app.post('/delete', function(req, res) {
- const name = req.body.name
- const token = req.body.token
+app.post('/delete', requireAuth, function(req, res) {
+ const name = req.jwt.name
+ const user = req.jwt.user
- if (!name || !token) {
+ if (!name || !user) {
return res.status(400).json({
- error: 'Invalid request.'
- })
- }
-
- let user
- try {
- const decoded = jwt.verify(token, process.env.JWT_KEY)
- user = decoded.email
- } catch (err) {
- return res.status(401).json({
- error: 'Invalid authentication token.'
+ error: 'Invalid request - missing fields.'
})
}
const child = execFile('./undeploy.sh', [name])
+ // const child = execFile('./dummy.sh', [repo, name])
const childProcessID = jobStatusMap.size.toString()
jobStatusMap.set(childProcessID, {
stdout: '',
A serve/dummy.sh => serve/dummy.sh +1 -0
@@ 0,0 1,1 @@
+echo hello
A serve/helpers/cli.sh => serve/helpers/cli.sh +59 -0
@@ 0,0 1,59 @@
+#!/bin/bash
+
+set -e
+set -u
+set -o pipefail
+
+function deploy() {
+ local secret=$1
+ local url=$2
+
+ local repo=$3
+ local name=$4
+ local user=$5
+
+ local token=$(jwt encode --secret $secret "{\"name\":\"$name\",\"repo\":\"$repo\",\"user\":\"$user\"}")
+ curl -s -X POST "${url}/deploy" \
+ -H 'Content-Type: application/json' \
+ -d "{\"token\":\"$token\"}" | jq
+}
+
+function status() {
+ local secret=$1
+ local url=$2
+
+ local jobID=$3
+
+ local token=$(jwt encode --secret $secret "{\"jobID\":\"$jobID\"}")
+ curl -s -X GET "${url}/status" \
+ -H 'Content-Type: application/json' \
+ -d "{\"token\":\"$token\"}" | jq
+}
+
+
+function delete() {
+ local secret=$1
+ local url=$2
+
+ local name=$3
+ local user=$4
+
+ local token=$(jwt encode --secret $secret "{\"name\":\"$name\",\"user\":\"$user\"}")
+ curl -s -X POST "${url}/delete" \
+ -H 'Content-Type: application/json' \
+ -d "{\"token\":\"$token\"}" | jq
+}
+
+case $1 in
+ deploy)
+ deploy $2 $3 $4 $5 $6
+ ;;
+ status)
+ status $2 $3 $4
+ ;;
+ delete)
+ delete $2 $3 $4 $5
+ ;;
+ *)
+ echo "usage: $0 <deploy|status|delete>"
+esac