diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b27f04d..e4eb74e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,88 +11,45 @@ on: - master workflow_dispatch: -permissions: - contents: read - security-events: write +permissions: {} jobs: - lint: - name: Lint Dockerfile - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Run hadolint - uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0 - with: - dockerfile: Dockerfile - ignore: DL3018 - - build: - name: Build Docker Image - runs-on: ubuntu-latest - needs: lint - permissions: - contents: read - steps: - - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - - name: Build Docker image - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - context: . - push: false - tags: ghcr.io/netresearch/docker-ftp-server:test - cache-from: type=gha - cache-to: type=gha,mode=max - - security-scan: - name: Security Scan - runs-on: ubuntu-latest - needs: build + # Gold-standard container CI via the org meta-reusable: Dockerfile lint + # (+ shellcheck), YAML/compose lint, validation-only image build with + # Trivy, secret scanning and dependency review — one call site. + # References @feat/docker-gold-standard until netresearch/.github#251 + # merges; flip to @main afterwards. + docker-ci: + uses: netresearch/.github/.github/workflows/docker-image-ci.yml@main permissions: contents: read + packages: write security-events: write - steps: - - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - - name: Build Docker image - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - context: . - load: true - tags: ghcr.io/netresearch/docker-ftp-server:test - cache-from: type=gha - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 - with: - image-ref: ghcr.io/netresearch/docker-ftp-server:test - format: 'sarif' - output: 'trivy-results.sarif' - severity: 'CRITICAL,HIGH' - - - name: Upload Trivy results to GitHub Security tab - uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 - if: always() - with: - sarif_file: 'trivy-results.sarif' - + id-token: write + attestations: write + actions: read + pull-requests: write + with: + image-name: docker-ftp-server + dockerfile: ./Dockerfile + lint-container: true + shell-scandirs: setup + lint-yaml: true + lint-markdown: false + lint-compose: true + compose-files: docker-compose.yml + enable-gitleaks: true + enable-codeql: false + enable-smoke-test: false + secrets: + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} + + # Bespoke runtime test — boots the image and asserts vsftpd listens on + # port 21. Not covered by the meta (its smoke-test uses + # container-structure-test), so it stays here. test: name: Test FTP Server runs-on: ubuntu-latest - needs: build permissions: contents: read steps: diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..99f5e53 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,6 @@ +--- +# Carries forward the DL3018 ignore the previous CI set inline +# (`ignore: DL3018`). Alpine `apk add` version pinning is impractical for +# this image, so DL3018 (pin package versions) stays suppressed. +ignored: + - DL3018 diff --git a/setup/docker-entrypoint.sh b/setup/docker-entrypoint.sh index f11f41c..5a879d5 100755 --- a/setup/docker-entrypoint.sh +++ b/setup/docker-entrypoint.sh @@ -3,25 +3,26 @@ set -e # Add Users and Home directories +# shellcheck disable=SC2206 # intentional split of ;-separated USERS into an array arrUSERS=(${USERS//;/ }) for userAndPass in "${arrUSERS[@]}" do username=${userAndPass%%:*} # Check if user exists and create if not - userInfo="$(id ${username} || echo 'no such user')" + userInfo="$(id "${username}" || echo 'no such user')" if [ "${userInfo}" == "no such user" ]; then - adduser -h /home/./${username} -s /bin/false -D ${username} + adduser -h "/home/./${username}" -s /bin/false -D "${username}" echo "Create user ${username}" fi echo "${userAndPass}" | /usr/sbin/chpasswd - if [ -z ${GROUP} ]; then + if [ -z "${GROUP}" ]; then GROUP=${username} fi - chown ${username}:${GROUP} /home/${username}/ -R - chmod 2700 /home/${username} - find /home/${username}/* -type d -exec chmod 2775 {} \; || : - find /home/${username}/* -type f -exec chmod 0664 {} \; || : + chown "${username}:${GROUP}" "/home/${username}/" -R + chmod 2700 "/home/${username}" + find "/home/${username}"/* -type d -exec chmod 2775 {} \; || : + find "/home/${username}"/* -type f -exec chmod 0664 {} \; || : done # Fix vsftpd.conf permissions @@ -30,6 +31,7 @@ chmod 664 /etc/vsftpd/vsftpd.conf if [ "${ENABLE_LOGGING}" == "yes" ]; then +# shellcheck disable=SC2129 # sequential appends to vsftpd.conf are clearer than a grouped block echo "xferlog_enable=YES" >> /etc/vsftpd/vsftpd.conf echo "xferlog_std_format=NO" >> /etc/vsftpd/vsftpd.conf echo "log_ftp_protocol=YES" >> /etc/vsftpd/vsftpd.conf @@ -40,15 +42,15 @@ if [ "${ENABLE_PASSIVE_MODE}" == "yes" ]; then echo "port_enable=YES" >> /etc/vsftpd/vsftpd.conf fi -if [ ! -z ${PASSIVE_MODE_MIN_PORT} ]; then +if [ -n "${PASSIVE_MODE_MIN_PORT}" ]; then echo "pasv_min_port=${PASSIVE_MODE_MIN_PORT}" >> /etc/vsftpd/vsftpd.conf fi -if [ ! -z ${PASSIVE_MODE_MAX_PORT} ]; then +if [ -n "${PASSIVE_MODE_MAX_PORT}" ]; then echo "pasv_max_port=${PASSIVE_MODE_MAX_PORT}" >> /etc/vsftpd/vsftpd.conf fi -if [ ! -z ${PASSIVE_MODE_ADDRESS} ]; then +if [ -n "${PASSIVE_MODE_ADDRESS}" ]; then echo "pasv_address=${PASSIVE_MODE_ADDRESS}" >> /etc/vsftpd/vsftpd.conf fi