From c7a506eb6e3421a8a716b45227fc05112e9db147 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 14:57:46 -0300 Subject: [PATCH 01/22] client: docker changes, add start and stop buttons --- src/public/client.ts | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/public/client.ts b/src/public/client.ts index 582b1e5c..fac28f25 100644 --- a/src/public/client.ts +++ b/src/public/client.ts @@ -15,6 +15,10 @@ type Status = { status: string } +type ContainerResponse = { + message?: string +} + const create: HTMLElement = helper.getElement('.create') const hostSelector: HTMLElement = helper.getElement('#hostSelector') const domainList: HTMLElement = helper.getElement('.domainList') @@ -46,7 +50,7 @@ class MappingItem { // they are not managed by pm2. let settingClass let logClass - if (data.status === 'online') { + if (data.status === 'running') { iconClass = 'fa fa-circle mr-1 mt-1' iconColor = 'rgba(50,255,50,0.5)' logClass = 'fa fa-file-text-o ml-1 mt-1' @@ -108,6 +112,18 @@ class MappingItem { ${data.gitLink} + + - - ${data.gitLink} @@ -170,18 +156,6 @@ class MappingItem { }) } } - const clearLogButton = helper.getElement('.deleteLogButton', mappingElement) - clearLogButton.onclick = (): void => { - if ( - confirm(`Are you sure you want to clear ${data.fullDomain}'s logs?`) - ) { - fetch(`/api/logs/${data.fullDomain}`, { - method: 'DELETE' - }).then(() => { - window.location.reload() - }) - } - } } } From 76dce9e255e810b183eb7ba25587bfdf9996eabe Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:28:14 -0300 Subject: [PATCH 04/22] npm: include 'dockerode' dependency --- package-lock.json | 182 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + 2 files changed, 181 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36b392be..668dd8fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -479,6 +479,15 @@ "@types/express": "*" } }, + "@types/dockerode": { + "version": "2.5.34", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.34.tgz", + "integrity": "sha512-LcbLGcvcBwBAvjH9UrUI+4qotY+A5WCer5r43DR5XHv2ZIEByNXFdPLo1XxR+v/BjkGjlggW8qUiXuVEhqfkpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -1053,6 +1062,11 @@ } } }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1061,6 +1075,33 @@ "tweetnacl": "^0.14.3" } }, + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -1157,6 +1198,15 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1267,6 +1317,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -1667,6 +1722,51 @@ "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", "dev": true }, + "docker-modem": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.4.tgz", + "integrity": "sha512-vDTzZjjO1sXMY7m0xKjGdFMMZL7vIUerkC3G4l6rnrpOET2M6AOufM8ajmQoOB+6RfSn6I/dlikCUq/Y91Q1sQ==", + "requires": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^0.8.7" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "dockerode": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.2.1.tgz", + "integrity": "sha512-XsSVB5Wu5HWMg1aelV5hFSqFJaKS5x1aiV/+sT7YOzOq1IRl49I/UwV8Pe4x6t0iF9kiGkWu5jwfvbkcFVupBw==", + "requires": { + "docker-modem": "^2.1.0", + "tar-fs": "~2.0.1" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2467,6 +2567,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3266,6 +3371,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", @@ -4588,6 +4698,11 @@ } } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "mri": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", @@ -6138,6 +6253,11 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -6152,6 +6272,24 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "ssh2": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", + "integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==", + "requires": { + "ssh2-streams": "~0.4.10" + } + }, + "ssh2-streams": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", + "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", + "requires": { + "asn1": "~0.2.0", + "bcrypt-pbkdf": "^1.0.2", + "streamsearch": "~0.1.2" + } + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -6206,6 +6344,11 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -6269,7 +6412,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6339,6 +6481,41 @@ } } }, + "tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", + "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -6644,8 +6821,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.0", diff --git a/package.json b/package.json index b1c6d8ac..2fe6d1d1 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@types/node-fetch": "^2.5.0", "@types/uuid": "^3.4.5", "cookie-parser": "^1.4.4", + "dockerode": "^3.2.1", "dotenv": "^8.1.0", "ejs": "^2.6.2", "express": "^4.17.1", @@ -48,6 +49,7 @@ "uuid": "^3.3.3" }, "devDependencies": { + "@types/dockerode": "^2.5.34", "@types/jest": "^24.0.17", "@types/node": "^12.7.1", "@typescript-eslint/eslint-plugin": "^2.0.0", From a71d38a503641c1f5a4b4dfefe93fd1fe769fd81 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:28:47 -0300 Subject: [PATCH 05/22] helpers: docker helper functions --- src/helpers/docker.ts | 96 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/helpers/docker.ts diff --git a/src/helpers/docker.ts b/src/helpers/docker.ts new file mode 100644 index 00000000..72f27fc8 --- /dev/null +++ b/src/helpers/docker.ts @@ -0,0 +1,96 @@ +import Docker from 'dockerode' +import path from 'path' +import { Readable, PassThrough } from 'stream' +import environment from '../helpers/environment' + +const docker = new Docker({ socketPath: '/var/run/docker.sock' }) + +const getContainersList = async (): Promise => { + const containers = await docker.listContainers({ all: true }) + return containers +} + +const getContainerLogs = async ( + id: string, + options: Docker.ContainerLogsOptions +): Promise => { + const container = docker.getContainer(id) + let logs = await container.logs(options) + if (!options.follow) { + // if follow = false, the logs are returned as a buffer + // so we need to convert into a stream + logs = Readable.from(logs) + } + const demuxedStream = new PassThrough() + container.modem.demuxStream(logs, demuxedStream, demuxedStream) + logs.on('end', () => demuxedStream.end()) + return demuxedStream +} + +const createContainer = async ( + fullDomain: string, + port: number +): Promise => { + const workPath = path.resolve(environment.WORKPATH, fullDomain) + return docker + .createContainer({ + Image: 'node:alpine', + name: fullDomain, + User: 'node', + ExposedPorts: { + '3000/tcp': {} + }, + Tty: false, + WorkingDir: '/home/node/app', + Env: ['NODE_ENV=production', 'PORT=3000'], + HostConfig: { + Binds: [`${workPath}:/home/node/app`], + RestartPolicy: { + Name: 'on-failure', + MaximumRetryCount: 3 + }, + LogConfig: { + Type: 'json-file', + Config: { + 'max-size': '10m', + 'max-file': '1' + } + }, + PortBindings: { + '3000/tcp': [ + { + HostIp: '', + HostPort: port.toString() + } + ] + } + }, + Cmd: ['node', '.'] + }) + .then(container => container.id) + .catch(err => err) +} + +const startContainer = async (id: string): Promise => { + const container = docker.getContainer(id) + return container.restart() +} + +const stopContainer = async (id: string): Promise => { + const container = docker.getContainer(id) + return container.stop() +} + +const removeContainer = async (id: string): Promise => { + const container = docker.getContainer(id) + return container.remove() +} + +export { + getContainersList, + getContainerLogs, + createContainer, + startContainer, + stopContainer, + removeContainer +} From 38d8f8ddf2fbae6b78d19bc2a6d6fd98c919a56a Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:29:37 -0300 Subject: [PATCH 06/22] api/mappings: use docker --- src/api/mapping.ts | 84 ++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index 66394282..b491ae9c 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -3,6 +3,8 @@ import express from 'express' import uuid4 from 'uuid/v4' import util from 'util' import cp from 'child_process' +import fs from 'fs' +import path from 'path' import { setData, getMappings, @@ -11,9 +13,15 @@ import { deleteDomain } from '../lib/data' import { Mapping } from '../types/general' -import prodConfigure from '../../scripts/prod.config.js' import { getGitUserId, getGitGroupId } from '../helpers/getGitUser' import environment from '../helpers/environment' +import { + getContainersList, + createContainer, + startContainer, + stopContainer, + removeContainer +} from '../helpers/docker' const mappingRouter = express.Router() const exec = util.promisify(cp.exec) const getNextPort = (map, start = 3002): number => { @@ -22,7 +30,7 @@ const getNextPort = (map, start = 3002): number => { return getNextPort(map, start) } -const { WORKPATH, isProduction } = environment +const { PORT, WORKPATH, isProduction } = environment mappingRouter.post('/', async (req, res) => { const domainKeys = getMappings() @@ -42,25 +50,19 @@ mappingRouter.post('/', async (req, res) => { return acc }, {}) const portCounter = getNextPort(map) - const prodConfigApp = [...prodConfigure.apps][0] - prodConfigApp.name = fullDomain - prodConfigApp.env_production.PORT = parseInt(req.body.port || portCounter, 10) - prodConfigApp.script = `npm run start:myproxy << /home/myproxy/.pm2/logs/${fullDomain}-out.log` - prodConfigApp.error_file = `/home/myproxy/.pm2/logs/${fullDomain}-err.log` - prodConfigApp.merge_logs = true - delete prodConfigApp.env_production.ADMIN - const prodConfig = { - apps: prodConfigApp - } + const port = parseInt(req.body.port || portCounter, 10) const scriptPath = '.scripts' + // Create a new container and get the id + const id = isProduction ? await createContainer(fullDomain, port) : uuid4() + const respond = (): void => { const mappingObject: Mapping = { domain: req.body.domain.toLowerCase(), subDomain: req.body.subDomain.toLowerCase(), - port: req.body.port || `${portCounter}`, + port: port.toString(), ip: req.body.ip || '127.0.0.1', - id: uuid4(), + id, gitLink: `git@${req.body.domain}:${WORKPATH}/${fullDomain}`, fullDomain } @@ -89,9 +91,9 @@ mappingRouter.post('/', async (req, res) => { cd ${fullDomain} git config user.email "root@ipaddress" git config user.name "user" - echo 'module.exports = ${JSON.stringify(prodConfig)}' > deploy.config.js git add . git commit -m "Initial Commit" + echo "curl --silent --request GET --url http://localhost:${PORT}/api/mappings/${id}/start" >> ${fullDomain}/.git/hooks/post-receive `, { uid: gitUserId, gid: gitGroupId } ) @@ -103,16 +105,15 @@ mappingRouter.post('/', async (req, res) => { mappingRouter.get('/', async (req, res) => { const domains = getMappings() + if (!isProduction()) return res.json(domains.map(el => ({ ...el, status: 'not started' }))) - const data = await exec('su - myproxy -c "pm2 jlist"') - - const outArr = data.stdout.split('\n') - const statusData = JSON.parse(outArr[outArr.length - 1]).reduce( + const data = await getContainersList() + const statusData = data.reduce( (statusObj, el) => ({ ...statusObj, - [el.name]: el.pm2_env.status + [el.Names[0].replace('/', '')]: el.State }), {} ) @@ -132,23 +133,18 @@ mappingRouter.delete('/:id', async (req, res) => { 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} - export PM2_HOME=/home/myproxy/.pm2 - if command ls ${deletedDomain.fullDomain} | grep "package.json" &>/dev/null; then - pm2 delete ${deletedDomain.fullDomain} - fi - rm -rf ${deletedDomain.fullDomain} - `, - { uid: gitUserId, gid: gitGroupId } - ).then(() => { - res.json(deletedDomain) - }) + // stop and remove container + removeContainer(deletedDomain.fullDomain) + .then(() => { + // delete the domain folder + fs.unlink(path.resolve(WORKPATH, deletedDomain.fullDomain), err => { + if (err) { + return res.status(500).json({ message: err.message }) + } + res.json(deletedDomain) + }) + }) + .catch(err => res.status(err.statusCode).json(err.json)) }) mappingRouter.get('/:id', (req, res) => { @@ -156,4 +152,18 @@ mappingRouter.get('/:id', (req, res) => { res.json(foundDomain || {}) }) +mappingRouter.get('/:id/start', (req, res) => { + const { id } = req.params + startContainer(id) + .then(() => res.sendStatus(204)) + .catch(err => res.status(err.statusCode).json(err.json)) +}) + +mappingRouter.get('/:id/stop', (req, res) => { + const { id } = req.params + stopContainer(id) + .then(() => res.sendStatus(204)) + .catch(err => res.status(err.statusCode).json(err.json)) +}) + export default mappingRouter From 7fc7eeb7002506168b378ea4987994a5197a19f6 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:30:34 -0300 Subject: [PATCH 07/22] api/logs: use single path with stream param Changed the api endpoint to use a single route, with a param for the stream (stdout or stderr) Removed the 'Clear Logs' feature, as Docker does not have a easy way to do that, but we can pass a 'tail=x' param in the URL. --- src/api/logs.ts | 60 +++++++++---------------------- src/tests/integration/log.test.ts | 13 ++++--- 2 files changed, 22 insertions(+), 51 deletions(-) diff --git a/src/api/logs.ts b/src/api/logs.ts index f5f1d616..7c7d6de9 100644 --- a/src/api/logs.ts +++ b/src/api/logs.ts @@ -1,65 +1,37 @@ import express from 'express' -import fs from 'fs' import environment from '../helpers/environment' import { getMappingByDomain } from '../lib/data' +import { getContainerLogs } from '../helpers/docker' const logsRouter = express.Router() const { isProduction } = environment -logsRouter.get('/err/:domain', (req, res) => { - const { domain } = req.params +logsRouter.get('/:stream/:domain', async (req, res) => { + const { stream, domain } = req.params + const { follow, tail } = req.query - if (isProduction()) { - // Only search for domain when running in production. The test does not - // require a valid domain since it only verifies the endpoint - const { fullDomain } = getMappingByDomain(domain) - // Pipes the error log files to res - res.setHeader('content-type', 'text/plain') - fs.createReadStream(`/home/myproxy/.pm2/logs/${fullDomain}-err.log`).pipe( - res - ) - } else { - res.send('OK') + // Stream validation + if (stream !== 'stdout' && stream !== 'stderr') { + return res + .status(400) + .json({ message: 'stream param must be stdout or stderr' }) } -}) - -logsRouter.get('/out/:domain', (req, res) => { - const { domain } = req.params if (isProduction()) { // Only search for domain when running in production. The test does not // require a valid domain since it only verifies the endpoint const { fullDomain } = getMappingByDomain(domain) - // Pipes the output log file to res. Console.Log from your app will appear here + // Pipes the log to res res.setHeader('content-type', 'text/plain') - fs.createReadStream(`/home/myproxy/.pm2/logs/${fullDomain}-out.log`).pipe( - res - ) + const logStream = await getContainerLogs(fullDomain, { + follow: Boolean(follow), + tail: Number(tail), + [stream]: true + }) + logStream.pipe(res) } else { res.send('OK') } }) -logsRouter.delete('/:domain', (req, res) => { - const { domain } = req.params - - if (isProduction()) - fs.writeFile( - `/home/myproxy/.pm2/logs/${domain}-out.log`, - 'Log cleared\n', - err => { - if (err) console.log('Error deleting output log') - } - ) - fs.writeFile( - `/home/myproxy/.pm2/logs/${domain}-err.log`, - 'Log cleared\n', - err => { - if (err) console.log('Error deleting error log') - } - ) - - res.send('LOGS DELETED') -}) - export default logsRouter diff --git a/src/tests/integration/log.test.ts b/src/tests/integration/log.test.ts index c1380297..3d9ed9a4 100644 --- a/src/tests/integration/log.test.ts +++ b/src/tests/integration/log.test.ts @@ -1,5 +1,4 @@ import { startAppServer } from '../../server/server' -import uuidv4 from 'uuid/v4' import { logAdapter } from '../helpers/logAdapter' const TEST_PORT = process.env.PORT || 50608 @@ -18,19 +17,19 @@ describe('/api/logs', () => { it('checks that output logs endpoint exists', async () => { const fullDomain = 'Cloud.Walker.com' - const logResponse = await logAdapter(`/out/${fullDomain}`, 'GET') + const logResponse = await logAdapter(`/stdout/${fullDomain}`, 'GET') expect(logResponse.status).toEqual(200) }) it('checks that error logs endpoint exists', async () => { const fullDomain = 'Luke.Walker.com' - const logResponse = await logAdapter(`/err/${fullDomain}`, 'GET') + const logResponse = await logAdapter(`/stderr/${fullDomain}`, 'GET') expect(logResponse.status).toEqual(200) }) - it('checks the delete endpoint exists', async () => { - const fullDomain = `${uuidv4()}.walker.com` - const logResponse = await logAdapter(`/${fullDomain}`, 'DELETE') - expect(logResponse.status).toEqual(200) + it('checks that unknown stream param returns an error', async () => { + const fullDomain = 'Luke.Walker.com' + const logResponse = await logAdapter(`/somestream/${fullDomain}`, 'GET') + expect(logResponse.status).toEqual(400) }) }) From aec3a23d18d2b638a5de865ac6b4225345a0b2cc Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:33:13 -0300 Subject: [PATCH 08/22] post-receive: changed to start the container with docker --- scripts/post-receive | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/post-receive b/scripts/post-receive index 2a196778..44bb362e 100755 --- a/scripts/post-receive +++ b/scripts/post-receive @@ -1,9 +1,12 @@ #!/bin/sh cd .. GIT_DIR='.git' +DOMAIN=$(basename $(pwd)) umask 002 && git reset --hard git clean -f git checkout master git branch -D prod npm install -sudo -u myproxy pm2 startOrRestart deploy.config.js --env production --update-env +echo "Starting the container..." +curl --silent --unix-socket /var/run/docker.sock -X POST "http:/localhost/containers/${DOMAIN}/restart" +echo "Your app is running at https://${DOMAIN}" From 2c853aed3b88c45cdc32c0ac40498183b07832be Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 18:38:44 -0300 Subject: [PATCH 09/22] setup: add users to the docker group --- scripts/setup.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 2ee97234..5bad5580 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -18,8 +18,11 @@ 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 + # Add myproxy and git to the docker group so they can run the commands + sudo groupadd docker + sudo usermod -aG docker myproxy + sudo usermod -aG docker git + newgrp docker # Create folders mkdir /home/myproxy/.ssh mkdir /home/git/.ssh From de1d77d9f7f38417da5f0833e35e47a0abcee5f3 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:02:04 -0300 Subject: [PATCH 10/22] api/mappings: remove unnecessary line from exec --- src/api/mapping.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index b491ae9c..42b6a2c3 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -93,7 +93,6 @@ mappingRouter.post('/', async (req, res) => { git config user.name "user" git add . git commit -m "Initial Commit" - echo "curl --silent --request GET --url http://localhost:${PORT}/api/mappings/${id}/start" >> ${fullDomain}/.git/hooks/post-receive `, { uid: gitUserId, gid: gitGroupId } ) From 82efff13f689b61a4e2a3885f67b4c2fb30ecddd Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:14:15 -0300 Subject: [PATCH 11/22] docker: add force options to containerRemove --- src/helpers/docker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/docker.ts b/src/helpers/docker.ts index 72f27fc8..9431acf1 100644 --- a/src/helpers/docker.ts +++ b/src/helpers/docker.ts @@ -83,7 +83,7 @@ const stopContainer = async (id: string): Promise => { const removeContainer = async (id: string): Promise => { const container = docker.getContainer(id) - return container.remove() + return container.remove({ v: true, force: true }) } export { From a8f8507d6e5d1be727f26d6ace7f982a93bc095c Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:15:38 -0300 Subject: [PATCH 12/22] misc fixes --- src/api/mapping.ts | 2 +- src/public/client.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index 42b6a2c3..3474fd65 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -30,7 +30,7 @@ const getNextPort = (map, start = 3002): number => { return getNextPort(map, start) } -const { PORT, WORKPATH, isProduction } = environment +const { WORKPATH, isProduction } = environment mappingRouter.post('/', async (req, res) => { const domainKeys = getMappings() diff --git a/src/public/client.ts b/src/public/client.ts index 66d38d7b..318465c2 100644 --- a/src/public/client.ts +++ b/src/public/client.ts @@ -48,13 +48,11 @@ class MappingItem { // The variables below are to hide log related icons when pm2 is not // being used to monitor the apps. These apps will not have status since // they are not managed by pm2. - let settingClass let logClass if (data.status === 'running') { iconClass = 'fa fa-circle mr-1 mt-1' iconColor = 'rgba(50,255,50,0.5)' logClass = 'fa fa-file-text-o ml-1 mt-1' - settingClass = 'ml-1 fa fa-cog' } else if (data.status === 'not started') { iconClass = '' iconColor = 'transparent' @@ -62,7 +60,6 @@ class MappingItem { iconClass = 'fa fa-circle mr-1 mt-1' iconColor = 'rgba(255, 50, 50, 0.5)' logClass = 'fa fa-file-text-o ml-1 mt-1' - settingClass = 'ml-1 fa fa-cog' } mappingElement.classList.add( 'list-group-item', From 5fac5b0f6462d96266a576875c023b02907ad847 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:29:00 -0300 Subject: [PATCH 13/22] mappings: fix isProduction() call --- src/api/mapping.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index 3474fd65..530ef707 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -54,7 +54,7 @@ mappingRouter.post('/', async (req, res) => { const scriptPath = '.scripts' // Create a new container and get the id - const id = isProduction ? await createContainer(fullDomain, port) : uuid4() + const id = isProduction() ? await createContainer(fullDomain, port) : uuid4() const respond = (): void => { const mappingObject: Mapping = { From e437c700073750ab6ba1bdf7681d9190de83d093 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:32:03 -0300 Subject: [PATCH 14/22] mappings: delete -> fix fs.unlink to fs.rmdir --- src/api/mapping.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index 530ef707..a0a3f45c 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -136,7 +136,7 @@ mappingRouter.delete('/:id', async (req, res) => { removeContainer(deletedDomain.fullDomain) .then(() => { // delete the domain folder - fs.unlink(path.resolve(WORKPATH, deletedDomain.fullDomain), err => { + fs.rmdir(path.resolve(WORKPATH, deletedDomain.fullDomain), err => { if (err) { return res.status(500).json({ message: err.message }) } From a3acc9f292d11b2889962d4ea78565e25cfb48f9 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 22:46:08 -0300 Subject: [PATCH 15/22] mappings: revert delete back to exec --- src/api/mapping.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/api/mapping.ts b/src/api/mapping.ts index a0a3f45c..2b84be3e 100644 --- a/src/api/mapping.ts +++ b/src/api/mapping.ts @@ -3,8 +3,6 @@ import express from 'express' import uuid4 from 'uuid/v4' import util from 'util' import cp from 'child_process' -import fs from 'fs' -import path from 'path' import { setData, getMappings, @@ -136,10 +134,10 @@ mappingRouter.delete('/:id', async (req, res) => { removeContainer(deletedDomain.fullDomain) .then(() => { // delete the domain folder - fs.rmdir(path.resolve(WORKPATH, deletedDomain.fullDomain), err => { - if (err) { - return res.status(500).json({ message: err.message }) - } + exec(` + cd ${WORKPATH} + rm -rf ${deletedDomain.fullDomain} + `).then(() => { res.json(deletedDomain) }) }) From de7ffa5877eb1f769e882e4c081040c8ee3f63cd Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 23:02:28 -0300 Subject: [PATCH 16/22] setup: check if docker is installed on the system --- scripts/setup.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/setup.sh b/scripts/setup.sh index 5bad5580..3d80e4b9 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,5 +1,12 @@ #!/bin/bash +# Check if docker is installed and stop the script if it's not +if ! command docker &> /dev/null; then + echo "myProxy requires Docker to run" + echo "Docker installation instructions: https://docs.docker.com/engine/install/" + exit +fi + if ! command node -v &>/dev/null; then sudo apt-get install curl curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - From 2483cb8d3f70a9372a67a046b80e9064733d5ec2 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 23:35:58 -0300 Subject: [PATCH 17/22] setup: refactor and add more checks --- scripts/setup.sh | 80 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 3d80e4b9..fd299801 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,19 +1,35 @@ #!/bin/bash +# Helper functions +command_exists() { + command -v "$@" > /dev/null 2>&1 +} + +user_exists() { + id "$1" &> /dev/null +} + # Check if docker is installed and stop the script if it's not -if ! command docker &> /dev/null; then +if ! command_exists docker; then echo "myProxy requires Docker to run" echo "Docker installation instructions: https://docs.docker.com/engine/install/" exit fi -if ! command node -v &>/dev/null; then +if ! command_exists node; then + echo "Installing node" sudo apt-get install curl curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - sudo apt-get install -y nodejs fi + +if ! command_exists pm2; then + echo "Installing pm2" + npm install pm2 -g +fi + npm install -npm install pm2 -g + if [ ! -d "./acme.sh" ] ; then git clone https://github.com/Neilpang/acme.sh.git cd ./acme.sh @@ -21,39 +37,57 @@ if [ ! -d "./acme.sh" ] ; then ./acme.sh --upgrade --auto-upgrade cd ../ fi -if [ ! -d "/home/myproxy" ] ; then - # Add users + +sudo groupadd -f docker + +if ! user_exists myproxy; then + echo "Creating user: myproxy" 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 myproxy and git to the docker group so they can run the commands - sudo groupadd docker sudo usermod -aG docker myproxy +fi + +if ! user_exists git; then + echo "Creating user: git" + sudo useradd -m -G myproxy -s $(which git-shell) -p $(echo $ADMIN | openssl passwd -1 -stdin) git sudo usermod -aG docker git - newgrp docker - # 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/ +fi + +if [ ! -d "/home/myproxy/.ssh" ]; then + mkdir /home/myproxy/.ssh +fi + +if [ ! -d "/home/myproxy/.scripts" ]; then + mkdir /home/myproxy/.scripts +fi + +if [ ! -d "/home/git/.ssh" ]; then + mkdir /home/git/.ssh +fi + +if [ -f "~/.ssh/authorized_keys" ]; then + cp ~/.ssh/authorized_keys /home/myproxy/.ssh/authorized_keys + cp ~/.ssh/authorized_keys /home/git/.ssh/authorized_keys # 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 + +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 + +# fix file permissions +chown myproxy:myproxy -R /home/myproxy/ +chown git:git -R /home/git/ +chmod 2775 -R /home/myproxy/ + npm run build + if [ ! -f "./data.db" ] ; then touch data.db fi From 823727b20c93584a4920fdf2b17382880b7d6dc6 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 23:46:05 -0300 Subject: [PATCH 18/22] setup: create authorized_keys if not existent --- scripts/setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/setup.sh b/scripts/setup.sh index fd299801..c188919f 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -75,6 +75,9 @@ if [ -f "~/.ssh/authorized_keys" ]; then cp ~/.ssh/authorized_keys /home/git/.ssh/authorized_keys # 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 +else + touch /home/myproxy/.ssh/authorized_keys + touch /home/git/.ssh/authorized_keys fi cp ./scripts/post-receive /home/myproxy/.scripts/post-receive From 4580c815173b4e4d48d2e2eea63cbfcb4a9f16b6 Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Thu, 3 Sep 2020 23:48:43 -0300 Subject: [PATCH 19/22] setup: pull node docker image --- scripts/setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/setup.sh b/scripts/setup.sh index c188919f..19562251 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -94,3 +94,6 @@ npm run build if [ ! -f "./data.db" ] ; then touch data.db fi + +# pull node docker image +docker pull node:alpine \ No newline at end of file From 0ac964b31d65d4d3208b07b696bbee2f6542e27a Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Fri, 4 Sep 2020 10:40:35 -0300 Subject: [PATCH 20/22] setup: check if user can run docker commands --- scripts/setup.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 19562251..bb2b5c2a 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -96,4 +96,11 @@ if [ ! -f "./data.db" ] ; then fi # pull node docker image -docker pull node:alpine \ No newline at end of file +if docker ps > /dev/null 2>&1; then + docker pull node:alpine +else + echo "WARNING: Couldn't run docker commands" + echo "WARNING: Make sure your user has the right permissions" + echo "WARNING: Go to this link to setup docker to run without root" + echo "WARNING: https://docs.docker.com/engine/install/linux-postinstall/" +fi \ No newline at end of file From e00670b6a9f32b2a52241ed98d7a448504308eed Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Fri, 4 Sep 2020 10:50:38 -0300 Subject: [PATCH 21/22] setup: changed mkdir to use -p flag --- scripts/setup.sh | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index bb2b5c2a..1a313c6b 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -44,32 +44,23 @@ if ! user_exists myproxy; then echo "Creating user: myproxy" sudo useradd -m -c "myproxy" myproxy -s /bin/bash -p $(echo $ADMIN | openssl passwd -1 -stdin) -d "/home/myproxy" sudo usermod -aG docker myproxy + mkdir -p /home/myproxy/.ssh + mkdir -p /home/myproxy/.scripts fi if ! user_exists git; then echo "Creating user: git" sudo useradd -m -G myproxy -s $(which git-shell) -p $(echo $ADMIN | openssl passwd -1 -stdin) git sudo usermod -aG docker git + mkdir -p /home/git/.ssh # Disable SSH MOTD message for git user touch /home/git/.hushlogin # Add git-shell message - mkdir /home/git/git-shell-commands + mkdir -p /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 fi -if [ ! -d "/home/myproxy/.ssh" ]; then - mkdir /home/myproxy/.ssh -fi - -if [ ! -d "/home/myproxy/.scripts" ]; then - mkdir /home/myproxy/.scripts -fi - -if [ ! -d "/home/git/.ssh" ]; then - mkdir /home/git/.ssh -fi - if [ -f "~/.ssh/authorized_keys" ]; then cp ~/.ssh/authorized_keys /home/myproxy/.ssh/authorized_keys cp ~/.ssh/authorized_keys /home/git/.ssh/authorized_keys From 6ee578fd8669a13bb560abb2c8723dd9da2cb01f Mon Sep 17 00:00:00 2001 From: ggwadera <16023489+ggwadera@users.noreply.github.com> Date: Fri, 4 Sep 2020 10:51:02 -0300 Subject: [PATCH 22/22] setup: add permissions comment --- scripts/setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/setup.sh b/scripts/setup.sh index 1a313c6b..446415a7 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -78,6 +78,9 @@ cp ./scripts/gitignore /home/myproxy/.scripts/.gitignore # fix file permissions chown myproxy:myproxy -R /home/myproxy/ chown git:git -R /home/git/ +# set the group permissions for /home/myproxy +# 2 = set the setgid bit for the files so group permissions are inherited +# 775 = set read+write permissions for the user and group chmod 2775 -R /home/myproxy/ npm run build