Use different names for deploy and service users on both servers

This commit is contained in:
continuist 2025-07-04 15:16:14 -04:00
parent 07de8f2f6a
commit 14b6eaeffa

View file

@ -99,17 +99,17 @@ This setup uses a **principle of least privilege** approach with separate users
- **SSH Access**: Disabled after setup
- **Privileges**: Full system access (used only during initial configuration)
2. **Deployment User (`DEPLOY_USER`)**
2. **Deployment User (`CI_DEPLOY_USER` on CI Linode, `PROD_DEPLOY_USER` on Production Linode)**
- **Purpose**: SSH access, deployment tasks, system administration
- **SSH Access**: Enabled with key-based authentication
- **Privileges**: Sudo access for deployment and administrative tasks
- **Examples**: `deploy`, `ci`, `admin`
- **Example**: `ci-deploy` / `prod-deploy`
3. **Service Account (`SERVICE_USER`)**
3. **Service Account (`CI_SERVICE_USER` on CI Linode, `PROD_SERVICE_USER` on Production Linode)**
- **Purpose**: Running application services (Docker containers, databases)
- **SSH Access**: None (no login shell)
- **Privileges**: No sudo access, minimal system access
- **Examples**: `appuser`, `service`, `app`
- **Example**: `ci-service` / `prod-service`
### Security Benefits
@ -121,9 +121,9 @@ This setup uses a **principle of least privilege** approach with separate users
### File Permissions
- **Application files**: Owned by `SERVICE_USER` for security
- **Docker operations**: Run by `DEPLOY_USER` with sudo (deployment only)
- **Service execution**: Run by `SERVICE_USER` (no sudo needed)
- **Application files**: Owned by `CI_SERVICE_USER` for security (CI Linode) / `PROD_SERVICE_USER` for security (Production Linode)
- **Docker operations**: Run by `CI_SERVICE_USER` with Docker group access (CI Linode) / `PROD_SERVICE_USER` with Docker group access (Production Linode)
- **Service execution**: Run by `CI_SERVICE_USER` (no sudo needed) / `PROD_SERVICE_USER` (no sudo needed)
---
@ -172,30 +172,30 @@ ssh root@YOUR_PRODUCTION_IP
Before proceeding, decide on:
1. **Service Account Name**: Choose a username for the service account (e.g., `appuser`, `deploy`, `service`)
- Replace `SERVICE_USER` in this guide with your chosen name
- This account runs the actual application services
1. **CI Service Account Name**: Choose a username for the CI service account (e.g., `ci-service`)
- Replace `CI_SERVICE_USER` in this guide with your chosen name
- This account runs the CI pipeline and Docker operations on the CI Linode
2. **Deployment User Name**: Choose a username for deployment tasks (e.g., `deploy`, `ci`, `admin`)
- Replace `DEPLOY_USER` in this guide with your chosen name
2. **CI Deployment User Name**: Choose a username for CI deployment tasks (e.g., `ci-deploy`)
- Replace `CI_DEPLOY_USER` in this guide with your chosen name
- This account has sudo privileges for deployment tasks
3. **Application Name**: Choose a name for your application (e.g., `myapp`, `webapp`, `api`)
3. **Application Name**: Choose a name for your application (e.g., `sharenet`)
- Replace `APP_NAME` in this guide with your chosen name
4. **Domain Name** (Optional): If you have a domain, note it for SSL configuration
- Replace `your-domain.com` in this guide with your actual domain
**Example**:
- If you choose `appuser` as service account, `deploy` as deployment user, and `myapp` as application name:
- Replace all `SERVICE_USER` with `appuser`
- Replace all `DEPLOY_USER` with `deploy`
- Replace all `APP_NAME` with `myapp`
- If you choose `ci-service` as CI service account, `ci-deploy` as CI deployment user, and `sharenet` as application name:
- Replace all `CI_SERVICE_USER` with `ci-service`
- Replace all `CI_DEPLOY_USER` with `ci-deploy`
- Replace all `APP_NAME` with `sharenet`
- If you have a domain `example.com`, replace `your-domain.com` with `example.com`
**Security Model**:
- **Service Account (`SERVICE_USER`)**: Runs application services, no sudo access
- **Deployment User (`DEPLOY_USER`)**: Handles deployments via SSH, has sudo access
- **CI Service Account (`CI_SERVICE_USER`)**: Runs CI pipeline and Docker operations, no sudo access
- **CI Deployment User (`CI_DEPLOY_USER`)**: Handles SSH communication and orchestration, has sudo access
- **Root**: Only used for initial setup, then disabled for SSH access
#### 0.4 Set Up SSH Key Authentication for Local Development
@ -257,50 +257,74 @@ ssh root@YOUR_PRODUCTION_IP 'echo "SSH key authentication works for Production"'
On both Linodes, create the deployment user with sudo privileges:
**For CI Linode:**
```bash
# Create deployment user
sudo useradd -m -s /bin/bash DEPLOY_USER
sudo usermod -aG sudo DEPLOY_USER
# Create CI deployment user
sudo useradd -m -s /bin/bash CI_DEPLOY_USER
sudo usermod -aG sudo CI_DEPLOY_USER
# Set a secure password (for emergency access only)
echo "DEPLOY_USER:$(openssl rand -base64 32)" | sudo chpasswd
echo "CI_DEPLOY_USER:$(openssl rand -base64 32)" | sudo chpasswd
# Copy your SSH key to the deployment user
sudo mkdir -p /home/DEPLOY_USER/.ssh
sudo cp ~/.ssh/authorized_keys /home/DEPLOY_USER/.ssh/
sudo chown -R DEPLOY_USER:DEPLOY_USER /home/DEPLOY_USER/.ssh
sudo chmod 700 /home/DEPLOY_USER/.ssh
sudo chmod 600 /home/DEPLOY_USER/.ssh/authorized_keys
# Copy your SSH key to the CI deployment user
sudo mkdir -p /home/CI_DEPLOY_USER/.ssh
sudo cp ~/.ssh/authorized_keys /home/CI_DEPLOY_USER/.ssh/
sudo chown -R CI_DEPLOY_USER:CI_DEPLOY_USER /home/CI_DEPLOY_USER/.ssh
sudo chmod 700 /home/CI_DEPLOY_USER/.ssh
sudo chmod 600 /home/CI_DEPLOY_USER/.ssh/authorized_keys
# Configure sudo to use SSH key authentication (most secure)
echo "DEPLOY_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/DEPLOY_USER
sudo chmod 440 /etc/sudoers.d/DEPLOY_USER
echo "CI_DEPLOY_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/CI_DEPLOY_USER
sudo chmod 440 /etc/sudoers.d/CI_DEPLOY_USER
```
**Security Note**: This configuration allows the DEPLOY_USER to use sudo without a password, which is more secure for CI/CD automation since there are no passwords to store or expose. The random password is set for emergency console access only.
**For Production Linode:**
```bash
# Create production deployment user
sudo useradd -m -s /bin/bash PROD_DEPLOY_USER
sudo usermod -aG sudo PROD_DEPLOY_USER
# Set a secure password (for emergency access only)
echo "PROD_DEPLOY_USER:$(openssl rand -base64 32)" | sudo chpasswd
# Copy your SSH key to the production deployment user
sudo mkdir -p /home/PROD_DEPLOY_USER/.ssh
sudo cp ~/.ssh/authorized_keys /home/PROD_DEPLOY_USER/.ssh/
sudo chown -R PROD_DEPLOY_USER:PROD_DEPLOY_USER /home/PROD_DEPLOY_USER/.ssh
sudo chmod 700 /home/PROD_DEPLOY_USER/.ssh
sudo chmod 600 /home/PROD_DEPLOY_USER/.ssh/authorized_keys
# Configure sudo to use SSH key authentication (most secure)
echo "PROD_DEPLOY_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/PROD_DEPLOY_USER
sudo chmod 440 /etc/sudoers.d/PROD_DEPLOY_USER
```
**Security Note**: This configuration allows the deployment users to use sudo without a password, which is more secure for CI/CD automation since there are no passwords to store or expose. The random password is set for emergency console access only.
##### 0.4.5 Test Sudo Access
Test that the deployment user can use sudo without password prompts:
Test that the deployment users can use sudo without password prompts:
```bash
# Test sudo access
ssh DEPLOY_USER@YOUR_CI_CD_IP 'sudo whoami'
ssh DEPLOY_USER@YOUR_PRODUCTION_IP 'sudo whoami'
# Test CI deployment user sudo access
ssh CI_DEPLOY_USER@YOUR_CI_CD_IP 'sudo whoami'
# Test production deployment user sudo access
ssh PROD_DEPLOY_USER@YOUR_PRODUCTION_IP 'sudo whoami'
```
**Expected output**: Both commands should return `root` without prompting for a password.
##### 0.4.6 Test Deployment User Access
Test that you can access both servers as the deployment user:
Test that you can access both servers as the deployment users:
```bash
# Test CI/CD Linode
ssh DEPLOY_USER@YOUR_CI_CD_IP 'echo "Deployment user SSH access works for CI/CD"'
ssh CI_DEPLOY_USER@YOUR_CI_CD_IP 'echo "CI deployment user SSH access works for CI/CD"'
# Test Production Linode
ssh DEPLOY_USER@YOUR_PRODUCTION_IP 'echo "Deployment user SSH access works for Production"'
ssh PROD_DEPLOY_USER@YOUR_PRODUCTION_IP 'echo "Production deployment user SSH access works for Production"'
```
**Expected output**: The echo messages should appear without password prompts.
@ -314,13 +338,13 @@ On your local machine, create an SSH config file for easy access:
cat > ~/.ssh/config << 'EOF'
Host ci-cd-dev
HostName YOUR_CI_CD_IP
User DEPLOY_USER
User CI_DEPLOY_USER
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no
Host production-dev
HostName YOUR_PRODUCTION_IP
User DEPLOY_USER
User PROD_DEPLOY_USER
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no
EOF
@ -405,26 +429,26 @@ sudo apt install -y \
### Step 2: Create Users
#### 2.1 Create Service Account
#### 2.1 Create CI Service Account
```bash
# Create dedicated group for the service account
sudo groupadd -r SERVICE_USER
# Create dedicated group for the CI service account
sudo groupadd -r CI_SERVICE_USER
# Create service account user with dedicated group
sudo useradd -r -g SERVICE_USER -s /bin/bash -m -d /home/SERVICE_USER SERVICE_USER
echo "SERVICE_USER:$(openssl rand -base64 32)" | sudo chpasswd
# Create CI service account user with dedicated group
sudo useradd -r -g CI_SERVICE_USER -s /bin/bash -m -d /home/CI_SERVICE_USER CI_SERVICE_USER
echo "CI_SERVICE_USER:$(openssl rand -base64 32)" | sudo chpasswd
```
#### 2.2 Verify Users
```bash
sudo su - SERVICE_USER
sudo su - CI_SERVICE_USER
whoami
pwd
exit
sudo su - DEPLOY_USER
sudo su - CI_DEPLOY_USER
whoami
pwd
exit
@ -433,15 +457,15 @@ exit
### Step 3: Clone Repository for Registry Configuration
```bash
# Switch to DEPLOY_USER (who has sudo access)
sudo su - DEPLOY_USER
# Switch to CI_DEPLOY_USER (who has sudo access)
sudo su - CI_DEPLOY_USER
# Create application directory and clone repository
sudo mkdir -p /opt/APP_NAME
sudo chown SERVICE_USER:SERVICE_USER /opt/APP_NAME
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME
cd /opt
sudo git clone https://your-forgejo-instance/your-username/APP_NAME.git
sudo chown -R SERVICE_USER:SERVICE_USER APP_NAME/
sudo chown -R CI_SERVICE_USER:CI_SERVICE_USER APP_NAME/
# Verify the registry folder exists
ls -la /opt/APP_NAME/registry/
@ -450,8 +474,8 @@ ls -la /opt/APP_NAME/registry/
**Important**: Replace `your-forgejo-instance`, `your-username`, and `APP_NAME` with your actual Forgejo instance URL, username, and application name.
**What this does**:
- DEPLOY_USER creates the directory structure and clones the repository
- SERVICE_USER owns all the files for security
- CI_DEPLOY_USER creates the directory structure and clones the repository
- CI_SERVICE_USER owns all the files for security
- Registry configuration files are now available at `/opt/APP_NAME/registry/`
### Step 4: Install Docker
@ -470,10 +494,10 @@ sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
#### 4.3 Configure Docker for Service Account
#### 4.3 Configure Docker for CI Service Account
```bash
sudo usermod -aG docker SERVICE_USER
sudo usermod -aG docker CI_SERVICE_USER
```
### Step 5: Set Up Harbor Container Registry
@ -493,8 +517,8 @@ echo "harbor:$(openssl rand -base64 32)" | sudo chpasswd
# Add harbor user to docker group
sudo usermod -aG docker harbor
# Add DEPLOY_USER to harbor group for monitoring access
sudo usermod -aG harbor DEPLOY_USER
# Add CI_DEPLOY_USER to harbor group for monitoring access
sudo usermod -aG harbor CI_DEPLOY_USER
# Set proper permissions on /opt/harbor directory
sudo chown harbor:harbor /opt/harbor
@ -573,8 +597,8 @@ echo "DB_PASSWORD: $DB_PASSWORD"
# Download and install Harbor
cd /opt/harbor
# Switch to the DEPLOY_USER
sudo su - DEPLOY_USER
# Switch to the CI_DEPLOY_USER
sudo su - CI_DEPLOY_USER
sudo wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
sudo tar -xzf harbor-offline-installer-v2.10.0.tgz
@ -594,8 +618,8 @@ sudo nano harbor.yml
**Note**: The default Harbor admin password is "Harbor12345" and will be changed in Step 5.6
```bash
# Run the following as the DEPLOY_USER
sudo su - DEPLOY_USER
# Run the following as the CI_DEPLOY_USER
sudo su - CI_DEPLOY_USER
cd /opt/harbor/harbor
@ -613,7 +637,7 @@ cd /opt/harbor/harbor
# Run the following to patially adjust the permissions correctly for the harbor user
./install.sh --with-trivy
# Exit harbor user shell to switch back to the DEPLOY_USER
# Exit harbor user shell to switch back to the CI_DEPLOY_USER
exit
cd /opt/harbor/harbor
@ -634,7 +658,7 @@ docker compose up -d
docker compose ps -a
# Verify using the Harbor API that all Harbor processes are healthy
curl -I -k https://localhost/api/v2.0/health
curl -k https://localhost/api/v2.0/health
```
#### 5.5 Create Systemd Service
@ -682,30 +706,30 @@ sudo journalctl -u harbor.service -f
#### 5.7 Test Harbor Setup
```bash
# Switch to DEPLOY_USER for testing
sudo su - DEPLOY_USER
# Switch to CI_SERVICE_USER for testing (CI_SERVICE_USER runs CI pipeline and Docker operations)
sudo su - CI_SERVICE_USER
# Test Docker login and push
docker login YOUR_CI_CD_IP:80 -u ci-user -p "your-secure-password"
echo "your-secure-password" | docker login YOUR_CI_CD_IP -u ci-user --password-stdin
# Create and push test image
echo "FROM alpine:latest" > /tmp/test.Dockerfile
docker build -f /tmp/test.Dockerfile -t YOUR_CI_CD_IP:80/APP_NAME/test:latest /tmp
docker push YOUR_CI_CD_IP:80/APP_NAME/test:latest
docker build -f /tmp/test.Dockerfile -t YOUR_CI_CD_IP/APP_NAME/test:latest /tmp
docker push YOUR_CI_CD_IP/APP_NAME/test:latest
# Test public pull (no authentication)
docker logout YOUR_CI_CD_IP:80
docker pull YOUR_CI_CD_IP:80/APP_NAME/test:latest
docker logout YOUR_CI_CD_IP
docker pull YOUR_CI_CD_IP/APP_NAME/test:latest
# Test that unauthorized push is blocked
echo "FROM alpine:latest" > /tmp/unauthorized.Dockerfile
docker build -f /tmp/unauthorized.Dockerfile -t YOUR_CI_CD_IP:80/APP_NAME/unauthorized:latest /tmp
docker push YOUR_CI_CD_IP:80/APP_NAME/unauthorized:latest
docker build -f /tmp/unauthorized.Dockerfile -t YOUR_CI_CD_IP/APP_NAME/unauthorized:latest /tmp
docker push YOUR_CI_CD_IP/APP_NAME/unauthorized:latest
# Expected: This should fail with authentication error
# Clean up
docker rmi YOUR_CI_CD_IP:80/APP_NAME/test:latest
docker rmi YOUR_CI_CD_IP:80/APP_NAME/unauthorized:latest
docker rmi YOUR_CI_CD_IP/APP_NAME/test:latest
docker rmi YOUR_CI_CD_IP/APP_NAME/unauthorized:latest
exit
```
@ -715,33 +739,24 @@ exit
- ✅ Unauthorized push is blocked
- ✅ Web UI accessible at `https://YOUR_CI_CD_IP`
cat /etc/docker/daemon.json
# 2. Check if certificate is in system CA store
ls -la /usr/local/share/ca-certificates/registry.crt
# 3. Update CA certificates and restart Docker
sudo update-ca-certificates
sudo systemctl restart docker
```
### Step 6: Set Up SSH for Production Communication
#### 6.1 Generate SSH Key Pair
**Important**: Run this command as the **DEPLOY_USER** (not root or SERVICE_USER). The DEPLOY_USER is responsible for deployment orchestration and SSH communication with the production server.
**Important**: Run this command as the **CI_SERVICE_USER** (not root or CI_DEPLOY_USER). The CI_SERVICE_USER runs the CI pipeline and needs to SSH to the production server for automated deployments.
```bash
ssh-keygen -t ed25519 -C "ci-cd-server" -f ~/.ssh/id_ed25519 -N ""
ssh-keygen -t ed25519 -C "CI_SERVICE_USER" -f ~/.ssh/id_ed25519 -N ""
```
**What this does**:
- Creates an SSH key pair for secure communication between CI/CD and production servers
- The DEPLOY_USER uses this key to SSH to the production server for deployments
- The key is stored in the DEPLOY_USER's home directory for security
- The CI_SERVICE_USER uses this key to SSH to the production server for automated deployments
- The key is stored in the CI_SERVICE_USER's home directory for security
**Security Note**: The DEPLOY_USER handles deployment orchestration, while the SERVICE_USER runs the actual CI pipeline. This separation provides better security through the principle of least privilege.
**Security Note**: The CI_SERVICE_USER runs the CI pipeline and performs deployments, so it needs direct SSH access to the production server. This provides a clean, direct execution path without user switching.
**Deployment Flow**: When the CI pipeline completes successfully, the CI_SERVICE_USER will automatically SSH to the production server (using this key) to pull the latest images from Harbor and deploy the application stack.
#### 6.2 Create SSH Config
@ -749,7 +764,7 @@ ssh-keygen -t ed25519 -C "ci-cd-server" -f ~/.ssh/id_ed25519 -N ""
cat > ~/.ssh/config << 'EOF'
Host production
HostName YOUR_PRODUCTION_IP
User DEPLOY_USER
User PROD_SERVICE_USER
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
@ -762,7 +777,7 @@ chmod 600 ~/.ssh/config
#### 7.1 Download Runner
**Important**: Run this step as the **DEPLOY_USER** (not root or SERVICE_USER). The DEPLOY_USER handles deployment tasks including downloading and installing the Forgejo runner.
**Important**: Run this step as the **CI_DEPLOY_USER** (not root or CI_SERVICE_USER). The CI_DEPLOY_USER handles deployment tasks including downloading and installing the Forgejo runner.
```bash
cd ~
@ -880,8 +895,8 @@ To add an existing user as an Administrator of an existing repository in Forgejo
**Step 3: Register the Runner**
```bash
# Switch to DEPLOY_USER to register the runner
sudo su - DEPLOY_USER
# Switch to CI_DEPLOY_USER to register the runner
sudo su - CI_DEPLOY_USER
cd ~
@ -899,7 +914,7 @@ forgejo-runner register \
**Note**: The `your-forgejo-instance` should be the **base URL** of your Forgejo instance (e.g., `https://git.<your-domain>/`), not the full path to the repository. The runner registration process will handle connecting to the specific repository based on the token you provide.
**What this does**:
- Creates the required `.runner` configuration file in the DEPLOY_USER's home directory
- Creates the required `.runner` configuration file in the CI_DEPLOY_USER's home directory
- Registers the runner with your Forgejo instance
- Sets up the runner with appropriate labels for Ubuntu and Docker environments
@ -910,10 +925,10 @@ forgejo-runner register \
sudo mkdir -p /etc/forgejo-runner
# Copy the runner configuration to system location
sudo cp /home/DEPLOY_USER/.runner /etc/forgejo-runner/.runner
sudo cp /home/CI_DEPLOY_USER/.runner /etc/forgejo-runner/.runner
# Set proper ownership and permissions
sudo chown SERVICE_USER:SERVICE_USER /etc/forgejo-runner/.runner
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/forgejo-runner/.runner
sudo chmod 600 /etc/forgejo-runner/.runner
```
@ -921,7 +936,7 @@ sudo chmod 600 /etc/forgejo-runner/.runner
**What this does**:
- Copies the configuration to the system location (`/etc/forgejo-runner/.runner`)
- Sets proper ownership and permissions for SERVICE_USER to access the config
- Sets proper ownership and permissions for CI_SERVICE_USER to access the config
- Registers the runner with your Forgejo instance
- Sets up the runner with appropriate labels for Ubuntu and Docker environments
@ -935,7 +950,7 @@ After=network.target
[Service]
Type=simple
User=SERVICE_USER
User=CI_SERVICE_USER
WorkingDirectory=/etc/forgejo-runner
ExecStart=/usr/bin/forgejo-runner daemon
Restart=always
@ -1005,8 +1020,8 @@ sudo journalctl -u forgejo-runner.service -f --no-pager
```bash
# Switch to DEPLOY_USER (who has sudo access for Docker operations)
sudo su - DEPLOY_USER
# Switch to CI_DEPLOY_USER (who has sudo access for Docker operations)
sudo su - CI_DEPLOY_USER
# Navigate to the application directory
cd /opt/APP_NAME
@ -1030,7 +1045,7 @@ sudo docker exec ci-dind docker version
- **Health checks**: Ensures DinD is fully ready before proceeding
- **Simple setup**: Direct Docker commands for maximum flexibility
**Why DEPLOY_USER**: The DEPLOY_USER handles deployment orchestration and has sudo access for Docker operations, following the principle of least privilege.
**Why CI_DEPLOY_USER**: The CI_DEPLOY_USER handles deployment orchestration and has sudo access for Docker operations, following the principle of least privilege.
#### 8.2 Configure DinD for Harbor Registry
@ -1281,35 +1296,35 @@ sudo apt install -y \
### Step 12: Create Users
#### 12.1 Create the SERVICE_USER User
#### 12.1 Create the PROD_SERVICE_USER User
```bash
# Create dedicated group for the service account
sudo groupadd -r SERVICE_USER
# Create dedicated group for the production service account
sudo groupadd -r PROD_SERVICE_USER
# Create service account user with dedicated group
sudo useradd -r -g SERVICE_USER -s /bin/bash -m -d /home/SERVICE_USER SERVICE_USER
echo "SERVICE_USER:$(openssl rand -base64 32)" | sudo chpasswd
# Create production service account user with dedicated group
sudo useradd -r -g PROD_SERVICE_USER -s /bin/bash -m -d /home/PROD_SERVICE_USER PROD_SERVICE_USER
echo "PROD_SERVICE_USER:$(openssl rand -base64 32)" | sudo chpasswd
```
#### 12.2 Create the DEPLOY_USER User
#### 12.2 Create the PROD_DEPLOY_USER User
```bash
# Create deployment user
sudo useradd -m -s /bin/bash DEPLOY_USER
sudo usermod -aG sudo DEPLOY_USER
echo "DEPLOY_USER:$(openssl rand -base64 32)" | sudo chpasswd
# Create production deployment user
sudo useradd -m -s /bin/bash PROD_DEPLOY_USER
sudo usermod -aG sudo PROD_DEPLOY_USER
echo "PROD_DEPLOY_USER:$(openssl rand -base64 32)" | sudo chpasswd
```
#### 12.3 Verify Users
```bash
sudo su - SERVICE_USER
sudo su - PROD_SERVICE_USER
whoami
pwd
exit
sudo su - DEPLOY_USER
sudo su - PROD_DEPLOY_USER
whoami
pwd
exit
@ -1331,10 +1346,10 @@ sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
#### 13.3 Configure Docker for Service Account
#### 13.3 Configure Docker for Production Service Account
```bash
sudo usermod -aG docker SERVICE_USER
sudo usermod -aG docker PROD_SERVICE_USER
```
### Step 14: Install Docker Compose
@ -1372,7 +1387,7 @@ sudo systemctl start fail2ban
```bash
sudo mkdir -p /opt/APP_NAME
sudo chown SERVICE_USER:SERVICE_USER /opt/APP_NAME
sudo chown PROD_SERVICE_USER:PROD_SERVICE_USER /opt/APP_NAME
```
**Note**: Replace `APP_NAME` with your actual application name. This directory name can be controlled via the `APP_NAME` secret in your Forgejo repository settings. If you set the `APP_NAME` secret to `myapp`, the deployment directory will be `/opt/myapp`.
@ -1381,15 +1396,15 @@ sudo chown SERVICE_USER:SERVICE_USER /opt/APP_NAME
```bash
sudo mkdir -p /opt/APP_NAME/nginx/ssl
sudo chown SERVICE_USER:SERVICE_USER /opt/APP_NAME/nginx/ssl
sudo chown PROD_SERVICE_USER:PROD_SERVICE_USER /opt/APP_NAME/nginx/ssl
```
### Step 17: Clone Repository and Set Up Application Files
#### 17.1 Switch to SERVICE_USER User
#### 17.1 Switch to PROD_SERVICE_USER User
```bash
sudo su - SERVICE_USER
sudo su - PROD_SERVICE_USER
```
#### 17.2 Clone Repository
@ -1453,7 +1468,7 @@ sudo systemctl restart docker
#### 18.1 Add CI/CD Public Key
```bash
# Create .ssh directory for SERVICE_USER
# Create .ssh directory for PROD_SERVICE_USER
mkdir -p ~/.ssh
chmod 700 ~/.ssh
@ -1669,9 +1684,9 @@ Go to your Forgejo repository and add these secrets in **Settings → Secrets an
**Required Secrets:**
- `CI_HOST`: Your CI/CD Linode IP address (used for Harbor registry access)
- `PRODUCTION_IP`: Your Production Linode IP address
- `DEPLOY_USER`: The deployment user name (e.g., `deploy`, `ci`, `admin`)
- `SERVICE_USER`: The service user name (e.g., `appuser`, `service`, `app`)
- `APP_NAME`: Your application name (e.g., `sharenet`, `myapp`)
- `PROD_DEPLOY_USER`: The production deployment user name (e.g., `prod-deploy`)
- `PROD_SERVICE_USER`: The production service user name (e.g., `prod-service`)
- `APP_NAME`: Your application name (e.g., `sharenet`)
- `POSTGRES_PASSWORD`: A strong password for the PostgreSQL database
- `HARBOR_CI_USER`: Harbor username for CI operations (e.g., `ci-user`)
- `HARBOR_CI_PASSWORD`: Harbor password for CI operations (the password you set for ci-user)