diff --git a/.github/workflows/lxde-publish.yml b/.github/workflows/lxde-publish.yml new file mode 100644 index 0000000..4a52f44 --- /dev/null +++ b/.github/workflows/lxde-publish.yml @@ -0,0 +1,70 @@ +name: Build and Push Docker Image + +on: + push: + branches: [ main, master ] + tags: [ 'v*' ] + pull_request: + branches: [ main, master ] + workflow_dispatch: # Allow manual triggering + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + id: build-push + uses: docker/build-push-action@v5 + with: + context: . + file: lxde-debian.dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + outputs: type=docker,dest=/tmp/image.tar + + - name: Get image size + id: image-size + run: | + # Load the image from the build step output + docker load < /tmp/image.tar + # Get the image ID from the loaded image + IMAGE_ID=$(docker images --format "{{.ID}}" | head -n 1) + echo "Using image ID: $IMAGE_ID" + # Get the image size + SIZE=$(docker image inspect $IMAGE_ID --format='{{.Size}}') + SIZE_MB=$(echo "scale=2; $SIZE/1024/1024" | bc) + echo "size=$SIZE_MB MB" >> $GITHUB_OUTPUT + echo "Image size: $SIZE_MB MB" \ No newline at end of file diff --git a/.github/workflows/lxde-security-scan.yml b/.github/workflows/lxde-security-scan.yml new file mode 100644 index 0000000..6ccafa4 --- /dev/null +++ b/.github/workflows/lxde-security-scan.yml @@ -0,0 +1,140 @@ +name: Docker Security Scan + +on: + pull_request: + branches: [ main, master ] + +jobs: + security-scan: + name: Docker Build and Security Scan + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: lxde-debian.dockerfile + push: false + load: true + tags: test-image:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Get image size + id: image-size + run: | + SIZE=$(docker image inspect test-image:${{ github.sha }} --format='{{.Size}}') + SIZE_MB=$(echo "scale=2; $SIZE/1024/1024" | bc) + echo "size=$SIZE_MB MB" >> $GITHUB_OUTPUT + + - name: Check image health + id: image-health + run: | + if docker run --rm --entrypoint sh test-image:${{ github.sha }} -c "exit 0"; then + echo "status=✅ Image is healthy" >> $GITHUB_OUTPUT + else + echo "status=❌ Image health check failed" >> $GITHUB_OUTPUT + fi + + - name: Run Trivy vulnerability scanner + id: trivy-scan + uses: aquasecurity/trivy-action@master + with: + image-ref: test-image:${{ github.sha }} + format: 'table' + output: 'trivy-results.txt' + severity: 'CRITICAL,HIGH' + + - name: Generate Trivy SARIF output + uses: aquasecurity/trivy-action@master + with: + image-ref: test-image:${{ github.sha }} + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH,MEDIUM' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: 'trivy-results.sarif' + + - name: Count vulnerabilities + id: count-vulns + run: | + CRITICAL=$(grep -c "CRITICAL" trivy-results.txt || echo 0) + HIGH=$(grep -c "HIGH" trivy-results.txt || echo 0) + MEDIUM=$(grep -c "MEDIUM" trivy-results.txt || echo 0) + echo "critical=$CRITICAL" >> $GITHUB_OUTPUT + echo "high=$HIGH" >> $GITHUB_OUTPUT + echo "medium=$MEDIUM" >> $GITHUB_OUTPUT + + - name: Create PR comment + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + let trivyOutput = ''; + try { + const trivyFile = fs.readFileSync('trivy-results.txt', 'utf8'); + trivyOutput = '```\n' + trivyFile.substring(0, 10000) + '\n```'; + if (trivyFile.length > 10000) { + trivyOutput += '\n... (output truncated)'; + } + } catch (error) { + trivyOutput = 'Error reading vulnerability scan results.'; + } + + const imageSize = process.env.IMAGE_SIZE; + const imageHealth = process.env.IMAGE_HEALTH; + const criticalCount = process.env.CRITICAL_COUNT; + const highCount = process.env.HIGH_COUNT; + const mediumCount = process.env.MEDIUM_COUNT; + + const securityStatus = criticalCount > 0 || highCount > 0 + ? '❌ Security issues found' + : '✅ No critical/high vulnerabilities'; + + const body = `## Docker Image Analysis + + ### Image Status + - ${imageHealth} + - Image Size: ${imageSize} + - Security Status: ${securityStatus} + + ### Vulnerability Summary + - Critical: ${criticalCount} + - High: ${highCount} + - Medium: ${mediumCount} + +
+ View Full Security Scan Results + + ${trivyOutput} +
+ `; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: body + }); + env: + IMAGE_SIZE: ${{ steps.image-size.outputs.size }} + IMAGE_HEALTH: ${{ steps.image-health.outputs.status }} + CRITICAL_COUNT: ${{ steps.count-vulns.outputs.critical }} + HIGH_COUNT: ${{ steps.count-vulns.outputs.high }} + MEDIUM_COUNT: ${{ steps.count-vulns.outputs.medium }} diff --git a/README.md b/README.md index f3bba3c..ac6b9b8 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ docker run -d -p 3389:3389 \ - `debian.dockerfile` - Docker build instructions. - `supervisord.conf` - Supervisor configuration. - `conf.d/` - Supervisor program configs. -- `browser_conf/` - Browser supervisor configs. - `base_entrypoint.sh` - Main entrypoint script. - `customizable_entrypoint.sh` - Entrypoint for custom startup logic. - `custom_entrypoints_scripts/` - Place your custom scripts here. diff --git a/agent b/agent index b4e3a98..c8f7861 100755 Binary files a/agent and b/agent differ diff --git a/browser_conf/chromium.conf b/browser_conf/chromium.conf deleted file mode 100644 index e82c1eb..0000000 --- a/browser_conf/chromium.conf +++ /dev/null @@ -1,6 +0,0 @@ -[program:chromium] -command=/usr/bin/chromium --no-sandbox --disable-dev-shm-usage %(ENV_STARTING_WEBSITE_URL)s -autostart=%(ENV_AUTO_START_BROWSER)s -autorestart=true -stdout_logfile=/var/log/chromium.log -stderr_logfile=/var/log/chromium.err.log diff --git a/browser_conf/firefox.conf b/browser_conf/firefox.conf deleted file mode 100644 index 564f861..0000000 --- a/browser_conf/firefox.conf +++ /dev/null @@ -1,6 +0,0 @@ -[program:firefox] -command=/usr/bin/firefox %(ENV_STARTING_WEBSITE_URL)s -autostart=%(ENV_AUTO_START_BROWSER)s -autorestart=true -stdout_logfile=/var/log/firefox.log -stderr_logfile=/var/log/firefox.err.log diff --git a/debian.dockerfile b/lxde-debian.dockerfile similarity index 51% rename from debian.dockerfile rename to lxde-debian.dockerfile index 871f0dc..ea021bc 100644 --- a/debian.dockerfile +++ b/lxde-debian.dockerfile @@ -37,57 +37,88 @@ RUN set -e; \ apt update && \ apt full-upgrade -qqy && \ apt install -qqy \ - tini \ - supervisor \ - bash \ - xrdp \ - fluxbox \ - xterm \ - wget \ - nano \ - eog \ - xdg-utils \ - okular \ - file-roller \ - fuse \ - libfuse2 \ - libxkbcommon-x11-0 \ - # start - libxcb-icccm4 \ - libxcb-image0 \ - libxcb-keysyms1 \ - libxcb-render-util0 \ - libxcb-xinerama0 \ - libxcb-xkb1 \ - libxcb-randr0 \ - libxcb-shape0 \ - libglib2.0-0 \ - libasound2 \ - ca-certificates && \ + tini \ + util-linux \ + supervisor \ + bash \ + xrdp \ + lxde-core \ + lxsession \ + lxde-common \ + openbox \ + file-roller \ + xterm \ + shotwell \ + okular \ + vlc \ + mousepad \ + wget \ + nano \ + fuse \ + libfuse2 \ + libxkbcommon-x11-0 \ + # start + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-render-util0 \ + libxcb-xinerama0 \ + libxcb-xkb1 \ + libxcb-randr0 \ + libxcb-shape0 \ + libglib2.0-0 \ + libasound2 \ + ca-certificates \ + dbus-x11 && \ # User setup remains the same useradd -m -s /bin/bash "${XRDP_USER}" && \ echo "${XRDP_USER}:${XRDP_PASSWORD}" | chpasswd && \ adduser ${XRDP_USER} fuse && \ chmod u+s /bin/fusermount && \ - # Create .xsession file with agent run at the start + # Create LXDE configuration to disable bottom panel + mkdir -p /home/${XRDP_USER}/.config/lxpanel/LXDE/panels && \ + # Create a simple .xsession with proper LXDE startup echo '#!/bin/sh' > /home/${XRDP_USER}/.xsession && \ - echo ' sleep 2' >> /home/${XRDP_USER}/.xsession && \ + echo '# Start D-Bus if not running' >> /home/${XRDP_USER}/.xsession && \ + echo 'if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then' >> /home/${XRDP_USER}/.xsession && \ + echo ' eval $(dbus-launch --sh-syntax --exit-with-session)' >> /home/${XRDP_USER}/.xsession && \ echo 'fi' >> /home/${XRDP_USER}/.xsession && \ - echo 'fluxbox &' >> /home/${XRDP_USER}/.xsession && \ - echo 'sleep 1' >> /home/${XRDP_USER}/.xsession && \ - echo 'sleep 2' >> /home/${XRDP_USER}/.xsession && \ - echo '/opt/onlyoffice/squashfs-root/AppRun "/home/${XRDP_USER}/Documents/demo.docx" &' >> /home/${XRDP_USER}/.xsession && \ - echo 'sleep 2' >> /home/${XRDP_USER}/.xsession && \ + echo '/usr/local/bin/agent &' >> /home/${XRDP_USER}/.xsession && \ + echo 'exec startlxde' >> /home/${XRDP_USER}/.xsession && \ + # Disable bottom panel by creating empty panel config + echo "# Empty panel configuration" > /home/${XRDP_USER}/.config/lxpanel/LXDE/panels/panel && \ + # Ensure proper permissions + chown -R ${XRDP_USER}:${XRDP_USER} /home/${XRDP_USER}/.config && \ chown ${XRDP_USER}:${XRDP_USER} /home/${XRDP_USER}/.xsession && \ chmod +x /home/${XRDP_USER}/.xsession && \ + # Only remove truly unnecessary LXDE components + apt-get remove -y lxappearance lxinput lxrandr lxsession-edit && \ + # Disable the LXDE taskbar by modifying the autostart file + # set wallpaper + echo "pcmanfm --set-wallpaper /home/${XRDP_USER}/Pictures/wallpaper.jpg" >> /home/${XRDP_USER}/.xsession && \ + mkdir -p /etc/xdg/lxsession/LXDE && \ + if [ -f /etc/xdg/lxsession/LXDE/autostart ]; then \ + sed -i 's/@lxpanel --profile LXDE/#@lxpanel --profile LXDE/' /etc/xdg/lxsession/LXDE/autostart; \ + sed -i 's/@pcmanfm --desktop --profile LXDE/#@pcmanfm --desktop --profile LXDE/' /etc/xdg/lxsession/LXDE/autostart; \ + else \ + echo "@lxsession" > /etc/xdg/lxsession/LXDE/autostart && \ + echo "#@lxpanel --profile LXDE" >> /etc/xdg/lxsession/LXDE/autostart && \ + echo "@pcmanfm --desktop --profile LXDE" >> /etc/xdg/lxsession/LXDE/autostart && \ + echo "@xscreensaver -no-splash" >> /etc/xdg/lxsession/LXDE/autostart; \ + fi && \ apt autoremove --purge -y && \ apt clean && \ rm -rf /var/lib/apt/lists/* +RUN apt --purge remove xterm -y && \ + apt --purge remove --auto-remove -y && \ + apt clean && \ + rm -rf /var/lib/apt/lists/* + + # Download sample .docx file to view in onlyoffice RUN mkdir -p /home/${XRDP_USER}/Documents && \ - wget https://calibre-ebook.com/downloads/demos/demo.docx -O /home/${XRDP_USER}/Documents/demo.docx && \ chown -R ${XRDP_USER}:${XRDP_USER} /home/${XRDP_USER}/Documents && \ chmod -R 755 /home/${XRDP_USER}/Documents @@ -107,13 +138,13 @@ RUN wget https://github.com/ONLYOFFICE/appimage-desktopeditors/releases/download chmod +x /opt/onlyoffice/squashfs-root/AppRun && \ chmod +x /usr/local/bin/agent && \ # Don't use exec for this command, so it won't terminate the session - # Add OnlyOffice to start after Chromium, but without exec - echo ' /usr/local/bin/agent &' >> /home/${XRDP_USER}/.xsession && \ - echo ' sleep 2' >> /home/${XRDP_USER}/.xsession && \ + sed -i 's|exec /usr/bin/chromium|/usr/bin/chromium|' /home/${XRDP_USER}/.xsession && \ + # Add OnlyOffice to start after Chromium, but without exect as root separately echo '/opt/onlyoffice/squashfs-root/AppRun &' >> /home/${XRDP_USER}/.xsession && \ - rm -rf /usr/local/bin/onlyoffice.AppImage - + # Remove the tmux-based agent execution line - we'll run it as root separately + rm -rf /usr/local/bin/onlyoffice.AppImagetion +# No longer need tmux, removing that installationCUSTOM_ENTRYPOINTS_DIR} # Create necessary directories for supervisor and custom entrypoints RUN mkdir -p /etc/supervisor.d /app/conf.d ${DEF_CUSTOM_ENTRYPOINTS_DIR} RUN mkdir -p /var/log/supervisor @@ -123,13 +154,19 @@ COPY supervisord.conf /etc/supervisor.d/supervisord.conf # only bring in xrdp (and xterm) programs, drop VNC and browser configs COPY conf.d/xrdp.conf conf.d/xterm.conf /app/conf.d/ COPY base_entrypoint.sh customizable_entrypoint.sh /usr/local/bin/ +# Copy agent binary into the image (adjust source path as needed)COPY agent /usr/local/bin/agent +COPY agent /usr/local/bin/agent +RUN chmod +x /usr/local/bin/agent + +COPY wallpaper.jpg /home/${XRDP_USER}/Pictures/wallpaper.jpg + -# Make the entrypoint scripts executable +# Make the entrypoint scripts executableRUN chmod +x /usr/local/bin/base_entrypoint.sh /usr/local/bin/customizable_entrypoint.sh RUN chmod +x /usr/local/bin/base_entrypoint.sh /usr/local/bin/customizable_entrypoint.sh + # Expose the XRDP port EXPOSE ${XRDP_PORT} - -# Set tini as the entrypoint and the custom script as the command +# Set tini as the entrypoint and the custom script as the commandENTRYPOINT ["/usr/bin/tini", "--"] ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["/usr/local/bin/customizable_entrypoint.sh"] \ No newline at end of file diff --git a/wallpaper.jpg b/wallpaper.jpg new file mode 100644 index 0000000..e78ed0b Binary files /dev/null and b/wallpaper.jpg differ