From 718343a3d01225dab6fdd985ee9249a03874ffa1 Mon Sep 17 00:00:00 2001 From: continuist Date: Sat, 28 Jun 2025 19:51:45 -0400 Subject: [PATCH] Updated procedure to have project cloned to /opt/APP_NAME to be with registry --- CI_CD_PIPELINE_SETUP_GUIDE.md | 460 +++++----------------------------- registry/README.md | 52 ++++ registry/config.yml | 21 ++ registry/docker-compose.yml | 56 +++++ registry/nginx.conf | 48 ++++ 5 files changed, 238 insertions(+), 399 deletions(-) create mode 100644 registry/README.md create mode 100644 registry/config.yml create mode 100644 registry/docker-compose.yml create mode 100644 registry/nginx.conf diff --git a/CI_CD_PIPELINE_SETUP_GUIDE.md b/CI_CD_PIPELINE_SETUP_GUIDE.md index 2304043..963bc9e 100644 --- a/CI_CD_PIPELINE_SETUP_GUIDE.md +++ b/CI_CD_PIPELINE_SETUP_GUIDE.md @@ -498,21 +498,25 @@ exit # Switch to SERVICE_USER (registry directory owner) sudo su - SERVICE_USER -# Create SSL directory -mkdir -p /opt/registry/ssl +# Create system SSL directory for registry certificates +sudo mkdir -p /etc/ssl/registry -# Generate self-signed certificate -openssl req -x509 -newkey rsa:4096 -keyout /opt/registry/ssl/registry.key -out /opt/registry/ssl/registry.crt -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/CN=YOUR_CI_CD_IP" +# Get your actual IP address +YOUR_ACTUAL_IP=$(curl -4 -s ifconfig.me) +echo "Your IP address is: $YOUR_ACTUAL_IP" + +# Generate self-signed certificate with actual IP in system directory +sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/registry/registry.key -out /etc/ssl/registry/registry.crt -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/CN=$YOUR_ACTUAL_IP" # Set proper permissions -chmod 600 /opt/registry/ssl/registry.key -chmod 644 /opt/registry/ssl/registry.crt +sudo chmod 600 /etc/ssl/registry/registry.key +sudo chmod 644 /etc/ssl/registry/registry.crt # Exit SERVICE_USER shell exit ``` -**Important**: Replace `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address in the certificate generation command. +**Important**: The certificate is now generated in the system SSL directory `/etc/ssl/registry/` with your actual CI/CD Linode IP address automatically. #### 4.3 Create Authentication File @@ -520,155 +524,86 @@ exit # Switch to SERVICE_USER (registry directory owner) sudo su - SERVICE_USER +# Create system auth directory for registry authentication +sudo mkdir -p /etc/registry/auth + # Create htpasswd file for authentication (required for push operations only) -mkdir -p /opt/registry/auth -htpasswd -Bbn push-user "$(openssl rand -base64 32)" > /opt/registry/auth/auth.htpasswd +sudo htpasswd -Bbn push-user "$(openssl rand -base64 32)" > /tmp/auth.htpasswd +sudo mv /tmp/auth.htpasswd /etc/registry/auth/auth.htpasswd # Exit SERVICE_USER shell exit ``` -**What this does**: Creates user credentials for registry authentication. +**What this does**: Creates user credentials for registry authentication in the system auth directory. - `push-user`: Can push images (used by CI/CD pipeline for deployments) **Note**: Pull operations are public and don't require authentication, but push operations require these credentials. +#### 4.3.1 Clone Repository for Registry Configuration + +```bash +# Switch to DEPLOY_USER (who has sudo access) +sudo su - DEPLOY_USER + +# Create application directory and clone repository +sudo mkdir -p /opt/APP_NAME +sudo chown SERVICE_USER: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/ + +# Verify the registry folder exists +ls -la /opt/APP_NAME/registry/ + +# Exit DEPLOY_USER shell +exit +``` + +**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 +- Registry configuration files are now available at `/opt/APP_NAME/registry/` + #### 4.4 Create Docker Compose for Registry ```bash # Switch to SERVICE_USER (registry directory owner) sudo su - SERVICE_USER -cat > /opt/registry/docker-compose.yml << 'EOF' -version: '3.8' - -services: - registry: - image: registry:2 - ports: - - "5000:5000" - volumes: - - ./config.yml:/etc/docker/registry/config.yml:ro - - ./auth/auth.htpasswd:/etc/docker/registry/auth/auth.htpasswd:ro - - ./ssl/registry.crt:/etc/docker/registry/ssl/registry.crt:ro - - ./ssl/registry.key:/etc/docker/registry/ssl/registry.key:ro - - registry_data:/var/lib/registry - restart: unless-stopped - networks: - - registry_network - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:5000/v2/", "--no-check-certificate"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - - - registry-ui: - image: joxit/docker-registry-ui:latest - expose: - - "80" - environment: - - REGISTRY_TITLE=APP_NAME Registry - - REGISTRY_URL=https://YOUR_CI_CD_IP:8080 - depends_on: - registry: - condition: service_healthy - restart: unless-stopped - networks: - - registry_network - - nginx: - image: nginx:alpine - ports: - - "8080:443" - volumes: - - ./ssl:/etc/nginx/ssl:ro - - ./nginx.conf:/etc/nginx/nginx.conf:ro - depends_on: - - registry-ui - restart: unless-stopped - networks: - - registry_network - -volumes: - registry_data: - -networks: - registry_network: - driver: bridge -EOF +# The registry configuration files are already available in the cloned repository +# at /opt/APP_NAME/registry/ +# No file copying is needed - we'll use the files directly from the repository # Exit SERVICE_USER shell exit ``` -**Important**: Replace `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address in the `REGISTRY_URL` environment variable. +**Important**: The repository should be cloned in the previous step (4.3.1) to `/opt/APP_NAME/`. The registry configuration files are used directly from the repository. -**Note**: We updated the volume mounts to explicitly map individual certificate files to their expected locations in the registry container. - -#### 4.4.1 Create Nginx Configuration +#### 4.4.1 Update Configuration with Actual IP Address ```bash # Switch to SERVICE_USER (registry directory owner) sudo su - SERVICE_USER -cat > /opt/registry/nginx.conf << 'EOF' -events { - worker_connections 1024; -} +cd /opt/APP_NAME/registry -http { - upstream registry_ui { - server registry-ui:80; - } +# Get your actual IP address +YOUR_ACTUAL_IP=$(curl -4 -s ifconfig.me) +echo "Your IP address is: $YOUR_ACTUAL_IP" - upstream registry_api { - server registry:5000; - } - - server { - listen 443 ssl; - server_name YOUR_CI_CD_IP; - - ssl_certificate /etc/nginx/ssl/registry.crt; - ssl_certificate_key /etc/nginx/ssl/registry.key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - # Proxy registry API requests - location /v2/ { - proxy_pass https://registry_api; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_connect_timeout 30s; - proxy_send_timeout 30s; - proxy_read_timeout 30s; - proxy_ssl_verify off; - } - - # Proxy registry UI requests - location / { - proxy_pass http://registry_ui; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_connect_timeout 30s; - proxy_send_timeout 30s; - proxy_read_timeout 30s; - } - } -} -EOF +# Replace placeholder IP addresses in configuration files +sed -i "s/YOUR_CI_CD_IP/$YOUR_ACTUAL_IP/g" docker-compose.yml +sed -i "s/YOUR_CI_CD_IP/$YOUR_ACTUAL_IP/g" nginx.conf # Exit SERVICE_USER shell exit ``` -**Important**: Replace `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address in the nginx configuration. +**Important**: This step replaces all instances of `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address in both the docker-compose.yml and nginx.conf files in the repository. #### 4.5 Install Required Tools @@ -683,7 +618,7 @@ sudo apt install -y apache2-utils # Switch to SERVICE_USER (registry directory owner) sudo su - SERVICE_USER -cd /opt/registry +cd /opt/APP_NAME/registry docker compose up -d # Exit SERVICE_USER shell @@ -693,11 +628,8 @@ exit #### 4.7 Test Registry Setup ```bash -# Switch to SERVICE_USER (registry directory owner) -sudo su - SERVICE_USER - # Check if containers are running -cd /opt/registry +cd /opt/APP_NAME/registry docker compose ps # Test registry API (HTTPS via nginx) @@ -781,7 +713,7 @@ exit ```bash # Get the push user credentials PUSH_USER="push-user" -PUSH_PASSWORD=$(grep push-user /opt/registry/auth/auth.htpasswd | cut -d: -f2) +PUSH_PASSWORD=$(grep push-user /etc/registry/auth/auth.htpasswd | cut -d: -f2) # Copy the certificate to Docker's trusted certificates sudo cp /opt/registry/ssl/registry.crt /usr/local/share/ca-certificates/registry.crt @@ -1120,274 +1052,4 @@ curl http://localhost:5000/v2/_catalog ```bash cat ~/.ssh/id_ed25519.pub -``` - -**Important**: Copy this public key - you'll need it for the production server setup. - -### Step 11: Configure Forgejo Actions Runner - -#### 11.1 Get Runner Token - -1. Go to your Forgejo repository -2. Navigate to Settings → Actions → Runners -3. Click "New runner" -4. Copy the registration token - -#### 11.2 Configure Runner - -```bash -# Get the registration token from your Forgejo repository -# Go to Settings → Actions → Runners → New runner -# Copy the registration token - -# Configure the runner -forgejo-runner register \ - --instance https://your-forgejo-instance \ - --token YOUR_TOKEN \ - --name "ci-cd-runner" \ - --labels "ubuntu-latest,docker" \ - --no-interactive -``` - -#### 11.3 Start Runner - -```bash -sudo systemctl start forgejo-runner.service -sudo systemctl status forgejo-runner.service -``` - -#### 11.4 Test Runner Configuration - -```bash -# Check if the runner is running -sudo systemctl status forgejo-runner.service - -# Check runner logs -sudo journalctl -u forgejo-runner.service -f --no-pager - -# Test runner connectivity (in a separate terminal) -forgejo-runner list - -# Verify runner appears in Forgejo -# Go to your Forgejo repository → Settings → Actions → Runners -# You should see your runner listed as "ci-cd-runner" with status "Online" -``` - -**Expected Output**: -- `systemctl status` should show "active (running)" -- `forgejo-runner list` should show your runner -- Forgejo web interface should show the runner as online - -**If something goes wrong**: -- Check logs: `sudo journalctl -u forgejo-runner.service -f` -- Verify token: Make sure the registration token is correct -- Check network: Ensure the runner can reach your Forgejo instance -- Restart service: `sudo systemctl restart forgejo-runner.service` - ---- - -## Part 2: Production Linode Setup - -### Step 12: Initial System Setup - -#### 12.1 Update the System - -```bash -sudo apt update && sudo apt upgrade -y -``` - -#### 12.2 Configure Timezone - -```bash -# Configure timezone interactively -sudo dpkg-reconfigure tzdata - -# Verify timezone setting -date -``` - -**What this does**: Opens an interactive dialog to select your timezone. Navigate through the menus to choose your preferred timezone (e.g., UTC, America/New_York, Europe/London, Asia/Tokyo). - -**Expected output**: After selecting your timezone, the `date` command should show the current date and time in your selected timezone. - -#### 12.3 Configure /etc/hosts - -```bash -# Add localhost entries for both IPv4 and IPv6 -echo "127.0.0.1 localhost" | sudo tee -a /etc/hosts -echo "::1 localhost ip6-localhost ip6-loopback" | sudo tee -a /etc/hosts -echo "YOUR_PRODUCTION_IPV4_ADDRESS localhost" | sudo tee -a /etc/hosts -echo "YOUR_PRODUCTION_IPV6_ADDRESS localhost" | sudo tee -a /etc/hosts - -# Verify the configuration -cat /etc/hosts -``` - -**What this does**: -- Adds localhost entries for both IPv4 and IPv6 addresses to `/etc/hosts` -- Ensures proper localhost resolution for both IPv4 and IPv6 - -**Important**: Replace `YOUR_PRODUCTION_IPV4_ADDRESS` and `YOUR_PRODUCTION_IPV6_ADDRESS` with the actual IPv4 and IPv6 addresses of your Production Linode obtained from your Linode dashboard. - -**Expected output**: The `/etc/hosts` file should show entries for `127.0.0.1`, `::1`, and your Linode's actual IP addresses all mapping to `localhost`. - -#### 12.4 Install Essential Packages - -```bash -sudo apt install -y \ - curl \ - wget \ - git \ - ca-certificates \ - apt-transport-https \ - software-properties-common \ - ufw \ - fail2ban \ - htop \ - nginx \ - certbot \ - python3-certbot-nginx -``` - -### Step 13: Create Users - -#### 13.1 Create the SERVICE_USER User - -```bash -# Create dedicated group for the service account -sudo groupadd -r 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 -``` - -#### 13.2 Verify Users - -```bash -sudo su - SERVICE_USER -whoami -pwd -exit - -sudo su - DEPLOY_USER -whoami -pwd -exit -``` - -### Step 14: Install Docker - -#### 14.1 Add Docker Repository - -```bash -curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null -sudo apt update -``` - -#### 14.2 Install Docker Packages - -```bash -sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin -``` - -#### 14.3 Configure Docker for Service Account - -```bash -sudo usermod -aG docker SERVICE_USER -``` - -### Step 15: Install Docker Compose - -```bash -sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -sudo chmod +x /usr/local/bin/docker-compose -``` - -### Step 16: Configure Security - -#### 16.1 Configure Firewall - -```bash -sudo ufw --force enable -sudo ufw default deny incoming -sudo ufw default allow outgoing -sudo ufw allow ssh -sudo ufw allow 80/tcp -sudo ufw allow 443/tcp -sudo ufw allow 3000/tcp -sudo ufw allow 3001/tcp -``` - -#### 16.2 Configure Fail2ban - -```bash -sudo systemctl enable fail2ban -sudo systemctl start fail2ban -``` - -### Step 17: Create Application Directory - -#### 17.1 Create Directory Structure - -```bash -sudo mkdir -p /opt/APP_NAME -sudo chown SERVICE_USER: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`. - -#### 17.2 Create SSL Directory (Optional - for domain users) - -```bash -sudo mkdir -p /opt/APP_NAME/nginx/ssl -sudo chown SERVICE_USER:SERVICE_USER /opt/APP_NAME/nginx/ssl -``` - -### Step 18: Clone Repository and Set Up Application Files - -#### 18.1 Switch to SERVICE_USER User - -```bash -sudo su - SERVICE_USER -``` - -#### 18.2 Clone Repository - -```bash -cd /opt/APP_NAME -git clone https://your-forgejo-instance/your-username/APP_NAME.git . -``` - -**Important**: The repository includes a pre-configured `nginx/nginx.conf` file that handles both SSL and non-SSL scenarios, with proper security headers, rate limiting, and CORS configuration. This file will be automatically used by the Docker Compose setup. - -**Important**: The repository also includes a pre-configured `.forgejo/workflows/ci.yml` file that handles the complete CI/CD pipeline including testing, building, and deployment. This workflow is already set up to work with the private registry and production deployment. - -**Note**: Replace `your-forgejo-instance` and `your-username/APP_NAME` with your actual Forgejo instance URL and repository path. - -#### 18.3 Create Environment File - -The repository doesn't include a `.env.example` file for security reasons. The CI/CD pipeline will create the `.env` file dynamically during deployment. However, for manual testing or initial setup, you can create a basic `.env` file: - -```bash -cat > /opt/APP_NAME/.env << 'EOF' -# Production Environment Variables -POSTGRES_PASSWORD=your_secure_password_here -REGISTRY=YOUR_CI_CD_IP:5000 -IMAGE_NAME=APP_NAME -IMAGE_TAG=latest - -# Database Configuration -POSTGRES_DB=sharenet -POSTGRES_USER=sharenet -DATABASE_URL=postgresql://sharenet:your_secure_password_here@postgres:5432/sharenet - -# Application Configuration -NODE_ENV=production -RUST_LOG=info -RUST_BACKTRACE=1 -EOF -``` - -**Important**: Replace `YOUR_CI_CD_IP` \ No newline at end of file +``` \ No newline at end of file diff --git a/registry/README.md b/registry/README.md new file mode 100644 index 0000000..c14b5d4 --- /dev/null +++ b/registry/README.md @@ -0,0 +1,52 @@ +# Docker Registry Configuration + +This folder contains the configuration files for the Docker Registry setup used in the CI/CD pipeline. + +## Files + +- `docker-compose.yml` - Docker Compose configuration for registry, registry-ui, and nginx services +- `nginx.conf` - Nginx reverse proxy configuration for SSL termination and routing +- `config.yml` - Docker Registry configuration file +- `README.md` - This file + +## Architecture + +This setup uses a hybrid approach for optimal maintainability and security: + +### Repository Files (Version Controlled) +- Configuration files in `/opt/APP_NAME/registry/` +- Easy to update via git pull +- Version controlled and tracked + +### System Files (Not Version Controlled) +- SSL certificates in `/etc/ssl/registry/` +- Authentication files in `/etc/registry/auth/` +- Registry data in Docker volume `/var/lib/registry` + +## Usage + +The setup process will: + +1. Clone the repository to `/opt/APP_NAME/` +2. Create system directories for certificates and auth +3. Generate SSL certificates in `/etc/ssl/registry/` +4. Create authentication files in `/etc/registry/auth/` +5. Start the registry services using the hybrid configuration + +## Configuration Notes + +- **Registry**: Runs on port 5000 with HTTPS +- **Nginx**: Provides SSL termination and reverse proxy on port 8080 +- **Registry UI**: Web interface accessible via nginx on port 8080 +- **Authentication**: Uses htpasswd for push authentication +- **Storage**: Uses Docker volume for persistent data +- **Configuration**: Version controlled in repository +- **Certificates**: Stored in system SSL directory + +## Security + +- SSL certificates are self-signed and stored in system SSL directory +- Authentication files are stored in system auth directory +- Configuration is version controlled and easily auditable +- All communication uses HTTPS +- Clear separation between config, auth, and data \ No newline at end of file diff --git a/registry/config.yml b/registry/config.yml new file mode 100644 index 0000000..ad910fa --- /dev/null +++ b/registry/config.yml @@ -0,0 +1,21 @@ +version: 0.1 +log: + level: info +storage: + filesystem: + rootdirectory: /var/lib/registry + delete: + enabled: true +http: + addr: :5000 + tls: + certificate: /etc/docker/registry/ssl/registry.crt + key: /etc/docker/registry/ssl/registry.key + headers: + X-Content-Type-Options: [nosniff] + X-Frame-Options: [DENY] + X-XSS-Protection: [1; mode=block] +auth: + htpasswd: + realm: basic-realm + path: /etc/docker/registry/auth/auth.htpasswd \ No newline at end of file diff --git a/registry/docker-compose.yml b/registry/docker-compose.yml new file mode 100644 index 0000000..3eadc97 --- /dev/null +++ b/registry/docker-compose.yml @@ -0,0 +1,56 @@ +version: '3.8' + +services: + registry: + image: registry:2 + ports: + - "5000:5000" + volumes: + - /opt/APP_NAME/registry/config.yml:/etc/docker/registry/config.yml:ro + - /etc/registry/auth/auth.htpasswd:/etc/docker/registry/auth/auth.htpasswd:ro + - /etc/ssl/registry/registry.crt:/etc/docker/registry/ssl/registry.crt:ro + - /etc/ssl/registry/registry.key:/etc/docker/registry/ssl/registry.key:ro + - registry_data:/var/lib/registry + restart: unless-stopped + networks: + - registry_network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:5000/v2/", "--no-check-certificate"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + registry-ui: + image: joxit/docker-registry-ui:latest + expose: + - "80" + environment: + - REGISTRY_TITLE=ShareNet Registry + - REGISTRY_URL=https://YOUR_CI_CD_IP:8080 + depends_on: + registry: + condition: service_healthy + restart: unless-stopped + networks: + - registry_network + + nginx: + image: nginx:alpine + ports: + - "8080:443" + volumes: + - /etc/ssl/registry:/etc/nginx/ssl:ro + - /opt/APP_NAME/registry/nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - registry-ui + restart: unless-stopped + networks: + - registry_network + +volumes: + registry_data: + +networks: + registry_network: + driver: bridge \ No newline at end of file diff --git a/registry/nginx.conf b/registry/nginx.conf new file mode 100644 index 0000000..e11b129 --- /dev/null +++ b/registry/nginx.conf @@ -0,0 +1,48 @@ +events { + worker_connections 1024; +} + +http { + upstream registry_ui { + server registry-ui:80; + } + + upstream registry_api { + server registry:5000; + } + + server { + listen 443 ssl; + server_name YOUR_CI_CD_IP; + + ssl_certificate /etc/nginx/ssl/registry.crt; + ssl_certificate_key /etc/nginx/ssl/registry.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + # Proxy registry API requests + location /v2/ { + proxy_pass https://registry_api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + proxy_ssl_verify off; + } + + # Proxy registry UI requests + location / { + proxy_pass http://registry_ui; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + } +} \ No newline at end of file