gimme-that-iso is a command-line tool that securely downloads Linux ISOs and other large files, verifying their integrity using GPG signatures and checksums. It is designed to be run as a standalone binary, in a Docker container, or as a CronJob in a Kubernetes cluster.
- Declarative Downloads: Configure all your ISO downloads in a single
isos.jsonfile. - Concurrent Downloads: Downloads multiple ISOs at the same time for improved performance.
- Secure: Verifies downloads using both GPG signatures and checksums (SHA512/SHA256).
- Flexible Verification: Supports multiple verification schemes:
- Signed Checksums: Standard flow (Debian, Ubuntu) where the checksum file is signed.
- Signed ISOs: Direct ISO signature verification (Alpine).
- Embedded Signatures: Parsing cleartext-signed checksum files (Fedora).
- Memory Efficient: Downloads files by streaming them to disk, not loading them into memory.
- Pipeline-Friendly Logging: Provides clean, timestamped, and tagged log output for every step (progress, verification), making it ideal for automation and easy to parse.
- Container Ready: Includes a multi-stage
Dockerfilefor building a minimal, secure image. - Kubernetes Native: Comes with a
cronjob.yamlmanifest for automated, scheduled execution in Kubernetes.
The application is configured using an isos.json file. You can add multiple files to be downloaded concurrently.
The verification_type field tells the downloader how to verify the file. If omitted, the tool attempts to guess based on the URL extensions.
| Type | Description | Example Distros |
|---|---|---|
checksum_signed |
Default. The checksum file is signed by a detached signature. | Debian, Ubuntu, Proxmox |
iso_signed |
The ISO file itself has a detached signature. | Alpine Linux |
checksum_embedded |
The checksum file is a cleartext-signed message containing the checksums. | Fedora |
isos.json example:
{
"isos": [
{
"name": "Debian Netinstall",
"url": "https://cdimage.debian.org/cdimage/release/current/amd64/iso-cd/debian-13.2.0-amd64-netinst.iso",
"signature_url": "https://cdimage.debian.org/cdimage/release/current/amd64/iso-cd/SHA512SUMS.sign",
"checksum_file_url": "https://cdimage.debian.org/cdimage/release/current/amd64/iso-cd/SHA512SUMS",
"gpg_key_url": "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xDA87E80D6294BE9B",
"verification_type": "checksum_signed"
},
{
"name": "Alpine Standard",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-standard-3.20.0-x86_64.iso",
"signature_url": "https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-standard-3.20.0-x86_64.iso.asc",
"checksum_file_url": "https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-standard-3.20.0-x86_64.iso.sha256",
"gpg_key_url": "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x293ACD0907D9495A",
"verification_type": "iso_signed"
},
{
"name": "Fedora Workstation",
"url": "https://ftp.uni-stuttgart.de/fedora/releases/43/Workstation/x86_64/iso/Fedora-Workstation-Live-43-1.6.x86_64.iso",
"checksum_file_url": "https://ftp.uni-stuttgart.de/fedora/releases/43/Workstation/x86_64/iso/Fedora-Workstation-43-1.6-x86_64-CHECKSUM",
"gpg_key_url": "https://fedoraproject.org/fedora.gpg",
"verification_type": "checksum_embedded"
}
]
}-
Build:
go build -o gimme-that-iso ./cmd/gimme-that-iso
-
Run: You can specify the download directory and the number of concurrent workers.
./gimme-that-iso --download-dir /path/to/your/downloads --workers 2
Example Output:
2025/12/14 00:08:05 gimme-that-iso starting... 2025/12/14 00:08:05 Configuration loaded successfully. Starting up to 2 workers... 2025/12/14 00:08:05 [Worker 2] [Debian Netinstall] Processing job. 2025/12/14 00:08:05 [Worker 1] [Alpine Standard] Processing job. 2025/12/14 00:08:07 [Worker 1] [Alpine Standard] Progress: 58% (121.12 MB / 209.00 MB) @ 61.94 MB/s, ETA: 0m1s 2025/12/14 00:08:07 [Worker 2] [Debian Netinstall] Progress: 13% (103.66 MB / 784.00 MB) @ 69.89 MB/s, ETA: 0m9s ... 2025/12/14 00:08:11 [Worker 1] [Alpine Standard] Successfully verified. ... 2025/12/14 00:08:20 [Worker 2] [Debian Netinstall] Successfully verified. 2025/12/14 00:08:20 All tasks completed.
-
Build the image:
docker build -t your-registry/gimme-that-iso:latest . -
Run the container: Mount a local directory for configuration and another for the downloads.
docker run --rm \ -v $(pwd)/isos.json:/app/isos.json \ -v $(pwd)/downloads:/downloads \ your-registry/gimme-that-iso:latest \ --download-dir=/downloads --workers=4
The cronjob.yaml manifest provides a template for running gimme-that-iso as a scheduled job in Kubernetes.
-
Prerequisites:
- A running Kubernetes cluster.
- A
PersistentVolumeavailable to satisfy thePersistentVolumeClaimfor storage. This could be backed by NFS, an SMB share (using a suitable CSI driver), or any other storage class.
-
Update the Image and Args: Before applying the manifest, you must update the
imagefield incronjob.yaml. You can also adjust the number of workers.# in cronjob.yaml ... containers: - name: gimme-that-iso # IMPORTANT: Replace this with the actual image from your registry. image: your-registry/gimme-that-iso:latest imagePullPolicy: IfNotPresent args: - "--download-dir=/downloads" - "--workers=4" ...
-
Deploy: Apply the manifest to your cluster.
kubectl apply -f cronjob.yaml
This will create:
- A
ConfigMapnamedgimme-that-iso-configwith theisos.jsondata. - A
PersistentVolumeClaimnamedgimme-that-iso-downloadsrequesting 10Gi of storage. - A
CronJobnamedgimme-that-iso-cronjobscheduled to run daily at 2 AM.
- A