diff --git a/scripts/no-interactive-login b/scripts/no-interactive-login new file mode 100644 index 00000000..12b1dc6d --- /dev/null +++ b/scripts/no-interactive-login @@ -0,0 +1,3 @@ +#!/bin/sh +printf '%s\n' "Hi! You've successfully authenticated, but myProxy does not provide interactive shell access." +exit 128 \ No newline at end of file diff --git a/scripts/post-receive b/scripts/post-receive index 26a3882e..2a196778 100755 --- a/scripts/post-receive +++ b/scripts/post-receive @@ -6,4 +6,4 @@ git clean -f git checkout master git branch -D prod npm install -pm2 startOrRestart deploy.config.js --env production --update-env +sudo -u myproxy pm2 startOrRestart deploy.config.js --env production --update-env diff --git a/scripts/setup.sh b/scripts/setup.sh index 7281ec02..2ee97234 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -15,14 +15,33 @@ if [ ! -d "./acme.sh" ] ; then cd ../ fi if [ ! -d "/home/myproxy" ] ; then + # Add users sudo useradd -m -c "myproxy" myproxy -s /bin/bash -p $(echo $ADMIN | openssl passwd -1 -stdin) -d "/home/myproxy" + sudo useradd -m -G myproxy -s $(which git-shell) -p $(echo $ADMIN | openssl passwd -1 -stdin) git + # Add sudoers rule for git user to run pm2 as myproxy, without password + echo "git ALL = (myproxy) NOPASSWD: /usr/bin/pm2" > /etc/sudoers.d/git + # Create folders mkdir /home/myproxy/.ssh + mkdir /home/git/.ssh mkdir /home/myproxy/.scripts + # Copy ssh keys and scripts cp ~/.ssh/authorized_keys /home/myproxy/.ssh/authorized_keys + cp ~/.ssh/authorized_keys /home/git/.ssh/authorized_keys cp ./scripts/post-receive /home/myproxy/.scripts/post-receive cp ./scripts/pre-receive /home/myproxy/.scripts/pre-receive cp ./scripts/gitignore /home/myproxy/.scripts/.gitignore + # Disable SSH MOTD message for git user + touch /home/git/.hushlogin + # Add git-shell message + mkdir /home/git/git-shell-commands + cp ./scripts/no-interactive-login /home/git/git-shell-commands/no-interactive-login + chmod +x /home/git/git-shell-commands/no-interactive-login + # fix file permissions chown myproxy:myproxy -R /home/myproxy/ + chown git:git -R /home/git/ + chmod 2775 -R /home/myproxy/ + # Prepend ssh options for authorized keys + sed -i '/^ssh-rsa/s/^/no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty /' /home/git/.ssh/authorized_keys fi npm run build if [ ! -f "./data.db" ] ; then diff --git a/src/api/mapping.ts b/src/api/mapping.ts index 06f8df77..80ed7a85 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -12,7 +12,7 @@ import { } from '../lib/data' import { Mapping } from '../types/general' import prodConfigure from '../../scripts/prod.config.js' -import { getGitUserId } from '../helpers/getGitUser' +import { getGitUserId, getGitGroupId } from '../helpers/getGitUser' import environment from '../helpers/environment' const mappingRouter = express.Router() const exec = util.promisify(cp.exec) @@ -61,7 +61,7 @@ mappingRouter.post('/', async (req, res) => { port: req.body.port || `${portCounter}`, ip: req.body.ip || '127.0.0.1', id: uuid4(), - gitLink: `myproxy@${req.body.domain}:${WORKPATH}/${fullDomain}`, + gitLink: `git@${req.body.domain}:${WORKPATH}/${fullDomain}`, fullDomain } domainKeys.push(mappingObject) @@ -72,9 +72,14 @@ mappingRouter.post('/', async (req, res) => { if (!isProduction()) { return respond() } + + // get user and group id to execute the commands with the correct permissions const gitUserId = await getGitUserId() + const gitGroupId = await getGitGroupId() + exec( ` + umask 002 cd ${WORKPATH} mkdir ${fullDomain} git init ${fullDomain} @@ -88,7 +93,7 @@ mappingRouter.post('/', async (req, res) => { git add . git commit -m "Initial Commit" `, - { uid: gitUserId } + { uid: gitUserId, gid: gitGroupId } ) .then(() => { respond() @@ -126,7 +131,11 @@ mappingRouter.delete('/:id', async (req, res) => { const deletedDomain = getMappingById(req.params.id) deleteDomain(deletedDomain.fullDomain) if (!isProduction()) return res.json(deletedDomain) + + // get user and group id to execute the commands with the correct permissions const gitUserId = await getGitUserId() + const gitGroupId = await getGitGroupId() + exec( ` cd ${WORKPATH} @@ -136,7 +145,7 @@ mappingRouter.delete('/:id', async (req, res) => { fi rm -rf ${deletedDomain.fullDomain} `, - { uid: gitUserId } + { uid: gitUserId, gid: gitGroupId } ).then(() => { res.json(deletedDomain) }) diff --git a/src/helpers/authorizedKeys.ts b/src/helpers/authorizedKeys.ts index d39e2561..f610511a 100644 --- a/src/helpers/authorizedKeys.ts +++ b/src/helpers/authorizedKeys.ts @@ -2,16 +2,18 @@ import fs from 'fs' import environment from '../helpers/environment' const { isProduction } = environment +const sshOptions = + 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty' let authorizedKeys: Array = [] const updateSSHKey = (): void => { if (isProduction()) { - const file = fs.createWriteStream('/home/myproxy/.ssh/authorized_keys') + const file = fs.createWriteStream('/home/git/.ssh/authorized_keys') file.on('error', err => { console.log(err) }) authorizedKeys.forEach(v => { - file.write(`${v}\n`) + file.write(`${sshOptions} ${v}\n`) }) file.end() } diff --git a/src/helpers/getGitUser.ts b/src/helpers/getGitUser.ts index 72fd6933..c82f7d53 100644 --- a/src/helpers/getGitUser.ts +++ b/src/helpers/getGitUser.ts @@ -7,4 +7,9 @@ const getGitUserId = async (): Promise => { return parseInt(gitId.stdout, 10) } -export { getGitUserId } +const getGitGroupId = async (): Promise => { + const gitId = await exec('getent group myproxy | cut -d: -f3') + return parseInt(gitId.stdout, 10) +} + +export { getGitUserId, getGitGroupId }