CI-CD is a GitHub-based continuous integration and deployment workflow that automates deployment of your applications to server. It contain two parts:
- Server:
NestJS server running on your Server. - Repository:
GitHub Repository with api workflow.
- About
- TOC
- Server Setup
- Repository Setup
- Configure Server
- Generating keys pair
- Architecture
- Security
- Prerequisites
- Used in:
- LICENSE
- TODO:
- Kitty
id -u ci-cd >/dev/null 2>&1 && echo "User exists" || echo "User does not exist"If exist remove or change in installation command. ci-cd user should be isolated from whole system to decrease attack range.
### ----------------------------------
### Settings directories and ownership
### ----------------------------------
#### Create /etc/ci-cd/ with all subfolder`s
sudo mkdir -p /etc/ci-cd/
sudo mkdir -p /etc/ci-cd/services
sudo mkdir -p /etc/ci-cd/keys
sudo mkdir -p /etc/ci-cd/scripts
#### Create user ci-cd for app
#### !!!! Warning !!!! Check if user exists before
sudo useradd -r -s /bin/false ci-cd || echo "User ci-cd already exists"
#### Set owner and permission for /etc/ci-cd/ and subfolder`s
sudo chown -R ci-cd:ci-cd /etc/ci-cd/
sudo chown -R root:ci-cd /etc/ci-cd/scripts/
sudo chmod -R 700 /etc/ci-cd/
sudo chmod -R 750 /etc/ci-cd/scripts/
### ----------------------------------
### Server files installation
### ----------------------------------
#### Clone repository to /etc/ci-cd/
sudo -u ci-cd git clone --depth 1 -b server https://github.com/Daynlight/CI-CD /etc/ci-cd/CI-CD
### ---------
### Build app
### ---------
#### Install nodejs and npm
sudo apt update
sudo apt install -y --no-install-recommends nodejs npm
#### Build app
sudo npm install --prefix /etc/ci-cd/CI-CD/service/
sudo npm run build --prefix /etc/ci-cd/CI-CD/service/
### ----------------------
### Create systemd service
### ----------------------
sudo tee /etc/systemd/system/ci-cd.service > /dev/null <<EOF
[Unit]
Description=CI-CD Server
After=network.target
[Service]
Type=simple
User=ci-cd
Group=ci-cd
WorkingDirectory=/etc/ci-cd/CI-CD/service
ExecStart=/usr/bin/node /etc/ci-cd/CI-CD/service/dist/main.js
Environment=NODE_ENV=production
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/etc/ci-cd/services
CapabilityBoundingSet=
RestrictSUIDSGID=true
LockPersonality=true
Restart=on-failure
RestartSec=5
MemoryMax=300M
CPUQuota=50%
TasksMax=100
[Install]
WantedBy=multi-user.target
EOF
### ----------------------
### Run systemd service
### ----------------------
sudo systemctl daemon-reload
sudo systemctl enable ci-cd.service
sudo systemctl start ci-cd.service
#### See logs
sudo journalctl -u ci-cd -f### ----------------------
### Stop and disable service
### ----------------------
sudo systemctl stop ci-cd.service || echo "Service already stopped"
sudo systemctl disable ci-cd.service || echo "Service already disabled"
### ----------------------
### Remove systemd service file
### ----------------------
sudo rm -f -- /etc/systemd/system/ci-cd.service
sudo systemctl daemon-reload
sudo systemctl reset-failed
### ----------------------
### Remove CI-CD server files
### ----------------------
sudo rm -rf -- /etc/ci-cd/CI-CD- Create repository on Github.
- Create production branch.
- In Settings setup:
- Environment for production branch with:
- Required reviewers (Good practice).
- Add reviewer.
- Prevent self-review.
- Wait timer.
- Avoid bypass rules (Good practice).
- Deployment branches and tags -> Select only your production branch.
- Add Environment secrets:
- CI_CD_PRIVATE_KEY with your private key.
- CI_CD_URL with your endpoint.
- Branch protection rules (Good practice):
- Set production branch as target.
- Require a pull request before merging.
- Required approvals at least one.
- Environment for production branch with:
- In
.github/workflows/folder or viaActionstab:- Create new
.yamlfile for exampledeploy.yaml. - Paste content of api.yaml.
- Change main branch to your production branch.
- Create new
To setup server you have to edit: /etc/ci-cd/services.json
[
{
"name": "service_name",
"repo_url": "https://github.com/owner_name/repo_name.git",
"repo_name": "owner_name/repo_name",
"branch": "branch_name",
"dir": "path/to/service/with/repo",
"sign": "path/to/public.key",
"start": "start command",
"status": "start command",
"stop": "stop command"
}
][
{
"name": "Big-data-service",
"repo_url": "https://github.com/Daynlight/Big-data-server.git",
"repo_name": "Daynlight/Big-data-server",
"branch": "production",
"dir": "/etc/ci-cd/services/Big-data-server/",
"sign": "/etc/ci-cd/keys/big-data-server/public.pem",
"start": "/etc/ci-cd/scripts/big-data-server/start.sh",
"status": "/etc/ci-cd/scripts/big-data-server/status.sh",
"stop": "/etc/ci-cd/scripts/big-data-server/stop.sh"
}
]openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private.pem -out public.pem- It uses keys pair for
api request signingwithtimestamp. public.keyis stored only on server and is provided by developer.private.keyis stored as secret in repository.Endpoint urlis stored as secret in repository.- When PR appears than must be approved by others and merge to production branch.
- When PR is approved than deployment must be approved.
- After that workflows run`s. It gets Environment secrets CI_CD_PRIVATE_KEY and CI_CD_URL that only exists for this branch.
- It gets current timestamp clock must be synced with repository and create signature with CI_CD_PRIVATE_KEY.
- Than request is send on CI_CD_URL and
private.pemthat was created is removed. - Server receives this request.
- Check all services registered in services.json.
- Checks if request is not too old.
- Checks signature with
public.keystored on server. - Check last commit on server and current commit.
- Check status and if is running than stop it via scripts stored on server.
- Do
git pull. - Uses
start scriptthan check status. - After success returns json with state.
- Do not commit or printing
private.key. - Set deployment and branch protection rules.
- Keep
private.keyprivate. - Avoid bypass rules.
- Avoid self reviews.
- Keep endpoint secret.
- Reverse proxy.
- Add network limitation to prevent ddos attacks.
- Keep one keys pair by repository.
- Server works on http to use https edit
src/main.tsand add cert and key. - Use
cloudflarefor tunneling, TLS and hiding global static IP. - Set
scriptsto chmod 750 and chown root:ci-cd
- openssl
- network
- git
- nodejs
- npm
- jsonc-parser
- other programs need for running for example docker
- request cooldown.
- spread servers.
- add https.
- last request diff.