Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions .github/workflows/citrix-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# SPDX-FileCopyrightText: 2025 Digg - Agency for Digital Government
#
# SPDX-License-Identifier: CC0-1.0
name: Citrix Update Check
on: # yamllint disable-line rule:truthy
schedule:
- cron: '0 8 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
check-citrix:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Fetch Citrix release info
id: citrix
run: |
page=$(curl -sL "https://www.citrix.com/downloads/workspace-app/linux/workspace-app-for-linux-latest.html")

version=$(printf '%s' "$page" | grep -oP 'icaclient_\K[0-9.]+(?=_amd64\.deb)' | head -n 1)
raw_date=$(printf '%s' "$page" | grep -oP 'Release Date: \K[A-Za-z]{3} [0-9]{1,2}, [0-9]{4}' | head -n 1)

if [[ -z "$version" || -z "$raw_date" ]]; then
echo "Could not retrieve Citrix version or release date" >&2
exit 1
fi

release_date=$(LC_TIME=C date -d "$raw_date" +"%Y-%m-%d")
release_epoch=$(LC_TIME=C date -d "$release_date" +%s)
cutoff_epoch=$(LC_TIME=C date -d "-7 days" +%s)

echo "version=$version" >> "$GITHUB_OUTPUT"
echo "release_date=$release_date" >> "$GITHUB_OUTPUT"

if [[ "$release_epoch" -le "$cutoff_epoch" ]]; then
echo "old_enough=true" >> "$GITHUB_OUTPUT"
else
echo "old_enough=false" >> "$GITHUB_OUTPUT"
echo "Release $version ($release_date) is less than 7 days old — skipping"
fi

- name: Check pinned version
if: steps.citrix.outputs.old_enough == 'true'
id: pinned
run: |
current=$(grep -oP 'set -g __citrix_version "\K[^"]+' dot/.config/fish/functions/devbase-citrix.fish)
echo "version=$current" >> "$GITHUB_OUTPUT"

- name: Open PR if version changed
if: >
steps.citrix.outputs.old_enough == 'true' &&
steps.citrix.outputs.version != steps.pinned.outputs.version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.citrix.outputs.version }}
RELEASE_DATE: ${{ steps.citrix.outputs.release_date }}
run: |
branch="citrix-update/${NEW_VERSION}"

if gh pr list --head "$branch" --json number --jq '.[0].number' | grep -q .; then
echo "PR for $NEW_VERSION already exists — skipping"
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$branch"

sed -i "s/set -g __citrix_version \"[^\"]*\"/set -g __citrix_version \"${NEW_VERSION}\"/" \
dot/.config/fish/functions/devbase-citrix.fish

git add dot/.config/fish/functions/devbase-citrix.fish
git commit -m "build(deps): update citrix-workspace to ${NEW_VERSION}"
git push origin "$branch"

gh pr create \
--title "build(deps): update citrix-workspace to ${NEW_VERSION}" \
--body "Citrix Workspace App ${NEW_VERSION} released on ${RELEASE_DATE} (>= 7 days old)."
89 changes: 89 additions & 0 deletions .github/workflows/oc-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# SPDX-FileCopyrightText: 2025 Digg - Agency for Digital Government
#
# SPDX-License-Identifier: CC0-1.0
name: OpenShift OC Update Check
on: # yamllint disable-line rule:truthy
schedule:
- cron: '0 8 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
check-oc:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Fetch OC release info
id: oc
run: |
latest=$(curl -s "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/" \
| grep -oP 'href="\K[0-9]+\.[0-9]+\.[0-9]+(?=/)' \
| sort -V \
| tail -n 1)

if [[ -z "$latest" ]]; then
echo "Could not determine latest OC version" >&2
exit 1
fi

created=$(curl -s "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/${latest}/release.txt" \
| grep -oP 'Created:\s+\K\S+')

if [[ -z "$created" ]]; then
echo "Could not read release date from release.txt for ${latest}" >&2
exit 1
fi

release_epoch=$(date -d "$created" +%s)
cutoff_epoch=$(date -d "-7 days" +%s)
release_date=$(date -d "$created" +"%Y-%m-%d")

echo "version=$latest" >> "$GITHUB_OUTPUT"
echo "release_date=$release_date" >> "$GITHUB_OUTPUT"

if [[ "$release_epoch" -le "$cutoff_epoch" ]]; then
echo "old_enough=true" >> "$GITHUB_OUTPUT"
else
echo "old_enough=false" >> "$GITHUB_OUTPUT"
echo "Release ${latest} (${release_date}) is less than 7 days old — skipping"
fi

- name: Check pinned version
if: steps.oc.outputs.old_enough == 'true'
id: pinned
run: |
current=$(grep -oP 'oc: \{version: "\K[^"]+' dot/.config/devbase/packages.yaml)
echo "version=$current" >> "$GITHUB_OUTPUT"

- name: Open PR if version changed
if: >
steps.oc.outputs.old_enough == 'true' &&
steps.oc.outputs.version != steps.pinned.outputs.version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.oc.outputs.version }}
RELEASE_DATE: ${{ steps.oc.outputs.release_date }}
run: |
branch="oc-update/${NEW_VERSION}"

if gh pr list --head "$branch" --json number --jq '.[0].number' | grep -q .; then
echo "PR for ${NEW_VERSION} already exists — skipping"
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$branch"

sed -i "s/\(oc: {version: \"\)[^\"]*\"/\1${NEW_VERSION}\"/" \
dot/.config/devbase/packages.yaml

git add dot/.config/devbase/packages.yaml
git commit -m "build(deps): update openshift-oc to ${NEW_VERSION}"
git push origin "$branch"

gh pr create \
--title "build(deps): update openshift-oc to ${NEW_VERSION}" \
--body "OpenShift OC ${NEW_VERSION} released on ${RELEASE_DATE} (>= 7 days old)."
164 changes: 104 additions & 60 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,38 @@ SPDX-License-Identifier: CC0-1.0

## Security Overview Matrix

| Category | Feature | Status | Implementation | Details |
|----------|---------|--------|----------------|---------|
| **Audit & Monitoring** | Security tools | ✅ 8 tools | Gitleaks, Lynis, Syft, etc. | Pre-installed |
| | CI security scans | ✅ Enabled | Every PR | Gitleaks, shellcheck |
| | Git audit trail | ✅ Enabled | All config changes | PR approval required |
| **Automatic Updates** | APT security patches | ✅ Enabled | unattended-upgrades | Ubuntu/Debian security repos |
| | Development tools | ✅ Enabled | Renovate Bot (84 tools) | Weekly schedule, 7-day wait |
| | Virus definitions | ✅ Enabled | ClamAV freshclam | Continuous updates |
| **Code Safety** | Bash safety flags | ✅ 21/21 scripts | `set -uo pipefail` | Fail on errors |
| | Input validation | ✅ Enabled | Whitelisted inputs | No arbitrary execution |
| | Dangerous patterns | ✅ None found | `curl\|sh`, `rm -rf /`, `chmod 777` | Zero matches |
| **Credentials** | No hardcoded secrets | ✅ Verified | Gitleaks scans | Zero matches |
| | Password policy | ✅ Enabled | 12-char minimum (NIST) | SSH passphrase validation |
| | Secure permissions | ✅ Enabled | SSH 600/700, configs 600 | Enforced at creation |
| **Cryptography** | Secure SSH | ✅ Enabled | ED25519, ChaCha20-Poly1305 | Default configuration |
| | SSH key type | ✅ Strong | ED25519 (256-bit) | Modern standard |
| **Temp Files** | Temp cleanup | ✅ Enabled | devbase temp directories cleaned | Respects TMPDIR/XDG_RUNTIME_DIR |
| **Malware Protection** | ClamAV scanning | ✅ Enabled | Daily full system scan | Low-priority, scheduled |
| | Virus definitions | ✅ Auto-update | freshclam service | Continuous updates |
| **Network** | Firewall (UFW) | ✅ Enabled | Default on Linux | K3s rules auto-configured |
| | HTTPS-only downloads | ✅ Enforced | All package sources | Zero HTTP downloads |
| | Connection timeouts | ✅ Enabled | 30s connect, 90s max | Prevents hanging |
| | Proxy support | ✅ Available | HTTP_PROXY, HTTPS_PROXY | Corporate proxy ready |
| **Privileges** | User-level install | ✅ Default | No root for tools | Only sudo for system packages |
| | Minimal sudo | ✅ 79 operations | apt/dpkg/snap/systemctl | Documented usage |
| **Supply Chain** | Version pinning | ✅ Enabled | 84 tools in YAML/TOML | All versions locked, tracked in git |
| | Checksums | ✅ Enabled | Mise, OpenShift CLI, IntelliJ, VS Code | SHA256 verification |
| | Renovate updates | ✅ Enabled | 7-day stability period | Auto-PRs with CI validation |
| | Aqua SLSA attestation | ✅ Enabled | 50+ mise tools | Supply chain verification |
| **System Hardening** | File descriptors | ✅ 65,536 | /etc/security/limits.d/ | Build-optimized |
| | Process limits | ✅ 32,768 | PAM limits | Parallel builds |
| | Memory locking | ✅ Unlimited | Container support | VM/container ready |
| Category | Feature | Status | Implementation | Details |
|------------------------|-----------------------|-----------------|----------------------------------------|-------------------------------------|
| **Audit & Monitoring** | Security tools | ✅ 8 tools | Gitleaks, Lynis, Syft, etc. | Pre-installed |
| | CI security scans | ✅ Enabled | Every PR | Gitleaks, shellcheck |
| | Git audit trail | ✅ Enabled | All config changes | PR approval required |
| **Automatic Updates** | APT security patches | ✅ Enabled | unattended-upgrades | Ubuntu/Debian security repos |
| | Development tools | ✅ Enabled | Renovate Bot (84 tools) | Weekly schedule, 7-day wait |
| | Virus definitions | ✅ Enabled | ClamAV freshclam | Continuous updates |
| **Code Safety** | Bash safety flags | ✅ 21/21 scripts | `set -uo pipefail` | Fail on errors |
| | Input validation | ✅ Enabled | Whitelisted inputs | No arbitrary execution |
| | Dangerous patterns | ✅ None found | `curl\|sh`, `rm -rf /`, `chmod 777` | Zero matches |
| **Credentials** | No hardcoded secrets | ✅ Verified | Gitleaks scans | Zero matches |
| | Password policy | ✅ Enabled | 12-char minimum (NIST) | SSH passphrase validation |
| | Secure permissions | ✅ Enabled | SSH 600/700, configs 600 | Enforced at creation |
| **Cryptography** | Secure SSH | ✅ Enabled | ED25519, ChaCha20-Poly1305 | Default configuration |
| | SSH key type | ✅ Strong | ED25519 (256-bit) | Modern standard |
| **Temp Files** | Temp cleanup | ✅ Enabled | devbase temp directories cleaned | Respects TMPDIR/XDG_RUNTIME_DIR |
| **Malware Protection** | ClamAV scanning | ✅ Enabled | Daily full system scan | Low-priority, scheduled |
| | Virus definitions | ✅ Auto-update | freshclam service | Continuous updates |
| **Network** | Firewall (UFW) | ✅ Enabled | Default on Linux | K3s rules auto-configured |
| | HTTPS-only downloads | ✅ Enforced | All package sources | Zero HTTP downloads |
| | Connection timeouts | ✅ Enabled | 30s connect, 90s max | Prevents hanging |
| | Proxy support | ✅ Available | HTTP_PROXY, HTTPS_PROXY | Corporate proxy ready |
| **Privileges** | User-level install | ✅ Default | No root for tools | Only sudo for system packages |
| | Minimal sudo | ✅ 79 operations | apt/dpkg/snap/systemctl | Documented usage |
| **Supply Chain** | Version pinning | ✅ Enabled | 84 tools in YAML/TOML | All versions locked, tracked in git |
| | Checksums | ✅ Enabled | Mise, OpenShift CLI, IntelliJ, VS Code | SHA256 verification |
| | Renovate updates | ✅ Enabled | 7-day stability period | Auto-PRs with CI validation |
| | Aqua SLSA attestation | ✅ Enabled | 50+ mise tools | Supply chain verification |
| **System Hardening** | File descriptors | ✅ 65,536 | /etc/security/limits.d/ | Build-optimized |
| | Process limits | ✅ 32,768 | PAM limits | Parallel builds |
| | Memory locking | ✅ Unlimited | Container support | VM/container ready |

**Legend**: ✅ Implemented, ⚠️ Known Issue, ❌ Not Implemented

Expand Down Expand Up @@ -72,19 +72,18 @@ SPDX-License-Identifier: CC0-1.0
#### Git Clones

- Version-pinned: `git clone --branch "4.4.5" ...`
- Commit hashes: `git checkout "803bc18"`

## Downloads & Checksums

| Tool | Checksum | Status |
|------|----------|--------|
| Mise | SHA256 vendor file | ✅ |
| OpenShift CLI | `sha256sum.txt` | ✅ |
| IntelliJ IDEA | `.sha256` file | ✅ |
| VS Code | Microsoft API | ✅ |
| JMC | None | HTTPS only |
| DBeaver | None | HTTPS + dpkg |
| KeyStore Explorer | None | HTTPS + dpkg |
| Tool | Checksum | Status |
|-------------------|--------------------|--------------|
| Mise | SHA256 vendor file | ✅ |
| OpenShift CLI | `sha256sum.txt` | ✅ |
| IntelliJ IDEA | `.sha256` file | ✅ |
| VS Code | Microsoft API | ✅ |
| JMC | None | HTTPS only |
| DBeaver | None | HTTPS + dpkg |
| KeyStore Explorer | None | HTTPS + dpkg |

## SSH Security

Expand Down Expand Up @@ -233,14 +232,14 @@ sudo ufw allow 8080/tcp comment 'custom app'

## File Permissions

| Resource | Permission |
|----------|-----------|
| SSH private keys (ED25519) | 600 |
| SSH public keys | 644 |
| SSH directory | 700 |
| SSH config | 600 |
| Passphrase temp files | 600 (deleted after use) |
| Cache directory | 700 |
| Resource | Permission |
|----------------------------|-------------------------|
| SSH private keys (ED25519) | 600 |
| SSH public keys | 644 |
| SSH directory | 700 |
| SSH config | 600 |
| Passphrase temp files | 600 (deleted after use) |
| Cache directory | 700 |

**Sudo Usage**: 79 operations total

Expand Down Expand Up @@ -485,6 +484,51 @@ sudo unattended-upgrade --dry-run --debug

**Configuration Location**: `renovate.json` (extends base config)

### Release Age Coverage per Datasource

`minimumReleaseAge: "7 days"` is set at the top level in `renovate.json` and applies to all datasources that expose a release timestamp. Some datasources cannot provide timestamps — those have explicit compensating controls.

#### Not tracked by Renovate

These package sources have no `# renovate:` annotations and are never touched by Renovate. They install whatever version is current when `setup.sh` runs.

| Source | Packages |
|-----------|-----------------------------|
| `snap` | chromium, ghostty, microk8s |
| `flatpak` | org.chromium.Chromium |

#### Tracked — `minimumReleaseAge` enforced

| Datasource | Used for |
|--------------------|------------------------------------------------|
| `github-releases` | Most mise tools (ripgrep, lazyvim, gh, k9s, …) |
| `gitlab-releases` | glab |
| `forgejo-releases` | gommitlint |
| `npm` | tree-sitter-cli, all VS Code extensions |
| `maven` | Maven |

#### Tracked — timestamp support uncertain (language runtimes)

These datasources fetch from each language's own versioning API. Whether Renovate surfaces a `releaseTimestamp` depends on the upstream API. In practice, language runtime releases go through months of public RC testing before appearing as stable, so the supply-chain risk is lower than for registry packages.

| Datasource | Used for |
|----------------------|----------------|
| `python-version` | Python |
| `node-version` | Node.js |
| `go-version` | Go |
| `ruby-version` | Ruby |
| `rust-version` | Rust |
| `java-version` | Java (Temurin) |
| `gradle-version` | Gradle |
| `jetbrains-releases` | IntelliJ IDEA |

#### Tracked — compensating control (run dailt)

| Datasource | Reason | Control |
|-----------------------|---------------------------------------------------------------|----------------|
| `custom.citrix` | Release date is in a `<span>` tag, unreachable by HTML parser | citrix-update |
| `custom.openshift-oc` | Mirror page returns empty `<time>` elements | oc-update |

## Network Security

### HTTPS Only
Expand Down Expand Up @@ -543,17 +587,17 @@ git config --global http.https://internal.com/.sslCAInfo /etc/ssl/certs/ca-certi

## Security Tools

| Tool | Purpose |
|------|---------|
| Gitleaks | Secret scanning |
| Syft | SBOM generation |
| Scorecard | Security scoring |
| Cosign | Container signing |
| Tool | Purpose |
|------------|------------------------|
| Gitleaks | Secret scanning |
| Syft | SBOM generation |
| Scorecard | Security scoring |
| Cosign | Container signing |
| Actionlint | GitHub Actions linting |
| Shellcheck | Shell linting |
| Hadolint | Dockerfile linting |
| Lynis | System auditing |
| ClamAV | Antivirus scanning |
| Shellcheck | Shell linting |
| Hadolint | Dockerfile linting |
| Lynis | System auditing |
| ClamAV | Antivirus scanning |

### ClamAV Antivirus

Expand Down
Loading
Loading