Skip to content

Commit

Permalink
AIOSEC-1 Protect auth-server with admin token (legacy style)
Browse files Browse the repository at this point in the history
  • Loading branch information
rocketeerbkw committed Feb 7, 2020
1 parent cbe2073 commit ea7b2c4
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 15 deletions.
1 change: 1 addition & 0 deletions services/auth-server/package.json
Expand Up @@ -23,6 +23,7 @@
"body-parser": "^1.18.2",
"express": "^4.16.2",
"flow-remove-types": "^1.2.3",
"jsonwebtoken": "^8.5.1",
"morgan": "^1.9.0",
"nano": "^6.4.3",
"ramda": "^0.25.0",
Expand Down
11 changes: 10 additions & 1 deletion services/auth-server/src/index.js
Expand Up @@ -6,6 +6,7 @@ const express = require('express');
const morgan = require('morgan');
const axios = require('axios');
const logger = require('./logger');
const validateToken = require('./util/auth');
const { generateRoute } = require('./routes');

import type { LagoonErrorWithStatus, $Request, $Response } from 'express';
Expand All @@ -25,6 +26,9 @@ app.use(
}),
);

// Only allow access with valid, admin Bearer (JWT) token
app.use(validateToken);

const port = process.env.PORT || 3000;
const lagoonKeycloakRoute = R.compose(
R.defaultTo('http://keycloak:8080'),
Expand Down Expand Up @@ -62,8 +66,13 @@ const getUserGrant = async (userId: string): Promise<keycloakGrant> => {

app.post('/generate', ...generateRoute(getUserGrant));

app.use((err: LagoonErrorWithStatus, req: $Request, res: $Response) => {
app.use((err: LagoonErrorWithStatus, req: $Request, res: $Response, next: Function) => {
logger.error(err.toString());

if (res.headersSent) {
return next(err)
}

res.status(err.status || 500);
res.send(`Request failed: ${err.toString()}`);
});
Expand Down
68 changes: 68 additions & 0 deletions services/auth-server/src/util/auth.js
@@ -0,0 +1,68 @@
const R = require('ramda');
const logger = require('../logger');
const JWT = require('jsonwebtoken');

const { JWTSECRET, JWTAUDIENCE } = process.env;

const parseBearerToken = R.compose(
R.ifElse(
splits =>
R.length(splits) === 2 &&
R.compose(
R.toLower,
R.defaultTo(''),
R.head,
)(splits) === 'bearer',
R.nth(1),
R.always(null),
),
R.split(' '),
R.defaultTo(''),
);

const validateToken = async (
req,
res,
next,
) => {
const token = parseBearerToken(req.get('Authorization'));

if (token == null) {
logger.debug('No Bearer Token');
return res
.status(401)
.send({ errors: [{ message: 'Unauthorized - Bearer Token Required' }] });
}

try {
decoded = JWT.verify(token, JWTSECRET);

if (decoded == null) {
throw new Error('Decoding token resulted in "null" or "undefined".');
}

const { aud } = decoded;

if (JWTAUDIENCE && aud !== JWTAUDIENCE) {
logger.info(`Invalid token with aud attribute: "${aud || ''}"`);
throw new Error('Token audience mismatch.');
}

const { role = 'none' } = decoded;

if (role !== 'admin') {
throw new Error('Cannot authenticate non-admin user with legacy token.');
}

next();
return;
} catch (e) {
return res.status(403).send({
errors: [{ message: `Forbidden - Invalid Auth Token: ${e.message}` }],
});
}

next();
};

module.exports = validateToken;
5 changes: 3 additions & 2 deletions services/ssh/home/grant.sh
Expand Up @@ -24,11 +24,12 @@ ADMIN_GRAPHQL="query GetUserIdBySshKey {
ADMIN_QUERY=$(echo $ADMIN_GRAPHQL | sed 's/"/\\"/g' | sed 's/\\n/\\\\n/g' | awk -F'\n' '{if(NR == 1) {printf $0} else {printf "\\n"$0}}')
USER_ID=$(curl -s -XPOST -H 'Content-Type: application/json' -H "$ADMIN_BEARER" api:3000/graphql -d "{\"query\": \"$ADMIN_QUERY\"}" | jq --raw-output '.data.userBySshKey.id')

header="Content-Type: application/json"
CONTENT_TYPE="Content-Type: application/json"
AUTHORIZATION="Authorization: Bearer $API_ADMIN_TOKEN"

# Prepare the post (containing the user id) as a JSON object.
data="{\"userId\": \"$USER_ID\", \"grant\": true}"

# Submit the token request as a POST request with the JSON data
# containing the key.
echo $(wget "$server/generate" --header "$header" --post-data "$data" -q -O -)
echo $(wget "$server/generate" --header "$CONTENT_TYPE" --header "$AUTHORIZATION" --post-data "$data" -q -O -)
16 changes: 7 additions & 9 deletions services/ssh/home/token-debug.sh
Expand Up @@ -5,12 +5,9 @@ USER_SSH_KEY=$2

echo "USER_SSH_KEY='${USER_SSH_KEY}'" >> /proc/1/fd/1

# This variable is replaced by envplate inside docker-entrypoint.
# We need this because during execution time inside the SSH
# connection we don't have access to the container environment
# variables.
# So we replace it during the start of the container.
server=${AUTH_SERVER}
# This variable is hardcoded because using envplate would wipe out all the
# other variables we want to debug.
server="http://auth-server:3000"

echo "server='${server}'" >> /proc/1/fd/1

Expand All @@ -29,14 +26,15 @@ USER_ID=$(curl -s -XPOST -H 'Content-Type: application/json' -H "$ADMIN_BEARER"

echo "USER_ID='${USER_ID}'" >> /proc/1/fd/1

header="Content-Type: application/json"
CONTENT_TYPE="Content-Type: application/json"
AUTHORIZATION="Authorization: Bearer $API_ADMIN_TOKEN"

# Prepare the post (containing the user id) as a JSON object.
data="{\"userId\": \"$USER_ID\"}"

token=$(wget "$server/generate" --header "$header" --post-data "$data" -d -v -O - 2>&1)
token=$(wget "$server/generate" --header "$CONTENT_TYPE" --header "$AUTHORIZATION" --post-data "$data" -d -v -O - 2>&1)
echo "token='${token}'" >> /proc/1/fd/1

# Submit the token request as a POST request with the JSON data
# containing the key.
echo $(wget "$server/generate" --header "$header" --post-data "$data" -q -O -)
echo $(wget "$server/generate" --header "$CONTENT_TYPE" --header "$AUTHORIZATION" --post-data "$data" -q -O -)
5 changes: 3 additions & 2 deletions services/ssh/home/token.sh
Expand Up @@ -24,11 +24,12 @@ ADMIN_GRAPHQL="query GetUserIdBySshKey {
ADMIN_QUERY=$(echo $ADMIN_GRAPHQL | sed 's/"/\\"/g' | sed 's/\\n/\\\\n/g' | awk -F'\n' '{if(NR == 1) {printf $0} else {printf "\\n"$0}}')
USER_ID=$(curl -s -XPOST -H 'Content-Type: application/json' -H "$ADMIN_BEARER" api:3000/graphql -d "{\"query\": \"$ADMIN_QUERY\"}" | jq --raw-output '.data.userBySshKey.id')

header="Content-Type: application/json"
CONTENT_TYPE="Content-Type: application/json"
AUTHORIZATION="Authorization: Bearer $API_ADMIN_TOKEN"

# Prepare the post (containing the user id) as a JSON object.
data="{\"userId\": \"$USER_ID\"}"

# Submit the token request as a POST request with the JSON data
# containing the key.
echo $(wget "$server/generate" --header "$header" --post-data "$data" -q -O -)
echo $(wget "$server/generate" --header "$CONTENT_TYPE" --header "$AUTHORIZATION" --post-data "$data" -q -O -)
2 changes: 1 addition & 1 deletion yarn.lock
Expand Up @@ -10179,7 +10179,7 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"

jsonwebtoken@^8.0.1:
jsonwebtoken@^8.0.1, jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
Expand Down

0 comments on commit ea7b2c4

Please sign in to comment.