Skip to content

Commit 704b582

Browse files
authored
Merge pull request #374 from ggwadera/master
Block SSH shell access to git repositories
2 parents bd936f1 + b562ef0 commit 704b582

File tree

6 files changed

+46
-8
lines changed

6 files changed

+46
-8
lines changed

scripts/no-interactive-login

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
printf '%s\n' "Hi! You've successfully authenticated, but myProxy does not provide interactive shell access."
3+
exit 128

scripts/post-receive

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ git clean -f
66
git checkout master
77
git branch -D prod
88
npm install
9-
pm2 startOrRestart deploy.config.js --env production --update-env
9+
sudo -u myproxy pm2 startOrRestart deploy.config.js --env production --update-env

scripts/setup.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,33 @@ if [ ! -d "./acme.sh" ] ; then
1515
cd ../
1616
fi
1717
if [ ! -d "/home/myproxy" ] ; then
18+
# Add users
1819
sudo useradd -m -c "myproxy" myproxy -s /bin/bash -p $(echo $ADMIN | openssl passwd -1 -stdin) -d "/home/myproxy"
20+
sudo useradd -m -G myproxy -s $(which git-shell) -p $(echo $ADMIN | openssl passwd -1 -stdin) git
21+
# Add sudoers rule for git user to run pm2 as myproxy, without password
22+
echo "git ALL = (myproxy) NOPASSWD: /usr/bin/pm2" > /etc/sudoers.d/git
23+
# Create folders
1924
mkdir /home/myproxy/.ssh
25+
mkdir /home/git/.ssh
2026
mkdir /home/myproxy/.scripts
27+
# Copy ssh keys and scripts
2128
cp ~/.ssh/authorized_keys /home/myproxy/.ssh/authorized_keys
29+
cp ~/.ssh/authorized_keys /home/git/.ssh/authorized_keys
2230
cp ./scripts/post-receive /home/myproxy/.scripts/post-receive
2331
cp ./scripts/pre-receive /home/myproxy/.scripts/pre-receive
2432
cp ./scripts/gitignore /home/myproxy/.scripts/.gitignore
33+
# Disable SSH MOTD message for git user
34+
touch /home/git/.hushlogin
35+
# Add git-shell message
36+
mkdir /home/git/git-shell-commands
37+
cp ./scripts/no-interactive-login /home/git/git-shell-commands/no-interactive-login
38+
chmod +x /home/git/git-shell-commands/no-interactive-login
39+
# fix file permissions
2540
chown myproxy:myproxy -R /home/myproxy/
41+
chown git:git -R /home/git/
42+
chmod 2775 -R /home/myproxy/
43+
# Prepend ssh options for authorized keys
44+
sed -i '/^ssh-rsa/s/^/no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty /' /home/git/.ssh/authorized_keys
2645
fi
2746
npm run build
2847
if [ ! -f "./data.db" ] ; then

src/api/mapping.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from '../lib/data'
1313
import { Mapping } from '../types/general'
1414
import prodConfigure from '../../scripts/prod.config.js'
15-
import { getGitUserId } from '../helpers/getGitUser'
15+
import { getGitUserId, getGitGroupId } from '../helpers/getGitUser'
1616
import environment from '../helpers/environment'
1717
const mappingRouter = express.Router()
1818
const exec = util.promisify(cp.exec)
@@ -61,7 +61,7 @@ mappingRouter.post('/', async (req, res) => {
6161
port: req.body.port || `${portCounter}`,
6262
ip: req.body.ip || '127.0.0.1',
6363
id: uuid4(),
64-
gitLink: `myproxy@${req.body.domain}:${WORKPATH}/${fullDomain}`,
64+
gitLink: `git@${req.body.domain}:${WORKPATH}/${fullDomain}`,
6565
fullDomain
6666
}
6767
domainKeys.push(mappingObject)
@@ -72,9 +72,14 @@ mappingRouter.post('/', async (req, res) => {
7272
if (!isProduction()) {
7373
return respond()
7474
}
75+
76+
// get user and group id to execute the commands with the correct permissions
7577
const gitUserId = await getGitUserId()
78+
const gitGroupId = await getGitGroupId()
79+
7680
exec(
7781
`
82+
umask 002
7883
cd ${WORKPATH}
7984
mkdir ${fullDomain}
8085
git init ${fullDomain}
@@ -88,7 +93,7 @@ mappingRouter.post('/', async (req, res) => {
8893
git add .
8994
git commit -m "Initial Commit"
9095
`,
91-
{ uid: gitUserId }
96+
{ uid: gitUserId, gid: gitGroupId }
9297
)
9398
.then(() => {
9499
respond()
@@ -126,7 +131,11 @@ mappingRouter.delete('/:id', async (req, res) => {
126131
const deletedDomain = getMappingById(req.params.id)
127132
deleteDomain(deletedDomain.fullDomain)
128133
if (!isProduction()) return res.json(deletedDomain)
134+
135+
// get user and group id to execute the commands with the correct permissions
129136
const gitUserId = await getGitUserId()
137+
const gitGroupId = await getGitGroupId()
138+
130139
exec(
131140
`
132141
cd ${WORKPATH}
@@ -136,7 +145,7 @@ mappingRouter.delete('/:id', async (req, res) => {
136145
fi
137146
rm -rf ${deletedDomain.fullDomain}
138147
`,
139-
{ uid: gitUserId }
148+
{ uid: gitUserId, gid: gitGroupId }
140149
).then(() => {
141150
res.json(deletedDomain)
142151
})

src/helpers/authorizedKeys.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@ import fs from 'fs'
22
import environment from '../helpers/environment'
33

44
const { isProduction } = environment
5+
const sshOptions =
6+
'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty'
57
let authorizedKeys: Array<string> = []
68

79
const updateSSHKey = (): void => {
810
if (isProduction()) {
9-
const file = fs.createWriteStream('/home/myproxy/.ssh/authorized_keys')
11+
const file = fs.createWriteStream('/home/git/.ssh/authorized_keys')
1012
file.on('error', err => {
1113
console.log(err)
1214
})
1315
authorizedKeys.forEach(v => {
14-
file.write(`${v}\n`)
16+
file.write(`${sshOptions} ${v}\n`)
1517
})
1618
file.end()
1719
}

src/helpers/getGitUser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@ const getGitUserId = async (): Promise<number> => {
77
return parseInt(gitId.stdout, 10)
88
}
99

10-
export { getGitUserId }
10+
const getGitGroupId = async (): Promise<number> => {
11+
const gitId = await exec('getent group myproxy | cut -d: -f3')
12+
return parseInt(gitId.stdout, 10)
13+
}
14+
15+
export { getGitUserId, getGitGroupId }

0 commit comments

Comments
 (0)